Monaco Editor installation, used in vue3, custom highlight, custom prompt, with complete code

1. Installation

	yarn add monaco-editor
	npm install monaco-editor --save

Two, use

<div ref="editorContainer" style="height:100%;"></div>
import * as monaco from 'monaco-editor';
const editorContainer = ref<any>(null)
const editor = ref<any>(null)
onMounted(() => {
    
    
  editor.value = monaco.editor.create(editorContainer.value,{
    
    
    value: "test",
    language:"javascript",
    folding: true, // 是否折叠
    foldingHighlight: true, // 折叠等高线
    foldingStrategy: "indentation", // 折叠方式  auto | indentation
    showFoldingControls: "always", // 是否一直显示折叠 always | mouseover
    disableLayerHinting: true, // 等宽优化
    emptySelectionClipboard: false, // 空选择剪切板
    selectionClipboard: false, // 选择剪切板
    automaticLayout: true, // 自动布局
    codeLens: false, // 代码镜头
    scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
    colorDecorators: true, // 颜色装饰器
    accessibilitySupport: "off", // 辅助功能支持  "auto" | "off" | "on"
    lineNumbers: "on", // 行号 取值: "on" | "off" | "relative" | "interval" | function
    lineNumbersMinChars: 5, // 行号最小字符   number
    readOnly: false, //是否只读  取值 true | false
  })
})

3. Custom highlighting

monaco.languages.register({
    
     id: 'mylanguages' })
monaco.languages.setMonarchTokensProvider('mylanguages', {
    
    
  ignoreCase: true, // 忽略大小写
  tokenizer: {
    
    
    root:[
          [/curl/, {
    
    token: "string.escape"}],
          [/-X|-H|-d/, {
    
    token: "keyword"}],
          [/POST|GET|DELETE|PATCH|PUT/, {
    
    token: "comment.doc"}],
    ],
  }
})

The highlighting rules are in the root. [/curl/, {token: “string.escape”}]: Indicates that the highlight color of 'curl' is pink.
Highlight color reference: https://microsoft.github.io/monaco-editor/monarch.html
Effect:
insert image description here

4. Custom prompts

monaco.languages.registerHoverProvider('mylanguages', {
    
     // 光标移入提示功能
    provideHover: function (model, position, token) {
    
    
      const lineword = model.getLineContent(position.lineNumber) // 获取光标悬停所在行的所有内容
      const word = model.getWordAtPosition(position)?.word // 获取光标悬停的单词
        if (word === "name") {
    
    
          return {
    
    
            contents: [
              {
    
     value: `${
      
      word}` },
              {
    
    
                value: [
                  "这是name的一些介绍",
                  "这是name的一些介绍",
                ].join("\n\n"),
              },
            ],
          };
        } else if(undefined !== word){
    
    
          return {
    
    
            contents: [
              {
    
     value: `${
      
      word}` },
              {
    
    
                value: [
                  lineword
                ].join("\n\n"),
              },
            ],
          }
        }
    },
  });

Effect:
insert image description here

insert image description here

Five, complete code

1. Parent component:
The height, initial content, highlight type, and read-only required by the parent component of HomeView.vue passed to the child component. The
child component passes the value to the parent component in real time through the editorChange method

<template>
  <div>
      <monaco
        ref="monacoEdit"
        :value="data"
        :readonly="false"
        type="curl"
        :height="300"
        @editorChange="editorChange"
      ></monaco>
  </div>
</template>
<script setup lang="ts">
import monaco from '../components/MonacoView.vue'
import {
    
     ref, toRefs, reactive } from "vue"
 const data = ref('test')
function editorChange(val: string) {
    
    
//val:子组件实时传过来的值
  console.log(val)
}
</script>
 
<style scoped>
  
</style>

2. Subcomponent: MonacoView.vue

<template>
  <div class="editorapp">
    <div ref="editorContainer" :style="{height:editor_height}"></div>
  </div>
  
</template>

<script setup lang="ts">
import {
    
     onMounted, ref, toRaw, watchEffect } from "vue"
// 引入方法一
import * as monaco from 'monaco-editor';

// 引入方法二( 这种引入方法体积小但没有鼠标悬停方法registerHoverProvider)
// import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
// import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'

const emit = defineEmits(['contentChange'])
const props = defineProps({
    
    
  value: {
    
    
    type: String,
    default: '',
  },
  height: {
    
     // 编辑器height
    type: [String, Number],
    default: 500,
  },
  readonly: {
    
     // 是否只读
    type: Boolean,
    default: false,
  },
  type: {
    
     // 高亮类型(javascript、curl等,javascript:自带的,curl:自定义的高亮规则)
    type: String,
    default: 'javascript',
  }
})
const editorContainer = ref<any>(null)
const editor = ref<any>(null)
const data = ref(props.value)
const editor_height = ref(`${
      
      props.height}px`)
