我之前在文章《CodeMirror基本使用及实现关键词高亮、自定义主题》中简单介绍了CodeMirror实现自动提示的功能,但是使用的是CodeMirror中封装好的几种指定语法的提示功能,如css、html、js、sql、xml,对应文件如下列表:
现在我开发中有需求做自定义语法的自动提示功能,并没有现成的mode可以使用,学习CodeMirror API和上述几个文件的实现方式之后找到了实现方法,记录一下我的实现方式。
本文以${model::point}为例,需求是输入${后直接出现下拉框,提示内容是所有的model,当选择或输入model后,输入::直接出现下拉框,提示内容为${后::前model里所有的point和attribute,在${和::后点击Ctrl,也出现和上面相同的提示信息。
要提示的数据举例如下:
{
"model_a":{
"points":[
"point_a1",
"point_a2",
"point_a3"
],
"attributes":[
"attribute_a1",
"attribute_a2",
"attribute_a3"
]
},
"model_b":{
"points":[
"point_b1",
"point_b2",
"point_b3"
],
"attributes":[
"attribute_b1",
"attribute_b2",
"attribute_b3"
]
}
}
要实现的效果如下:
实现方式:
import React, {Component} from 'react';
import autobind from 'class-autobind';
import {inject, observer} from 'mobx-react';
import CodeMirror from 'react-codemirror';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/anyword-hint.js';
import 'codemirror/lib/codemirror.css';
import './index.less';
@inject('configureStore')
@observer
export default class CodeEditor extends Component {
constructor(props) {
super(props);
autobind(this, CodeEditor.prototype);
this.options = {
lineNumbers: false,
// mode: {name: "text/x-mysql"},
extraKeys: {"Ctrl": "autocomplete"},
theme: 'sqlTheme',
lineWrapping:true,
hintOptions: {hint: this.handleShowHint, completeSingle: false}
};
}
componentDidMount(){
this.codeEditor = this.codeEditorRef.getCodeMirror();
}
handleShowHint(){
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)}`;
const {activeItem, openedItemsData} = this.props.configureStore;
const {variablePool} = openedItemsData[activeItem].config;
const variablePoolKeys = variablePool ? Object.keys(variablePool): [];
if(cursorTwoCharactersBefore === '${'){
list = variablePoolKeys;
}else if(cursorTwoCharactersBefore === '::'){
const lastIndex = curLine.lastIndexOf('${', start);
const modelId = curLine.substring(lastIndex + 2, start - 2);
if(lastIndex !== -1 && modelId && variablePool[modelId]){
list = variablePool[modelId].attributes.concat(variablePool[modelId].points);
}else {
list = [];
}
}
return {list: list, from: codeMirrorInstance.Pos(cur.line, start), to: codeMirrorInstance.Pos(cur.line, end)};
}
handleCodeChange(value){
this.codeEditor.closeHint();
this.codeEditor.showHint();
this.props.onChange(value);
}
render() {
return (
this.codeEditorRef = ref}
value={this.props.value}
onChange={this.handleCodeChange}
options={this.options}
/>
);
}
}
demo中使用了mobx管理state,variablePool是从configureStore中读取的。
更新:这里介绍的方式需要提前准备好提示的内容,且不支持搜索,只能选择。在《CodeMirror实现自定义提示功能增强版(支持搜索、调用接口查询提示内容)》里更新了一个增强版,可以支持所有和调用接口查询提示数据。