MONACO EDITOR基本使用指南

MONACO EDITOR基本使用指南

Monaco Editor 是由 Microsoft 开发并开源的一款代码编辑器,它是 Visual Studio Code 的核心编辑器部分。同时还提供了丰富的 API,使得开发者可以根据自己的需求进行定制。Monaco Editor 有着强大的功能且可以满足各种奇葩产品的癖好。

但是由于官方文档过于精简,demo 看不懂的情况下,简单介绍一下 Monaco Editor 的基本使用。由于项目框架是vue2 + webpack,所以基础封装以 vue2 为主。

官网

Monaco Editor 官网

官网的 Playground 可以在线跑 demo,以及提供的了很多功能点的 demo,比较容易理解。
官网的 Documentation 是 api 文档,可以通过快速搜索找到 api 的传参格式样例。

安装

安装monaco-editor

shell 复制代码
npm install monaco-editor

yarn add monaco-editor

安装 monaco-editor-webpack-plugin,是一个用于简化 Monaco Editor 在 Webpack 构建系统中的集成的插件。Monaco Editor 由许多模块和语言特性组成,如果手动管理这些资源可能会非常复杂。使用这个插件,你可以更容易地将 Monaco Editor 集成到你的 Webpack 项目中。

shell 复制代码
npm install monaco-editor-webpack-plugin

yarn add monaco-editor-webpack-plugin
  • webpack.config.js
js 复制代码
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");

module.exports = {
  // ...其他配置
  plugins: [
    new MonacoWebpackPlugin({
      languages: ["javascript", "sql"], // 还有很多不同的语言
    }),
  ],
};

monaco-editor-webpack-plugin 可以配置语言,只对需要的语言进行打包。

monaco-editor-webpack-plugin 的版本要和 monaco editor 匹配。

monaco-editor-webpack-plugin monaco-editor
7.*.* >= 0.31.0
6.*.* 0.30.*
5.*.* 0.29.*
4.*.* 0.25.*, 0.26.*, 0.27.*, 0.28.*
3.*.* 0.22.*, 0.23.*, 0.24.*
2.*.* 0.21.*
1.9.* 0.20.*
1.8.* 0.19.*
1.7.* 0.18.*

组件封装

  1. 引入monaco
js 复制代码
import * as monaco from "monaco-editor";
  1. 创建Monaco Editor实例
js 复制代码
this.editor = monaco.editor.create(this.$el, options);
  1. 监听内容变化更新 (v-model双向绑定)
js 复制代码
this.editor.onDidChangeModelContent((event) => {
  const value = this.editor.getValue();
  if (this.value !== value) {
    this.$emit("change", value, event);
  }
});

常用的option配置

这里是所有的option配置项,但用上的不是很多而且没有翻译,这里列举一些我使用过的配置项。官方配置项文档

js 复制代码
{
  fontSize: 16, // 字体大小
  automaticLayout: true, // 编辑器自适应页面大小
  dropIntoEditor: {
    enabled: false // 能否把文字拖拽进编辑器
  },
  value: '初始内容', // 编辑器内容
  theme:  'vs' // 主题 'vs-dark', 'hc-black', 'hc-light',
  language: 'javascript', // 语言
  readOnly: true, // 只读
  minimap: {
    enabled: false, // 是否开启右侧代码小窗
  }
}

自定义主题

Monaco Editor 支持自定义主题,官方案例

