From 796bc359783a75d22cf328e0ed05319b84185029 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sun, 16 Mar 2008 18:09:00 +0000 Subject: [PATCH] Added Tab completion to file arguments. --- rox/shell/directory.py | 1 - rox/shell/shell.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/rox/shell/directory.py b/rox/shell/directory.py index 151c353..59c9d7c 100644 --- a/rox/shell/directory.py +++ b/rox/shell/directory.py @@ -107,6 +107,5 @@ def get_dir_model(file): return dirs[uri] except KeyError: dm = DirModel(file) - print "new", uri dirs[uri] = dm return dm diff --git a/rox/shell/shell.py b/rox/shell/shell.py index ed22bd6..3124ee2 100644 --- a/rox/shell/shell.py +++ b/rox/shell/shell.py @@ -44,6 +44,9 @@ class BaseArgument: def validate(self): pass + def tab(self, entry): + pass + class Argument(BaseArgument): """Represents a word entered by the user for a command.""" # This is a bit complicated. An argument can be any of these: @@ -70,6 +73,15 @@ class Argument(BaseArgument): if x in '*?[': return 'glob' return 'filename' + + def iter_matches(self, match, case_insensitive): + """Return all rows with a name matching match""" + for i, row in self.view.iter_contents(): + name = row[0] + if case_insensitive: + name = name.lower() + if name.startswith(match): + yield i, row def entry_changed(self, entry): self.value = entry.get_text() @@ -112,7 +124,7 @@ class Argument(BaseArgument): exact_case_match = None exact_match = None prefix_match = None - for i, row in self.view.iter_contents(): + for i, row in self.iter_matches(leaf, case_insensitive): name = row[0] if name == leaf: exact_case_match = model.get_path(i) @@ -121,7 +133,7 @@ class Argument(BaseArgument): name = name.lower() if name == leaf: exact_match = model.get_path(i) - if name.startswith(leaf) and not prefix_match: + if not prefix_match: prefix_match = model.get_path(i) if case_insensitive and cursor_filename: cursor_filename = cursor_filename.lower() @@ -129,7 +141,7 @@ class Argument(BaseArgument): to_select = [exact_case_match] elif exact_match: to_select = [exact_match] - elif cursor_filename.startswith(leaf): + elif cursor_filename and cursor_filename.startswith(leaf): to_select = [cursor_path] elif prefix_match: to_select = [prefix_match] @@ -150,6 +162,36 @@ class Argument(BaseArgument): iv.select_path(path) if cursor_path not in to_select: iv.set_cursor(to_select[0]) + + def tab(self, entry): + if self.type == 'filename': + value = self.value + elif self.type == 'newfile': + value = self.value[1:] + else: + return + + path, leaf = os.path.split(self.value) + case_insensitive = (leaf == leaf.lower()) + prefix_match = None + for i, row in self.iter_matches(leaf, case_insensitive): + name = row[directory.DirModel.NAME] + if prefix_match is not None: + if not name.startswith(prefix_match): + # Have to shorten the match then + same = [] + for a, b in zip(prefix_match, name): + if a == b: + same.append(a) + else: + break + prefix_match = ''.join(same) + else: + prefix_match = name + if prefix_match and prefix_match != leaf: + new = os.path.join(path, prefix_match) + entry.set_text(new) + entry.set_position(len(new)) def finish_edit(self): iv = self.view.iv @@ -303,6 +345,11 @@ class ArgvView: self.widgets[i + 1].grab_focus() return True + def tab(self): + if self.active_entry.get_position() == len(self.active_entry.get_text()): + self.edit_arg.tab(self.active_entry) + return True + def key_press_event(self, kev): if not self.active_entry: return False @@ -415,6 +462,10 @@ class ShellView: if self.command_argv.space(): return True + if kev.keyval == keysyms.Tab: + if self.command_argv.tab(): + return True + if kev.keyval == keysyms.Escape: self.reset() return True -- 2.11.4.GIT