如何让Ace编辑器(react-ace)每次按键都调用自定义自动补全器?
问题:Ace编辑器自定义补全器无法每次按键触发
我给Ace编辑器(通过react-ace使用)添加了自定义自动补全器,功能可正常运行,但补全器并非每次按键都会被调用,无法满足业务需求:
- 输入"W"时,补全器返回
["W", "Waiter", "Wales"];接着输入"Wi",补全器未被调用,由Ace自行过滤列表仅显示"Waiter"。我需要每次按键都调用补全器,向后端请求基于当前前缀的新选项,而非依赖本地过滤。 - 输入"University"时,补全器仅在输入"U"和"Unive"时触发,需输入5个字符才会再次调用,该行为原因不明。
我希望实现Ace每次按键都调用自定义补全器函数,是否可行?
相关代码
react-ace组件代码
<AceEditor mode="java" theme="github" onChange={(value) => setText(value)} enableLiveAutocompletion={true} />
自定义补全器代码
import * as ace from "ace-builds/src-noconflict/ace"; const langTools = ace.acequire("ace/ext/language_tools"); const myCompleter = { getCompletions: function (editor, session, pos, prefix, callback) { if (prefix.length === 0) { callback(null, []); return; } backend.autocomplete(prefix).then((options) => { callback( null, options.map((option, index) => { return { caption: option, value: option, }; }) ); }); }, }; langTools.setCompleters([myCompleter]);
补充说明
查看Ace源码后我理解了原因:Ace默认设计是补全提供者返回所有选项,由Ace自行本地过滤;当过滤无结果时,会调用Autocomplete.prototype.detach使补全器失活,下一次按键才会重新调用补全器。若修改Autocomplete.prototype.updateCompletions的keepPopupPosition为false可实现需求,但我的场景似乎不符合Ace原有设计,是否只能提交PR解决?若理解有误请指出!
解决方案
不需要修改Ace源码或提交PR,以下两种方案可实现需求:
方案1:标记补全器为动态数据源
给补全器添加retainItems: false属性,告诉Ace该补全器的选项是动态的,无需缓存本地过滤,每次输入都需重新调用getCompletions:
const myCompleter = { retainItems: false, // 关键配置,禁用本地缓存过滤 getCompletions: function (editor, session, pos, prefix, callback) { // 原有逻辑保持不变 if (prefix.length === 0) { callback(null, []); return; } backend.autocomplete(prefix).then((options) => { callback( null, options.map((option, index) => { return { caption: option, value: option, }; }) ); }); }, };
方案2:手动强制触发补全更新
通过监听编辑器change事件,手动调用补全更新方法,强制触发补全器调用:
import { useRef, useEffect } from 'react'; import * as ace from "ace-builds/src-noconflict/ace"; // 获取编辑器实例ref const editorRef = useRef(null); useEffect(() => { const editor = editorRef.current?.editor; if (!editor) return; const handleChange = () => { const langTools = ace.acequire("ace/ext/language_tools"); const autocomplete = langTools.autocomplete; if (autocomplete && autocomplete.isActive) { // 强制更新补全列表 autocomplete.updateCompletions(); } }; editor.on("change", handleChange); return () => editor.off("change", handleChange); }, []); // 给AceEditor组件添加ref <AceEditor ref={editorRef} mode="java" theme="github" onChange={(value) => setText(value)} enableLiveAutocompletion={true} />
原理说明
你对Ace源码的理解是正确的:默认情况下,Ace会缓存补全器返回的所有选项,仅当本地过滤结果为空或前缀长度达到默认阈值(5个字符)时,才会重新调用补全器。retainItems: false是打破该缓存逻辑的官方配置,无需修改源码即可实现每次按键触发补全器的需求。
内容的提问来源于stack exchange,提问作者cw124
相关产品推荐
相关产品推荐

