先上图
![在这里插入图片描述](https://img-blog.csdnimg.cn/c070cde11a314c45a859ccb0ca7f41c7.png
产品经理的需求是编辑框中用了占位符的话,占位符中的变量高亮,简单吧!还有一个附加要求,必须是外部定义的变量,不是只用{{}}来区分的,从图中也能看到{{xxx}}就没有高亮。
接到这个需求我的第一反应是想跟产品经理说,这个需求提的很好,下次别提了,哈哈,开玩笑的。
我第一反应想到了用codemirror,以我对codemirror不多的理解,这个需求应该用语言高亮的功能能实现,毕竟我也简单的用过@codemirror/lang-javascript。然后我就开始研究语言包怎么写。研究结果是,语言包只能给定规则,这是用Lezer定义的上下文无关文法实现的,但是我没想到动态传入变量的方法,也就是解决不了上图中{{xxx}}不高亮的问题。
开始了啃codemirror文档的漫长之旅。我参考了文档https://codemirror.net/examples/decoration/中添加下划线的代码,基本实现了使用我想要的功能,不过不完善。
然后把这个需求的优先级放低,先做手上的工作,有空就研究codemirror文档。其实也没啥空。也就昨天下午到今天有空,不死心又去啃文档了。今天终于解决了!最终的解决方案是借鉴了@codemirror/search中的代码实现的
看codemirror的源码发现自己的能力差太多了,好羡慕人家那么强的能力,有空我想通读一下codemirror的源码,一想到它里面对字符串的操作算法我就头大,根本看不懂:)
代码有空在写进来,今天就这样
补全代码如下
// 代码参考了@codemirror/search
import { Decoration, EditorView, ViewPlugin, ViewUpdate, type DecorationSet } from '@codemirror/view'
import { Facet, combineConfig } from '@codemirror/state'
//#region 定义插件
type HighlightOptions = {
getKeywords: () => string[];
}
const defaultHighlightOptions: HighlightOptions = {
getKeywords: () => []
}
const highlightConfig = Facet.define<HighlightOptions, Required<HighlightOptions>>({
combine(options: readonly HighlightOptions[]) {
return combineConfig(options, {})
}
})
const matchDeco = Decoration.mark({class: "cm-variableMatch"})
const matchHighlighter = ViewPlugin.fromClass(class {
decorations: DecorationSet
constructor(view: EditorView) {
this.decorations = this.getDeco(view)
}
generateRanges(text: string, keywords: string[]) {
const ret = [] as Array<{from: number; to: number}>
keywords.forEach(keyword => {
let index = 0
while((index = text.indexOf(`{{${keyword}}}`, index)) !== -1) {
ret.push({ from: index + 2, to: index + 2 + keyword.length })
index += 4 + keyword.length
}
})
return ret
}
update(update: ViewUpdate) {
if (update.docChanged || update.viewportChanged) {
this.decorations = this.getDeco(update.view)
}
}
getDeco(view: EditorView) {
const keywords = view.state.facet(highlightConfig).getKeywords()
const ranges = this.generateRanges(view.state.doc.toString(), keywords)
if (ranges.length) {
return Decoration.set(ranges.map(r => matchDeco.range(r.from, r.to)), true)
}
return Decoration.none
}
}, {
decorations: v => v.decorations
})
const defaultTheme = EditorView.baseTheme({
".cm-variableMatch": { color: "#1890FF" }
})
//#endregion
export default (options: HighlightOptions) => {
return [defaultTheme, matchHighlighter, highlightConfig.of(options)]
}
如果您觉得对您有帮助,请点赞哦:)