CodeMirror代码编辑器实现自定义提示功能增强版(支持搜索、调用接口查询提示内容)
本文提供了一种解决上述两个问题的实现方式,不同的主要是handleShowHint
这个方法,修改了一些提示逻辑。
# 方式一
具体代码如下:
handleShowHint() {
const {modelData, modelPointsAttributes} = this.props.configureStore;
const modelDataList = modelData.map(item => (item.modelId));
const codeMirrorInstance = this.codeEditorRef.getCodeMirrorInstance();
const cur = this.codeEditor.getCursor();
const curLine = this.codeEditor.getLine(cur.line);
const end = cur.ch;
const start = end;
let list = [];
// 根据不同情况给list赋值,默认为[],即不显示提示框。
const cursorTwoCharactersBefore = `${curLine.charAt(start - 2)}${curLine.charAt(start - 1)}`;
if (cursorTwoCharactersBefore === '${') {
list = modelDataList;
} else if (cursorTwoCharactersBefore === '::') {
const lastIndex = curLine.lastIndexOf('${', start);
const modelId = curLine.substring(lastIndex + 2, start - 2);
if (modelPointsAttributes[modelId]) {
list = modelPointsAttributes[modelId];
} else {
list = [];
this.props.configureStore.getModelPointsAttributes(modelId, this.handleHint);
}
} else {
const lastStartIndex = curLine.lastIndexOf('${', start);
const lastEndIndex = curLine.lastIndexOf('}', start);
const lastColonIndex = curLine.lastIndexOf('::', start);
if (start > lastStartIndex && (start <= lastColonIndex || lastColonIndex < lastStartIndex)) {
const modelId = curLine.substring(lastStartIndex + 2, start);
list = modelDataList.filter(item => (item.indexOf(modelId) !== -1));
// eslint-disable-next-line
return {list, from: codeMirrorInstance.Pos(cur.line, lastStartIndex + 2), to: codeMirrorInstance.Pos(cur.line, end)};
} else if (start > lastStartIndex && start > lastColonIndex && (start <= lastEndIndex || (lastEndIndex < lastColonIndex && lastEndIndex < lastStartIndex))) {
const modelId = curLine.substring(lastStartIndex + 2, lastColonIndex);
const pointAttrId = curLine.substring(lastColonIndex + 2, start);
if (modelPointsAttributes[modelId]) {
list = modelPointsAttributes[modelId].filter(item => (item.indexOf(pointAttrId) !== -1));
// eslint-disable-next-line
return {list, from: codeMirrorInstance.Pos(cur.line, lastColonIndex + 2), to: codeMirrorInstance.Pos(cur.line, end)};
}
}
}
// eslint-disable-next-line
return {list, from: codeMirrorInstance.Pos(cur.line, start), to: codeMirrorInstance.Pos(cur.line, end)};
}
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
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
实现中使用如下代码请求所选模型下点:
this.props.configureStore.getModelPointsAttributes(modelId, this.handleHint);
1
具体实现在mobx的store中,不再贴相应代码。
注意
需要注意的是:我在项目里使用了mobx管理state,发请求的位置在store中,不在组件内,所以通过回调函数的形式当接口数据成功返回后调用this.handleHint来进行提示;
需要注意提供正确的提示框的位置,类似于下边的形式:
{list, from: codeMirrorInstance.Pos(cur.line, lastColonIndex + 2), to: codeMirrorInstance.Pos(cur.line, end)};
1
codemirror提供了一种基于Promise的异步提示方式,因为我发请求的操作不在组件里,所以没有使用,在可以的情况下,Promise的实现应该会更合适。
# 方式二
或者新建mode,为了方便,所以使用zzzz
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("zzzz", function(config, parserConfig) {
var jsonldMode = parserConfig.jsonld;
var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
function parseWords(str) {
var obj = {},
words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
// 关键字
var keywords = parseWords("s ss sss");
var type, content;
function ret(tp, style, cont) {
type = tp;
content = cont;
return style;
}
function tokenBase(stream, state) {
var beforeParams = state.beforeParams;
state.beforeParams = false;
var ch = stream.next();
if (ch == '"' || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
} else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
return ret("number", "number");
} else if (ch == '[') {
stream.skipTo(']');
stream.eat(']');
return ret("string", "string");
} else if (/\d/.test(ch)) {
stream.eatWhile(/[\w\.]/);
return "number";
} else {
stream.eatWhile(/[\w\$_{}\xa1-\uffff]/);
var word = stream.current();
if (keywords && keywords.propertyIsEnumerable(word)) {
state.beforeParams = true;
return "keyword";
}
return null;
}
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false,
next;
if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)) {
state.tokenize = tokenBase;
return ret("jsonld-keyword", "meta");
}
while ((next = stream.next()) != null) {
if (next == quote && !escaped) break;
escaped = !escaped && next == "\\";
}
if (!escaped) state.tokenize = tokenBase;
return ret("string", "string");
};
}
return {
startState: function() {
return {
tokenize: tokenBase,
beforeParams: false,
inParams: false
};
},
token: function(stream, state) {
if (stream.eatSpace()) return null;
return state.tokenize(stream, state);
}
};
});
CodeMirror.registerHelper("hint", "zzzz", function(cm) {
//自动补全
var hintList = ['s', 'ss', 'sss'];
var cur = cm.getCursor(),
token = cm.getTokenAt(cur);
var start = token.start,
end = cur.ch
var str = token.string
var list = hintList.filter(function(item) {
return item.indexOf(str) === 0
})
if (list.length) return {
list: list,
from: CodeMirror.Pos(cur.line, start),
to: CodeMirror.Pos(cur.line, end)
};
});
CodeMirror.defineMIME("text/x-zzzz", "zzzz");
});
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
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
上次更新: 2024/01/30, 00:35:17
- 01
- Node与GLIBC_2.27不兼容解决方案08-19
- 02
- Git清空本地文件跟踪缓存08-13