在线代码编辑器monaco-editor的使用

最近接到一个项目中增加在线代码编辑器的功能需求,经过对比(其实算是半指定),选择了monaco-editor,这个库跟VSCode的源码高度重合,所以本身功能是比较强大的,包含自定义主题、自定义提示、设置代码对比、多文件编辑、代码信息提示等功能。但是目前项目中暂时不需要比较复杂的功能,主要功能需求点就是获取指定函数的函数体,添加代码注释及提示以及部分行禁用的功能,以下进行简单介绍以及遇到的踩坑点。

项目采用Vue + TS, 使用的monaco-editor及Vue的版本为:

"vue": "^3.4.19",
"monaco-editor": "0.30.1",

"typescript": "^5.3.3",

1. 安装

yarn 或者 npm 或者pnpm

2. 引入(使用前准备)

import * as monaco from 'monaco-editor';

集成到项目中的时候控制台报了一个错误,关于getWorker报错,在网上找的解决办法:

// 解决getWorker报错问题 Uncaught (in promise) Error: Unexpected usage at _EditorSimpleWorker.loadForeignModule
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';

// @ts-ignore
  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();
    },
  };

3. 使用

1. 编辑器结构(可支持展示多个编辑器,进行分别输入、取值等)

// 如果仅需一个编辑器
<div id="container" class="app-monaco"></div>
// 如果项目中需要多个编辑器,可以传入对应的id(不可相同)
<div :id="containerId" class="app-monaco"></div>

// monaco 实例
const editor = ref<monaco.editor.IStandaloneCodeEditor | null>(null);
// 因为会存在一个页面上有两个monaco-editor实例的问题,所以动态展示id
const containerId = ref(props.containerTitle ?? 'container');

2. 配置项(可根据项目需要进行选择)

// monaco editor配置
  const monacoEditorConfig = ref({
    automaticLayout: true, // 自动布局,编辑器自适应大小
    theme: 'vs-dark', // 官方自带三种主题vs, hc-black, or vs-dark
    minimap: {
      enabled: true, // 是否开启右侧代码小窗
    },
    folding: true, // 是否折叠
    foldingHighlight: true, // 折叠等高线
    foldingStrategy: 'auto', // 折叠方式
    showFoldingControls: 'always', // 是否一直显示折叠
    disableLayerHinting: true, // 等宽优化
    emptySelectionClipboard: false, // 空选择剪切板
    selectionClipboard: false, // 选择剪切板
    codeLens: true, // 代码镜头
    scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
    colorDecorators: true, // 颜色装饰器
    accessibilitySupport: 'on', // 辅助功能支持"auto" | "off" | "on"
    selectOnLineNumbers: true, //显示行号
    lineNumbers: 'on', // 行号 取值: "on" | "off" | "relative" | "interval" | function
    lineNumbersMinChars: 4, // 行号最小字符   number
    enableSplitViewResizing: false,
    readOnly: false, //是否只读  取值 true | false
    fontSize: 18,
    cursorStyle: 'line', //光标样式
    glyphMargin: true, //字形边缘
    useTabStops: false,
    autoIndent: true, //自动布局
    quickSuggestionsDelay: 100, //代码提示延时
    dropIntoEditor: {
      enabled: false, // 能否把文字拖拽进编辑器
    },
  });

3. 创建编辑器

// 初始化manoca编辑器
  const initEditor = () => {
    const config = Object.assign({}, monacoEditorConfig.value, {
      language: props.language,
      value: textValue.value,
    });
    // 创建 monaco 实例
    editor.value = monaco.editor.create(document.getElementById(containerId.value)!, config);
  };

4. 编辑器取值事件(通过onDidChangeCursorPosition方法,通过onChange函数暴露出去)

