Add tab completion for save and load path specs.

This commit is contained in:
Aldo Cortesi 2011-01-27 10:29:37 +13:00
parent bd6c0499fb
commit 460107589c
2 changed files with 110 additions and 5 deletions

View File

@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import Queue, mailcap, mimetypes, tempfile, os, subprocess import Queue, mailcap, mimetypes, tempfile, os, subprocess, glob
import os.path, sys import os.path, sys
import cStringIO import cStringIO
import urwid.curses_display import urwid.curses_display
@ -392,6 +392,70 @@ class ConnectionView(WWrap):
return key return key
class _PathCompleter:
DEFAULTPATH = "/bin:/usr/bin:/usr/local/bin"
def __init__(self, _testing=False):
"""
_testing: disables reloading of the lookup table to make testing possible.
"""
self.lookup, self.offset = None, None
self.final = None
self._testing = _testing
def reset(self):
self.lookup = None
self.offset = -1
def complete(self, txt):
"""
Returns the next completion for txt, or None if there is no completion.
"""
path = os.path.expanduser(txt)
if not self.lookup:
if not self._testing:
# Lookup is a set of (display value, actual value) tuples.
self.lookup = []
if os.path.isdir(path):
files = glob.glob(os.path.join(path, "*"))
prefix = txt
else:
files = glob.glob(path+"*")
prefix = os.path.dirname(txt)
prefix = prefix.rstrip("/") or "./"
for f in files:
display = os.path.join(prefix, os.path.basename(f))
if os.path.isdir(f):
display += "/"
self.lookup.append((display, f))
if not self.lookup:
self.final = path
return path
self.lookup.sort()
self.offset = -1
self.lookup.append((txt, txt))
self.offset += 1
if self.offset >= len(self.lookup):
self.offset = 0
ret = self.lookup[self.offset]
self.final = ret[1]
return ret[0]
class PathEdit(urwid.Edit, _PathCompleter):
def __init__(self, *args, **kwargs):
urwid.Edit.__init__(self, *args, **kwargs)
_PathCompleter.__init__(self)
def keypress(self, size, key):
if key == "tab":
comp = self.complete(self.get_edit_text())
self.set_edit_text(comp)
self.set_edit_pos(len(comp))
else:
self.reset()
return urwid.Edit.keypress(self, size, key)
class ActionBar(WWrap): class ActionBar(WWrap):
def __init__(self): def __init__(self):
self.message("") self.message("")
@ -399,6 +463,9 @@ class ActionBar(WWrap):
def selectable(self): def selectable(self):
return True return True
def path_prompt(self, prompt):
self.w = PathEdit(prompt)
def prompt(self, prompt): def prompt(self, prompt):
self.w = urwid.Edit(prompt) self.w = urwid.Edit(prompt)
@ -436,8 +503,11 @@ class StatusBar(WWrap):
def get_edit_text(self): def get_edit_text(self):
return self.ab.w.get_edit_text() return self.ab.w.get_edit_text()
def path_prompt(self, prompt):
return self.ab.path_prompt(prompt)
def prompt(self, prompt): def prompt(self, prompt):
self.ab.prompt(prompt) return self.ab.prompt(prompt)
def message(self, msg): def message(self, msg):
self.ab.message(msg) self.ab.message(msg)
@ -786,6 +856,11 @@ class ConsoleMaster(controller.Master):
self.nested = True self.nested = True
self.make_view() self.make_view()
def path_prompt(self, prompt, callback):
self.statusbar.path_prompt(prompt)
self.view.set_focus("footer")
self.prompting = callback
def prompt(self, prompt, callback): def prompt(self, prompt, callback):
self.statusbar.prompt(prompt) self.statusbar.prompt(prompt)
self.view.set_focus("footer") self.view.set_focus("footer")
@ -928,10 +1003,10 @@ class ConsoleMaster(controller.Master):
else: else:
raise Stop raise Stop
elif k == "S": elif k == "S":
self.prompt("Save flows: ", self.save_flows) self.path_prompt("Save flows: ", self.save_flows)
k = None k = None
elif k == "L": elif k == "L":
self.prompt("Load flows: ", self.load_flows) self.path_prompt("Load flows: ", self.load_flows)
k = None k = None
elif k == "c": elif k == "c":
self.prompt("Sticky cookie: ", self.set_stickycookie) self.prompt("Sticky cookie: ", self.set_stickycookie)

View File

@ -288,9 +288,39 @@ class uformat_keyvals(libpry.AutoTree):
] ]
) )
class uPathCompleter(libpry.AutoTree):
def test_completion(self):
c = console._PathCompleter(True)
c.reset()
c.lookup = [
("a", "x/a"),
("aa", "x/aa"),
]
assert c.complete("a") == "a"
assert c.final == "x/a"
assert c.complete("a") == "aa"
assert c.complete("a") == "a"
c = console._PathCompleter(True)
r = c.complete("l")
assert c.final.endswith(r)
c.reset()
assert c.complete("/nonexistent") == "/nonexistent"
assert c.final == "/nonexistent"
c.reset()
assert c.complete("~") != "~"
c.reset()
s = "thisisatotallynonexistantpathforsure"
assert c.complete(s) == s
assert c.final == s
tests = [ tests = [
uFlow(), uFlow(),
uformat_keyvals(), uformat_keyvals(),
uState() uState(),
uPathCompleter()
] ]