diff options
-rw-r--r-- | CHANGELOG | 13 | ||||
-rw-r--r-- | ranger/api/commands.py | 41 | ||||
-rw-r--r-- | ranger/defaults/commands.py | 89 | ||||
-rw-r--r-- | ranger/ext/command_parser.py | 105 |
4 files changed, 73 insertions, 175 deletions
diff --git a/CHANGELOG b/CHANGELOG index 5152c73e..fc2a3dfd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,18 @@ This log documents changes between stable versions. +2011-10-10: Version 1.5.0 +* Change in commands.py syntax: + * Using parse(self.line) to parse the line is unnecessary now. + In most cases you can write self.foo() instead of parse(self.line).foo(). + For example, parse(self.line).rest(n) is now self.rest(n). + However, parse(self.line).chunk(n) has been renamed to self.arg(n). + * parse(self.line) + X is now self.firstpart + X + * New special attribute "resolve_macros" which decides whether strings + like %f should be expanded to the name of the current file, etc. + * New special attribute "escape_macros_for_shell" to toggle whether or + not macros should be escaped, so you can use them in other commands + than :shell, for example :edit %f + 2011-10-02: Version 1.4.4 * Added keys for chmod (like +ow for "chmod o+w", etc) * Added "c" flag for running files diff --git a/ranger/api/commands.py b/ranger/api/commands.py index 1941de2c..5b1555a0 100644 --- a/ranger/api/commands.py +++ b/ranger/api/commands.py @@ -15,12 +15,13 @@ import os import ranger +import re from collections import deque from ranger.api import * from ranger.core.shared import FileManagerAware from ranger.ext.lazy_property import lazy_property -from ranger.ext.command_parser import LazyParser as parse +SETTINGS_RE = re.compile(r'^([^\s]+?)=(.*)$') DELETE_WARNING = 'delete seriously? ' def alias(*_): pass # COMPAT @@ -89,11 +90,16 @@ class Command(FileManagerAware): escape_macros_for_shell = False quantifier = None _shifted = 0 + _setting_line = None def __init__(self, line, quantifier=None): self.line = line self.args = line.split() self.quantifier = quantifier + try: + self.firstpart = line[:line.rindex(' ') + 1] + except ValueError: + self.firstpart = '' @classmethod def get_name(self): @@ -149,6 +155,18 @@ class Command(FileManagerAware): def tabinsert(self, word): return ''.join([self._tabinsert_left, word, self._tabinsert_right]) + def parse_setting_line(self): + if self._setting_line is not None: + return self._setting_line + match = SETTINGS_RE.match(self.rest(1)) + if match: + self.firstpart += match.group(1) + '=' + result = [match.group(1), match.group(2), True] + else: + result = [self.arg(1), self.rest(2), ' ' in self.rest(1)] + self._setting_line = result + return result + # XXX: Lazy properties? Not so smart? self.line can change after all! @lazy_property def _tabinsert_left(self): @@ -165,13 +183,9 @@ class Command(FileManagerAware): def _tab_only_directories(self): from os.path import dirname, basename, expanduser, join - line = parse(self.line) cwd = self.fm.env.cwd.path - try: - rel_dest = line.rest(1) - except IndexError: - rel_dest = '' + rel_dest = self.rest(1) # expand the tilde into the user directory if rel_dest.startswith('~'): @@ -205,22 +219,19 @@ class Command(FileManagerAware): # one result. since it must be a directory, append a slash. if len(dirnames) == 1: - return line.start(1) + join(rel_dirname, dirnames[0]) + '/' + return self.start(1) + join(rel_dirname, dirnames[0]) + '/' # more than one result. append no slash, so the user can # manually type in the slash to advance into that directory - return (line.start(1) + join(rel_dirname, dirname) for dirname in dirnames) + return (self.start(1) + join(rel_dirname, dirname) + for dirname in dirnames) def _tab_directory_content(self): from os.path import dirname, basename, expanduser, join - line = parse(self.line) cwd = self.fm.env.cwd.path - try: - rel_dest = line.rest(1) - except IndexError: - rel_dest = '' + rel_dest = self.rest(1) # expand the tilde into the user directory if rel_dest.startswith('~'): @@ -255,11 +266,11 @@ class Command(FileManagerAware): # one result. since it must be a directory, append a slash. if len(names) == 1: - return line.start(1) + join(rel_dirname, names[0]) + '/' + return self.start(1) + join(rel_dirname, names[0]) + '/' # more than one result. append no slash, so the user can # manually type in the slash to advance into that directory - return (line.start(1) + join(rel_dirname, name) for name in names) + return (self.start(1) + join(rel_dirname, name) for name in names) class FunctionCommand(Command): diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index cd76896c..bd2bc025 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -99,13 +99,8 @@ class cd(Command): def tab(self): from os.path import dirname, basename, expanduser, join - line = parse(self.line) cwd = self.fm.env.cwd.path - - try: - rel_dest = line.rest(1) - except IndexError: - rel_dest = '' + rel_dest = self.rest(1) bookmarks = [v.path for v in self.fm.bookmarks.dct.values() if rel_dest in v.path ] @@ -143,11 +138,11 @@ class cd(Command): # one result. since it must be a directory, append a slash. if len(dirnames) == 1: - return line.start(1) + join(rel_dirname, dirnames[0]) + '/' + return self.start(1) + join(rel_dirname, dirnames[0]) + '/' # more than one result. append no slash, so the user can # manually type in the slash to advance into that directory - return (line.start(1) + join(rel_dirname, dirname) for dirname in dirnames) + return (self.start(1) + join(rel_dirname, dirname) for dirname in dirnames) class chain(Command): @@ -162,25 +157,24 @@ class chain(Command): class search(Command): def execute(self): - self.fm.search_file(parse(self.line).rest(1), regexp=True) + self.fm.search_file(self.rest(1), regexp=True) class search_inc(Command): def quick(self): - self.fm.search_file(parse(self.line).rest(1), regexp=True, offset=0) + self.fm.search_file(self.rest(1), regexp=True, offset=0) class shell(Command): escape_macros_for_shell = True def execute(self): - line = parse(self.line) - if line.chunk(1) and line.chunk(1)[0] == '-': - flags = line.chunk(1)[1:] - command = line.rest(2) + if self.arg(1) and self.arg(1)[0] == '-': + flags = self.arg(1)[1:] + command = self.rest(2) else: flags = '' - command = line.rest(1) + command = self.rest(1) if not command and 'p' in flags: command = 'cat %f' if command: @@ -189,11 +183,10 @@ class shell(Command): self.fm.execute_command(command, flags=flags) def tab(self): - line = parse(self.line) - if line.chunk(1) and line.chunk(1)[0] == '-': - command = line.rest(2) + if self.arg(1) and line.arg(1)[0] == '-': + command = self.rest(2) else: - command = line.rest(1) + command = self.rest(1) start = self.line[0:len(self.line) - len(command)] try: @@ -211,8 +204,7 @@ class shell(Command): class open_with(Command): def execute(self): - line = parse(self.line) - app, flags, mode = self._get_app_flags_mode(line.rest(1)) + app, flags, mode = self._get_app_flags_mode(self.rest(1)) self.fm.execute_file( files = [f for f in self.fm.env.cwd.get_selection()], app = app, @@ -293,8 +285,7 @@ class open_with(Command): return app, flags, int(mode) def _get_tab(self): - line = parse(self.line) - data = line.rest(1) + data = self.rest(1) if ' ' not in data: all_apps = self.fm.apps.all() if all_apps: @@ -334,11 +325,9 @@ class find(Command): def quick(self): self.count = 0 - line = parse(self.line) cwd = self.fm.env.cwd - try: - arg = line.rest(1) - except IndexError: + arg = self.rest(1) + if not arg: return False if arg == '.': @@ -375,9 +364,8 @@ class set_(Command): """ name = 'set' # don't override the builtin set class def execute(self): - line = parse(self.line) - name = line.chunk(1) - name, value, _ = line.parse_setting_line() + name = self.arg(1) + name, value, _ = self.parse_setting_line() if name and value: from re import compile as regexp try: @@ -387,21 +375,20 @@ class set_(Command): self.fm.settings[name] = value def tab(self): - line = parse(self.line) - name, value, name_done = line.parse_setting_line() + name, value, name_done = self.parse_setting_line() settings = self.fm.settings if not name: - return (line + setting for setting in settings) + return (self.firstpart + setting for setting in settings) if not value and not name_done: - return (line + setting for setting in settings \ + return (self.firstpart + setting for setting in settings \ if setting.startswith(name)) if not value: - return line + repr(settings[name]) + return self.firstpart + repr(settings[name]) if bool in settings.types_of(name): if 'true'.startswith(value.lower()): - return line + 'True' + return self.firstpart + 'True' if 'false'.startswith(value.lower()): - return line + 'False' + return self.firstpart + 'False' class quit(Command): @@ -468,8 +455,7 @@ class delete(Command): allow_abbrev = False def execute(self): - line = parse(self.line) - lastword = line.chunk(-1) + lastword = self.arg(-1) if lastword.startswith('y'): # user confirmed deletion! @@ -502,8 +488,7 @@ class mark(Command): def execute(self): import re cwd = self.fm.env.cwd - line = parse(self.line) - input = line.rest(1) + input = self.rest(1) searchflags = re.UNICODE if input.lower() == input: # "smartcase" searchflags |= re.IGNORECASE @@ -593,8 +578,7 @@ class mkdir(Command): from os.path import join, expanduser, lexists from os import mkdir - line = parse(self.line) - dirname = join(self.fm.env.cwd.path, expanduser(line.rest(1))) + dirname = join(self.fm.env.cwd.path, expanduser(self.rest(1))) if not lexists(dirname): mkdir(dirname) else: @@ -611,8 +595,7 @@ class touch(Command): def execute(self): from os.path import join, expanduser, lexists - line = parse(self.line) - fname = join(self.fm.env.cwd.path, expanduser(line.rest(1))) + fname = join(self.fm.env.cwd.path, expanduser(self.rest(1))) if not lexists(fname): open(fname, 'a').close() else: @@ -627,11 +610,10 @@ class edit(Command): """ def execute(self): - line = parse(self.line) - if not line.chunk(1): + if not self.arg(1): self.fm.edit_file(self.fm.env.cf.path) else: - self.fm.edit_file(line.rest(1)) + self.fm.edit_file(self.rest(1)) def tab(self): return self._tab_directory_content() @@ -689,8 +671,7 @@ class rename(Command): from ranger.fsobject import File from os import access - line = parse(self.line) - new_name = line.rest(1) + new_name = self.rest(1) if not new_name: return self.fm.notify('Syntax: rename <newname>', bad=True) @@ -948,8 +929,7 @@ class filter(Command): """ def execute(self): - line = parse(self.line) - self.fm.set_filter(line.rest(1)) + self.fm.set_filter(self.rest(1)) self.fm.reload_cwd() @@ -961,9 +941,8 @@ class grep(Command): """ def execute(self): - line = parse(self.line) - if line.rest(1): + if self.rest(1): action = ['grep', '--color=always', '--line-number'] - action.extend(['-e', line.rest(1), '-r']) + action.extend(['-e', self.rest(1), '-r']) action.extend(f.path for f in self.fm.env.get_selection()) self.fm.execute_command(action, flags='p') diff --git a/ranger/ext/command_parser.py b/ranger/ext/command_parser.py deleted file mode 100644 index 8bb72c76..00000000 --- a/ranger/ext/command_parser.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann <romanz@lavabit.com> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import re -SETTINGS_RE = re.compile(r'^([^\s]+?)=(.*)$') - -# TODO: complete the merge of this into api/commands -class LazyParser(object): - """Parse commands and extract information""" - def __init__(self, line): - self.line = line - self._chunks = None - self._rests = None - self._setting_line = None - self._rests_loaded = 0 - self._rests_gen_instance = None - self._starts = None - - try: - self.firstpart = line[:line.rindex(' ') + 1] - except ValueError: - self.firstpart = '' - - def chunk(self, n, otherwise=''): - """Chunks are pieces of the command seperated by spaces""" - if self._chunks is None: - self._chunks = self.line.split() - - if len(self._chunks) > n: - return self._chunks[n] - else: - return otherwise - - def rest(self, n, otherwise=''): - """Rests are the strings which come after each word.""" - if self._rests is None: - self._rests = list(self._rest_generator()) - # TODO: Don't calculate all the rest elements if not needed - - if len(self._rests) > n: - return self._rests[n] - else: - return otherwise - - def start(self, n): - if self._starts is None: - self._starts = [''] - line = self.line - result = "" - while True: - try: - index = line.index(' ') + 1 - except: - break - if index == 1: - line = line[1:] - continue - result = line[:index] - self._starts.append(result) - line = line[index:] - try: - return self._starts[n] - except: - return self._starts[-1] - - def _rest_generator(self): - lastrest = self.line - n = 0 - while n < len(lastrest): - if lastrest[n] == ' ': - n += 1 - else: - yield lastrest[n:] - n = lastrest.find(' ', n) + 1 - if n <= 0: - break - lastrest = lastrest[n:] - n = 0 - - def parse_setting_line(self): - if self._setting_line is not None: - return self._setting_line - match = SETTINGS_RE.match(self.rest(1)) - if match: - self.firstpart += match.group(1) + '=' - result = [match.group(1), match.group(2), True] - else: - result = [self.chunk(1), self.rest(2), ' ' in self.rest(1)] - self._setting_line = result - return result - - def __add__(self, newpart): - return self.firstpart + newpart |