您现在的位置是:网站首页> 编程资料编程资料

Monaco Editor开发SQL代码提示编辑器实例详解_vue.js_

2023-05-24 303人已围观

简介 Monaco Editor开发SQL代码提示编辑器实例详解_vue.js_

安装

安装依赖,这里请注意版本

yarn add monaco-editor@0.29.1 yarn add monaco-editor-webpack-plugin@5.0.0 

配置 webpack 插件

// vue.config.js ... const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin') module.export = { ... configureWebpack: { name: name, resolve: { alias: { '@': resolve('src'), }, }, plugins: [new MonacoWebpackPlugin()], }, ... } 

请注意 monaco-editor-webpack-plugin 和 monaco-editor 的对应关系,否则可能会出现无法运行的情况。

monaco-editor-webpack-pluginmonaco-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.*

简易 SQL 编辑器

先上干货!

相关功能

获取选中代码

 getSelectionVal() { const selection = this.monacoEditor.getSelection() // 获取光标选中的值 const { startLineNumber, endLineNumber, startColumn, endColumn } = selection const model = this.monacoEditor.getModel() return model.getValueInRange({ startLineNumber, startColumn, endLineNumber, endColumn, }) }, 

替换选中代码

insertStringInTemplate(str) { const selection = this.monacoEditor.getSelection() // 获取光标选中的值 const { startLineNumber, endLineNumber, startColumn, endColumn } = selection const model = this.monacoEditor.getModel() const textBeforeSelection = model.getValueInRange({ startLineNumber: 1, startColumn: 0, endLineNumber: startLineNumber, endColumn: startColumn, }) const textAfterSelection = model.getValueInRange({ startLineNumber: endLineNumber, startColumn: endColumn, endLineNumber: model.getLineCount(), endColumn: model.getLineMaxColumn(model.getLineCount()), }) this.monacoEditor.setValue(textBeforeSelection + str + textAfterSelection) this.monacoEditor.focus() this.monacoEditor.setPosition({ lineNumber: startLineNumber, column: startColumn + str.length, }) }, 

处理光标位置

 setPosition(column, lineNumber) { this.monacoEditor.setPosition({ column, lineNumber }) }, getPosition() { return this.monacoEditor.getPosition() }, 

自定义 SQL 库表提示,并保留原有 SQL 提示

首先由后端提供具体的库表信息:

export const hintData = { adbs: ['dim_realtime_recharge_paycfg_range', 'dim_realtime_recharge_range'], dimi: ['ads_adid', 'ads_spec_adid_category'], } 

然后根据已有库表信息进行自定义 AutoComplete

import * as monaco from 'monaco-editor' import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql' const { keywords } = language export default { ... mounted() { this.initEditor() }, methods: { ... registerCompletion() { const _that = this monaco.languages.registerCompletionItemProvider('sql', { triggerCharacters: ['.', ...keywords], provideCompletionItems: (model, position) => { let suggestions = [] const { lineNumber, column } = position const textBeforePointer = model.getValueInRange({ startLineNumber: lineNumber, startColumn: 0, endLineNumber: lineNumber, endColumn: column, }) const tokens = textBeforePointer.trim().split(/\s+/) const lastToken = tokens[tokens.length - 1] // 获取最后一段非空字符串 if (lastToken.endsWith('.')) { const tokenNoDot = lastToken.slice(0, lastToken.length - 1) if (Object.keys(_that.hintData).includes(tokenNoDot)) { suggestions = [..._that.getTableSuggest(tokenNoDot)] } } else if (lastToken === '.') { suggestions = [] } else { suggestions = [..._that.getDBSuggest(), ..._that.getSQLSuggest()] } return { suggestions, } }, }) }, // 获取 SQL 语法提示 getSQLSuggest() { return keywords.map((key) => ({ label: key, kind: monaco.languages.CompletionItemKind.Enum, insertText: key, })) }, getDBSuggest() { return Object.keys(this.hintData).map((key) => ({ label: key, kind: monaco.languages.CompletionItemKind.Constant, insertText: key, })) }, getTableSuggest(dbName) { const tableNames = this.hintData[dbName] if (!tableNames) { return [] } return tableNames.map((name) => ({ label: name, kind: monaco.languages.CompletionItemKind.Constant, insertText: name, })) }, initEditor() { if (this.$refs.codeContainer) { this.registerCompletion() // 初始化编辑器,确保dom已经渲染 this.monacoEditor = monaco.editor.create(this.$refs.codeContainer, { value: '', // 编辑器初始显示文字 language: 'sql', // 语言 readOnly: this.readOnly, // 是否只读 Defaults to false | true automaticLayout: true, // 自动布局 theme: this.theme, // 官方自带三种主题vs, hc-black, or vs-dark minimap: { // 关闭小地图 enabled: false, }, tabSize: 2, // tab缩进长度 }) } this.setValue(this.value) }, } } 

编辑器 resize

 resize() { this.monacoEditor.layout() }, 

编辑器设置主题

注意!设置主题并非在编辑器实例上修改的哦!

 setTheme() { monaco.editor.setTheme(this.theme) }, 

SQL 代码格式化

编辑器自身不支持 sql 格式化(试了下 JavaScript 是支持的),所以用到了 sql-formatter 这个库。

import { format } from 'sql-formatter' ... format() { this.monacoEditor.setValue( format(this.monacoEditor.getValue(), { indentStyle: 'tabularLeft', }), ) }, ... 

右键菜单汉化

需要安装以下两个库

npm install monaco-editor-nls --save npm install monaco-editor-esm-webpack-plugin --save-dev 

具体用法可以直接去 www.npmjs.com/package/mon… 里面看,我就不搬运了~

记得销毁编辑器对象哦

 beforeDestroy() { if (this.monacoEditor) { this.monacoEditor.dispose() } }, 

踩坑

下面是我遇到的几个坑。

  • 最新版本的 Monaco Editor 已经使用了 ES2022 的语法,所以老项目可能会出现编译不过的问题。所以我把版本调低了一些。
  • 在最初调试编辑器的时候出现了无法编辑的情况,后来发现是同事用到了 default-passive-events 这个库来关闭 chrome 的 Added non-passive event listener to a scroll-blocking event. Consider marking event handler as 'passive' to make the page more responsive 警

-六神源码网