Hi,
My question might be a bit stupid (apologies for that), but I can't figure out how to use a custom mode / syntax highlighter / auto-completion. Can anyone help me? I am very new to brace, ace, and react but I saw some materials on how to create and set your custom mode using Ace. What I couldn't figure out is how to do the same with react-ace.
Christian
Hi,
and what's your implementation? I can show what works for me. I don't remeber it exactly, but I think I had to install also brace library at first. So
npm install -D brace react-ace
And this is my component with Ace editor:
import React from 'react'
import brace from 'brace'
import AceEditor from 'react-ace'
import 'brace/mode/javascript'
import 'brace/theme/tomorrow'
const MyEditor = () => {
return (
<AceEditor
name="my-editor"
mode="javascript"
theme="tomorrow"
value=""
width="100%"
height="500px" />
)
}
export default MyEditor
And here is screenshot:

I could use the default modes from brace indeed. What I wonder is how to use my own mode? I wrote a custom mode by myself and want to include that, but was not able to do that with react-ace so far.
@sichvoge Did you make any progress on this at all?
Not really. In the end I developed my own react component using Ace and not brace.
Fair enough - perhaps you could share it when you're happy with it? Thanks @sichvoge
Sure, I will.
@sichvoge , can you please share your component?
I'm having the same problem and it will be really helpful :)
I am trying to do a similar thing. Importing the mode in the front end doesn't seem to work for me.
It seems that AceEditor is looking for it as a server side file. So I ended up simply serving the mode file separately to my webpack bundled script. (This is probably not at all the right way to do it but it does seem to work....)
So say my custom mode is called 'eikelang'
My React code looks like this
import React from 'react';
import AceEditor from 'react-ace';
import 'brace/theme/github';
export default ({value}) => {
<AceEditor
mode="eikelang"
theme="github"
width="auto"
height="200px"
value={value}
/>
};
I have a js file called 'mode-eikelang.js' served from my server's root directory, which is just a rip off of the mysql brace mode file with some names changed:
ace.define('ace/mode/doc_comment_highlight_rules',
['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text_highlight_rules'],
function(acequire, exports, module) {
'use strict';
let oop = acequire('../lib/oop');
let TextHighlightRules = acequire('./text_highlight_rules').TextHighlightRules;
var DocCommentHighlightRules = function() {
this.$rules = {
'start': [{
token: 'comment.doc.tag',
regex: '@[\\w\\d_]+' // TODO: fix email addresses
},
DocCommentHighlightRules.getTagRule(),
{
defaultToken: 'comment.doc',
caseInsensitive: true
}]
};
};
oop.inherits(DocCommentHighlightRules, TextHighlightRules);
DocCommentHighlightRules.getTagRule = function(start) {
return {
token: 'comment.doc.tag.storage.type',
regex: '\\b(?:TODO|FIXME|XXX|HACK)\\b'
};
};
DocCommentHighlightRules.getStartRule = function(start) {
return {
token: 'comment.doc', // doc comment
regex: '\\/\\*(?=\\*)',
next: start
};
};
DocCommentHighlightRules.getEndRule = function(start) {
return {
token: 'comment.doc', // closing comment
regex: '\\*\\/',
next: start
};
};
exports.DocCommentHighlightRules = DocCommentHighlightRules;
});
ace.define('ace/mode/eikelang_highlight_rules', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/lib/lang', 'ace/mode/doc_comment_highlight_rules', 'ace/mode/text_highlight_rules'], function(acequire, exports, module) {
let oop = acequire('../lib/oop');
let lang = acequire('../lib/lang');
let DocCommentHighlightRules = acequire('./doc_comment_highlight_rules').DocCommentHighlightRules;
let TextHighlightRules = acequire('./text_highlight_rules').TextHighlightRules;
let EikelangHighlightRules = function() {
let builtins = 'drop|hash|keep|linear_transformation|map|rename|string_manipulation|string_submatcher';
let keywordMapper = this.createKeywordMapper({
'support.function': builtins
}, 'identifier', true);
function string(rule) {
let start = rule.start;
let escapeSeq = rule.escape;
return {
token: 'string.start',
regex: start,
next: [
{token: 'constant.language.escape', regex: escapeSeq},
{token: 'string.end', next: 'start', regex: start},
{defaultToken: 'string'}
]
};
}
this.$rules = {
'start': [{
token: 'comment', regex: '(?:-- |#).*$'
},
string({start: '"', escape: /\\[0'"bnrtZ\\%_]?/}),
string({start: '\'', escape: /\\[0'"bnrtZ\\%_]?/}),
DocCommentHighlightRules.getStartRule('doc-start'),
{
token: 'comment', // multi line comment
regex: /\/\*/,
next: 'comment'
}, {
token: 'constant.numeric', // hex
regex: /0[xX][0-9a-fA-F]+|[xX]'[0-9a-fA-F]+'|0[bB][01]+|[bB]'[01]+'/
}, {
token: 'constant.numeric', // float
regex: '[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b'
}, {
token: keywordMapper,
regex: '[a-zA-Z_$][a-zA-Z0-9_$]*\\b'
}, {
token: 'constant.class',
regex: '@@?[a-zA-Z_$][a-zA-Z0-9_$]*\\b'
}, {
token: 'constant.buildin',
regex: '`[^`]*`'
}, {
token: 'keyword.operator',
regex: '\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|='
}, {
token: 'paren.lparen',
regex: '[\\(]'
}, {
token: 'paren.rparen',
regex: '[\\)]'
}, {
token: 'text',
regex: '\\s+'
}],
'comment': [
{token: 'comment', regex: '\\*\\/', next: 'start'},
{defaultToken: 'comment'}
]
};
this.embedRules(DocCommentHighlightRules, 'doc-', [DocCommentHighlightRules.getEndRule('start')]);
this.normalizeRules();
};
oop.inherits(EikelangHighlightRules, TextHighlightRules);
exports.EikelangHighlightRules = EikelangHighlightRules;
});
ace.define('ace/mode/eikelang', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text', 'ace/mode/eikelang_highlight_rules'],
function(acequire, exports, module) {
let oop = acequire('../lib/oop');
let TextMode = acequire('../mode/text').Mode;
let EikelangHighlightRules = acequire('./eikelang_highlight_rules').EikelangHighlightRules;
let Mode = function() {
this.HighlightRules = EikelangHighlightRules;
this.$behaviour = this.$defaultBehaviour;
};
oop.inherits(Mode, TextMode);
(function() {
this.lineCommentStart = ['--', '#']; // todo space
this.blockComment = {start: '/*', end: '*/'};
this.$id = 'ace/mode/eikelang';
}).call(Mode.prototype);
exports.Mode = Mode;
});
Hi @pollen8,
If you take a look at react-ace code, you'll see that the component tries to set the mode using it's name
Meaning that you have to make sure your mode is already in "Ace" cache using ace.define and oop.inherits(Mode, TextMode) and other native Ace code, which i find pretty old when you have ES6 on your side.
So why not writing your custom mode simply as we write any other class?
It would make your custom mode class look really better
(less lines of code + better syntax => less maintenance)
So I:
componentDidMount function and called session.setMode with an instance of my custom mode.My custom mode is:
export default class CustomSqlMode extends ace.acequire('ace/mode/text').Mode {
constructor(){
super();
// Your code goes here
}
}
And my react-ace code looks like:
render() {
return <div>
<AceEditor
ref="aceEditor"
mode="sql" // Default value since this props must be set.
theme="chrome" // Default value since this props must be set.
/>
</div>;
}
componentDidMount() {
const customMode = new CustomSqlMode();
this.refs.aceEditor.editor.getSession().setMode(customMode);
}
The only downside it see is depending on ace.acequire('ace/mode/text').Mode to be a valid line when i define the mode class.
But it's valid anyway since react-ace puts the "ace" module under window.ace (https://github.com/ajaxorg/ace/blob/4c7e5eb3f5d5ca9434847be51834a4e41661b852/lib/ace/worker/worker.js#L19)
@AlonBe @pollen8
Does anyone have a working jsfiddle or example for this?
Tried following the directions that @AlonBe listed with the custom mode provided by @pollen8 and I cannot unfortunately get the syntax highlighter to register correctly... :(
Any help would be appreciated.
Where my editor is being rendered:
componentDidMount () {
const customMode = new CustomSqlMode();
this.refs.ace.editor.getSession().setMode(test);
console.log(customMode);
}
@solemnify, Did you try to do this.refs.ace.editor.getSession().setMode(customMode);?
Try to follow the CustomSqlMode class I created.
To see if it works you can put inside the constructor the following line:
this.lineCommentStart = '--';
If you want to use syntax highlighter you need to override this.HighlightRules of the custom mode class.
Take a look at one of the current modes such as sql_mode
@AlonBe
Could you provide an example of code to put in your CustomSqlMode component ?
I don't know how to transform the highlight rules in es6...
Thanks in advance for your help.
The following code worked for me:
App.js
import React, { Component } from 'react';
import brace from 'brace';
import AceEditor from 'react-ace';
import CustomSqlMode from './CustomSqlMode.js'
import 'brace/theme/github';
class App extends Component {
componentDidMount() {
const customMode = new CustomSqlMode();
this.refs.aceEditor.editor.getSession().setMode(customMode);
}
render() {
return (
<div className="App">
<AceEditor
ref="aceEditor"
mode="text"
theme="github"
name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }}
/>
</div>
);
}
}
export default App;
CustomSqlMode.js
import 'brace/mode/java';
export class CustomHighlightRules extends window.ace.acequire("ace/mode/text_highlight_rules").TextHighlightRules {
constructor() {
super();
this.$rules = {
"start": [{
token: "comment",
regex: "#.*$"
}, {
token: "string",
regex: '".*?"'
}]
};
}
}
export default class CustomSqlMode extends window.ace.acequire('ace/mode/java').Mode {
constructor() {
super();
this.HighlightRules = CustomHighlightRules;
}
}
The previous code highlights the comments and strings only. I'm not sure if what I did was the good practice or not, but at least it works.
@newint33h thanks!It works for me.
@newint33h I tried to use this structure but it complains that TypeError: Cannot read property 'editor' of undefined.
@shuotongli I had the same problem and it looks like that issue is because React's this.refs is now deprecated. There's are a few ways to create a ref, depending on the version of React you're using. For the currently supported way, check this out:
https://reactjs.org/docs/refs-and-the-dom.html#creating-refs
The following code worked for me:
App.js
import React, { Component } from 'react'; import brace from 'brace'; import AceEditor from 'react-ace'; import CustomSqlMode from './CustomSqlMode.js' import 'brace/theme/github'; class App extends Component { componentDidMount() { const customMode = new CustomSqlMode(); this.refs.aceEditor.editor.getSession().setMode(customMode); } render() { return ( <div className="App"> <AceEditor ref="aceEditor" mode="text" theme="github" name="UNIQUE_ID_OF_DIV" editorProps={{ $blockScrolling: true }} /> </div> ); } } export default App;CustomSqlMode.js
import 'brace/mode/java'; export class CustomHighlightRules extends window.ace.acequire("ace/mode/text_highlight_rules").TextHighlightRules { constructor() { super(); this.$rules = { "start": [{ token: "comment", regex: "#.*$" }, { token: "string", regex: '".*?"' }] }; } } export default class CustomSqlMode extends window.ace.acequire('ace/mode/java').Mode { constructor() { super(); this.HighlightRules = CustomHighlightRules; } }The previous code highlights the comments and strings only. I'm not sure if what I did was the good practice or not, but at least it works.
why the mode='text', what does text mean?
@TaurusWood, as the first render occurs before componentDidMount - you must pass a valid "mode" to react-ace Component.
later on, on componentDidMount you can see that the code overrides that mode (setMode)
and BTW, if I'm not getting wrong, mode='text' is the basic mode of Ace that all the others are overriding
Can anyone explain why is't it working anymore, all of my codes are interpreted as a text instead of java file after setting a custom mode? Thanks
The following code worked for me:
App.js
import React, { Component } from 'react'; import brace from 'brace'; import AceEditor from 'react-ace'; import CustomSqlMode from './CustomSqlMode.js' import 'brace/theme/github'; class App extends Component { componentDidMount() { const customMode = new CustomSqlMode(); this.refs.aceEditor.editor.getSession().setMode(customMode); } render() { return ( <div className="App"> <AceEditor ref="aceEditor" mode="text" theme="github" name="UNIQUE_ID_OF_DIV" editorProps={{ $blockScrolling: true }} /> </div> ); } } export default App;CustomSqlMode.js
import 'brace/mode/java'; export class CustomHighlightRules extends window.ace.acequire("ace/mode/text_highlight_rules").TextHighlightRules { constructor() { super(); this.$rules = { "start": [{ token: "comment", regex: "#.*$" }, { token: "string", regex: '".*?"' }] }; } } export default class CustomSqlMode extends window.ace.acequire('ace/mode/java').Mode { constructor() { super(); this.HighlightRules = CustomHighlightRules; } }The previous code highlights the comments and strings only. I'm not sure if what I did was the good practice or not, but at least it works.
Most helpful comment
The following code worked for me:
App.js
CustomSqlMode.js
The previous code highlights the comments and strings only. I'm not sure if what I did was the good practice or not, but at least it works.