js 复制代码
// 根据语言设置token
monaco.languages.setMonarchTokensProvider("自定义语言", {
	tokenizer: {
		root: [
			[/[error.*/, "custom-error"],
			[/[notice.*/, "custom-notice"],
			[/[info.*/, "custom-info"],
			[/[[a-zA-Z 0-9:]+]/, "custom-date"],
		],
	},
});

monaco.editor.defineTheme("自定义主题", {
	base: "vs",
	inherit: false,
	rules: [
		{ token: "custom-info", foreground: "808080" },
		{ token: "custom-error", foreground: "ff0000", fontStyle: "bold" },
		{ token: "custom-notice", foreground: "FFA500" },
		{ token: "custom-date", foreground: "008800" },
	],
	colors: {
		"editor.foreground": "#000000",
	},
});

自定义快捷键,右键菜单

官方案例

js 复制代码
editor.addAction({
	// action唯一的id.
	id: "my-unique-id",

	// 右键菜单栏显示的名字.
	label: "My Label!!!",

	// 绑定的按键
	keybindings: [
		monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10,
		// 组合键
		monaco.KeyMod.chord(
			monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK,
			monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyM
		),
	],

  // 在右键菜单中的哪个分组
	contextMenuGroupId: "navigation",

	contextMenuOrder: 1.5,

  // 触发的方法
	run: function (ed) {
		alert("i'm running => " + ed.getPosition());
	},
});

代码补全

官方案例

在初始化的时候注册语言的补全提示 registerCompletionItemProvider。语言包关键词可以在node_modules/monaco-editor/esm/vs/basic_languages中找到。

js 复制代码
monaco.languages.registerCompletionItemProvider("sql", {
  provideCompletionItems: async (model, position) => {
    let suggestions = [];

    const { lineNumber, column } = position;

    const textBeforePointer = model.getValueInRange({
      startLineNumber: lineNumber,
      startColumn: 0,
      endLineNumber: lineNumber,
      endColumn: column,
    });

    /**
     * 修改suggestion,suggestion就是代码提示
     * 
     * 这里可以调用接口,拉去后端提供的提示
     */

    return {
      suggestions,
    };
  },
});

插入代码

可以通过 executeEdits 方法插入代码,可以直接替换range内的代码。

range 是代表光标位置的一个对象,可以通过 editor.getPosition() 来获得。也可以通过 editor.getSelection() 来获取选中部分的位置。

js 复制代码
editor.executeEdits("", [
 {
   range: range, // 要修改的范围
   text: code, // 要插入的内容
   forceMoveMarkers: true, // 是否强制移动光标
 },
]);

高亮部分代码

官方示例

通过 createDecorationsCollection 方法来设置高亮代码,这里也需要一个range对象,来代表高亮的位置。注意,这里高亮是会跟随文字输入移动的,比如说你在高亮范围内换行了,新的一行也会继续高亮。

js 复制代码
let decorations = editor.createDecorationsCollection([
	{
    range: new monaco.Range(startLine, 1, endLine, 1),
    options: {
      isWholeLine: true,
      className: className,
    },
  },
]);

// 移除高亮
decorations.clear()

// 获得高亮的范围
decorations.getRanges()

插入空行

官方案例

插入空行是指在两行中间增加一行不属于编辑器的一行。copilot就是在输入的过程中,在后面多加几行,实现换行的效果。插入空行并不影响代码内容,一般是用来加个按钮啥的。

js 复制代码
let viewZoneId = null;
editor.changeViewZones(function (changeAccessor) {
	let domNode = document.createElement("div");
	domNode.style.background = "lightgreen";
	viewZoneId = changeAccessor.addZone({
		afterLineNumber: 3, // 在哪一行后面增加空行
		heightInLines: 3, // 空多少行
		domNode: domNode,
	});
});

changeViewZones 还有 removeZone(viewZoneId) 方法,可以删除空行。

插入额外元素

插入额外元素的demo和上面插入空行的是同一个,先创建一个 contentWidget,然后通过 addContentWidget 方法插入到编辑器中。可以配合插入空行的方法,在编辑器任意位置插入一段话/一个按钮。

js 复制代码
var contentWidget = {
	domNode: (function () {
		var domNode = document.createElement("div");
		domNode.innerHTML = "My content widget";
		return domNode;
	})(),
	getId: function () {
		return "my.content.widget";
	},
	getDomNode: function () {
		return this.domNode;
	},
	getPosition: function () {
		return {
			position: {
				lineNumber: 7,
				column: 8,
			},
			preference: [
				monaco.editor.ContentWidgetPositionPreference.ABOVE,
				monaco.editor.ContentWidgetPositionPreference.BELOW, // 这里也可以用EXECT, 这样元素会准确的放置在指定行。
			],
		};
	},
};
editor.addContentWidget(contentWidget);

如果要移除元素,可以通过 editor.removeContentWidget(id) 来将元素移除。

注意,如果元素不在额外空行,而在编辑器内,这个元素是可以选中的,可以调节他的z-index,让他不影响用户使用。

实现一个copilot

掌握上述技巧后,实现一个copilot就非常简单了。

  1. 先通过 editor.getPosition() 获取光标位置。
  2. editor.getValueInRange() 来获得光标前的内容,然后发送给gpt,获得建议的内容。
  3. 然后在光标位置用 editor.changeViewZones() 后面插入空行,空行数就是gpt的行数。
  4. 在光标位置后面用 editor.addContentWidget() 插入元素,内容是gpt的建议内容。
  5. 增加action,按tab的时候把gpt的建议内容用 executeEdits 替换光标位置的内容。且同时把新增的空行和新增的元素移除。
  6. 完事

还有一种简单点的方法 基于Monaco Editor实现在线版Copilot

踩坑

  1. 在组件外更改了内容,不能 ctrl + z 回退咋办?
js 复制代码
editor.pushUndoStop();

// 监听value,通过这个方法更改内容
editor.executeEdits("code", [
  {
    range: editor.getModel().getFullModelRange(), // full range
    text: newValue, // target value here
  },
]);

editor.pushUndoStop();
  1. 同一个页面内,不能存在两个 monaco editor, 只能设置同一种主题

Can not create two editor with different theme · Issue #1713 · microsoft/monaco-editor

相关推荐
y先森5 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy5 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189115 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿6 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡7 小时前
commitlint校验git提交信息
前端
虾球xz7 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇7 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒7 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员8 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐8 小时前
前端图像处理(一)
前端