diff options
-rwxr-xr-x | ranger.py | 2 | ||||
-rw-r--r-- | ranger/commands.py | 99 | ||||
-rw-r--r-- | ranger/defaults/keys.py | 5 | ||||
-rw-r--r-- | ranger/fm.py | 15 | ||||
-rw-r--r-- | ranger/fsobject/fsobject.py | 2 | ||||
-rw-r--r-- | ranger/gui/widgets/console.py | 53 | ||||
-rw-r--r-- | ranger/gui/widgets/filelistcontainer.py | 2 | ||||
-rw-r--r-- | uml/2.session | 6 |
8 files changed, 146 insertions, 38 deletions
diff --git a/ranger.py b/ranger.py index 807e28a6..2e380581 100755 --- a/ranger.py +++ b/ranger.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python -OO # coding=utf-8 # ranger: Browse your files inside the terminal. diff --git a/ranger/commands.py b/ranger/commands.py index e61e9fd8..a4ef2b22 100644 --- a/ranger/commands.py +++ b/ranger/commands.py @@ -1,10 +1,10 @@ import os - from ranger.shared import FileManagerAware # -------------------------------- helper classes class parse(object): + """Parse commands and extract information""" def __init__(self, line): self.line = line self.chunks = line.split() @@ -14,13 +14,24 @@ class parse(object): except ValueError: self.firstpart = '' + def chunk(self, n, otherwise=''): + if len(self.chunks) >= n: + return self.chunks[n] + else: + return otherwise + + def __add__(self, newpart): return self.firstpart + newpart class Command(FileManagerAware): + """Abstract command class""" name = None - def __init__(self, line): + mode = ':' + line = '' + def __init__(self, line, mode): self.line = line + self.mode = mode def execute(self): pass @@ -28,12 +39,16 @@ class Command(FileManagerAware): def tab(self): pass - def _no_change(self): - return (self.line for i in range(100)) # -------------------------------- definitions class cd(Command): + """The cd command changes the directory. The command 'cd -' is +equivalent to typing ``. In the quick console, the directory +will be entered without the need to press enter, as soon as there +is one unambiguous match. +""" + def execute(self): line = parse(self.line) try: @@ -84,6 +99,65 @@ class cd(Command): return line + join(rel_dirname, dirnames[0]) + '/' return (line + join(rel_dirname, dirname) for dirname in dirnames) + + def quick_open(self): + from os.path import isdir, join, normpath + line = parse(self.line) + pwd = self.fm.env.pwd.path + + try: + rel_dest = line.chunks[1] + except IndexError: + return False + + abs_dest = normpath(join(pwd, rel_dest)) + return rel_dest != '.' and isdir(abs_dest) + +class find(Command): + """The find command will attempt to find a partial, case insensitive +match in the filenames of the current directory. In the quick command +console, once there is one unambiguous match, the file will be run +automatically. +""" + count = 0 + def execute(self): + if self.mode != '>': + self._search() + + import re + search = parse(self.line).chunk(1) + search = re.escape(search) + self.fm.env.last_search = re.compile(search, re.IGNORECASE) + + def quick_open(self): + self._search() + if self.count == 1: + self.fm.move_right() + self.fm.block_input(0.5) + return True + + def _search(self): + self.count = 0 + line = parse(self.line) + pwd = self.fm.env.pwd + try: + arg = line.chunks[1] + except IndexError: + return False + + length = len(pwd.files) + for i in range(length): + actual_index = (pwd.pointed_index + i) % length + filename = pwd.files[actual_index].basename_lower + if arg in filename: + self.count += 1 + if self.count == 1: + pwd.move_pointer(absolute=actual_index) + self.fm.env.cf = pwd.pointed_file + if self.count > 1: + return False + + return self.count == 1 # -------------------------------- rest @@ -94,20 +168,3 @@ for varname, var in vars().copy().items(): by_name[var.name or varname] = var except TypeError: pass - -def execute(name, line): - try: - command = by_name[name](line) - except KeyError: - pass - else: - command.execute() - -def tab(name, line): - try: - command = by_name[name](line) - except KeyError: - pass - else: - return command.tab() - diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 20af8063..acfe1178 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -1,5 +1,6 @@ import curses from curses.ascii import * +from ranger import RANGERDIR from ranger.gui.widgets.console import Console from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS @@ -40,6 +41,7 @@ def initialize_commands(command_list): bind('td', do('toggle_boolean_option', 'directories_first')) bind('cd', do('open_console', ':', 'cd ')) + bind('f', do('open_console', '>', 'find ')) # key combinations which change the current directory def cd(path): @@ -53,6 +55,7 @@ def initialize_commands(command_list): bind('gn', do('cd', '/mnt')) bind('gt', do('cd', '~/.trash')) bind('gs', do('cd', '/srv')) + bind('gR', do('cd', RANGERDIR)) bind('n', do('search_forward')) bind('N', do('search_backward')) @@ -71,6 +74,7 @@ def initialize_commands(command_list): bind(curses.KEY_RESIZE, do('resize')) bind(curses.KEY_MOUSE, do('handle_mouse')) bind(':', do('open_console', ':')) + bind('>', do('open_console', '>')) bind('/', do('open_console', '/')) bind('?', do('open_console', '?')) bind('!', do('open_console', '!')) @@ -103,6 +107,7 @@ def initialize_console_commands(command_list): bind(ctrl('w'), do('delete_word')) bind(ctrl('k'), do('delete_rest', 1)) bind(ctrl('u'), do('delete_rest', -1)) + bind(ctrl('y'), do('paste')) # system functions bind(ctrl('c'), ESC, do('close')) diff --git a/ranger/fm.py b/ranger/fm.py index 271e4138..b59a3e8d 100644 --- a/ranger/fm.py +++ b/ranger/fm.py @@ -1,3 +1,5 @@ +from time import time + from ranger.actions import Actions from ranger.container import Bookmarks from ranger.ext.relpath import relpath_conf @@ -7,6 +9,8 @@ CTRL_C = 3 TICKS_BEFORE_COLLECTING_GARBAGE = 100 class FM(Actions): + input_blocked = False + input_blocked_until = 0 def __init__(self, ui = None, bookmarks = None): """Initialize FM.""" Actions.__init__(self) @@ -36,6 +40,10 @@ class FM(Actions): self.ui = DefaultUI() self.ui.initialize() + def block_input(self, sec=0): + self.input_blocked = sec != 0 + self.input_blocked_until = time() + sec + def loop(self): """The main loop consists of: 1. reloading bookmarks if outdated @@ -57,7 +65,12 @@ class FM(Actions): self.ui.finalize() key = self.ui.get_next_key() - self.ui.handle_key(key) + + if self.input_blocked and \ + time() > self.input_blocked_until: + self.input_blocked = False + if not self.input_blocked: + self.ui.handle_key(key) gc_tick += 1 if gc_tick > TICKS_BEFORE_COLLECTING_GARBAGE: diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index 30ed7839..2f65fbc5 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -7,6 +7,7 @@ from ranger.shared import MimeTypeAware, FileManagerAware class FileSystemObject(MimeTypeAware, FileManagerAware): path = None basename = None + basename_lower = None dirname = None extension = None exists = False @@ -42,6 +43,7 @@ class FileSystemObject(MimeTypeAware, FileManagerAware): self.path = path self.basename = basename(path) + self.basename_lower = self.basename.lower() self.dirname = dirname(path) try: diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 7421db98..ca21189c 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -11,6 +11,7 @@ class Console(Widget): commandlist = None last_cursor_mode = 1 prompt = ':' + copy = '' tab_deque = None original_line = None @@ -90,6 +91,7 @@ class Console(Widget): self.line = self.line[:self.pos] + key + self.line[self.pos:] self.pos += len(key) + self.on_line_change() def move(self, relative = 0, absolute = None): if absolute is not None: @@ -103,10 +105,21 @@ class Console(Widget): def delete_rest(self, direction): self.tab_deque = None if direction > 0: + self.copy = self.line[self.pos:] self.line = self.line[:self.pos] else: + self.copy = self.line[:self.pos] self.line = self.line[self.pos:] self.pos = 0 + self.on_line_change() + + def paste(self): + if self.pos == len(self.line): + self.line += self.copy + else: + self.line = self.line[:self.pos] + self.copy + self.line[self.pos:] + self.pos += len(self.copy) + self.on_line_change() def delete_word(self): self.tab_deque = None @@ -117,6 +130,7 @@ class Console(Widget): except ValueError: self.line = '' self.pos = 0 + self.on_line_change() def delete(self, mod): self.tab_deque = None @@ -126,21 +140,26 @@ class Console(Widget): self.line = self.line[0:pos] + self.line[pos+1:] self.move(relative = mod) + self.on_line_change() def execute(self): self.tab_deque = None - self.line = '' - self.pos = 0 + self.clear() self.close() def tab(self): pass + def on_line_change(self): + pass + class CommandConsole(Console): prompt = ':' def execute(self): - commands.execute(self._get_cmd(), self.line) + cmd = self._get_cmd() + if cmd: cmd.execute() + Console.execute(self) def tab(self, n=1): @@ -150,6 +169,7 @@ class CommandConsole(Console): if isinstance(tab_result, str): self.line = tab_result self.pos = len(tab_result) + self.on_line_change() elif tab_result == None: pass @@ -162,24 +182,35 @@ class CommandConsole(Console): self.tab_deque.rotate(-n) self.line = self.tab_deque[0] self.pos = len(self.line) + self.on_line_change() def _get_cmd(self): try: - return self.line.split()[0] + command_name = self.line.split()[0] except: - return '' + return None + + try: + command_class = commands.by_name[command_name] + except KeyError: + return None + + return command_class(self.line, self.mode) def _get_tab(self): cmd = self._get_cmd() - try: - return commands.tab(cmd, self.line) - except KeyError: - return commands.tab(None, self.line) + if cmd: + return cmd.tab() + else: + return None -class QuickCommandConsole(Console): +class QuickCommandConsole(CommandConsole): prompt = '>' - + def on_line_change(self): + cmd = self._get_cmd() + if cmd and cmd.quick_open(): + self.execute() class SearchConsole(Console): prompt = '/' diff --git a/ranger/gui/widgets/filelistcontainer.py b/ranger/gui/widgets/filelistcontainer.py index cc45f2c9..6e925f83 100644 --- a/ranger/gui/widgets/filelistcontainer.py +++ b/ranger/gui/widgets/filelistcontainer.py @@ -20,7 +20,7 @@ class FileListContainer(Widget, DisplayableContainer): ratio_sum = float(reduce(lambda x,y: x + y, ratios)) self.ratios = tuple(map(lambda x: x / ratio_sum, ratios)) - if self.ratios >= 2: + if len(self.ratios) >= 2: self.stretch_ratios = self.ratios[:-2] + \ ((self.ratios[-2] + self.ratios[-1] * 0.9), \ (self.ratios[-1] * 0.1)) diff --git a/uml/2.session b/uml/2.session index 5af415f6..7110e9db 100644 --- a/uml/2.session +++ b/uml/2.session @@ -1,15 +1,15 @@ window_sizes 1678 1033 393 1275 797 144 motifplus_style diagrams - classdiagram_ref 128002 // Displayable Hierarchy + active classdiagram_ref 128002 // Displayable Hierarchy 1275 795 100 4 0 0 classdiagram_ref 134530 // Overview 1275 795 100 4 0 0 - active sequencediagram_ref 141058 // Basic Logic + sequencediagram_ref 141058 // Basic Logic 1275 795 100 4 0 0 end show_stereotypes -selected sequencediagram_ref 141058 // Basic Logic +selected classdiagram_ref 128002 // Displayable Hierarchy open class_ref 128002 // Displayable class_ref 128130 // UI |