使用 Monaco Editor 开发 SQL 编辑器

2024-04-22 18:20

本文主要是介绍使用 Monaco Editor 开发 SQL 编辑器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

安装

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

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 | trueautomaticLayout: true, // 自动布局theme: this.theme, // 官方自带三种主题vs, hc-black, or vs-darkminimap: {// 关闭小地图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 } = selectionconst 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 } = selectionconst model = this.monacoEditor.getModel()return model.getValueInRange({startLineNumber,startColumn,endLineNumber,endColumn,})},

替换选中代码

insertStringInTemplate(str) {const selection = this.monacoEditor.getSelection() // 获取光标选中的值const { startLineNumber, endLineNumber, startColumn, endColumn } = selectionconst 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 } = languageexport default {...mounted() {this.initEditor()},methods: {...registerCompletion() {const _that = thismonaco.languages.registerCompletionItemProvider('sql', {triggerCharacters: ['.', ...keywords],provideCompletionItems: (model, position) => {let suggestions = []const { lineNumber, column } = positionconst 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 | trueautomaticLayout: true, // 自动布局theme: this.theme, // 官方自带三种主题vs, hc-black, or vs-darkminimap: {// 关闭小地图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

具体用法可以直接去 https://www.npmjs.com/package/monaco-editor-esm-webpack-plugin 里面看,我就不搬运了~

记得销毁编辑器对象哦

  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 就会发现容易理解多了。逐步发散理解更多关联功能。

参考资料

  • 官方文档
    • https://microsoft.github.io/monaco-editor/index.html
  • 相关库
    • Monaco Editor https://www.npmjs.com/package/monaco-editor
    • 右键菜单汉化 https://www.npmjs.com/package/monaco-editor-nls
    • webpack 插件 https://www.npmjs.com/package/monaco-editor-webpack-plugin
    • 汉化 webpack 插件 https://www.npmjs.com/package/monaco-editor-esm-webpack-plugin
    • SQL 代码格式化 https://www.npmjs.com/package/sql-formatter
  • 博客
    • https://blog.csdn.net/m0_37986789/article/details/121135519
    • https://juejin.cn/post/6986907628937379871

这篇关于使用 Monaco Editor 开发 SQL 编辑器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/926518

相关文章

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java