封装一下,创建一个编辑器
<template>
<div ref="editor" class="editor" style="height: 100%;"></div>
</template>
<script>
import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
// 在初始化之前,先设置MonacoEnvironment
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') {
return new jsonWorker();
}
if (label === 'css' || label === 'scss' || label === 'less') {
return new cssWorker();
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return new htmlWorker();
}
if (label === 'typescript' || label === 'javascript') {
return new tsWorker();
}
return new editorWorker();
}
};
export default {
name: 'MonacoEditor',
props:{
lang:{
type:String,
default: 'sql'
},
val: {
type: String,
default: ''
}
},
setup(){
return{
editor: null,
};
},
mounted(){
this.init();
},
methods:{
init(){
this.editor = monaco.editor.create(this.$refs.editor, {
value: this.val,
theme: 'vs',
language: this.lang,
minimap: {
enabled: false
},
automaticLayout: true,
unicodeHighlight: {
invisibleCharacters: false,
ambiguousCharacters: false,
},
readOnly: false,
wordWrap: 'on', // 文本超出换行,不出现滚动条
});
}
}
};
</script>
<style scoped lang="less">
</style>
用setup是因为用data的话,会加一层响应式的东西,最后导致 什么获取值等方法不能用。。。
获取编辑器的值
<MonacoEditor ref="monaco_editor" v-model:val="sql"></MonacoEditor>
this.$refs.monaco_editor.editor.getValue();
获取选中的区域的值
const monacoEditor = this.$refs.monaco_editor.editor;
let sqls = [];
const selections = monacoEditor.getSelections();
selections.map(selection=>{
sqls.push(monacoEditor.getModel().getValueInRange(selection));
});
这样就得到了全部语句的数组
自定义语言
因为需要,所以要看一下怎么自定义语言
注册语言:
import * as monaco from 'monaco-editor';
const languageId = 'mylanguage';
monaco.languages.register({
id: languageId
});
这样我们在使用编辑器的时候,对于language的参数填入 mylanguage 就可以使用自定义语言了
高亮配置 setMonarchTokensProvider
Token 可以理解为标记器,那么我们就是通过标记给代码高亮染色的
monarch 不知道是啥意思,查翻译是啥帝王,君主的意思,所以是最高优先级的标记???
monaco.languages.setMonarchTokensProvider(languageId, language);
两个参数,第一个是语言id没什么好说的,第二个相当于一个配置对象,下面细讲
语法配置 setLanguageConfiguration
monaco.languages.setLanguageConfiguration(languageId, conf);
第二个参数也是相当于语法配置,比如:
export const conf = {
comments: {
lineComment: '--',
blockComment: ['/*', '*/'],
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
],
wordPattern: /[\w#$]+/i,
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' },
{ open: '`', close: '`' },
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' },
{ open: '`', close: '`' },
],
};
自动补全 registerCompletionItemProvider
名字还是很贴切的
- completion 完成
- item 个、项
- provider 提供
monaco.languages.registerCompletionItemProvider(languageId, new AutoComplete(dbInfo));
AutoComplete 是一个我们声明的变量,需要实现 provideCompletionItems 方法,这个方法提供了一些参数供我们分析应该提示什么
provideCompletionItems(model, position, context, token){
const triggerCharacter = context.triggerCharacter;
const delimiter = ';';
const word = model.getWordUntilPosition(position);
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
};
const offset = model.getOffsetAt(position);
return this.getCompleteWordFromOffset(offset, model.getValue(), delimiter, range, model, triggerCharacter);
}
这里 odc 又转到了 getCompleteWordFromOffset 方法
getCompleteWordFromOffset(offset, input, delimiter, range, model, triggerCharacter) {
return __awaiter(this, void 0, void 0, function* (){
const parser = worker.parser;
const result = yield parser.getAutoCompletion(input, delimiter, offset);
if(result){
let suggestions = [];
for(let item of result){
if(typeof item === 'string') {
suggestions.push(keywordItem(item, range));
}else if(item.type === 'allTables') {
suggestions = suggestions.concat(yield this.getTableList(model, item.schema, range));
}else if (item.type === 'tableColumns') {
suggestions = suggestions.concat(yield this.getColumnList(model, item, range));
}else if(item.type === 'withTable') {
suggestions.push(tableItem(item.tableName, 'CTE', false, range));
}else if(item.type === 'allSchemas'){
suggestions = suggestions.concat(yield this.getSchemaList(model, range));
}else if (item.type === 'objectAccess') {
console.log('objectAccess', item);
const objectName = item.objectName;
const schemaList = yield this.service.getSchemaList(model);
const schema = schemaList === null || schemaList === void 0 ? void 0 : schemaList.find(s => s === objectName);
if (schema) {
suggestions = suggestions.concat(yield this.getTableList(model, item.objectName, range));
continue;
}
const arr = objectName.split('.');
let tableName = arr.length > 1 ? arr[1] : arr[0];
let schemaName = arr.length > 1 ? arr[0] : undefined;
suggestions = suggestions.concat(yield this.getColumnList(model, { tableName, schemaName }, range));
}
else if (item.type === 'fromTable') {
suggestions.push(tableItem(item.tableName, item.schemaName, true, range));
}
else if (item.type === 'allFunction') {
suggestions = suggestions.concat(yield this.getFunctions(model, range));
}
}
return {
suggestions,
incomplete: false
};
}
return {
suggestions: [],
incomplete: false
};
});
}
可以看出来,这里调用了一个 worker.parser 来分析,并返回了建议的结果集,然后遍历结果集进行查询和包装。
格式化
注册格式化方法
// 格式化
monaco.languages.registerDocumentFormattingEditProvider(languageId, new DocumentFormattingEditProvider());
monaco.languages.registerDocumentRangeFormattingEditProvider(languageId, new DocumentRangeFormattingEditProvider());
使用
const monacoEditor = this.$refs.monaco_editor.editor;
monacoEditor?.trigger('editor', 'editor.action.formatDocument', null);