Python + readline + 自动补全(tab):为什么破折号和问号被视为单词分隔符?
大家好,程序员们,还有勇敢使用GNU-readline的朋友们,
几个月前,我开始使用Python的readline模块来开发一个类似命令行的应用程序。这个应用程序和文件或文件系统没有关系,它是为某个专有管理软件量身定制的解决方案。
昨天我发现某些特定的文本会导致意想不到的自动补全行为,但在文档中找不到解决办法。我非常需要你们的帮助。接下来我会给出一个例子,并附上一个代码片段,展示这个不想要的行为。
用于自动补全的值是:
aaa0 aaa1 aaa2 bbb_0 bbb_1 bbb_2
ccc-0 ccc-1 ccc-2 ddd?0 ddd?1 ddd?2
...然后不想要的行为如下(每个操作后面跟着结果输出,|符号表示光标位置):
- 输入'b'。
输入> b|
- 按TAB键(在我的配置中,TAB键是用来自动补全的)。
输入> bbb_|
- 再按一次TAB。你的文本保持不变,但会收到以下提示:
bbb_0 bbb_1 bbb_2
输入> bbb_| - 输入'0'并按TAB。
bbb_0 bbb_1 bbb_2
输入> bbb_0 |
注意'0'字符和光标之间的空格(下面的代码片段会解释这个)。
到这里为止一切正常,尝试输入'a'也会得到类似的输出,只是没有下划线(aaa0, aaa1, aaa2)。 - 重新开始,输入'c'。
输入> c
- 按TAB。
输入> ccc-
- 再按一次TAB。
aaa0 aaa1 aaa2 bbb_0 bbb_1 bbb_2 ccc-0 ccc-1 ccc-2 ddd?0 ddd?1 ddd?2
输入> ccc-|
这就是我问题的前半部分。所有值都被显示出来,而不是仅仅显示以'ccc-'开头的值。 - 输入'0'并按TAB。
aaa0 aaa1 aaa2 bbb_0 bbb_1 bbb_2 ccc-0 ccc-1 ccc-2 ddd?0 ddd?1 ddd?2
输入> ccc-0|
这就是我问题的后半部分,你看,'0'字符和光标之间没有空格(同样,下面的代码片段会解释为什么应该有空格)。实际上,按TAB既没有改变文本,也没有显示提示,进一步按TAB的行为也是一样。
实际上,在第7步发生的事情是一个误解。Readline把'-'字符误认为是单词分隔符(如果你尝试自动补全'ddd?',问号'?'字符也是如此;其他常见的单词分隔符还有空格、制表符、'='等)。所以,由于当前行的缓冲区以单词分隔符结尾,那么就该开始一个新单词了,对吧?因此,在第7步(我们现在的位置),按TAB时会显示所有值。
在第8步,当行看起来像这样"输入> ccc-0|
"时,按TAB没有效果,因为'-'作为单词分隔符,把这一行分成了两个单词:'ccc'和'0'。所以,要补全的单词是'0',但可惜的是,没有任何可能的值以'0'开头,因此没有效果。
现在,遗憾的是,这里没有对错之分。例如,在我的应用程序中,等号'='实际上是一个单词分隔符,但'-'不是。我想这可能是配置的问题,但我还没找到配置哪些字符作为单词分隔符的方法。这就是我需要帮助的地方。
我说到做到,这里是我承诺的代码片段:
import readline
values = ['aaa0', 'aaa1', 'aaa2', 'bbb_0', 'bbb_1', 'bbb_2',
'ccc-0', 'ccc-1', 'ccc-2', 'ddd?0', 'ddd?1', 'ddd?2']
def complete(text, state):
matches = [v for v in values if v.startswith(text)]
if len(matches) == 1 and matches[0] == text:
# Add space if the current text is the same as the only match
return "{} ".format(matches[0]) if state == 0 else None
if state >= len(matches):
return None
return matches[state]
readline.set_completer(complete)
for line in ("tab: complete", "set show-all-if-unmodified on"):
readline.parse_and_bind(line)
raw_input("Input> ")
各位朋友,请帮帮我!我保证会非常感激,甚至会回报你们的帮助。:)
非常感谢,
Amnon G
1 个回答
从 dir(readline)
的输出来看,函数 get_completer_delims()
和 set_completer_delims()
看起来可能会很有用。实际上,readline
模块的文档中包含了:
set_completer_delims(...)
set_completer_delims(string) -> None
set the readline word delimiters for tab-completion
我觉得这正好描述了你想要的功能。这是在 Python 2.6.7 上的内容;如果你使用的是更早的版本,可能就没有这个功能了。