一、前言
Monaco Editor 是一款微软开源的运行在浏览器环境中的代码编辑器,其不仅提供了基本的代码编辑功能,还支持自定义功能的扩展,使得开发者能够根据项目需求定制编辑器的行为,为编辑器注入新的生命力。本文将深入探讨在实际开发过程中,我们如何通过 Monaco Editor 原生的 API 进行自定义扩展功能,将 Monaco Editor 从一个简单的文本编辑工具,转变为一个具备代码上下文理解、智能提示、格式化以及错误检测等高级功能的智能编辑器。无论您是 Monaco Editor 的新手,还是已经有一定经验的老手,本文都将为您提供宝贵的信息和见解。文章分为上下两篇,当前为"下篇"内容:
二、编辑器介绍
Monaco Editor 提供了一个轻量级的代码编辑器,可以在任何基于 Web 技术构建的项目中使用。它不仅支持多种编程语言,还提供了丰富的 API 和事件系统,使得开发者可以轻松地与编辑器交互。编辑器功能特性请点击 👉 链接,编辑器使用文档请点击 👉 链接,编辑器在线体验请点击 👉 链接
2.1 常用配置项
js
{
value: '', // 编辑器初始文本
language: 'javascript', // 语言
theme: 'vs', // 主题
readOnly: false, // 是否只读
minimap: { enabled: false }, // 是否启用小地图
fontSize: 14, // 字体大小
tabSize: 2, // tab缩进长度
automaticLayout: true, // 自动布局
lineNumbers: 'off', // 是否启用行号
contextmenu: true, // 是否启用上下文菜单
folding: true, // 是否启用代码折叠
foldingStrategy: 'auto', // 代码折叠策略
wordWrap: 'on', // 自动换行设置
wrappingIndent: 'indent', // 换行缩进
formatOnPaste: true, // 粘贴时是否自动格式化
formatOnType: true, // 输入时是否自动格式化
dragAndDrop: true, // 是否允许拖放
cursorStyle: 'line', // 光标样式
cursorBlinking: 'blink', // 光标闪烁方式
scrollbar: {
vertical: 'auto', // 垂直滚动条的显示方式
horizontal: 'auto', // 水平滚动条的显示方式
verticalScrollbarSize: 2, // 垂直滚动条的宽
horizontalScrollbarSize: 2, // 水平滚动条的高度
}
}
2.2 常用API
js
// 获取 model 实例
editor.getModel();
// 获取代码内容(字符串)
editor.getModel().getValue();
// 获取代码长度
editor.getModel().getValueLength();
// 获取光标位置
editor.getPosition();
// 光标跳到给定位置
editor.setPosition({ lineNumber: 1, column: 1 });
// 编辑器聚焦
editor.focus();
// 销毁编辑器
editor.dispose();
// 设置代码
editor.setValue('console.log("Hello world!");');
// 指定位置插入代码
editor.executeEdits();
// 获取选中的行信息
editor.getSelection();
// 获取某一行的内容,不传值则返回全部
editor.getModel().getLineContent(1);
// 编辑器滚到指定行
editor.revealLine(1);
// 编辑器视图滚到指定位置
editor.revealPositionInCenter({ lineNumber: 1, column: 1});
// 手动触发 action
editor.trigger('actionName', args)
// 内容改变事件
editor.onDidChangeModelContent((e)=>console.log(e)});
三、自定义功能
3.1 语法检查
Monaco Editor 语法检查结果通过 Markers 展示,并提供了 editor.setModelMarkers 来自定义 Marker 标注。
浏览器自带的错误/警告提示等内容是通过"Marker"来展示的,通过 setModelMarkers 方法我们可以自定义一些语法检查规则,然后设置报错/警告等内容,其中 Marker 的信息等级主要分为 4 类:
- monaco.MarkerSeverity.Error
- monaco.MarkerSeverity.Warning
- monaco.MarkerSeverity.Info
- monaco.MarkerSeverity.Hint
js
function validate(model) {
const markers = [];
// lines start at 1
for (let i = 1; i < model.getLineCount() + 1; i++) {
const range = {
startLineNumber: i,
startColumn: 1,
endLineNumber: i,
endColumn: model.getLineLength(i) + 1,
};
const content = model.getValueInRange(range).trim();
// 进行一些自定义语法检查,例如检查是否为空行
if (content === "") {
markers.push({
message: "This line is empty", // marker 信息
severity: monaco.MarkerSeverity.Error, // marker 类型
source: "source", // 来源文件
code: "code", // 相关代码
startLineNumber: i,
startColumn: 1,
endLineNumber: i,
endColumn: model.getLineLength(i) + 1,
});
}
}
monaco.editor.setModelMarkers(model, "owner", markers);
}
const value = /* set from `myEditor.getModel()`: */ `function hello() {
alert('Hello world!');
}
`;
const editor = monaco.editor.create(document.getElementById("container"), {
value,
language: "javascript",
automaticLayout: true,
});
const model = editor.getModel();
validate(model);
editor.onDidChangeModelContent(() => {
validate(model);
});
操作 Markers 的方法主要有以下几个:
- 设置 Markers:monaco.editor.setModelMarkers
- 获取 Markers:monaco.editor.getModelMarkers
- 移出 Markers:monaco.editor.removeAllMarkers
- 监听 Markers:monaco.editor.onDidChangeMarkers
通过 onDidChangeMarkers 监听我们能够获取所有的 Markers 的数据内容,基于该数据我们能够通过统一语法检查情况,以更加直观地了解当前编辑器代码是否存在语法问题。
3.2 代码格式化
Monaco Editor 内置了格式化代码功能,如果设置项contextmenu
为 true,则点击右键选择"Format Document"即可格式化,当然也支持我们手动调用 API 来执行代码格式化,await editor.getAction('editor.action.formatDocument').run()
。除此之外,如果对于代码格式化有自己的要求,也可以结合 js-beautify 或 prettier 等格式化库来自定义实现代码格式化,具体实现可参考下方自定义菜单示例。
3.3 自定义菜单
Monaco Editor 提供 editor.IStandaloneCodeEditor.addAction 来自定义菜单项。
Monaco Editor 中用户点击鼠标右键会弹出菜单栏,我们可以通过addAction
来新增自定义的菜单项,通过 addAction 的方式我们既可以为编辑器绑定快捷键,也可以注册为 action,并支持在右键新增菜单项,也可以使用 trigger 方法触发,相当于注册为一个较完整的快捷键事件。例如,我们现在就要新增一个菜单项,期望其能够基于 js-beautify 来格式化代码。
js
import beautify from "js-beautify";
editor.addAction({
id: "beautify-js-format", // 唯一id,不能重复
label: "格式化代码", // 菜单项名单
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS], // 触发组合键
contextMenuOrder: 1, // 菜单项的排序权重 越小,越靠前
contextMenuGroupId: "customCommand", // 菜单项的分组
// precondition: 先决条件
// 菜单项执行函数
run() {
const oldCode = myEditor.getValue();
const newCode = beautify(oldCode, { indent_size: 2 });
myEditor.executeEdits("", [
{ range: new monaco.Range(1, 1, myEditor.getModel()!.getLineCount() + 1, 1), text: newCode },
]);
},
});
如果想知道有哪些 action 可供使用,在 editor 实例有一个 _actions
属性,其存放了所有可用的 action 的信息。
3.4 单行编辑器
单行编辑器是指代码在编辑器中编写时不允许换行且只支持在一行输入。一般情况下单行编辑器展示为 input 输入框的形式,配置内容可参考"Vue Single Line Monaco Editor"。
四、总结
Monaco Editor 以其强大的功能和灵活性成为了前端开发中不可或缺的一部分,本文总结了我们在对代码编辑器在 Javascript 语言下进行自定义功能扩展的经验,通过自定义语法检查、代码格式化、自定义菜单、单行编辑器模式 等功能,极大地增强了编辑器的交互性和用户体验,使得我们使用 JS 代码编辑器能够更加高效(自定义代码提示、悬浮提示、点击跳转、插入代码等功能实现请跳转上篇查看)。总的来说,Monaco Editor 的自定义功能为开发者提供了无限的可能性,使得开发者能够根据具体需求定制出最适合自己项目的编辑器。
参考资料