最近调研前端代码编辑器,选中了 Ace editor,其本身功能丰富,扩展性也极强,插件目前更新稳定。ace 本身支持上百种语言的语法,然鹅日常项目中总有些奇葩的需求,以致我们需要去了解更多的技术研究。
技术支持: Vue3 + Vite ,基于 ace-builds 进行二次扩展封装。
Ace(Ajax.org Cloud9 Editor)是一个用 JavaScript 编写的可嵌入代码编辑器。它与 Sublime,Vim 和 TextMate 等本地编辑器的功能和性能相匹配。它可以轻松地嵌入任何网页和 JavaScript 应用程序中。
使用 pnpm 包管理器,引入 ace-builds 库
pnpm i ace-builds -D
pnpm i vue-loader-v16 -D // 引入 ace 报错时需安装
引入基础模块,测试ace编辑器基础功能
<template>
<div class="aceEditor" ref="aceEditor"></div>
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import * as ace from 'ace-builds';
import "ace-builds/src-noconflict/mode-javascript"; // 语言模式
import "ace-builds/src-noconflict/theme-monokai" // 主题
import "ace-builds/src-noconflict/ext-language_tools"; // 语法提示
import "ace-builds/src-noconflict/snippets/javascript"; // 语法段提示模块
</script>
const options = {
theme: 'ace/theme/monokai', // 设置语法高亮主题
mode: 'ace/mode/javascript', // 设置语法 mode
tabSize: 1,
maxLines: 25,
minLines: 25,
showPrintMargin: false,
fontSize: 14,
printMarginColumn: 20,
useWorker: false,
showLineNumbers: true, // 显示行号
showGutter: true, // 显示行号区域
highlightActiveLine: false,
highlightSelectedWord: false, // 高亮选中文本
readOnly: false, // 控制编辑器是否只读
enableSnippets: true, // 启用代码段提示
enableLiveAutocompletion: true, // 启用实时自动完成
enableBasicAutocompletion: true, // 启用基本自动完成
}
const initEditor = () => {
if (editor) editor.destroy();
// 初始化
editor = ace.edit(aceEditor.value, options);
// 切换自动换行
editor.getSession().setUseWrapMode(true);
}
onMounted(() => {
initEditor()
});
测试 aceEditor 基础封装是否功能正常,方便后续扩展开发。
Ace 编辑器本身不支持直接自定义语言,但支持通过扩展语言模式的方式实现对自定义语言的支持。在 Ace 中,语言模式是指将文本分解为标记,定义了每个标记的样式和语法高亮规则,以此来实现语法高亮、代码折叠、自动完成等功能。
想要自定义新增语言模型,首先需要了解如何定义一个 mode (语言模式),可参考官方文档 Ace - The High Performance Code Editor for the Web (c9.io
1、在 node_modules\ace-builds\src-noconflict 下新增一个 mode-mylang.js 的文件,定义语言模式、语言高亮规则及代码提示。
ace.define('ace/mode/mylang', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text', 'ace/mode/custom_highlight_rules'], (require, exports, module) => {
const oop = require('ace/lib/oop');
const TextMode = require('ace/mode/text').Mode;
const { MyLangHighlightRules } = require('ace/mode/mylang_highlight_rules');
const { Tokenizer } = require('ace/tokenizer');
const Mode = function () {
this.HighlightRules = MyLangHighlightRules;
this.$tokenizer = new Tokenizer(new MyLangHighlightRules().getRules());
};
oop.inherits(Mode, TextMode);
(function () {
// 加载css 样式设置,以便控制自定义语言关键词高亮颜色
const dom = require('ace/lib/dom');
dom.importCssString(exports.cssText, exports.cssClass);
}).call(Mode.prototype);
(function () {
// 添加代码提示
this.completer = {
getCompletions(editor, session, pos, prefix, callback) {
const wordList = [
'hello',
'world',
'AceEditor',
'hello world this is AceEditor',
];
callback(
null,
wordList.map((word) => ({
caption: word,
value: word,
meta: 'mylang', // 自定义语言标识
})),
);
},
};
}.call(Mode.prototype));
exports.Mode = Mode;
});
2、接着定于语法高亮规则, 高亮规则里面定义一系列的规则(Rules), 每个规则描述了如何处理输入文本中的单个字符序列。Ace Editor 中的规则是由 Tokenizer 对象处理的。Tokenizer 是 Ace Editor 内置的一种基于正则表达式的解析器,用于将输入文本转换为标记(Token)流。标记是 Ace Editor 中的基本元素,它们由不同类型的 token 组成,例如:keyword、comment、string 等等。
ace.define(
'ace/mode/mylang_highlight_rules',
['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text_highlight_rules'],
(require, exports, module) => {
const oop = require('ace/lib/oop');
const { TextHighlightRules } = require('ace/mode/text_highlight_rules');
const MyLangHighlightRules = function () {
// 定义高亮规则
const keywordList = 'let|const|function|world'; // 高亮关键词
this.$rules = {
start: [
{
token: 'keyword',
regex: `\\b(?:${keywordList})\\b`,
},
{
token: 'string',
regex: '".*?"',
},
{
token: 'constant',
regex: /\b(true|false|null)\b/,
},
{
token: 'comment',
regex: /\/\/.*$/,
},
{
token: 'comment',
start: '/\\*',
end: '\\*/',
},
{
token: 'mylang',
regex: '\\b(?:hello|world|AceEditor)\\b',
},
],
};
};
oop.inherits(MyLangHighlightRules, TextHighlightRules);
exports.MyLangHighlightRules = MyLangHighlightRules;
},
);
3、自定义特殊高亮颜色,比如我们想对 true | false | null 进行特殊颜色高亮,只需更具规则中 “token” 属性值,以 .ace_[属性值] 形式定义css。
例:
/* 自定义语言,匹配不同类型关键词高亮颜色 */
.ace_constant {
color: #FF00FF;
font-weight: bold;
}
4、最后引入我们自定义语言即可,可直接在option中配置即可
mode: 'ace/mode/mylang',
插件本身还有很多配置和 api , 感兴趣的可以继续探索,本文旨在对初阶自定义功能实现讲解,后续会慢慢更新更深层次自定义方案。
<template>
<div class="aceEditor" ref="aceEditor"></div>
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import * as ace from 'ace-builds';
import "ace-builds/src-noconflict/mode-javascript"; // 语言模式
import "ace-builds/src-noconflict/theme-monokai" // 主题
import "ace-builds/src-noconflict/ext-language_tools"; // 语法提示
import "ace-builds/src-noconflict/snippets/javascript"; // 语法段提示模块
// 自定义语言
import "ace-builds/src-noconflict/mode-mylang"; // 此模块需对应目录创建
const props = withDefaults(defineProps<{
value: string,
}>(), {
})
const emits = defineEmits(['update:value']);
let editor: any = null;
const aceEditor = ref<string | Element>('')
// 编辑器默认配置项
const options = {
theme: 'ace/theme/monokai',
//mode: 'ace/mode/javascript',
mode: 'ace/mode/mylang',
tabSize: 1,
maxLines: 25,
minLines: 25,
showPrintMargin: false,
fontSize: 14,
printMarginColumn: 20,
useWorker: false,
showLineNumbers: true, // 显示行号
showGutter: true, // 显示行号区域
highlightActiveLine: false,
highlightSelectedWord: false, // 高亮选中文本
readOnly: false, // 控制编辑器是否只读
enableSnippets: true, // 启用代码段
enableLiveAutocompletion: true, // 启用实时自动完成
enableBasicAutocompletion: true, // 启用基本自动完成
}
// 初始化编辑器
const initEditor = () => {
if (editor) editor.destroy();
// 初始化
editor = ace.edit(aceEditor.value, options);
// 切换自动换行
editor.getSession().setUseWrapMode(true);
// 支持双向绑定
editor.setValue(props.value ? props.value : "");
editor.on("change", () => {
emits("update:value", editor.getValue());
})
}
watch(
() => props.value,
(newProps) => {
//解决光标移动问题
const position = editor.getCursorPosition();
editor.getSession().setValue(newProps);
editor.clearSelection();
editor.moveCursorToPosition(position);
}
);
onMounted(() => {
initEditor()
});
onBeforeUnmount(() => {
editor.destroy();
});
</script>
<style>
.aceEditor {
width: 500px;
height: 500px;
}
/* 自定义语言,匹配不同类型关键词高亮颜色 */
.ace_constant {
color: #FF00FF;
font-weight: bold;
}
</style>
官网: https://ace.c9.io
GitHub: https://github.com/ajaxorg/ace
Vue3 ace组件: https://github.com/CarterLi/vue3-ace-editor
在线测试: https://ace.c9.io/build/kitchen-sink.html
创作不易,如果本篇文章你觉得还不错的话求个赞