From 7ec262f86c81c4df4360643b05dfb788d294a8ae Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 15 Dec 2009 18:10:28 +0100 Subject: Added console commands + tab completion --- ranger/actions.py | 4 +- ranger/commands.py | 110 ++++++++++++++++++++++++++++++++++++++++++ ranger/defaults/keys.py | 8 ++- ranger/gui/defaultui.py | 4 +- ranger/gui/widgets/console.py | 64 ++++++++++++++++++++++-- 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 ranger/commands.py diff --git a/ranger/actions.py b/ranger/actions.py index 3a06aff0..63d907e6 100644 --- a/ranger/actions.py +++ b/ranger/actions.py @@ -98,10 +98,10 @@ Both flags and mode specify how the program is run.""" return self.execute_file(self.env.cf, app = 'editor') - def open_console(self, mode = ':'): + def open_console(self, mode=':', string=''): """Open the console if the current UI supports that""" if hasattr(self.ui, 'open_console'): - self.ui.open_console(mode) + self.ui.open_console(mode, string) def move_pointer(self, relative = 0, absolute = None): """Move the pointer down by or to """ diff --git a/ranger/commands.py b/ranger/commands.py new file mode 100644 index 00000000..d016a822 --- /dev/null +++ b/ranger/commands.py @@ -0,0 +1,110 @@ +import os + +from ranger.shared import FileManagerAware + +# -------------------------------- helper classes + +class parse(object): + def __init__(self, line): + self.line = line + self.chunks = line.split() + + try: + self.firstpart = line[:line.rindex(' ') + 1] + except ValueError: + self.firstpart = '' + + def __add__(self, newpart): + return self.firstpart + newpart + +class Command(FileManagerAware): + name = None + def __init__(self, line): + self.line = line + + def execute(self): + pass + + def tab(self): + pass + + def _no_change(self): + return (self.line for i in range(100)) + +# -------------------------------- definitions + +class cd(Command): + def execute(self): + line = parse(self.line) + try: + destination = line.chunks[1] + except IndexError: + destination = '~' + + if destination == '-': + self.fm.enter_bookmark('`') + else: + self.fm.enter_dir(destination) + + def tab(self): + line = parse(self.line) + try: + dest = line.chunks[1] + except IndexError: + dest = '' + + if dest.startswith('~'): + return line + os.path.expanduser(dest) + '/' + + absolute = lambda path: os.path.join(self.fm.env.pwd.path, path) + absdest = absolute(dest) + +# if dest == '': +# return sorted(os.listdir(dest)) + + if dest.endswith('/') or dest == '': + if os.path.isdir(dest): + walker = os.walk(absdest) + _, dirnames, _ = walker.next() + dirnames.sort() + return (line.line + dirname for dirname in dirnames) + + try: + original_dirname = os.path.dirname(absdest) + basename = os.path.basename(absdest) + + walker = os.walk(original_dirname) + _, dirnames, _ = walker.next() + dirnames = [dn for dn in dirnames if dn.startswith(basename)] + + dirnames.sort() + + start = line + os.path.dirname(dest) + '/' + if len(dirnames) == 0: + return + elif len(dirnames) == 1: + if os.path.isdir(os.path.join(absdest, dirnames[0])): + return start + dirnames[0] + '/' + else: + return start + dirnames[0] + else: + return (start + dirname for dirname in dirnames) + except OSError: + pass + + +# -------------------------------- rest + +by_name = {} +for varname, var in vars().copy().items(): + try: + if issubclass(var, Command) and var != Command: + by_name[var.name or varname] = var + except TypeError: + pass + +def execute(name, line): + return by_name[name](line).execute() + +def tab(name, line): + return by_name[name](line).tab() diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index f2e941c1..20af8063 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -1,5 +1,5 @@ import curses -from curses.ascii import ctrl, ESC, DEL +from curses.ascii import * from ranger.gui.widgets.console import Console from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS @@ -39,6 +39,8 @@ def initialize_commands(command_list): bind('tp', do('toggle_boolean_option', 'preview_files')) bind('td', do('toggle_boolean_option', 'directories_first')) + bind('cd', do('open_console', ':', 'cd ')) + # key combinations which change the current directory def cd(path): return lambda fm: fm.enter_dir(path) @@ -106,13 +108,15 @@ def initialize_console_commands(command_list): bind(ctrl('c'), ESC, do('close')) bind(ctrl('j'), curses.KEY_ENTER, do('execute')) bind(ctrl('l'), do_fm('redraw')) + bind(TAB, do('tab')) + bind(curses.KEY_BTAB, do('tab', -1)) bind(curses.KEY_RESIZE, do_fm('resize')) # type keys def type_key(key): return lambda con: con.type_key(key) - for i in range(ord(' '), ord('~')): + for i in range(ord(' '), ord('~')+1): bind(i, type_key(i)) command_list.rebuild_paths() diff --git a/ranger/gui/defaultui.py b/ranger/gui/defaultui.py index 93bfe248..24cc6b38 100644 --- a/ranger/gui/defaultui.py +++ b/ranger/gui/defaultui.py @@ -32,8 +32,8 @@ class DefaultUI(UI): self.status.resize(y - 1, 0, 1, x) self.console.resize(y - 1, 0, 1, x) - def open_console(self, mode): - if self.console.open(mode): + def open_console(self, mode, string=''): + if self.console.open(mode, string): self.console.on_close = self.close_console self.console.visible = True self.status.visible = False diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index e9356613..25341e00 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -1,8 +1,9 @@ """The Console widget implements a vim-like console for entering commands, searching and executing files.""" from . import Widget +from ranger import commands import curses -from ranger import log +from collections import deque class Console(Widget): mode = None @@ -10,6 +11,8 @@ class Console(Widget): commandlist = None last_cursor_mode = 1 prompt = ':' + tab_deque = None + original_line = None def __init__(self, win): from ranger.container import CommandList @@ -32,7 +35,7 @@ class Console(Widget): except: pass - def open(self, mode): + def open(self, mode, string=''): if mode not in self.mode_classes: return False @@ -40,8 +43,11 @@ class Console(Widget): self.mode = mode self.__class__ = self.mode_classes[mode] self.init() + self.tab_deque = None self.focused = True self.visible = True + self.line = string + self.pos = len(string) return True def close(self): @@ -73,6 +79,8 @@ class Console(Widget): self.env.key_clear() def type_key(self, key): + self.tab_deque = None + if isinstance(key, int): key = chr(key) @@ -93,6 +101,7 @@ class Console(Widget): self.pos = min(max(0, self.pos + relative), len(self.line)) def delete_rest(self, direction): + self.tab_deque = None if direction > 0: self.line = self.line[:self.pos] else: @@ -100,6 +109,7 @@ class Console(Widget): self.pos = 0 def delete_word(self): + self.tab_deque = None try: i = self.line.rindex(' ', 0, self.pos - 1) + 1 self.line = self.line[:i] + self.line[self.pos:] @@ -109,6 +119,7 @@ class Console(Widget): self.pos = 0 def delete(self, mod): + self.tab_deque = None if mod == -1 and len(self.line) == 0: self.close() pos = self.pos + mod @@ -117,15 +128,61 @@ class Console(Widget): self.move(relative = mod) def execute(self): - log("aww") + self.tab_deque = None self.line = '' self.pos = 0 self.close() + def tab(self): + pass class CommandConsole(Console): prompt = ':' + def execute(self): + self._exec_cmd() + Console.execute(self) + + def tab(self, n=1): + if self.tab_deque is None: + tab_result = self._get_tab() + + if isinstance(tab_result, str): + self.line = tab_result + self.pos = len(tab_result) + + elif tab_result == None: + pass + + elif hasattr(tab_result, '__iter__'): + self.tab_deque = deque(tab_result) + self.tab_deque.appendleft(self.line) + + if self.tab_deque is not None: + self.tab_deque.rotate(-n) + self.line = self.tab_deque[0] + self.pos = len(self.line) + + def _get_tab(self): + cmd = self._get_cmd() + try: + return commands.tab(cmd, self.line) + except KeyError: + return None + + def _get_cmd(self): + try: + return self.line.split()[0] + except: + return '' + + def _exec_cmd(self): + cmd = self._get_cmd() + try: + commands.execute(cmd, self.line) + except KeyError: + pass # command not found! + class QuickCommandConsole(Console): prompt = '>' @@ -134,7 +191,6 @@ class QuickCommandConsole(Console): class SearchConsole(Console): prompt = '/' def execute(self): - log("yay") import re if self.fm.env.pwd: regexp = re.compile(self.line, re.L | re.U | re.I) -- cgit 1.4.1-2-gfad0