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

npm install monaco-editor

yarn add monaco-editor

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

npm install monaco-editor-webpack-plugin

yarn add monaco-editor-webpack-plugin
  • webpack.config.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
import * as monaco from "monaco-editor";
  1. 创建Monaco Editor实例
this.editor = monaco.editor.create(this.$el, options);
  1. 监听内容变化更新 (v-model双向绑定)
this.editor.onDidChangeModelContent((event) => {
  const value = this.editor.getValue();
  if (this.value !== value) {
    this.$emit("change", value, event);
  }
});

常用的option配置

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

{
  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 支持自定义主题,官方案例

// 根据语言设置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",
	},
});

自定义快捷键,右键菜单

官方案例

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中找到。

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() 来获取选中部分的位置。

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

高亮部分代码

官方示例

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

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

// 移除高亮
decorations.clear()

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

插入空行

官方案例

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

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 方法插入到编辑器中。可以配合插入空行的方法,在编辑器任意位置插入一段话/一个按钮。

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 回退咋办?
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

原文链接:https://juejin.cn/post/7344571285848866828 作者:水煮鱼11

(0)
上一篇 2024年3月11日 上午10:26
下一篇 2024年3月11日 上午10:36

相关推荐

发表回复

登录后才能评论