Skip to content

Commit 73f427f

Browse files
authored
Merge pull request #56 from xsnippet/aceeditor
Replace CodeMirror with AceEditor
2 parents 825bf1d + e89840e commit 73f427f

File tree

7 files changed

+200
-62
lines changed

7 files changed

+200
-62
lines changed

package-lock.json

Lines changed: 25 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727
}
2828
},
2929
"dependencies": {
30-
"codemirror": "^5.33.0",
30+
"brace": "^0.11.0",
3131
"immutable": "^3.8.2",
3232
"parse-link-header": "^1.0.1",
3333
"prop-types": "^15.6.0",
3434
"react": "^16.0.0",
35-
"react-codemirror2": "^3.0.7",
35+
"react-ace": "^5.9.0",
3636
"react-custom-scrollbars": "^4.2.1",
3737
"react-dom": "^16.0.0",
3838
"react-redux": "^5.0.6",

src/components/NewSnippet.jsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React from 'react';
22
import { connect } from 'react-redux';
3-
import { Controlled as CodeMirror } from 'react-codemirror2';
3+
import AceEditor from 'react-ace';
44
import Tags from 'react-tagging-input';
55

6-
import 'codemirror/lib/codemirror.css';
6+
import brace from 'brace';
7+
import 'brace/ext/modelist';
78

89
import Title from './common/Title';
910
import ListBoxWithSearch from './ListBoxWithSearch';
@@ -21,9 +22,11 @@ class NewSnippet extends React.Component {
2122
syntax: '', // eslint-disable-line react/no-unused-state
2223
};
2324
this.onKeyPress = (e) => {
24-
if (e.which === 13) { // keyCode for Enter button
25+
// e.target.nodeName !=== 'TEXTAREA' is an ugly hack to allow enter
26+
// to insert "newline" into code editor. We need to figure out
27+
// a better way to handle this.
28+
if (e.which === 13 && e.target.nodeName !== 'TEXTAREA') { // keyCode for Enter button
2529
e.preventDefault();
26-
e.stopPropagation();
2730
}
2831
};
2932
this.postSnippet = this.postSnippet.bind(this);
@@ -63,6 +66,13 @@ class NewSnippet extends React.Component {
6366
}
6467

6568
render() {
69+
const { modesByName } = brace.acequire('ace/ext/modelist');
70+
const mode = modesByName[this.state.syntax] || modesByName.text;
71+
const syntaxes = this.props.syntaxes.map(item => ({
72+
name: modesByName[item].caption,
73+
value: item,
74+
}));
75+
6676
return (
6777
[
6878
<Title title="New snippet" key="New Snippet Title" />,
@@ -92,19 +102,29 @@ class NewSnippet extends React.Component {
92102
/>
93103
</div>
94104
<div className="new-snippet-code">
95-
<CodeMirror
105+
<AceEditor
106+
mode={mode.name}
107+
width="100%"
108+
height="100%"
109+
focus
110+
setOptions={{
111+
showFoldWidgets: false,
112+
useWorker: false,
113+
maxLines: Infinity,
114+
showPrintMargin: false,
115+
}}
96116
value={this.state.content}
97-
options={{ lineNumbers: true }}
98-
onBeforeChange={(editor, data, content) => { this.setState({ content }); }}
117+
onChange={(content) => { this.setState({ content }); }}
99118
/>
119+
100120
<div className="new-snippet-code-bottom-bar">
101121
<input type="submit" value="POST" />
102122
</div>
103123
</div>
104124
</div>
105125
<div className="new-snippet-lang-wrapper">
106126
<ListBoxWithSearch
107-
items={this.props.syntaxes}
127+
items={syntaxes}
108128
onClick={this.onSyntaxClick}
109129
/>
110130
</div>

src/components/Snippet.jsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import React from 'react';
22
import { connect } from 'react-redux';
3-
import { Controlled as CodeMirror } from 'react-codemirror2';
4-
5-
import codemirror from 'codemirror';
6-
import 'codemirror/lib/codemirror.css';
3+
import AceEditor from 'react-ace';
74

85
import Title from './common/Title';
96
import Spinner from './common/Spinner';
@@ -41,7 +38,6 @@ class Snippet extends React.Component {
4138
if (!snippet) return <Spinner />;
4239

4340
const snippetTitle = snippet.get('title') || `#${snippet.get('id')}, Untitled`;
44-
const modeInfo = codemirror.findModeByName(snippet.get('syntax'));
4541

4642
return (
4743
[
@@ -76,9 +72,20 @@ class Snippet extends React.Component {
7672
/>
7773
</div>
7874
<div className="snippet-code">
79-
<CodeMirror
75+
<AceEditor
76+
mode={snippet.get('syntax')}
77+
width="100%"
78+
height="100%"
79+
setOptions={{
80+
readOnly: true,
81+
highlightActiveLine: false,
82+
highlightGutterLine: false,
83+
showFoldWidgets: false,
84+
useWorker: false,
85+
maxLines: Infinity,
86+
showPrintMargin: false,
87+
}}
8088
value={`${snippet.get('content')}`}
81-
options={{ lineNumbers: true, readOnly: true, mode: modeInfo.mime }}
8289
/>
8390
<div className="snippet-code-bottom-bar">
8491
<button className="snippet-button light">Raw</button>

src/helpers/index.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import codemirror from 'codemirror';
1+
import brace from 'brace';
2+
import 'brace/ext/modelist';
23

34
const regExpEscape = string => string.replace(/[-[\]{}()*+?.,\\^$|]/g, '\\$&');
45

@@ -19,17 +20,20 @@ function download(text, name, mime) {
1920
}
2021

2122
function downloadSnippet(snippet) {
22-
// Despite using CodeMirror's modes as syntaxes on XSnippet API, we might
23-
// imagine other setup when more syntaxes can be used on server. Hence, we
24-
// must be prepared and fallback on "Plain Text" mode if we can't figure out
25-
// what's extension and/or MIME type.
26-
const modeInfo = codemirror.findModeByName(snippet.get('syntax'))
27-
|| codemirror.findModeByName('Plain Text');
23+
const { modesByName } = brace.acequire('ace/ext/modelist');
2824

25+
// Despite using AceEditor's modes as syntaxes, we can imagine other setup
26+
// when more or even other syntaxes can be used on API side. Hence, we better
27+
// be prepared and fallback to "Text" mode if unknown syntaxes it is.
28+
const mode = modesByName[snippet.get('syntax')] || modesByName.text;
29+
const ext = mode.extensions.split('|')[0] || 'txt';
2930
const content = snippet.get('content');
30-
const name = `${snippet.get('id')}.${modeInfo.ext[0]}`;
31+
const name = `${snippet.get('id')}.${ext}`;
3132

32-
download(content, name, modeInfo.mime);
33+
// Unfortunately, AceEditor doesn't maintain MIME type map so we don't know
34+
// for sure which mode corresponds to which MIME type. Hence, let's use
35+
// text/plain until we come up with better idea.
36+
download(content, name, 'text/plain');
3337
}
3438

3539
export { regExpEscape, downloadSnippet };

src/styles/common/overwrite.styl

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,11 @@
11
@import './common/variables.styl'
22
@import './common/mixins.styl'
33

4-
.react-codemirror2,
5-
.CodeMirror
6-
height: 100% !important
7-
line-height: 1.4
4+
.snippet .ace_gutter-layer
5+
width: 40px !important
86

9-
.new-snippet .react-codemirror2,
10-
.new-snippet .CodeMirror
11-
color: text-dark !important
12-
13-
.CodeMirror-gutter
14-
&.CodeMirror-linenumbers
15-
width: 39px
16-
17-
.CodeMirror-sizer
18-
margin-left: 39px
19-
padding-top: 5px
20-
21-
.CodeMirror-gutters
22-
border-right: none
23-
24-
.snippet .CodeMirror-line
25-
padding-left: 20px !important
26-
27-
.snippet .CodeMirror-cursor {
7+
.snippet .ace_cursor
288
display: none !important
29-
}
309

3110
.react-tags
3211
display: flex

0 commit comments

Comments
 (0)