Vue3 中集成 Monaco Editor

7,968 阅读4分钟

官网: microsoft.github.io/monaco-edit…

文档地址: microsoft.github.io/monaco-edit…

npm 地址: www.npmjs.com/package/mon…

Vue3 中集成 Monaco Editor

安装

npm i monaco-editor

创建一个简单的代码编辑器

本段介绍了如何在项目中引入 Monaco Editor 以及 如何创建一个简单的代码编辑器。

实现效果

image-20240305150450440

实现代码

<script setup>
import { ref, onMounted } from 'vue'

// 引入 Monaco Editor
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'

// 引入自定义语言 Monarch
import customLangMonarch from '@/components/editor/custom-lang-monarch'

// 注册自定义语言
monaco.languages.register({ id: 'custom' })
// 设置 Monarch Tokens 提供者
monaco.languages.setMonarchTokensProvider('custom', customLangMonarch)

// 创建对编辑器的引用
const editor = ref()

// 在组件挂载后执行
onMounted(() => {
  // 创建 Monaco Editor 实例
  monaco.editor.create(editor.value, {
    // 设置初始代码值
    value: `// 在此处输入您的语言的源代码...
class MyClass {
  @attribute
  void main() {
    Console.writeln( "Hello Monarch world\n");
  }
}`,
    // 设置语言为自定义语言
    language: 'custom'
  })
})
</script>

<template>
  <div class="editor-container">
    <!-- 将编辑器容器绑定到 ref -->
    <div id="editor" ref="editor"></div>
  </div>
</template>

<style scoped>
.editor-container {
  width: 100%;
  height: 100%;
  padding: 15px;
  background: #ffffff;
}
#editor {
  width: 100%;
  height: 100%;
}
</style>

Monaco Editor的自定义语言定义,使用了Monarch格式。它定义了关键字、类型关键字、运算符、符号等各种语法元素的词法分析规则。注释部分详细说明了各个规则的作用和匹配方式。

/**
 * 自定义语言定义,参考自 https://microsoft.github.io/monaco-editor/monarch.html
 * (采用 Monarch 格式)
 */
