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

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

目录

安装

简易 SQL 编辑器

相关功能

获取选中代码

替换选中代码

处理光标位置

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

编辑器 resize

编辑器设置主题

SQL 代码格式化

右键菜单汉化

记得销毁编辑器对象哦

踩坑

如何快速去看懂 Monaco Editor

安装

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

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 编辑器

先上干货!

<template> <div ref="codeContainer" class="editor-container" :style="{ height: height + 'px' }" /> </template> <script> import * as monaco from 'monaco-editor' /** * VS Code 编辑器 * * 通过 getEditorVal 函数向外传递编辑器即时内容 * 通过 initValue 用于初始化编辑器内容。 * 编辑器默认 sql 语言,支持的语言请参考 node_modules\monaco-editor\esm\vs\basic-languages 目录下~ * 编辑器样式仅有 'vs', 'vs-dark', 'hc-black' 三种 */ export default { name: 'MonacoEditor', props: { initValue: { type: String, default: '', }, readOnly: Boolean, language: { type: String, default: 'sql', }, height: { type: Number, default: 300, }, theme: { type: String, default: 'vs', }, }, data() { return { monacoEditor: null, // 语言编辑器 } }, computed: { inputVal() { return this.monacoEditor?.getValue() }, }, watch: { inputVal() { if (this.monacoEditor) { this.$emit('change', this.monacoEditor.getValue()) } }, theme() { this.setTheme(this.theme) }, height() { this.layout() }, }, mounted() { this.initEditor() }, beforeDestroy() { if (this.monacoEditor) { this.monacoEditor.dispose() } }, methods: { 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.setInitValue() }, focus() { this.monacoEditor.focus() }, layout() { this.monacoEditor.layout() }, getValue() { return this.monacoEditor.getValue() }, // 将 initValue Property 同步到编辑器中 setInitValue() { this.monacoEditor.setValue(this.initValue) }, setTheme() { monaco.editor.setTheme(this.theme) }, getSelectionVal() { const selection = this.monacoEditor.getSelection() // 获取光标选中的值 const { startLineNumber, endLineNumber, startColumn, endColumn } = selection const model = this.monacoEditor.getModel() return model.getValueInRange({ startLineNumber, startColumn, endLineNumber, endColumn, }) }, setPosition(column, lineNumber) { this.monacoEditor.setPosition({ column, lineNumber }) }, getPosition() { return this.monacoEditor.getPosition() }, }, } </script> <style lang="scss" scoped></style> 相关功能 获取选中代码 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 <some> event. Consider marking event handler as 'passive' to make the page more responsive 警告。结果拦截一些 event。

如何快速去看懂 Monaco Editor

一开始我看它的官方文档是非常懵的,各种接口、函数、对象的定义,完全不像是个前端库那么好理解。鼓捣了好久才慢慢找到门路。

先看示例查看它的 playground,上面其实是有一些功能可以直接找到的。

查看它在 github 上的 /samples 目录,里面也有不少示例。

去掘金这类网站上找别人写的示例,能有不少启发。

再看 API了解了自己所需要的功能相关的代码,再去看它文档的 API 就会发现容易理解多了。逐步发散理解更多关联功能。

参考资料

官方文档

microsoft.github.io/monaco-edit…

相关库

Monaco Editor www.npmjs.com/package/mon…

右键菜单汉化 www.npmjs.com/package/mon…

webpack 插件 www.npmjs.com/package/mon…

汉化 webpack 插件 www.npmjs.com/package/mon…

SQL 代码格式化 www.npmjs.com/package/sql…

博客

https://www.jb51.net/article/258307.htm

https://www.jb51.net/article/258269.htm

以上就是Monaco Editor开发SQL编辑器实例详解的详细内容,更多关于Monaco Editor开发SQL编辑器的资料请关注易知道(ezd.cc)其它相关文章!

推荐阅读