vue3 monoca-editor SQL:自动补全、自定义颜色、格式化代码、标记错误
vue3 monoca-editor使用SQL:自动补全、自定义颜色、格式化代码、标记错误
·
vue2 monoca-editor使用SQL:自动补全、自定义颜色、格式化代码、标记错误
一、安装
yarn add monaco-editor
yarn add vite-plugin-monaco-editor -D
二、配置vite.config.js
import { defineConfig } from 'vite'
import monacoEditorPlugin from "vite-plugin-monaco-editor"
export default defineConfig({
plugins: [
monacoEditorPlugin()
})
三、使用
1.导入
// 全局导入
import * as monaco from 'monaco-editor'
// 局部导入需要的功能和依赖
import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main'
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
2.封装组件
基本框架
<template>
<div id="code"></div>
</template>
<script setup>
import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main'
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
// import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql'
// language.keywords插件自带关键词的不全,网上找了一份自己维护
import sqlKeywords from './sqlKeywords.js'
const props = defineProps({
database: {
type: String,
default: ''
}
})
const editor = ref(null)
const initEditor = () => {
// 初始化编辑器,确保dom已经渲染
editor.value = monaco.editor.create(document.getElementById('code'), {
//初始化配置
value: props.value,
theme: 'vs-dark',
autoIndex: true,
language: 'sql', // 语言类型
tabCompletion: 'on',
cursorSmoothCaretAnimation: true,
formatOnPaste: true,
mouseWheelZoom: true,
folding: true, //代码折叠
autoClosingBrackets: 'always',
autoClosingOvertype: 'always',
autoClosingQuotes: 'always',
automaticLayout: 'always'
})
}
// 父组件获取值
const handleValue = () => {
return toRaw(editor.value).getValue()
}
// 父组件设置值
const setValue = (content) => {
toRaw(editor.value).setValue(content)
}
onMounted(() => {
initEditor()
})
defineExpose({
handleValue,
setValue
})
</script>
自定义提示
const suggestion = ref(null)
// 数据库对应的表名
const hintData = reactive({
a: ['group', 'area'],
b: ['user', 'client']
})
// 表对应的字段名
const tableData = reactive({
user: ['age', 'gender'],
group: ['id', 'name']
})
// 关键字提示
const getSQLSuggest = () => {
return sqlKeywords.map((key) => ({
label: key,
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: key,
detail: 'keyword'
}))
}
// 表名提示
const getTableSuggest = (dbName) => {
const tableNames = hintData[dbName]
if (!tableNames) {
return []
}
return tableNames.map((name) => ({
label: name,
kind: monaco.languages.CompletionItemKind.Constant,
insertText: name,
detail: dbName
}))
}
// 字段名提示
const getParamSuggest = (tableName) => {
const params = tableData[tableName]
if (!params) {
return []
}
return params.map((name) => ({
label: name,
kind: monaco.languages.CompletionItemKind.Constant,
insertText: name,
detail: 'param'
}))
}
// 数据库名提示
const getDBSuggest = () => {
return Object.keys(hintData).map((key) => ({
label: key,
kind: monaco.languages.CompletionItemKind.Enum,
insertText: key,
detail: 'database'
}))
}
const initEditor = () => {
suggestion.value = monaco.languages.registerCompletionItemProvider('sql', {
// 触发条件,也可以不写,不写的话只要输入满足label就会提示
// 只能配置单字符
triggerCharacters: ['.', ' '],
provideCompletionItems: (model, position) => {
let suggestions = []
const { lineNumber, column } = position
const textBeforePointer = model.getValueInRange({
startLineNumber: lineNumber,
startColumn: 0,
endLineNumber: lineNumber,
endColumn: column,
})
const tokens = textBeforePointer.toLocaleLowerCase().trim().split(/\s+/)
const lastToken = tokens[tokens.length - 1] // 获取最后一段非空字符串
const word = model.getWordUntilPosition(position)
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
}
if (lastToken.endsWith('.')) {
// 提示该数据库下的表名
const tokenNoDot = lastToken.slice(0, lastToken.length - 1)
if (Object.keys(hintData).includes(tokenNoDot)) {
suggestions = [...getTableSuggest(tokenNoDot)]
}
} else if (lastToken === '.') {
suggestions = []
} else if (textBeforePointer.endsWith(' ')) {
if (textBeforePointer.endsWith('select * from ')) {
// select * from 提示指定数据库的表名
suggestions = getTableSuggest(props.database)
} else if (lastToken === 'where') {
// select * from tableName where 提示指定表的字段名
const lastToken2 = tokens[tokens.length - 2]
const lastToken3 = tokens[tokens.length - 3]
const lastToken4 = tokens[tokens.length - 4]
const lastToken5 = tokens[tokens.length - 5]
if (lastToken5 + lastToken4 + lastToken3 === 'select*from') {
suggestions = [...getParamSuggest(lastToken2)]
} else {
suggestions = []
}
}else {
suggestions = []
}
} else {
// 提示数据库名和关键词
suggestions = [...getDBSuggest(), ...getSQLSuggest()]
}
return {
suggestions
}
}
})
}
自定义文本颜色,自带也有
const color = ref(null)
const initEditor = () => {
let reg = '/'
sqlKeywords.forEach((keyword) => {
reg += `${keyword}|`
})
reg += '/'
color.value = monaco.languages.setMonarchTokensProvider('sql', {
ignoreCase: true,
tokenizer: {
root: [
[
reg,
{ token: 'keyword' },
], //蓝色
[
/[+]|[-]|[*]|[/]|[%]|[>]|[<]|[=]|[!]|[:]|[&&]|[||]/,
{ token: 'string' },
], //红色
[/'.*?'|".*?"/, { token: 'string.escape' }], //橙色
[/#--.*?\--#/, { token: 'comment' }], //绿色
[/null/, { token: 'regexp' }], //粉色
[/[{]|[}]/, { token: 'type' }], //青色
[/[\u4e00-\u9fa5]/, { token: 'predefined' }],//亮粉色
[/''/, { token: 'invalid' }],//红色
[/[\u4e00-\u9fa5]/, { token: 'number.binary' }],//浅绿
[/(?!.*[a-zA-Z])[0-9]/, { token: 'number.hex' }], //浅绿
[/[(]|[)]/, { token: 'number.octal' }], //浅绿
[/[\u4e00-\u9fa5]/, { token: 'number.float' }],//浅绿
],
},
})
}
格式化代码&标记错误
yarn add sql-formatter -D
import { format } from 'sql-formatter'
const initEditor = () => {
// 改写插件自带格式化功能
formatProvider.value = monaco.languages.registerDocumentFormattingEditProvider('sql', {
provideDocumentFormattingEdits(model) {
return [{
text: formatSql(1),
range: model.getFullModelRange()
}]
}
})
// 格式化代码
const formatSql = (needValue) => {
clearMistake()
try {
setValue(format(toRaw(editor.value).getValue()))
} catch (e) {
const {message} = e
const list = message.split(' ')
const line = list.indexOf('line')
const column = list.indexOf('column')
markMistake({
startLineNumber: Number(list[line + 1]),
endLineNumber: Number(list[line + 1]),
startColumn: Number(list[column + 1]),
endColumn: Number(list[column + 1])
}, 'Error', message)
}
if (needValue) {
return toRaw(editor.value).getValue()
}
}
// 标记错误信息
const markMistake = (range, type, message) => {
const {startLineNumber, endLineNumber, startColumn, endColumn} = range
monaco.editor.setModelMarkers(
toRaw(editor.value).getModel(),
'eslint',
[{
startLineNumber,
endLineNumber,
startColumn,
endColumn,
severity: monaco.MarkerSeverity[type], // type可以是Error,Warning,Info
message
}]
)
}
// 清除错误信息
const clearMistake = () => {
monaco.editor.setModelMarkers(
toRaw(editor.value).getModel(),
'eslint',
[]
)
}
}
监听值变化
const initEditor = () => {
toRaw(editor.value).onDidChangeModelContent(() => {
console.log('value', toRaw(editor.value).getValue())
})
}
销毁编辑器及其配置,防止自定义提示数据重复
onBeforeUnmount(() => {
if (editor.value) {
clearMistake()
toRaw(editor.value).dispose()
toRaw(color.value).dispose()
toRaw(suggestion.value).dispose()
toRaw(formatProvider.value).dispose()
}
})
更多推荐
已为社区贡献3条内容
所有评论(0)