Rich 没有高亮词却返回匹配数,过滤空词表再调用
Rich 的空高亮词表被拼成空正则,在字符边界生成零长度 spans。过滤空字符串并对空结果跳过调用,保留字面匹配和基础样式。
搜索框已经清空,Rich 的 text.highlight_words([], "red") 却返回了匹配数,后续样式输出也变得零碎。Rich 15.0.0 会把空词表拼成空正则;对 "abc",空正则在四个字符边界匹配,并追加四个零长度 span。
返回 4 不表示找到了四个真实单词。这个方法会修改原来的 Text,因此不能只忽略返回值,让无意义的 span 一直留在对象中。
空词表不调用,空字符串单独过滤
业务定义“没有非空关键词就不高亮”时,可以在调用边界过滤,并把可能的一次性迭代器只消费一次:
from rich.text import Text
def highlight_nonempty(text: Text, words, style="red") -> int:
selected = [word for word in words if word != ""]
if not selected:
return 0
return text.highlight_words(selected, style)
这里约定 words 是字符串序列或字符串迭代器。若调用方允许其他类型,应先按自己的输入契约拒绝或转换,不要让 str(None) 意外成为搜索词。
不能只写 if words:。非空列表 [''] 仍会产生空模式,生成器对象即使最终没有元素也通常为真。先生成 selected 可以同时处理这两种情况。过滤规则只移除长度为零的字符串;不调用 .strip(),因为空格本身可能是用户有意搜索的内容。
该封装保留 highlight_words() 的字面转义行为。不要为绕开空词表把它换成直接调用 highlight_regex(),否则关键词里的括号、点号等字符可能开始按正则解释,改变原来含义。
已经污染的 Text,重新按展示数据构造
如果每次搜索都继续修改同一个 Text,旧搜索高亮本来也可能累积。可以保留独立的基础文本或带基础样式的副本,每次从该状态生成一份展示对象,再应用本次关键词。
不要只为清除零长度 span 就把全部 spans 清空:原有语法色、链接或强调也可能存在。也不要从已经高亮过的对象反复 copy,期待它自动恢复未高亮状态。基础对象应在搜索修改之前保存,并符合应用自己的生命周期。
截至 2026 年 10 月 26 日,正式 Rich 15.0.0 的实现仍直接拼接词表并遍历正则匹配,未先拦截空模式。验收包含空列表、空生成器、仅含空字符串,以及混合空词和真实词;没有有效词时返回 0,原有 span 内容不变。
最后再用真实关键词核对次数和渲染效果,尤其是关键词本身含正则特殊字符的情况。匹配数恢复为零只覆盖空输入分支,不代表应用已经正确撤销上一轮搜索的高亮。


