我正在尝试为我的某个程序实现文件夹名称完成,并且我正在使用readline模块。我写了一个Completer类(文件autocompletion.py
):
import os.path
import readline
class Completer(object):
def _match_path(self, text):
text = readline.get_line_buffer()
return DirectoryPathCompleter(text).completions()
def complete(self, text, state):
matches = self._match_path(text)
try:
return matches[state]
except IndexError:
return None
class DirectoryPathCompleter(object):
def __init__(self, text):
self.text = text
def completions(self):
path, rest = os.path.split(self.text)
if rest == '.' or rest == '..':
result = [rest + '/']
else:
result = [i + '/' for i in get_dirs_under(path) if i.startswith(rest)]
return result
def get_dirs_under(path):
if not path:
path = '.'
dirpath, dirnames, filenames = os.walk(path).next()
return dirnames
在这里尝试这个是一个小脚本try_readline.py
#!/usr/bin/env python
from autocompletion import Completer
import readline
readline.parse_and_bind('tab: complete')
readline.set_completer(Completer().complete)
raw_input('Test it! > ')
我可以看到,只要我不尝试完成可以部分完成的dirnames,它就能很好地工作。当我创建一个具有以下结构的目录并通过shell运行程序时:
$ mkdir -p test/bla-foo test/bla-bar test/bla-baz test/something test/else
$ cp autocompletion.py try_readline.py test
$ cd test; tree
.
├── autocompletion.py
├── bla-bar
├── bla-baz
├── bla-foo
├── else
├── something
└── try_readline.py
5 directories, 2 files
$ python try_readline.py
我尝试通过TAB完成bla-
,因此我得到bla-bla-
。我希望完成时坚持使用bla-
并显示可以完成的替代方案。
我怎样才能做到这一点?
修改
好的,我不知道为什么会发生这种情况。如果我将try_readline.py
脚本更改为如下所示:
#!/usr/bin/env python
import readline
readline.parse_and_bind('tab: complete')
raw_input('Test it! > ')
所以我没有使用自己的Completer
并且我在上面显示的相同文件夹结构上运行它,我可以看到类似的行为:
Test it! > bla-b<TAB>
Test it! > bla-bla-
我希望:
Test it! > bla-b<TAB>
Test it! > bla-ba<TAB><TAB>
bla-bar/ bla-baz/
EDIT2
好的我现在有另一种方法,花了这么长时间,因为readline文档非常稀疏..我仍然无法确切地知道为什么我必须这样做,但它的工作原理。
如果我查看readline正在使用的分隔符,我得到:
In [1]: readline.get_completer_delims()
Out[1]: ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
特别是\\
部分对我来说很奇怪,所以我确实在try_readline.py
#!/usr/bin/env python
import readline
from autocompletion import Completer
readline.parse_and_bind('tab: complete')
readline.set_completer_delims(' \t\n')
raw_input('Test it! > ')
我现在正在使用标准的readline完成,因为我不知道如何编写正确的Completer
...我想重用标准的readline完成函数,只是过滤掉任何非目录但是我不知道如何从模块中提取它。