巨无敌大坑:设置的文本提示项有range字段时,光标必须在range范围内,提示才会正常显示
<template> <div class="container1" v-loading="loading" element-loading-text="资源加载中..."> <div> <el-button size="mini" @click="formatContent">格式化</el-button> </div> <div ref="codeContainer" style="height: calc( 100vh - 300px )" id="container"/> <div v-for="(item, index) in errorMsg" :key="index">{{item}}</div> <div v-if="false"> <i class="el-icon-star-off"></i>g4Tokens <pre><code>{{g4Tokens}}</code></pre> <i class="el-icon-tsar-off"></i>g4Data <pre><code>{{g4Data}}</code></pre> <i class="el-icon-star-off"></i>contentBasicLexer <pre><code>{{contentBasicLexer}}</code></pre> <i class="el-icon-star-off"></i>contentBasicParser <pre><code>{{contentBasicParser}}</code></pre> </div> </div> </template> <script> var antlr4 = require('antlr4').default; import * as monaco from "monaco-editor/esm/vs/editor/editor.api" import * as c3 from "./antlr4C3/index.ts"; import { selectReturnMacheFunction } from "@/api/rule/function" import { readResource } from "@/api/rule/resource" import { selectModelList } from "@/api/rule/model" import { selectVariableDetail } from "@/api/rule/variable" import { expressionList } from "@/assets/data/expressionList.js" import {antiShaking} from "@/utils/antiShakingThrottle" export default { props: { ruleData: {}, entranceG4: { type: String, default: 'ruleStatement' }, // 用于向父组件传递ruleMap ruleMapProp: {} }, data() { return { loading: false, // 根据g4入口,获取的规则类型列表 ruleTypeList: [], // 变量列表 variableList: null, // 对象列表 objectList: null, // 所有语句的平铺数组 allExpressionList: [], // 空间项目信息 spaceData: null, contentBasicLexer: null, contentBasicParser: null, dialog: false, model: 'login', orgRule: '', treeData: [], editMsg: null, // monaco-editor注册方法 registerObject: { completionItemProvider: null }, tokens: [], selectToken: null, errorMsg: '', LExprLexer: null, LExprParser: null, // // 规则提示列表 // hintList: null, // 特殊类型列表 typeMap: { COMPARE: '比较符', STRING: '文本', VARIABLE: '变量', DIGIT: '整数', SEMICOLON : ";" }, // 光标前的tokens beforeTokens: [], beforeTokensAll: [], // 词法和语法的 tokenMap: [], ruleMap: null, // 预定义生成的map predefineMap: {}, g4Data: null, g4Tokens: null } }, created() { let treeSelect = JSON.parse(sessionStorage.getItem("treeSelect")) this.spaceData = { spaceName: treeSelect.spaceName, projectName: treeSelect.projectName, } this.getG4File() }, watch: { ruleData: { handler(n) { this.orgRule = n if( this.editMsg ){ var currentModel = this.editMsg.getModel(); currentModel.setValue(n) } }, deep: true, immediate: true }, entranceG4: { handler(n) { console.log("入口修改", n) }, immediate: true }, }, mounted() { // // 根据g4入口,获取规则类型列表 // this.getRuleTypeListByEntranceG4() // // 获取变量集里的变量列表 // this.getVariableList() // // 获取对象列表 // this.getObjectList().then(() => { // this.initMonaco() // }), // // 获取所有语句的平铺数组 // this.getAllExpressionList() }, methods: { // 初始化所有数据 initData() { // 根据g4入口,获取规则类型列表 this.getRuleTypeListByEntranceG4() // 获取变量集里的变量列表 this.getVariableList() // 获取对象列表 this.getObjectList().then(() => { this.initMonaco() }), // 获取所有语句的平铺数组 this.getAllExpressionList() }, // 根据g4入口,获取规则类型列表 getRuleTypeListByEntranceG4() { switch (this.entranceG4) { case "ruleStatement": this.ruleTypeList = ["预定义", "前置条件", "如果", "那么", "否则", "或者", "并且"] break; case "flowRuleStatement": this.ruleTypeList = ["预定义", "如果", "或者", "并且"] break; case "booleanExpr": this.ruleTypeList = ["如果", "或者", "并且"] break; case "actionStatements": this.ruleTypeList = ["那么"] default: break; } }, // 获取所有语句的平铺数组 getAllExpressionList() { for( let key in expressionList ){ if( key != "一个void" && key != "一个定义" ) this.allExpressionList.push(...expressionList[key]) } }, // 获取变量集变量列表 getVariableList() { selectVariableDetail(this.spaceData).then((response) => { console.log("获取变量列表", response) if( response.code == 200 ){ this.variableList = response.data.propertyVos } }) }, // 获取对象列表 async getObjectList(){ let response = await selectModelList(this.spaceData) console.log("获取对象列表", response) if( response.code == 200 ){ this.objectList = response.data let hintObj = response.data.map((v) => { return `一个${v.describe}` }) hintObj.forEach((v) => { if( expressionList.一个模型.indexOf(v) == -1 ){ expressionList.一个模型.push(v) } }) } }, // 获取g4生成的js文件 getG4File(timer = 1) { // ------- TODO 修改为一个loading--------请求应该为异步---------- this.loading = true let tasks = [] let tasks1 = new Promise((resolve, reject) => { // BasicParser let data = { ...this.spaceData, type: "js", name: "BasicParser" } readResource(data).then((BasicParserRes) => { if( BasicParserRes.code == 200 ){ let BasicParser = BasicParserRes.data.content // BasicListener let data1 = { ...this.spaceData, type: "js", name: "BasicListener" } readResource(data1).then((BasicListenerRes) => { if( BasicListenerRes.code == 200 ){ let BasicListener = BasicListenerRes.data.content // console.log("获取g4生成的js文件_BasicParser_BasicListener", BasicParser, BasicListener) // 处理 BasicParser BasicParser = BasicParser.replace("import antlr4 from 'antlr4';", "") BasicParser = BasicParser.replace("import BasicListener from './BasicListener.js';", "") BasicParser = BasicParser.replace("export default ", "") // 处理 BasicListener BasicListener = BasicListener.replace("import antlr4 from 'antlr4';", "") BasicListener = BasicListener.replace("export default ", "") BasicListener = BasicListener + "that.LExprParser = BasicParser" // 组合 BasicParser 和 BasicListener在一个文件 let contentTwo = BasicParser + BasicListener new Function('antlr4', 'that', contentTwo)(antlr4, this) this.contentBasicParser = contentTwo this.ParseTreeAll(this.orgRule) resolve(BasicParserRes.code); }else{ reject(BasicParserRes.code) } }) }else{ reject(BasicParserRes.code) } // // BasicLexer // let data2 = { // ...this.spaceData, // type: "js", // name: "BasicLexer" // } // readResource(data2).then((response) => { // // console.log("获取g4生成的js文件", response) // if( response.code == 200 ){ // let content = response.data.content.replace("import antlr4 from 'antlr4';", "") // content = content.replace("export default ", "") // content = content + "that.LExprLexer = BasicLexer" // new Function('antlr4', 'that', content)(antlr4, this) // this.contentBasicLexer = content // } // }) // }else if( BasicParserRes.code == 5001 ){ // if( timer > 4 ){ // this.loading = false // this.$message.error("获取资源失败") // return // } // setTimeout(() => { // timer += 1 // this.getG4File(timer) // }, 4000) // } }) }); // let tasks2 = new Promise((resolve, reject) => { // // BasicListener // let data1 = { // ...this.spaceData, // type: "js", // name: "BasicListener" // } // readResource(data1).then((BasicListenerRes) => { // if( BasicListenerRes.code == 200 ){ // resolve(BasicListenerRes.code); // let BasicListener = BasicListenerRes.data.content // // console.log("获取g4生成的js文件_BasicParser_BasicListener", BasicParser, BasicListener) // // 处理 BasicParser // BasicParser = BasicParser.replace("import antlr4 from 'antlr4';", "") // BasicParser = BasicParser.replace("import BasicListener from './BasicListener.js';", "") // BasicParser = BasicParser.replace("export default ", "") // // 处理 BasicListener // BasicListener = BasicListener.replace("import antlr4 from 'antlr4';", "") // BasicListener = BasicListener.replace("export default ", "") // BasicListener = BasicListener + "that.LExprParser = BasicParser" // // 组合 BasicParser 和 BasicListener在一个文件 // let contentTwo = BasicParser + BasicListener // new Function('antlr4', 'that', contentTwo)(antlr4, this) // this.contentBasicParser = contentTwo // this.ParseTreeAll(this.orgRule) // // resolve(BasicListenerRes.code); // }else{ // reject(BasicListenerRes.code) // } // }) // }); let tasks3 = new Promise((resolve, reject) => { // BasicLexer let data2 = { ...this.spaceData, type: "js", name: "BasicLexer" } readResource(data2).then((response) => { // console.log("获取g4生成的js文件", response) if( response.code == 200 ){ let content = response.data.content.replace("import antlr4 from 'antlr4';", "") content = content.replace("export default ", "") content = content + "that.LExprLexer = BasicLexer" new Function('antlr4', 'that', content)(antlr4, this) this.contentBasicLexer = content resolve(response.code); }else{ reject(response.code) } }) }); tasks = [ tasks1, tasks3 ] console.log(8544, tasks) Promise.all(tasks).then((codeList) => { console.log("全部资源获取", codeList) // 所有资源获取成功 this.loading = false this.initData() }).catch((codeList) => { console.log("全部资源获取_失败", codeList) if( timer > 4 ){ this.loading = false this.$message.error("获取资源失败") return } setTimeout(() => { timer += 1 this.getG4File(timer) }, 4000) }) // 暂时 获取g4文件-------------------------------------------------- let data1 = { ...this.spaceData, type: "g4", name: "Basic" } readResource(data1).then((response) => { // console.log("获取g4生成的js文件", response) if( response.code == 200 ){ this.g4Data = response.data.content } }) let data2 = { ...this.spaceData, type: "tokens", name: "Basic" } readResource(data2).then((response) => { // console.log("获取g4生成的js文件", response) if( response.code == 200 ){ this.g4Tokens = response.data.content } }) // // BasicParser // let data = { // ...this.spaceData, // type: "js", // name: "BasicParser" // } // readResource(data).then((BasicParserRes) => { // if( BasicParserRes.code == 200 ){ // this.loading = false // let BasicParser = BasicParserRes.data.content // // BasicListener // let data1 = { // ...this.spaceData, // type: "js", // name: "BasicListener" // } // readResource(data1).then((BasicListenerRes) => { // if( BasicListenerRes.code == 200 ){ // let BasicListener = BasicListenerRes.data.content // // console.log("获取g4生成的js文件_BasicParser_BasicListener", BasicParser, BasicListener) // // 处理 BasicParser // BasicParser = BasicParser.replace("import antlr4 from 'antlr4';", "") // BasicParser = BasicParser.replace("import BasicListener from './BasicListener.js';", "") // BasicParser = BasicParser.replace("export default ", "") // // 处理 BasicListener // BasicListener = BasicListener.replace("import antlr4 from 'antlr4';", "") // BasicListener = BasicListener.replace("export default ", "") // BasicListener = BasicListener + "that.LExprParser = BasicParser" // // 组合 BasicParser 和 BasicListener在一个文件 // let contentTwo = BasicParser + BasicListener // new Function('antlr4', 'that', contentTwo)(antlr4, this) // this.contentBasicParser = contentTwo // this.ParseTreeAll(this.orgRule) // } // }) // // BasicLexer // let data2 = { // ...this.spaceData, // type: "js", // name: "BasicLexer" // } // readResource(data2).then((response) => { // // console.log("获取g4生成的js文件", response) // if( response.code == 200 ){ // let content = response.data.content.replace("import antlr4 from 'antlr4';", "") // content = content.replace("export default ", "") // content = content + "that.LExprLexer = BasicLexer" // new Function('antlr4', 'that', content)(antlr4, this) // this.contentBasicLexer = content // } // }) // }else if( BasicParserRes.code == 5001 ){ // if( timer > 4 ){ // this.loading = false // this.$message.error("获取资源失败") // return // } // setTimeout(() => { // timer += 1 // this.getG4File(timer) // }, 4000) // } // }) }, // 设置代码提示及语句高亮等 async setLang() { let root = [ [/".*?"/, 'string'], [/'.*?'/, 'variable'], [/[0-9]+/, 'number'], [/<[^>]*?>(.*?)/gi, 'write'], [/预定义|如果|那么|否则/, 'keyTitle'], [/并且|或者|;/, 'linkText'], [/来自\(in\)/, 'predefineFrom'], ] // 添加 一个对象/一个文本 的形式的代码高亮 expressionList.一个模型.forEach((v) => { root.push([new RegExp(v), 'predefineObj']) }) monaco.languages.register({id: 'myLang'}); monaco.languages.setMonarchTokensProvider('myLang', { tokenizer: { root } }); this.registerObject.completionItemProvider = monaco.languages.registerCompletionItemProvider('myLang', { provideCompletionItems: async (model, position, context, token) => { // antiShaking(async () => { console.log("代码提示", { this: this, 'position': position, 'token': token, 'context': context, 'model': model, "selectToken": this.selectToken, "文本内容": JSON.stringify(model.getLinesContent()) }) var textUntilPosition = model.getValueInRange({ startLineNumber: 1, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column }); var word = model.getWordUntilPosition(position) let range = { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: word.startColumn, endColumn: word.endColumn }; if( this.selectToken ){ range = { startLineNumber: this.selectToken.line, endLineNumber: this.selectToken.line, startColumn: this.selectToken.column + 1, endColumn: this.selectToken.column + this.selectToken.text.length + 1 }; } console.log("替代范围", range, word, "当前内容", textUntilPosition) // 获取提示语 let suggestions = [] if( this.selectToken ){ console.log("根据点击的token获取下拉列表", this.selectToken, this.tokens) let text = this.selectToken.text // 去掉尖括号和一个/一些语句 text = text.replace("<", "") text = text.replace(">", "") let quantifier = text.substr(0, 2) let returnType = text.substr(2) let dataList = quantifier == "一些"? true: false // 转换操作为void if( returnType == "操作" ){ returnType = "void" } let data = { ...this.spaceData, dataList, returnType } let response = await this.handleRequestData(data, range, this.selectToken.text) suggestions = response return {suggestions: suggestions} }else{ let beforeTokenNames = this.beforeTokens.map(v => v.text) let lastToken = this.beforeTokens[this.beforeTokens.length - 1] && this.beforeTokens[this.beforeTokens.length - 1] console.log("tokens提示判断", this.beforeTokens, lastToken) if( !lastToken ){ // 获取规则类型语句 suggestions = this.getTypeSuggestions(null, range) }else if( lastToken.text == '预定义' ){ // 最后一个token为预定义语句, 提示定义语句 let data = { ...this.spaceData, dataList: false, returnType: "定义" } let suggestions = await this.handleRequestData(data, range) return {suggestions: suggestions} }else if( lastToken.text == '如果' ){ // 最后一个token为如果语句,查询所有变量,提示变量列表和<一个布尔> // // 获取变量列表提示 ------------------正向 --------------------------------- // let objList = this.variableList.map((variableItem) => { // return ({ // label: variableItem.describe, // kind: monaco.languages.CompletionItemKind.Function, // documentation: '', // insertText: `'${variableItem.describe}'`, // sortText: '1' // }) // }) // // 获取返回值为布尔的提示 // let data = { // ...this.spaceData, // dataList: false, // returnType: "布尔" // } // let response = await this.handleRequestData(data, range) // // 组合两种提示 // suggestions = [...objList, ...response] // return {suggestions: suggestions} // ------------------暂时替代带正向的 --------------------------------- // 获取返回值为布尔的提示 let data = { ...this.spaceData, dataList: false, returnType: "布尔" } let response = await this.handleRequestData(data, range) // 组合两种提示 suggestions = [ ...response] return {suggestions: suggestions} }else if( lastToken.text == '那么' ){ // 最后一个token为那么语句, 提示返回为void的操作语句 let data = { ...this.spaceData, dataList: false, returnType: "void" } let response = await this.handleRequestData(data, range) suggestions = response return {suggestions: suggestions} }else if( this.tokenMap[lastToken.type] == 'VARIABLE' ){ // 最后一个token为变量, // 1. 查询变量属性 // 2. 查询填空有当前变量的函数 // // ------------------模拟 -----------------------------------------------正向 --------------------------------- // suggestions = [ // { // label: '"的名称"', // kind: monaco.languages.CompletionItemKind.Function, // documentation: '', // insertText: '的名称' // } // ] }else if( beforeTokenNames.indexOf("预定义") != -1 && beforeTokenNames.indexOf("如果") == -1 ){ // 预定义语句 1. 一个对象/一个基础数据类型 后面接 来自(in) <一个xx列表> // console.log("预定义111", lastToken.type, this.tokenMap[lastToken.type], expressionList.一个模型, lastToken.text) if( this.tokenMap[lastToken.type] == 'ID' && expressionList.一个模型.indexOf(lastToken.text) != -1 ){ let text = `来自(in) <一些${lastToken.text.slice(2)}>;` suggestions = [ { label: text, kind: monaco.languages.CompletionItemKind.Function, documentation: '', insertText: text } ] }else{ let data = { ...this.spaceData, dataList: false, returnType: "定义" } let suggestions1 = await this.handleRequestData(data, range) let suggestions2 = this.getTypeSuggestions("预定义", range) suggestions = [...suggestions1, ...suggestions2] } }else if( beforeTokenNames.indexOf("如果") != -1 && beforeTokenNames.indexOf("那么") == -1 ){ // // 如果语句, ------------------正向 --------------------------------- // // 1. 根据前面的tokens 获取最终组合出的类型 // // 2. 根据前面的类型获取能接的数据生成代码提示 // // 3. 最后拼接上内置的代码提示(语句、函数) // let type = "一个文本" // // 内置语句 // let promptList1 = this.allExpressionList.filter((v) => { // if( v.indexOf( `<${type}>` ) != -1 ){ // return v // } // }) // // 函数 ---------------------- 还未获取 ------------------------- // let promptList2 = [] // let promptList = [...promptList1, ...promptList2] // suggestions = this.replaceTextByVariableTokens(promptList, type) // ------------------未完成 前面步骤,先拼接内置提示-------------------- let suggestionsAdd = this.getTypeSuggestions("如果", range) suggestions = [...suggestions, ...suggestionsAdd] }else if( beforeTokenNames.indexOf("那么") != -1 ){ let data = { ...this.spaceData, dataList: false, returnType: "void" } suggestions = await this.handleRequestData(data, range) } console.log("suggestions", suggestions) return {suggestions: suggestions} } // }, 'timer3') }, triggerCharacters: [" "] }); monaco.editor.defineTheme("myLang-theme", { //基础 base: "vs", //继承 inherit: true, //规则 rules: [ { token: "write", foreground: "767676", fontStyle: 'italic' }, { token: "variable", foreground: "757500" }, { token: "variable", foreground: "757500" }, { token: "keyTitle", foreground: "00008b", fontStyle: 'bold italic' }, { token: "linkText", fontStyle: 'italic'}, { token: "predefineObj", foreground: "8b008b", fontStyle: 'bold' }, { token: "predefineFrom", foreground: "00008b", fontStyle: 'bold' }, ], colors: { 'editor.foreground': '#000000', 'editor.background': '#ffffff', 'editorCursor.foreground': '#666', 'editor.lineHighlightBackground': '#e8f4ff', 'editorLineNumber.foreground': '#008800', 'editor.selectionBackground': '#88000030', 'editor.inactiveSelectionBackground': '#88000015' } }); }, // 获取规则类型语句 getTypeSuggestions(beforeType, range) { let suggestions switch (beforeType) { case null: suggestions = [ { label: "预定义", kind: monaco.languages.CompletionItemKind.Function, documentation: '定义一个变量', insertText: '预定义\n\t<一个定义>', range: range }, { label: "如果", kind: monaco.languages.CompletionItemKind.Function, documentation: '如果语句', insertText: '如果\n\t<一个布尔>', range: range } ] break; case "预定义": suggestions = [ { label: "如果", kind: monaco.languages.CompletionItemKind.Function, documentation: '如果语句', insertText: '如果\n\t<一个布尔>', range: range } ] break; case "如果": suggestions = [ { label: "或者", kind: monaco.languages.CompletionItemKind.Function, documentation: '或者语句', insertText: '或者\n\t<一个布尔>', // command: { // id: 'vs.editor.ICodeEditor:1:geshihua', // title: '选中这个建议选项后,触发格式化操作' // } }, { label: "并且", kind: monaco.languages.CompletionItemKind.Function, documentation: '并且语句', insertText: '并且\n\t<一个布尔>', // command: { // id: 'vs.editor.ICodeEditor:1:geshihua', // title: '选中这个建议选项后,触发格式化操作' // } }, { label: "那么", kind: monaco.languages.CompletionItemKind.Function, documentation: '如果语句', insertText: '那么\n\t<一个操作>;', // command: { // id: 'vs.editor.ICodeEditor:1:geshihua', // title: '选中这个建议选项后,触发格式化操作' // } } ] break; case "那么": suggestions = [] break; default: suggestions = [] break; } for(var i = 0; i< suggestions.length; i++){ let v = suggestions[i] if( this.ruleTypeList.indexOf(v.label) == -1 ){ suggestions.splice(i,1); i=i-1; } } return suggestions }, // 根据变量到当前光标的tokens 和 提示语句,生成替换 变量到当前光标 的文本的 suggestions replaceTextByVariableTokens(promptList, type) { let suggestions = [] let variableTokens = this.getVariableTokens(this.beforeTokensAll) let firstVariable = variableTokens[0] let lastVariable = variableTokens[variableTokens.length - 1] let variableText = '' variableTokens.forEach((v) => { variableText += v.text }) let range = { startLineNumber: firstVariable.line, endLineNumber: lastVariable.line, startColumn: firstVariable.column + 1, endColumn: lastVariable.column + lastVariable.text.length + 1, } // console.log("变量集到结束的tokens", variableTokens, range) promptList.forEach((promptItem) => { let newText = promptItem.replace(new RegExp("<" + type + ">"), variableText) suggestions.push({ label: newText, kind: monaco.languages.CompletionItemKind.Function, documentation: '', insertText: '', additionalTextEdits: [ { range, text: newText, forceMoveMarkers: false } ] }) }) return suggestions }, // 获取最近的变量结束处的所有有实际显示的token getVariableTokens(tokenList) { let variableTokenList = null tokenList.forEach((tokenItem) => { if( this.tokenMap[tokenItem.type] == 'VARIABLE' ){ variableTokenList = [tokenItem] }else if( variableTokenList ) { variableTokenList.push(tokenItem) } }) return variableTokenList }, // 处理接口获取数据 async handleRequestData(data, range, filterText) { let suggestions = [] let returnType = data.returnType let returnTypeIsList = `${data.dataList? '一些': '一个'}${data.returnType}` const response = await selectReturnMacheFunction(data) console.log("获取下拉列表", response) if( response.code == 200 ){ let beforeTokenNames = this.beforeTokens.map(v => v.text) if( response.data.functionList.length > 0 ){ suggestions = response.data.functionList.map((dataItem) => { let string = dataItem // 去掉获取的语句的单引号转 string.replace(/\\\'/g, "'") return { label: string, kind: monaco.languages.CompletionItemKind.Function, documentation: string, insertText: string, filterText: filterText, range: range } }) } // 添加静态的方法 if( expressionList[returnTypeIsList] && expressionList[returnTypeIsList].length > 0 ){ let suggestionsAdd = expressionList[returnTypeIsList].map((expressionItem) => { let string = expressionItem return { label: string, kind: monaco.languages.CompletionItemKind.Constant, documentation: string, insertText: string, filterText: filterText, range: range } }) suggestions.unshift(...suggestionsAdd) } // 获取预定义的模型 if( beforeTokenNames.indexOf("如果") != -1 && this.predefineMap[returnTypeIsList] && this.predefineMap[returnTypeIsList].length > 0 ){ let suggestionsAdd = this.predefineMap[returnTypeIsList].map((expressionItem) => { let string = expressionItem return { label: string, kind: monaco.languages.CompletionItemKind.Variable, documentation: string, insertText: string, filterText: filterText, range: range } }) suggestions.unshift(...suggestionsAdd) } // 获取自定义输入的占位内容 if( ['文本', '变量', 'void', '数字', '变量'].indexOf(returnType) != -1 && !data.dataList ){ let inputText = '' if( returnType == '文本' ){ inputText = `""` }else if( returnType == '变量' ){ inputText = `''` }else if( returnType == '数字' ){ inputText = `0` } suggestions.unshift({ label: "自定义输入", kind: monaco.languages.CompletionItemKind.Text, documentation: "自定义输入的字符串", insertText: inputText, filterText: filterText, range: range }) } console.log("选择列表", suggestions) return suggestions // return {suggestions: suggestions} } }, initMonaco(){ this.setLang() this.editMsg = monaco.editor.create(this.$refs.codeContainer, { language: 'myLang', // 语言,需要引入相应文件 value: this.orgRule, theme: 'myLang-theme', // 编辑器主题 // readOnly: false, // 是否只读 // lineNumbers: true, // 是否显示行数 // lineHeight: 20, // 行高 // tabSize: 2, // 缩进 // automaticLayout: true, // 是否自适应宽高,设置为true的话会有性能问题 }) let selectToken = null // 更改光标位置 this.editMsg.onDidChangeCursorPosition((positionData) => { antiShaking(() => { let position = positionData.position let positionColumn = position.column - 1 // 获取到光标为止所有的tokens this.beforeTokens = this.getTokensByPosition(position.lineNumber, positionColumn, 'list') console.log("beforeTokens", this.beforeTokens) }, "timer1") // let position = positionData.position // let positionColumn = position.column - 1 // // 获取到光标为止所有的tokens // this.beforeTokens = this.getTokensByPosition(position.lineNumber, positionColumn, 'list') // console.log("beforeTokens", this.beforeTokens) // this.addCodeCompletionCore1(beforeTokens) }) // 输入值变化 this.editMsg.onDidChangeModelContent((data) => { // antiShaking(() => { this.selectToken = null let value = this.editMsg.getValue() this.ParseTreeAll(value) // }, "timer2") }) // 点击 this.editMsg.onMouseDown((data) => { // console.log("点击", data) let position = data.target.position this.selectToken = null let textData = this.editMsg.getModel().getValue() // console.log("光标位置选择", position, textData) this.ParseTree(textData) // console.log("所有token", this.tokens) let positionColumn = position.column - 1 this.selectToken = this.getTokenByPosition(position.lineNumber, positionColumn) // 点击位置不在token内,直接返回 if( !this.selectToken ){ return } let selectTokenText = this.selectToken.text let selectTokenTextLength = selectTokenText.length if( selectTokenText[0] == '<' && selectTokenText[selectTokenTextLength - 1] == '>' ){ this.getDropList() } }) //绑定快捷键 var myBinding = this.editMsg.addCommand(monaco.KeyCode.F10, () => { this.selectToken = selectToken this.getDropList() }); // var myBindings = this.editMsg.addCommand(monaco.KeyCode.F1, () => { // // this.editMsg.trigger("anything", "editor.action.formatDocument"); // let textData = this.editMsg.getModel().getValue() // this.ParseTree(textData) // }); this.addAction() }, // 添加菜单事件 addAction() { this.editMsg.addAction({ id: 'geshihua', // 菜单项 id label: '格式化', // 菜单项名称 // keybindings: [this.monaco.KeyMod.CtrlCmd | this.monaco.KeyCode.KEY_J], // 绑定快捷键 contextMenuGroupId: '9_cutcopypaste', // 所属菜单的分组 run: (data) => { this.formatContent() }, }) }, // 规则内容格式化 formatContent() { try{ let content = this.editMsg.getModel().getValue() console.log("格式化数据", content) let title = ['预定义', '如果', '那么', '否则'] let regTitle = title.map((v) => { return new RegExp("[\\s\\n\\r]*" + v + "[\\s\\n\\r]*", 'g'); }) let regTitleFirst = title.map((v) => { return new RegExp("^[\\s\\n\\r]*" + v + "[\\s\\n\\r]*", 'g'); }) let regIfSplit = /[\s\\n\\r]*(并且|或者)[\s\\n\\r]*/g let regDoSplit = /[\s\\n\\r]*;[\s\\n\\r]*(如果|否则)/g let regDoSplitInner = /[\s\\n\\r]*;[\s\\n\\r]*/g // -----多余的匹配操作,待修改------------ let msgChina = content regTitle.forEach((regItem, index) => { msgChina = msgChina.replace(regItem, (res) => { return `\n${title[index]}\r\n ` }) let regTitleFirstItem = regTitleFirst[index] msgChina = msgChina.replace(regTitleFirstItem, (res) => { return `${title[index]}\r\n ` }) }) msgChina = msgChina.replace(regDoSplitInner, (res) => { return ';\r\n ' }) msgChina = msgChina.replace(regDoSplit, (res, $1) => { return `;\r\n${$1}` }) msgChina = msgChina.replace(regIfSplit, (res, $1) => { return `\n ${$1}\r\n ` }) // 重新设置编辑器显示内容 this.setRuleData(msgChina) // var currentModel = this.editMsg.getModel(); // currentModel.setValue(msgChina) }catch(err){ console.log("规则内容格式化错误", err) } }, // 获取下拉列表 getDropList(tokenItem) { console.log("触发提示", tokenItem) setTimeout(() => { this.editMsg.trigger('', 'editor.action.triggerSuggest', {}); }) }, // 对全部输入内容分析 ParseTreeAll(sql){ // 如果编辑器还未生成,半秒后重新尝试调用 if( !this.editMsg ){ setTimeout(() => { this.ParseTreeAll(sql) }, 500) return } // 打印树 this.consoleParseTree(sql) let LExprLexer = this.LExprLexer let LExprParser = this.LExprParser console.log("校验内容", sql) const chars = new antlr4.InputStream(sql); const lexer = new LExprLexer(chars); const tokens = new antlr4.CommonTokenStream(lexer); const parser = new LExprParser(tokens); this.tokens = tokens.tokens if( !this.tokenMap || !this.ruleMap ){ // 获取词法map let tokenMap = parser.symbolicNames.map((symbolicNames, i) => { if( symbolicNames === null ){ return parser.literalNames[i] }else{ return symbolicNames } }) this.tokenMap = tokenMap // 获取语法map并传递到父组件 this.ruleMap = parser.ruleNames this.$emit("update:ruleMapProp", this.ruleMap) console.log("获取词法和语法的map", this.tokenMap, this.ruleMap) } // 添加错误监听器 let ErrorListener = new antlr4.error.ErrorListener parser.removeErrorListeners() monaco.editor.setModelMarkers(this.editMsg.getModel(), "owner", []); // monaco-editor 添加错误标志 let markers = []; this.errorMsg = [] ErrorListener.syntaxError = (recognizer, offendingSymbol, line, column, msg, e) => { console.log("错误提示信息", 1, recognizer, 2, offendingSymbol, 3, line, 4, column, 5, msg, 6, e) let msgChina = this.getMsgChina(msg) || msg this.errorMsg.push(`${line}:${column} ${msgChina}`) markers.push({ startLineNumber: line, endLineNumber: line, startColumn: offendingSymbol.start + 1, endColumn: offendingSymbol.stop + 1, message: msg, severity: monaco.MarkerSeverity.Error, }); monaco.editor.setModelMarkers(this.editMsg.getModel(), "owner", markers); } parser.addErrorListener(ErrorListener) // 生成语法树 parser.buildParseTrees = true; const tree = parser[this.entranceG4](); // 防抖生成遍历语法树 antiShaking(() => { console.log("文本tree更新", tree) if( !tree.children ){ return } // 遍历树,获取预定义语句 let DefinitionStatementsContext = [] // 遍历获取所有预定义元素 tree.children.forEach((treeItem) => { if( treeItem.constructor.name == "PredefineStatementContext" ){ // 遍历获取预定义的语句 treeItem.children.forEach((PredefineElement) => { if( PredefineElement.constructor.name == "DefinitionStatementsContext" ){ DefinitionStatementsContext = PredefineElement } }) } }) // 获取预定义map this.getPredefineData(DefinitionStatementsContext) }, "timer3", 500) }, // 获取预定义map getPredefineData(predefineTree) { this.predefineMap = {} if( !predefineTree.children ){ return } predefineTree.children.forEach((predefineItem) => { // 获取每一条预定义语句 if( predefineItem.constructor.name == "DefinitionStatementContext" ){ let key let value predefineItem.children.forEach((predefineItemElement) => { let type = predefineItemElement.symbol && predefineItemElement.symbol.type if( this.tokenMap[type] == 'ID' ){ key = predefineItemElement.symbol.text }else if(this.tokenMap[type] == 'VARIABLE'){ value = predefineItemElement.symbol.text } }) if( !this.predefineMap[key] ){ this.predefineMap[key] = [value] }else{ this.predefineMap[key].push(value) } } }) console.log("获取预定义map", this.predefineMap) }, ParseTree(sql){ let LExprLexer = this.LExprLexer let LExprParser = this.LExprParser // const inputStream = CharStreams.fromString(sql); // const lexer = new LExprLexer(inputStream); // const tokenStream = new CommonTokenStream(lexer); // const parser = new LExprParser(tokenStream); // // const errorListener = new ErrorListener(); // // parser.addErrorListener(errorListener); // parser.numberCompareExpr(); // sql = "SELECT * FROM `adb_mysql_dblink`.`adb_mysql_1124qie`.`courses`" const chars = new antlr4.InputStream(sql); const lexer = new LExprLexer(chars); const tokens = new antlr4.CommonTokenStream(lexer); const parser = new LExprParser(tokens); // 生成语法树 parser.buildParseTrees = true; const tree = parser[this.entranceG4](); // console.log(78888, tree, 3, antlr4) return tree // 添加错误监听器 // let ErrorListener = new antlr4.error.ErrorListener // parser.removeErrorListeners() // ErrorListener.syntaxError = (recognizer, offendingSymbol, line, column, msg, e) => { // console.log("错误提示信息", 1, recognizer, 2, offendingSymbol, 3, line, 4, column, 5, msg, 6, e) // this.errorMsg = `${line}:${column} ${msg}` // // monaco-editor 添加错误标志 // let markers = []; // markers.push({ // startLineNumber: line, // endLineNumber: line, // startColumn: offendingSymbol.start, // endColumn: offendingSymbol.stop, // message: msg, // severity: monaco.MarkerSeverity.Error, // }); // monaco.editor.setModelMarkers(this.editMsg.getModel(), "owner", markers); // } // parser.addErrorListener(ErrorListener) // this.tokens = tokens.tokens // parser.reset() // 添加解析器 // this.addCodeCompletionCore(parser) // class Visitor { // visitChildren(ctx) { // if (!ctx) { // return; // } // if (ctx.children) { // return ctx.children.map(child => { // if (child.children && child.children.length != 0) { // return child.accept(this); // } else { // return child.getText(); // } // }); // } // } // } // tree.accept(new Visitor()); // // 自定义侦听器 // class KeyPrinter extends LExprListener { // // override default listener behavior // exitKey(ctx) { // console.log("Oh, a key!"); // } // } // const printer = new KeyPrinter(); // antlr4.tree.ParseTreeWalker.DEFAULT.walk(printer, tree); // const walker = new tree.ParseTreeWalker(); // console.log(8888, walker) // // 自定义的监听器,采用Listener模式 // const extractor = new LExprListener({ // enterAliasedRelation: this.enterAliasedRelation, // this.enterAliasedRelatio是具体的业务逻辑 // enterQualifiedName: this.enterQualifiedName, // }); // walker.walk(extractor, tree); }, addCodeCompletionCore(parser) { // console.log(43534, parser) const core = new c3.CodeCompletionCore(parser); // 1) At the input start. let candidates = core.collectCandidates(6); // const core = new c3.CodeCompletionCore(parser); // // 1) At the input start. // let candidates = core.collectCandidates(1); console.log(5454646, candidates) }, // // 错误分析获取解析 // addCodeCompletionCore1(sql) { // let LExprLexer = this.LExprLexer // let LExprParser = this.LExprParser // console.log("校验内容2", sql) // const chars = new antlr4.InputStream(sql); // const lexer = new LExprLexer(chars); // const tokens = new antlr4.CommonTokenStream(lexer); // const parser = new LExprParser(tokens); // // 添加错误监听器 // let ErrorListener1 = new antlr4.error.ErrorListener // parser.removeErrorListeners() // // monaco-editor 添加错误标志 // let typeList = []; // ErrorListener1.syntaxError = (recognizer, offendingSymbol, line, column, msg, e) => { // console.log("--错误提示信息", 1, recognizer, 2, offendingSymbol, 3, line, 4, column, 5, msg, 6, e) // let reg1 = /^mismatched input '<EOF>' expecting ([\s\S]*)$/ // if( reg1.test(msg) ){ // msg.replace(reg1, (res, $1) => { // // console.log("错误的结束", $1) // typeList = this.gettypeList($1, {}).split(",") // console.log("错误的结束", typeList) // this.hintList = typeList // }) // } // } // parser.addErrorListener(ErrorListener1) // // 生成语法树 // parser.buildParseTrees = true; // const tree = parser.ruleStatement(); // }, // 根据tokens列表,获取当前光标所选token getTokenByPosition(line, column) { // console.log(4534456, this.tokens) let selectToken this.tokens.forEach((tokenItem) => { if( line == tokenItem.line && column > tokenItem.column && column < (tokenItem.column + tokenItem.text.length) ){ console.log("光标选中token", tokenItem, tokenItem.text) selectToken = tokenItem } }) return selectToken }, // 根据tokens列表及点击位置,获取点击位置之前的token列表 getTokensByPosition(line, column, type) { let beforeTokens = '' let tokenList = [] this.beforeTokensAll = [] this.tokens.forEach((tokenItem) => { if( line > tokenItem.line || (line == tokenItem.line && column >= (tokenItem.column + tokenItem.text.length)) ){ // console.log("光标之前的token列表", tokenItem, tokenItem.text, line, column) if( type == "list" ){ this.beforeTokensAll.push(tokenItem) let tokenItemValue = tokenItem.text.replace(/\s+/g, "") tokenItemValue = tokenItemValue.replace(/[\r\n]/g, "") if( tokenItemValue ){ tokenList.push(tokenItem) } }else{ beforeTokens += tokenItem.text } } }) if( type == 'list' ) { return tokenList }else{ return beforeTokens } }, // 翻译错误提示 getMsgChina(msg) { let typeMap = this.typeMap let msgChina // 处理 "mismatched input '等于' expecting COMPARE" 情况 var reg1 = /^mismatched input '([\s\S]*)' expecting ([\s\S]*)$/ // 处理 no viable alternative at input '<一个数字> 小于' 情况 var reg2 = /^no viable alternative at input '([\s\S]*)'$/ // 处理 extraneous input '<一个文本>' expecting {' 的姓名 为 ', STRING, VARIABLE} 情况 var reg3 = /^extraneous input '([\s\S]*)' expecting ([\s\S]*)$/ // 处理 missing ' 的姓名列表' at ';' 情况 var reg4 = /^missing ([\s\S]*) at ([\s\S]*)$/ if( reg1.test(msg) || reg3.test(msg) ){ let reg = reg1.test(msg)? reg1: reg3 msgChina = msg.replace(reg, (res, $1, $2) => { let cmsg = '' let test1 = /^\{([\s\S]*)\}$/ let listArrayChina = '' if( test1.test($2) ){ // 处理 "mismatched input '等于' expecting {'(', '非', '真', '假', STRING, VARIABLE, DIGIT}" 情况 listArrayChina = this.gettypeList($2, typeMap) }else{ // 处理 "mismatched input '等于' expecting COMPARE" 情况 listArrayChina = typeMap[$2] || $2 } if( $1 == '<EOF>' ){ cmsg = `错误的结束,期望输入${listArrayChina}` }else{ cmsg = `'${$1}'输入不匹配,期望输入${listArrayChina}` } return cmsg }) }else if( reg2.test(msg) ){ msgChina = msg.replace(reg2, (res, $1) => { return `无法识别输入'${$1}'` }) }else if( reg4.test(msg) ){ msgChina = msg.replace(reg4, (res, $1, $2) => { if( $2 == "<EOF>" ){ return `缺少结束符'${$1}'` }else{ return `${$2} 处缺少 '${typeMap[$1] || $1}'` } }) } return msgChina }, // 获取类型列表 gettypeList($2, typeMap) { let listData = $2.replace(/\}$/, '') listData = listData.replace(/^\{/, '') let listArray = listData.split(',') // 获取所需输入列表的中文 let listArrayChina = listArray.map((v) => { let type = v.replace(/(^\s*)|(\s*$)/g, "") return typeMap[type] || type }) return listArrayChina.join(",") }, // 获取规则数据 getRuleData() { return this.editMsg.getValue() }, // 设置规则数据 setRuleData(msgChina) { let firstDecorations = this.tokens[0] let lastDecorations = this.tokens[this.tokens.length - 1] let range = { startLineNumber: firstDecorations.line, startColumn: firstDecorations.column, endLineNumber: lastDecorations.line, endColumn: lastDecorations.column + lastDecorations.text.length + 1, } this.editMsg.executeEdits('insert-code', [ { range, text: msgChina, }, ]) }, // 控制台打印树 consoleParseTree(data) { let tree = this.ParseTree(data) console.log("生成树", tree) } }, beforeDestroy() { this.registerObject.completionItemProvider.dispose() this.editMsg.dispose() } } </script> <style lang="scss" scoped> .container { position: relative; height: 100vh; } </style>