Vue使用monaco-editor编辑器 自定义提示 代码高亮

安装

1
2
npm install monaco-editor@0.20.0
npm install monaco-editor-webpack-plugin@1.9.0

vue.config

1
2
3
4
5
6
7
8
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new MonacoWebpackPlugin()
]
}
};

文件结构

1
2
3
4
5
6
monaco-editor
|- util
|- |- javascript-completion.js
|- |- sql-completion.js
|- |- log-language.js
|- MonacoEditor.vue

monaco-editor/MonacoEditor.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<template>
<div ref="editor" class="main"></div>
</template>

<script>
import * as monaco from "monaco-editor";
import createSqlCompleter from "./util/sql-completion";
import createJavascriptCompleter from "./util/javascript-completion";
import registerLanguage from "./util/log-language";
const global = {};

const getHints = (model) => {
let id = model.id.substring(6);
return (global[id] && global[id].hints) || [];
};
monaco.languages.registerCompletionItemProvider(
"sql",
createSqlCompleter(getHints)
);
monaco.languages.registerCompletionItemProvider(
"javascript",
createJavascriptCompleter(getHints)
);
registerLanguage(monaco);
/**
* monaco options
* https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html
*/
export default {
props: {
options: {
type: Object,
default() {
return {};
},
},
value: {
type: String,
required: true,
},
language: {
type: String,
},
hints: {
type: Array,
default() {
return [];
},
},
},
name: "MonacoEditor",
data() {
return {
editorInstance: null,
defaultOptions: {
theme: "vs",
fontSize: 14,
},
};
},
watch: {
value() {
if (this.value !== this.editorInstance.getValue()) {
this.editorInstance.setValue(this.value);
}
},
},
mounted() {
this.initEditor();
global[this.editorInstance._id] = this;
window.addEventListener("resize", this.layout);
},
destroyed() {
this.editorInstance.dispose();
global[this.editorInstance._id] = null;
window.removeEventListener("resize", this.layout);
},
methods: {
layout() {
this.editorInstance.layout();
},
undo() {
this.editorInstance.trigger("anyString", "undo");
this.onValueChange();
},
redo() {
this.editorInstance.trigger("anyString", "redo");
this.onValueChange();
},
getOptions() {
let props = { value: this.value };
this.language !== undefined && (props.language = this.language);
let options = Object.assign({}, this.defaultOptions, this.options, props);
return options;
},
onValueChange() {
this.$emit("input", this.editorInstance.getValue());
this.$emit("change", this.editorInstance.getValue());
},
initEditor() {
this.MonacoEnvironment = {
getWorkerUrl: function () {
return "./editor.worker.bundle.js";
},
};

this.editorInstance = monaco.editor.create(
this.$refs.editor,
this.getOptions()
);
this.editorInstance.onContextMenu((e) => {
this.$emit("contextmenu", e);
});
this.editorInstance.onDidChangeModelContent(() => {
this.onValueChange();
});
this.editorInstance.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S,
() => {
this.$emit("save", this.editorInstance.getValue());
}
);
},
},
};
</script>

<style scoped>
.main /deep/ .view-lines * {
font-family: Consolas, "Courier New", monospace !important;
}
</style>

monaco-editor/util/javascript-completion.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import * as monaco from 'monaco-editor'
// js 有内置提示
function createCompleter(getExtraHints) {
const createSuggestions = function (model, textUntilPosition) {
let text = model.getValue();
textUntilPosition = textUntilPosition.replace(/[\*\[\]@\$\(\)]/g, "").replace(/(\s+|\.)/g, " ");
let arr = textUntilPosition.split(/[\s;]/);
let activeStr = arr[arr.length - 1];
let len = activeStr.length;
let rexp = new RegExp("([^\\w]|^)" + activeStr + "\\w*", "gim");
let match = text.match(rexp);
let mergeHints = Array.from(new Set([...getExtraHints(model)]))
.sort()
.filter(ele => {
let rexp = new RegExp(ele.substr(0, len), "gim");
return (match && match.length === 1 && ele === activeStr) ||
ele.length === 1 ? false : activeStr.match(rexp);
});
return mergeHints.map(ele => ({
label: ele,
kind: monaco.languages.CompletionItemKind.Text,
documentation: ele,
insertText: ele
}));
};
return {
provideCompletionItems(model, position) {
let textUntilPosition = model.getValueInRange({
startLineNumber: position.lineNumber,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column
});
return { suggestions: createSuggestions(model, textUntilPosition) };
}
}
}
export default createCompleter;