export default {
  // 关键字
  keywords: [
    'abstract', 'continue', 'for', 'new', 'switch', 'assert', 'goto', 'do', 'if', 'private',
    'this', 'break', 'protected', 'throw', 'else', 'public', 'enum', 'return', 'catch', 'try',
    'interface', 'static', 'class', 'finally', 'const', 'super', 'while', 'true', 'false'
  ],

  // 类型关键字
  typeKeywords: ['boolean', 'double', 'byte', 'int', 'short', 'char', 'void', 'long', 'float'],

  // 运算符
  operators: [
    '=', '>', '<', '!', '~', '?', ':', '==', '<=', '>=', '!=', '&&', '||', '++', '--', '+', '-',
    '*', '/', '&', '|', '^', '%', '<<', '>>', '>>>', '+=', '-=', '*=', '/=', '&=', '|=', '^=',
    '%=', '<<=', '>>=', '>>>='
  ],

  // 符号
  symbols: /[=><!~?:&|+\-*\/\^%]+/,

  // 转义字符
  escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,

  // 主要的标记器定义
  tokenizer: {
    root: [
      // 标识符和关键字
      [
        /[a-z_$][\w$]*/,
        {
          cases: {
            '@typeKeywords': 'keyword',
            '@keywords': 'keyword',
            '@default': 'identifier'
          }
        }
      ],
      [/[A-Z][\w\$]*/, 'type.identifier'], // 为了美观显示类名

      // 空白字符
      { include: '@whitespace' },

      // 分隔符和运算符
      [/[{}()\[\]]/, '@brackets'],
      [/[<>](?!@symbols)/, '@brackets'],
      [
        /@symbols/,
        {
          cases: {
            '@operators': 'operator',
            '@default': ''
          }
        }
      ],

      // @ 注解
      [/@\s*[a-zA-Z_\$][\w\$]*/, { token: 'annotation', log: 'annotation token: $0' }],

      // 数字
      [/\d*\.\d+([eE][\-+]?\d+)?/, 'number.float'],
      [/0[xX][0-9a-fA-F]+/, 'number.hex'],
      [/\d+/, 'number'],

      // 分隔符:放在数字后面是因为 .\d 浮点数
      [/[;,.]/, 'delimiter'],

      // 字符串
      [/"([^"\\]|\\.)*$/, 'string.invalid'], // 未终止的字符串
      [/"/, { token: 'string.quote', bracket: '@open', next: '@string' }],

      // 字符
      [/'[^\\']'/, 'string'],
      [/(')(@escapes)(')/, ['string', 'string.escape', 'string']],
      [/'/, 'string.invalid']
    ],

    comment: [
      [/[^\/*]+/, 'comment'],
      [/\/\*/, 'comment', '@push'], // 嵌套注释
      ['\\*/', 'comment', '@pop'],
      [/[\/*]/, 'comment']
    ],

    string: [
      [/[^\\"]+/, 'string'],
      [/@escapes/, 'string.escape'],
      [/\\./, 'string.escape.invalid'],
      [/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }]
    ],

    whitespace: [
      [/[ \t\r\n]+/, 'white'],
      [/\/\*/, 'comment', '@comment'],
      [/\/\/.*$/, 'comment']
    ]
  }
}

Monaco Editor的API和基本用法

常见的 API

API / 基本用法描述
monaco.editor.create()创建一个新的编辑器实例
monaco.editor.setModel()设置编辑器的数据模型
monaco.editor.getModels()获取当前所有已创建的编辑器的数据模型
monaco.editor.IStandaloneCodeEditor表示一个独立的代码编辑器实例
monaco.editor.IModel表示编辑器的数据模型
monaco.editor.createModel()创建一个新的编辑器数据模型
editor.setValue(content)设置编辑器的文本内容
editor.getValue()获取编辑器的文本内容
editor.onDidChangeModelContent(callback)监听编辑器内容变化事件
editor.dispose()销毁编辑器实例
monaco.editor.defineTheme()定义编辑器主题
monaco.languages.register()注册新的语言
monaco.languages.setMonarchTokensProvider()设置 Monarch 语法解析器提供者

下面将常见的几个 API 列举一些使用实例。

基本用法

1. 创建编辑器实例:

通过 monaco.editor.create() 方法创建一个新的编辑器实例,并指定其容器和初始配置。

const editor = monaco.editor.create(document.getElementById('editor-container'), {
    value: 'console.log("Hello, world!")',
    language: 'javascript'
});

2. 设置编辑器内容:

使用 editor.setValue() 方法设置编辑器的文本内容。

editor.setValue('console.log("Hello, world!")');

3. 获取编辑器内容:

使用 editor.getValue() 方法获取编辑器的文本内容。

const content = editor.getValue();

4. 监听编辑器事件:

使用 editor.onDidChangeModelContent() 方法监听编辑器内容变化事件。

editor.onDidChangeModelContent(event => {
    console.log('Editor content changed:', event);
    console.log(editor.getValue()) // 获取更新后的内容
    emits('change', editor.getValue()) // 将更新后的内容暴露出去
});

5. 销毁编辑器实例:

使用 editor.dispose() 方法销毁编辑器实例。

editor.dispose();

创建编辑器的Options

const editorRef =ref()

onMounted(()=>{
  const editor = monaco.editor.create(editorRef.value, {
    value:"", // 编辑器初始显示内容
    language: "javascript", // 支持语言
    theme:"vs-dark" // 主题
    // .........
	})
})

<div id="editor" ref="editorRef"></div>

控制是否显示缩略图

因为manaco editor 的文档不是特别直观, 所以通过分析 "控制是否显示缩略图" 属性的来探索一下遇到其它问题的学习路径。

  1. 通过文档 microsoft.github.io/monaco-edit… 找到响应的属性
  2. 查看对应属性的描述 Interface IEditorMinimapOptions 或者通过在编辑器中跳转到属性的实现文档ts
  3. 分析各个属性

minimap: IEditorMinimapOptions

export interface IEditorMinimapOptions {
    /**
     * 是否启用缩略图的渲染。
     * 默认为 true。
     */
    enabled?: boolean;
    /**
     * 控制缩略图的渲染。
     */
    autohide?: boolean;
    /**
     * 控制缩略图在编辑器中的位置。
     * 默认为 'right'。
     */
    side?: 'right' | 'left';
    /**
     * 控制缩略图的渲染模式。
     * 默认为 'actual'。
     */
    size?: 'proportional' | 'fill' | 'fit';
    /**
     * 控制缩略图滑块的渲染。
     * 默认为 'mouseover'。
     */
    showSlider?: 'always' | 'mouseover';
    /**
     * 是否在行上渲染实际文本(而不是颜色块)。
     * 默认为 true。
     */
    renderCharacters?: boolean;
    /**
     * 限制缩略图的宽度,最多渲染一定数量的列。
     * 默认为 120。
     */
    maxColumn?: number;
    /**
     * 缩略图中字体的相对大小。默认为 1。
     */
    scale?: number;
}

options列表

value
language
theme
readOnly
selectOnLineNumbers: true,
roundedSelection: false,
readOnly: false, // 只读
writeOnly: false,
cursorStyle: 'line', //光标样式
automaticLayout: true, //自动布局
glyphMargin: true, //字形边缘
useTabStops: false,
fontSize: 18, //字体大小
autoIndent: true, //自动布局
//quickSuggestionsDelay: 500,   //代码提示延时

查询 options 的 地址

microsoft.github.io/monaco-edit… 很多很全

image-20240305153049248

Vue3 集成 Monaco.png