onMounted(() => {
    
    
   // 初始化编辑器,确保dom已经渲染
  if(props.type === 'curl'){
    
     // 如果是curl 类型则重新定义高亮规则,否则使用自带的高亮语言
    monaco.languages.register({
    
     id: props.type })
    monaco.languages.setMonarchTokensProvider(props.type, {
    
    
      ignoreCase: true,
      tokenizer: {
    
    
        root:[
              [/curl/, {
    
    token: "string.escape"}],
              [/-X|-H|-d/, {
    
    token: "keyword"}],
              [/POST|GET|DELETE|PATCH|PUT/, {
    
    token: "comment.doc"}],
        ],
      }
    })
  }
  monaco.languages.registerHoverProvider(props.type, {
    
     // 光标移入提示功能
    provideHover: function (model, position, token) {
    
    
      const lineword = model.getLineContent(position.lineNumber) // 获取光标悬停所在行的所有内容
      const word = model.getWordAtPosition(position)?.word // 获取光标悬停的单词
        if (word === "name") {
    
    
          return {
    
    
            contents: [
              {
    
     value: `${
      
      word}` },
              {
    
    
                value: [
                  "这是name的一些介绍",
                  "这是name的一些介绍",
                ].join("\n\n"),
              },
            ],
          };
        } else if(undefined !== word){
    
    
          return {
    
    
            contents: [
              {
    
     value: `${
      
      word}` },
              {
    
    
                value: [
                  lineword
                ].join("\n\n"),
              },
            ],
          }
        }
    },
  });
  editor.value = monaco.editor.create(editorContainer.value,{
    
    
    value: data.value,
    language:props.type,
    folding: true, // 是否折叠
    foldingHighlight: true, // 折叠等高线
    foldingStrategy: "indentation", // 折叠方式  auto | indentation
    showFoldingControls: "always", // 是否一直显示折叠 always | mouseover
    disableLayerHinting: true, // 等宽优化
    emptySelectionClipboard: false, // 空选择剪切板
    selectionClipboard: false, // 选择剪切板
    automaticLayout: true, // 自动布局
    codeLens: false, // 代码镜头
    scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
    colorDecorators: true, // 颜色装饰器
    accessibilitySupport: "off", // 辅助功能支持  "auto" | "off" | "on"
    lineNumbers: "on", // 行号 取值: "on" | "off" | "relative" | "interval" | function
    lineNumbersMinChars: 5, // 行号最小字符   number
    readOnly: props.readonly, //是否只读  取值 true | false

  })
  editor.value.onDidChangeModelContent((val: any) => {
    
    
    //内容改变时给父组件实时返回值
    emit('editorChange', toRaw(editor.value).getValue())
  })
})
watchEffect(()=>{
    
     // 监听父组件值的变化,重新赋值给编辑器
  if(editor.value)
    toRaw(editor.value).setValue(props.value)
})
</script>

<style scoped>
.editorapp {
    
    
  height: 100%;
  width: 100vh;
}
</style>

Effect:
insert image description here
insert image description here

6. Practical issues

Two-way binding is not implemented. It will be troublesome for the child component to pass the value to the parent component. If many pages are used, it is necessary to repeatedly write a lot of methods for receiving parameters, and repeatedly define many additional parameters to receive the value of the child component.

The reason for defining additional parameters in the parent component to receive the passed value of the child component:
![parent component](https://img-blog.csdnimg.cn/cdbb00c111a041dd88761807ce28aa22.png
Modification:
In the parent component: changed to v-model, the editorChange method is no longer needed

<monaco-editor ref="monacoEdit" v-model="data" :readonly="false" main="javascript" bgcolor="vs-dark'" />

In the subcomponent: (only the modified part is displayed)

// 编辑器避免重复赋值
watchEffect(() => {
    
    
  if (editor.value && toRaw(editor.value).getValue() !== props.modelValue)
    toRaw(editor.value).setValue(props.modelValue)
})
const emit = defineEmits<{
    
    
  (e: 'update:modelValue', value: string): void
}>()
// 监听值的变化
  editor.value.onDidChangeModelContent((val: any) => {
    
    
    // 给父组件实时返回最新文本
    emit('update:modelValue', toRaw(editor.value).getValue())
  })

Guess you like

Origin blog.csdn.net/qq_29184685/article/details/129087445