monaco-editor/util/sql-completion.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import * as monaco from 'monaco-editor'
const hints = [
"SELECT",
"INSERT",
"DELETE",
"UPDATE",
"CREATE TABLE",
"DROP TABLE",
"ALTER TABLE",
"CREATE VIEW",
"DROP VIEW",
"CREATE INDEX",
"DROP INDEX",
"CREATE PROCEDURE",
"DROP PROCEDURE",
"CREATE TRIGGER",
"DROP TRIGGER",
"CREATE SCHEMA",
"DROP SCHEMA",
"CREATE DOMAIN",
"ALTER DOMAIN",
"DROP DOMAIN",
"GRANT",
"DENY",
"REVOKE",
"COMMIT",
"ROLLBACK",
"SET TRANSACTION",
"DECLARE",
"EXPLAN",
"OPEN",
"FETCH",
"CLOSE",
"PREPARE",
"EXECUTE",
"DESCRIBE",
"FORM",
"ORDER BY"]
function createCompleter(getExtraHints) {
const createSuggestions = function (model, textUntilPosition) {
let text = model.getValue();
textUntilPosition = textUntilPosition.replace(/[\*\[\]@\$\(\)]/g, "").replace(/(\s+|\.)/g, " ");
let arr = textUntilPosition.split(/[\s;]/);
let activeStr = arr[arr.length - 1];
let len = activeStr.length;
let rexp = new RegExp("([^\\w]|^)" + activeStr + "\\w*", "gim");
let match = text.match(rexp);
let textHints = !match ? [] :
match.map(ele => {
let rexp = new RegExp(activeStr, "gim");
let search = ele.search(rexp);
return ele.substr(search);
});
let mergeHints = Array.from(new Set([...hints, ...textHints, ...getExtraHints(model)]))
.sort()
.filter(ele => {
let rexp = new RegExp(ele.substr(0, len), "gim");
return (match && match.length === 1 && ele === activeStr) ||
ele.length === 1 ? false : activeStr.match(rexp);
});
return mergeHints.map(ele => ({
label: ele,
kind: hints.indexOf(ele) > -1 ?
monaco.languages.CompletionItemKind.Keyword :
monaco.languages.CompletionItemKind.Text,
documentation: ele,
insertText: ele
}));
};
return {
provideCompletionItems(model, position) {
let textUntilPosition = model.getValueInRange({
startLineNumber: position.lineNumber,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column
});
return { suggestions: createSuggestions(model, textUntilPosition) };
}
}
}
export default createCompleter;

monaco-editor/util/log-language.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function registerLanguage(monaco) {
monaco.languages.register({
id: "log"
});
monaco.languages.setMonarchTokensProvider("log", {
tokenizer: {
root: [
[/(^[=a-zA-Z].*|\d\s.*)/, "log-normal"],
[/\sERROR\s.*/, "log-error"],
[/\sWARN\s.*/, "log-warn"],
[/\sINFO\s.*/, "log-info"],
[
/^([0-9]{4}||[0-9]{2})-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]{3})?/,
"log-date",
],
[
/^[0-9]{2}\/[0-9]{2}\/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]{3})?/,
"log-date",
],
[/(^\*\*Waiting queue:.*)/, "log-info"],
[/(^\*\*result tips:.*)/, "log-info"],
],
},
});
monaco.editor.defineTheme("log", {
base: "vs",
inherit: true,
rules: [{
token: "log-info",
foreground: "4b71ca"
},
{
token: "log-error",
foreground: "ff0000",
fontStyle: "bold"
},
{
token: "log-warn",
foreground: "FFA500"
},
{
token: "log-date",
foreground: "008800"
},
{
token: "log-normal",
foreground: "808080"
},
],
colors: {
"editor.lineHighlightBackground": "#ffffff",
"editorGutter.background": "#f7f7f7",
},
});

}

export default registerLanguage;

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<monaco-editor
v-model="value"
language="sql"
:options="{}"
:hints="['table_name']"
style="width:400px;height:300px"
/>
</template>
<script>
import MonacoEditor from "@/components/monaco-editor/MonacoEditor";
export default {
components: {
MonacoEditor,
},
data() {
return {
value: "xxx",
};
},
};
</script>