ido-completing-read处理含_/-字符时匹配候选项异常消失问题
现象产生原因
这个匹配异常是ido默认的分层匹配+自动窄化逻辑导致的,具体规则和现象对应关系如下:
- 首先是分词规则:ido会自动将候选项中的非字母数字字符(如
-、_)识别为分词边界,把原字符串拆成多个独立匹配块,默认情况下块之间的分隔符不会作为普通字符参与连续子串匹配。 - 其次是匹配优先级机制:ido的匹配结果按优先级从高到低分为四档:同块连续前缀匹配 > 跨块首字母连续匹配 > 块内子串匹配 > 模糊字符匹配。默认逻辑下,只要存在高优先级匹配结果,低优先级的匹配项会被自动过滤,不会出现在补全列表中。
- 最后是自动窄化逻辑:当输入字符串长度刚好等于某个分词块的长度时,ido会默认用户正在做该块的精确连续匹配,不会自动跳过分隔符去匹配下一个块的内容,除非用户手动输入对应的分隔符。
对应两组测试场景的表现完全符合上述逻辑:
- 第一组候选项为
DALL-E、monoidallens:- 输入
dall时,长度刚好匹配DALL-E第一个分词块DALL的长度,此时高优先级匹配结果数量不足触发窄化过滤,因此靠块内子串匹配到的monoidallens也会正常展示。 - 输入到
dalle时,monoidallens是无分隔符的单块字符串,dalle是它的连续子串,属于高优先级的同块连续匹配;而DALL-E第一个块长度仅为4,第5个输入字符e前没有手动输入-分隔符,无法自动跨块匹配到后面的E,属于低优先级匹配,直接被过滤隐藏。
- 输入
- 第二组候选项为
DALL-E、monoidal_lens:- 输入
dall时,DALL-E的第一个块被完全匹配,属于最高优先级的同块前缀匹配,此时monoidal_lens中的dall子串位于第一个块的非前缀位置,属于低优先级匹配,直接被过滤。 - 输入到
dalle时,两个候选项都不满足最高优先级的同块连续前缀匹配要求(DALL-E卡在分隔符校验,monoidal_lens第一个块长度为8,dalle不是它的前缀),ido自动降级到全字符串模糊匹配模式,忽略分隔符做匹配,因此两个候选项都能被命中展示。
- 输入
配置解决方法
可以通过调整ido内置配置变量,关闭自动窄化和优先级过滤逻辑,让所有符合子串匹配规则的候选项始终展示,基础配置如下:
;; 开启全局模糊匹配,支持不连续字符命中 (setq ido-enable-flex-matching t) ;; 关闭匹配到前缀时自动窄化、过滤其他低优先级匹配项的逻辑 (setq ido-auto-narrow nil) ;; 匹配时自动忽略候选项中的-、_等非字母数字分隔符,无需手动输入分隔符即可跨块匹配 (setq ido-ignore-non-alnum-chars-in-matching t) ;; 放宽前缀匹配校验,允许跨块匹配时无需手动输入分隔符 (setq ido-prefix-lax t)
如果做完上述配置仍存在个别候选项被异常过滤的情况,可以直接替换ido的默认匹配函数,用最直接的“忽略大小写、忽略非字母数字字符的子串匹配”逻辑,彻底避免候选项异常消失:
(setq ido-make-matches-function (lambda (items) (let* ((input (downcase ido-text)) (filtered (cl-remove-if-not (lambda (item) (string-match-p input (downcase (replace-regexp-in-string "[^a-zA-Z0-9]" "" item)))) items))) (list filtered filtered filtered))))
这段代码会先把所有候选项的非字母数字字符剔除、转成小写,只要输入是处理后字符串的子串就会保留在补全列表里,不会因为输入长度变化意外过滤已匹配的候选项。
内容的提问来源于stack exchange,提问作者Iceland_jack
相关产品推荐
相关产品推荐

