title: react-ace代码编辑器自定义mode攻略
date: 2022-12-02 15:48:45
tags:
- React
背景
最近使用了react-ace实现一个代码编辑器,由于都是封装完整的,想要自定义主题、mode比较困难,下面是本人整理的自定义mode、theme示例:
开发版本
- react: ^18.2.0
- react-dom: ^18.2.0
- react-ace: ^10.1.0
- 均采用函数式组件
$ npm install --save react-ace # 网络上的内容比较乱,自测只需要这个即可
实现效果
github.com/dzzhyk/react-ace-custom-mode-theme-demo
核心代码
这里是部分核心代码,具体查看完整工程!
App.js
// react-ace editor
import AceEditor from "react-ace";
// 这两个插件用于编辑器内搜索框、代码联想提示
import "ace-builds/src-min-noconflict/ext-searchbox"
import "ace-builds/src-min-noconflict/ext-language_tools"
// 解决webpack打包问题
import "ace-builds/webpack-resolver"
import {useEffect, useRef, useState} from "react";
// 引入自定义mode
import "./mode-yankai"
import "./theme-yankai.css"
function App(props) {
const [content, setContent] = useState("");
const editorRef = useRef(null);
// 直接onLoad增加complete存在bug,需要使用useEffect解决
useEffect(() => {
if (editorRef.current) {
complete(editorRef.current.editor)
}
}, [editorRef])
// 自定义编辑器的代码补全器
const complete = editor => {
const YankaiCompleter = [
{
name: "and",
value: "and",
score: 100, // 提示优先级
meta: "[关键字] 逻辑与"
}, {
name: "println",
value: "println(str)",
score: 99,
meta: "[内置函数] 标准输出"
},
]
editor.completers = [{
getCompletions: function (editor, session, pos, prefix, callback) {
callback(null, YankaiCompleter)
}
}]
}
return (
<div>
<AceEditor
ref={editorRef}
mode="yankai"
placeholder="由此开始输入代码"
setOptions={{
enableBasicAutocompletion: false,
enableLiveAutocompletion: true,
enableSnippets: false,
showLineNumbers: true,
tabSize: 4
}}
fontSize={13}
style={{height: 500, width: '100%', border: '1px solid #d9d9d9'}}
value={content}
onChange={(value) => setContent(value)}
onLoad={complete}
/>
<div>其他内容</div>
</div>
);
}
export default App;
mode-yankai.js:自定义的mode
/*
load custom yankai_highlight_rules for me.
this mode is based on python_highlight_rules, you can find in node_modules/ace-builds/src/mode-python.js
*/
ace.define("ace/mode/yankai_highlight_rules", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text_highlight_rules"], function (ace_require, exports, module) {
var oop = ace_require("../lib/oop");
var TextHighlightRules = ace_require("./text_highlight_rules").TextHighlightRules;
var YankaiHighlightRules = function () {
var keywords = "and|in|not|or|any";
var builtinConstants = "true|false|null";
// we defined builtin function 'yankai' and 'cool' here, so u will find it highlighted in ace-editor which mode="yankai"
var builtinFunctions = ("yankai|cool");
var keywordMapper = this.createKeywordMapper({
"support.function": builtinFunctions,
"constant.language": builtinConstants,
"keyword": keywords
}, "identifier");
var strPre = "[uU]?";
var strRawPre = "[rR]";
var strFormatPre = "[fF]";
var strRawFormatPre = "(?:[rR][fF]|[fF][rR])";
var decimalInteger = "(?:(?:[1-9]\\d*)|(?:0))";
var octInteger = "(?:0[oO]?[0-7]+)";
var hexInteger = "(?:0[xX][\\dA-Fa-f]+)";
var binInteger = "(?:0[bB][01]+)";
var integer = "(?:" + decimalInteger + "|" + octInteger + "|" + hexInteger + "|" + binInteger + ")";
var exponent = "(?:[eE][+-]?\\d+)";
var fraction = "(?:\\.\\d+)";
var intPart = "(?:\\d+)";
var pointFloat = "(?:(?:" + intPart + "?" + fraction + ")|(?:" + intPart + "\\.))";
var exponentFloat = "(?:(?:" + pointFloat + "|" + intPart + ")" + exponent + ")";
var floatNumber = "(?:" + exponentFloat + "|" + pointFloat + ")";
var stringEscape = "\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})";
this.$rules = {
// ... 这部分是匹配各种语言元素规则,太长了已省略请查看github完整工程
}
};
oop.inherits(YankaiHighlightRules, TextHighlightRules);
exports.YankaiHighlightRules = YankaiHighlightRules;
});
// load custom mode-yankai for me, now I should use mode="yankai" in ace-editor
ace.define("ace/mode/yankai", function (acequire, exports, module) {
const oop = acequire("../lib/oop");
const TextMode = acequire("./text").Mode;
const YankaiHighlightRules = acequire("./yankai_highlight_rules").YankaiHighlightRules;
// create my custom mode
const YankaiMode = function () {
this.HighlightRules = YankaiHighlightRules;
};
oop.inherits(YankaiMode, TextMode);
exports.Mode = YankaiMode;
});
theme-yankai.js
/* autocomplete width */
.ace_editor.ace_autocomplete {
width: 40vw;
}
/* override highlight style for custom yankai mode */
.ace_function {
color: orangered !important;
font-weight: bold
}
.ace_constant {
color: blue !important;
}
.ace_operator {
color: red !important;
}
.ace_string {
color: green !important;
}