概述
项目当中,难免会遇到代码编辑器的功能需求,自己实现肯定不现实的,因此开源库成为了首要选择,因此这里主要介绍Monaco Editor的使用,Monaco Editor 是一个浏览器端的代码编辑器库,同时它也是 VS Code 所使用的编辑器,因此该插件功能十分强大,但是官方的文档看着实在是不太友好,因此这里简单记录插件的基本使用
具体使用
安装
npm install monaco-editor
创建编辑器
import * as monaco from "monaco-editor";
import "./work.js";
window.onload = () => {
const container = document.querySelector(".box");
const value = `function hello() {
alert('Hello world!');
}`;
const options = {
value, //值
language: "javascript", //语言
theme: "vs-dark", //主题
contextmenu: false, //禁用右击菜单
readOnly: true, //是否只读
automaticLayout: true, // 自动布局
minimap: {
// 关闭小地图
enabled: true,
},
};
const editor = monaco.editor.create(container, options);
};
设置代码提示
一定要导入对应的代码提示文件,不然页面代码输入没有代码提示 ./work.js
import * as monaco from "monaco-editor";
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === "json") {
return new jsonWorker();
}
if (label === "css" || label === "scss" || label === "less") {
return new cssWorker();
}
if (label === "html" || label === "handlebars" || label === "razor") {
return new htmlWorker();
}
if (label === "typescript" || label === "javascript") {
return new tsWorker();
}
return new editorWorker();
},
};
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
自定义联想代码补全
monaco.languages.registerCompletionItemProvider("sql", {
// 触发条件,也可以不写,不写的话只要输入满足配置的label就会提示;仅支持单字符(重要)
triggerCharacters: [".", " ",'s'],
provideCompletionItems(model, position) {
console.log("model, position", model, position);
return {
suggestions: [
{
label: "test",//输入对应文本后出现提示
insertText: "测试文件",//回车插入的文本
kind: monaco.languages.CompletionItemKind.Color,//前面的图标
detail: "m描述",//后面的描述
},
{
label: "test2",
insertText: "测试文件2",
kind: monaco.languages.CompletionItemKind.Constant,
detail: "m描述2",
},
{
label: "test3",
insertText: "测试文件3",
kind: monaco.languages.CompletionItemKind.Method,
detail: "m描述3",
},
],
};
},
});
自定义格式化
import { format } from "sql-formatter";
// 格式化sql
monaco.languages.registerDocumentFormattingEditProvider("sql", {
displayName: "格式化sql",
provideDocumentFormattingEdits(model) {
return [
{
text: format(myEditor.getValue()), //格式化文本
range: model.getFullModelRange(),//范围为全文档
},
];
},
});
自定义右击菜单
//实例方法使用
editorInstance.addAction({
id: "custom.action",
label: "格式化文本",
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_J], // 可选的快捷键
contextMenuGroupId: "9_cutcopypaste", // 上下文菜单组见输出菜单逻辑中的“contextMenuLinks”的group的值
run: (editor) => {
myEditor.setValue(format(myEditor.getValue()));
},
});
获取到所有的注册的行为(需要id的时候可以查看)
//获取到所有的注册的行为
const allActions = myEditor._actions;
删除默认右击菜单某一项
import * as actions from "monaco-editor/esm/vs/platform/actions/common/actions";
// 移除多余菜单项
// // 要移除的菜单项的命令ID列表
export const removableIds = [
// 'editor.action.clipboardCutAction',//剪切
// 'editor.action.clipboardCopyAction',//复制
// 'editor.action.clipboardPasteAction',//粘贴
'editor.action.refactor',
'editor.action.sourceAction',
'editor.action.revealDefinition',
'editor.action.revealDeclaration',
'editor.action.goToTypeDefinition',
'editor.action.goToImplementation',
'editor.action.goToReferences',
'editor.action.formatDocument',
'editor.action.formatSelection',
'editor.action.changeAll',
'editor.action.rename',
'editor.action.quickOutline',
'editor.action.quickCommand',
'Peek'
];
//获取 Monaco Editor 的菜单项注册表
const menus = actions.MenuRegistry._menuItems;
//查找编辑器上下文菜单的注册条目
const contextMenuEntry = [...menus].find(
(entry) =>
entry[0]._debugName == "EditorContext" || entry[0].id == "EditorContext"
);
//// 从上下文菜单的注册条目中提取菜单项
const contextMenuLinks = contextMenuEntry[1];
console.log(contextMenuLinks);
// 移除菜单项的函数
const removeById = (list, ids) => {
let node = list._first;
//// 检查当前菜单项是否应该移除
do {
// 注意 node.element?.command?.id || node.element?.title 的用法
const shouldRemove = ids.includes(
node.element?.command?.id || node.element?.title
);
// 如果应该移除,则从链表中移除当前节点
if (shouldRemove) {
list._remove(node);
}
} while ((node = node.next));
};
// 通过调用 removeById 函数移除特定的菜单项
removeById(contextMenuLinks, removableIds);
监听文本变化
// 监听编辑器内容变化
this.editor.onDidChangeModelContent(() => {
// 触发父组件更新代码内容
this.$emit('update:code', this.editor.getValue()); }
);
//监听是否选中
this.editor.onDidChangeCursorSelection(() => {
// 触发父组件更新代码内容
console.log("选中了")
);
常用方法
editor.dispose();//销毁
editor.setValue('')//设置值
editor.getValue();//获取值
editor.getModel().getValueInRange(editor.getSelection());//获取选中内容
//根据范围获取编辑器中的文本(获取固定行,格式化选中文本的时候有用)
myEditor.getModel().getValueInRange({
startLineNumber: 1,//开始行
startColumn: 0,//开始列
endLineNumber: 3,//结束行
endColumn: 43,//结束列
})
汉化
vue.config.js
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const MonacoLocalesPlugin = require('monaco-editor-locales-plugin');
configureWebpack: (config) => {
config.plugins.push(
new MonacoWebpackPlugin({
languages: ['sql'] // 目前只处理SQL语言
// features: ['coreCommands', 'find'] // 基本命令和搜索功能
})
);
config.plugins.push(
new MonacoLocalesPlugin({
//设置支持的语言
languages: ['zh-cn'],
//默认语言
defaultLanguage: 'zh-cn',
//打印不匹配的文本
logUnmatched: false
})
);
版本相关
"monaco-editor": "^0.46.0",
"monaco-editor-webpack-plugin": "^6.0.0",
"monaco-editor-locales-plugin": "^0.0.3",
效果
总结
更多的用法可以参考Monaco Editor在git上面的示例.