// 编辑器改变的回调,用来取方法内的值 
// 编辑器内容change事件
editor.value.onDidChangeModelContent((event: any) => {
   // @ts-ignore
   // 触发父组件的 change 事件,通知编辑器内容变化(直接获取编辑器内容)
   // props.onChange?.(rawEditor.getValue());
   // 只取方法内的内容
   const content = getLineVal();
   props.onChange?.(content);
}

5. 获取值(getValue方法)

editor.value.getValue();

6.设置值(setValue方法)

editor.value.setValue('123');

7. 获取指定范围的内容(主要是通过确定对应的行号及列号,getValueInRange方法)

const getLineVal = () => {
    const model = toRaw(editor.value)?.getModel();
    // 获取行数
    const lineNum = model?.getLineCount();
    let value;
    if (lineNum) {
      // 去掉注释以及函数头的行,再去掉最后返回值以及大括号的行
      value = model?.getValueInRange({
        startLineNumber: 11,
        startColumn: 1,
        endLineNumber: lineNum - 1,
        endColumn: 1,
      });
    }
    return value;
  };

8. 如果父组件有禁用事件,动态修改编辑器是否可用(updateOptions方法)

// 监听disabled变化,设置禁用
  watchEffect(() => {
    if (props.disabled) {
      toRaw(editor.value)?.updateOptions({ readOnly: true });
    }
});

9. 编辑器重新布局(layout方法)

// 当容器尺寸发生变化的时候(例如:浏览器 resize),需要通过 layout 接口让 MonacoEditor 重新计算布局
  // 编辑器 resize
  const layout = () => {
    if (editor.value) {
      toRaw(editor.value)?.layout();
    }
  };

// 可以加个防抖
const debounedLayout = debounce(layout, 500);

// 重新计算尺寸
window.addEventListener('resize', debounedLayout);

10. 改变光标位置(setPosition方法)

const changePosition = () => {
    toRaw(editor.value)?.setPosition({ lineNumber: 1, column: 1 });
    toRaw(editor.value)?.focus();
};

11. 设置主题(setTheme方法)

// 编辑器设置主题 并非在实例上
const setEditorTheme = () => {
    monaco.editor.setTheme('vs');
};

12. 获取选中的代码(getSelection方法)

const getSelectionVal = () => {
    const selection: monaco.Selection | null | undefined = toRaw(editor.value)?.getSelection();
    console.log('===selection', selection);
    if (selection) {
      const { startLineNumber, endLineNumber, startColumn, endColumn } = selection;
      const model = toRaw(editor.value)?.getModel();

      return model?.getValueInRange({
        startLineNumber,
        startColumn,
        endLineNumber,
        endColumn,
      });
    }
  };

13. 向指定位置插入代码(executeEdits方法)

const insertCode = (code: string) => {
    const rawEditor = toRaw(editor.value);
    const model = rawEditor?.getModel();
    // 获取行数
    const lineNum = model?.getLineCount();
    if (lineNum) {
      // 计算范围,插入到第一行和最后一行中间
      const range = {
        startLineNumber: 2,
        startColumn: 1,
        endLineNumber: lineNum,
        endColumn: 1,
      };
      // 增加空格
      if (!code.startsWith(' ')) {
        code = '    ' + code;
      }
      // 增加三行空格,以免返回的数据没有空格,不好看
      // const textCode = [code, '', '', ''].join('\n');
      rawEditor?.executeEdits('', [
        {
          range: range, // 要修改的范围
          // text: textCode, // 要插入的内容
          text: code, // 要插入的内容
          forceMoveMarkers: true, // 是否强制移动光标
        },
      ]);
    }
  };

14. 指定行设置禁用(就是改变光标的位置,只让它达到你的只读范围)

// 光标改变的回调,用来设置某些行不可用
editor.value.onDidChangeCursorPosition(function (e) {
   // 获取行数
   const lineNum = model?.getLineCount();
   // 前10行不可编辑
   if (e.position.lineNumber < 11) {
       rawEditor?.setPosition({
         lineNumber: 11,
         column: 1,
        });
   }
    // 后2行不可编辑
    if (e.position.lineNumber > lineNum - 2) {
      rawEditor?.setPosition({
         lineNumber: lineNum - 2,
         column: 1,
       });
    }
 });

15. 销毁编辑器实例

const destroyEditor = () => {
    if (editor.value == null) return;
    // 销毁编辑器
    toRaw(editor.value)?.dispose();
    editor.value = null;
};

4. 遇到的大坑

1. 可以看到,我的代码中有时会使用toRaw这个方法,是因为editor的实例在创建时被代理了,导致本身实例上的很多方法没有了,所以将其转为纯粹的变量,有些方法才可以使用,如果没遇到此问题的话,可以直接editor.(相关方法);

2. position或者range相关的,包含开始和结束的位置或者范围信息,包含开始,但是不包含结束,比如startLine是1,endLine是10,getValueRange时就是前9行,这个使用时注意下;

暂时就发现这些问题,以及基本的方法使用,后续需求更新时再加,希望对各位有点帮助~

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React-Monaco-Editor 是一个 React 封装的 Monaco Editor,可以在 React 应用中方便地使用 Monaco EditorMonaco Editor 是一个强大的代码编辑器,被广泛应用于微软的 VS Code 编辑器中。 下面是使用 React-Monaco-Editor 的步骤: 1. 安装 React-Monaco-Editor 使用 npm 或者 yarn 安装: ``` npm install react-monaco-editor ``` 或者 ``` yarn add react-monaco-editor ``` 2. 导入 React-Monaco-Editor 在需要使用 Monaco Editor 的组件中导入 React-Monaco-Editor: ```jsx import React, { useState } from 'react'; import MonacoEditor from 'react-monaco-editor'; ``` 3. 使用 React-Monaco-Editor 使用 React-Monaco-Editor 组件,传入需要编辑的代码和一些配置项: ```jsx function MyEditor() { const [code, setCode] = useState('const hello = "Hello, world!";'); const options = { selectOnLineNumbers: true }; return ( <MonacoEditor width="800" height="600" language="javascript" theme="vs-dark" value={code} options={options} onChange={setCode} /> ); } ``` 在上面的例子中,我们使用 useState 来管理编辑器中的代码,然后传入了一些配置项,比如语言是 JavaScript,主题是暗色,宽度和高度是 800 和 600。还传入了一个 onChange 函数,用来实时更新编辑器中的代码。 4. 更多配置项 React-Monaco-Editor 支持很多配置项,比如: - language:编辑器的语言,如 JavaScript、HTML、CSS 等。 - theme:编辑器的主题,如 vs、vs-dark、hc-black 等。 - value:编辑器的默认值。 - options:编辑器的选项,如是否显示行号、缩进大小等。 - editorDidMount:当编辑器初始化完成后的回调函数。 - onChange:当编辑器内容改变时的回调函数。 更多配置项可以参考官方文档:https://github.com/superRaytin/react-monaco-editor#readme。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值