diff options
-rw-r--r-- | ranger/api/commands.py | 4 | ||||
-rw-r--r-- | ranger/api/keys.py | 1 | ||||
-rw-r--r-- | ranger/core/actions.py | 86 | ||||
-rw-r--r-- | ranger/defaults/commands.py | 155 | ||||
-rw-r--r-- | ranger/defaults/keys.py | 36 | ||||
-rw-r--r-- | ranger/gui/defaultui.py | 4 | ||||
-rw-r--r-- | ranger/gui/widgets/console.py | 448 | ||||
-rw-r--r-- | ranger/gui/widgets/console_mode.py | 55 |
8 files changed, 295 insertions, 494 deletions
diff --git a/ranger/api/commands.py b/ranger/api/commands.py index ca3f730d..f4e2ca76 100644 --- a/ranger/api/commands.py +++ b/ranger/api/commands.py @@ -17,7 +17,6 @@ import os from collections import deque from ranger.api import * from ranger.shared import FileManagerAware -from ranger.gui.widgets import console_mode as cmode from ranger.ext.command_parser import LazyParser as parse @@ -71,9 +70,8 @@ class Command(FileManagerAware): """Abstract command class""" name = None allow_abbrev = True - def __init__(self, line, mode): + def __init__(self, line): self.line = line - self.mode = mode def execute(self): """Override this""" diff --git a/ranger/api/keys.py b/ranger/api/keys.py index 13a4b07f..5812de39 100644 --- a/ranger/api/keys.py +++ b/ranger/api/keys.py @@ -20,7 +20,6 @@ from inspect import getargspec, ismethod from ranger import RANGERDIR from ranger.api import * -from ranger.gui.widgets import console_mode as cmode from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS from ranger.container.keymap import KeyMap, Direction, KeyMapWithDirections diff --git a/ranger/core/actions.py b/ranger/core/actions.py index a298b304..c93fa0a5 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -16,19 +16,25 @@ import os import re import shutil +import string from os.path import join, isdir from os import symlink, getcwd from inspect import cleandoc import ranger from ranger.ext.direction import Direction +from ranger.ext.shell_escape import shell_quote from ranger import fsobject from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware -from ranger.gui.widgets import console_mode as cmode from ranger.fsobject import File from ranger.ext import shutil_generatorized as shutil_g from ranger.core.loader import LoadableObject +class _MacroTemplate(string.Template): + """A template for substituting macros in commands""" + delimiter = '%' + idpattern = '\d?[a-z]' + class Actions(FileManagerAware, EnvironmentAware, SettingsAware): search_method = 'ctime' search_forward = False @@ -69,17 +75,80 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): """Redraw the window""" self.ui.redraw_window() - def open_console(self, mode=cmode.COMMAND, string='', prompt=None): + def open_console(self, string='', prompt=None, position=None): """Open the console if the current UI supports that""" if hasattr(self.ui, 'open_console'): - self.ui.open_console(mode, string, prompt=prompt) + self.ui.open_console(string, prompt=prompt, position=position) - def execute_console(self, string='', mode=cmode.COMMAND): + def execute_console(self, string=''): """Execute a command for the console""" - self.open_console(mode=mode, string=string) + self.open_console(string=string) self.ui.console.line = string self.ui.console.execute() + def substitute_metachars(self, string): + return _MacroTemplate(string).safe_substitute(self._get_macros()) + + def _get_macros(self): + macros = {} + + if self.fm.env.cf: + macros['f'] = shell_quote(self.fm.env.cf.basename) + else: + macros['f'] = '' + + macros['s'] = ' '.join(shell_quote(fl.basename) \ + for fl in self.fm.env.get_selection()) + + macros['c'] = ' '.join(shell_quote(fl.path) + for fl in self.fm.env.copy) + + macros['t'] = ' '.join(shell_quote(fl.basename) + for fl in self.fm.env.cwd.files + if fl.realpath in self.fm.tags) + + if self.fm.env.cwd: + macros['d'] = shell_quote(self.fm.env.cwd.path) + else: + macros['d'] = '.' + + # define d/f/s macros for each tab + for i in range(1,10): + try: + tab_dir_path = self.fm.tabs[i] + except: + continue + tab_dir = self.fm.env.get_directory(tab_dir_path) + i = str(i) + macros[i + 'd'] = shell_quote(tab_dir_path) + macros[i + 'f'] = shell_quote(tab_dir.pointed_obj.path) + macros[i + 's'] = ' '.join(shell_quote(fl.path) + for fl in tab_dir.get_selection()) + + # define D/F/S for the next tab + found_current_tab = False + next_tab_path = None + first_tab = None + for tab in self.fm.tabs: + if not first_tab: + first_tab = tab + if found_current_tab: + next_tab_path = self.fm.tabs[tab] + break + if self.fm.current_tab == tab: + found_current_tab = True + if found_current_tab and not next_tab_path: + next_tab_path = self.fm.tabs[first_tab] + next_tab = self.fm.env.get_directory(next_tab_path) + + macros['D'] = shell_quote(next_tab) + macros['F'] = shell_quote(next_tab.pointed_obj.path) + macros['S'] = ' '.join(shell_quote(fl.path) + for fl in next_tab.get_selection()) + + return macros + + def execute_file(self, files, **kw): """Execute a file. app is the name of a method in Applications, without the "app_" @@ -134,7 +203,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): selection = self.env.get_selection() if not self.env.enter_dir(cf) and selection: if self.execute_file(selection, mode=mode) is False: - self.open_console(cmode.OPEN_QUICK) + self.open_console('open_with ') elif direction.vertical(): newpos = direction.move( direction=direction.down(), @@ -297,7 +366,10 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def search_file(self, text, regexp=True): if isinstance(text, str) and regexp: - text = re.compile(text, re.L | re.U | re.I) + try: + text = re.compile(text, re.L | re.U | re.I) + except: + return False self.env.last_search = text self.search(order='search') diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index 8728f9be..acfbfffb 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -55,6 +55,8 @@ For a list of all actions, check /ranger/core/actions.py. ''' from ranger.api.commands import * +from ranger.ext.get_executables import get_executables +from ranger.core.runner import ALLOWED_FLAGS alias('e', 'edit') alias('q', 'quit') @@ -100,6 +102,154 @@ class cd(Command): return rel_dest != '.' and isdir(abs_dest) +class search(Command): + def execute(self): + self.fm.search_file(parse(self.line).rest(1), regexp=True) + + +class shell(Command): + def execute(self): + line = parse(self.line) + if line.chunk(1)[0] == '-': + flags = line.chunk(1)[1:] + command = line.rest(2) + else: + flags = '' + command = line.rest(1) + + if not command and 'p' in flags: command = 'cat %f' + if command: + if '%' in command: + command = self.fm.substitute_metachars(command) + self.fm.execute_command(command, flags=flags) + + def tab(self): + line = parse(self.line) + if line.chunk(1)[0] == '-': + flags = line.chunk(1)[1:] + command = line.rest(2) + else: + flags = '' + command = line.rest(1) + start = self.line[0:len(self.line) - len(command)] + + try: + position_of_last_space = command.rindex(" ") + except ValueError: + return (start + program + ' ' for program \ + in get_executables() if program.startswith(command)) + if position_of_last_space == len(command) - 1: + return self.line + '%s ' + else: + before_word, start_of_word = self.line.rsplit(' ', 1) + return (before_word + ' ' + file.shell_escaped_basename \ + for file in self.fm.env.cwd.files \ + if file.shell_escaped_basename.startswith(start_of_word)) + +class open_with(Command): + def execute(self): + line = parse(self.line) + app, flags, mode = self._get_app_flags_mode(line.rest(1)) + self.fm.execute_file( + files = [self.fm.env.cf], + app = app, + flags = flags, + mode = mode) + + def _get_app_flags_mode(self, string): + """ + Extracts the application, flags and mode from a string. + + examples: + "mplayer d 1" => ("mplayer", "d", 1) + "aunpack 4" => ("aunpack", "", 4) + "p" => ("", "p", 0) + "" => None + """ + + app = '' + flags = '' + mode = 0 + split = string.split() + + if len(split) == 0: + pass + + elif len(split) == 1: + part = split[0] + if self._is_app(part): + app = part + elif self._is_flags(part): + flags = part + elif self._is_mode(part): + mode = part + + elif len(split) == 2: + part0 = split[0] + part1 = split[1] + + if self._is_app(part0): + app = part0 + if self._is_flags(part1): + flags = part1 + elif self._is_mode(part1): + mode = part1 + elif self._is_flags(part0): + flags = part0 + if self._is_mode(part1): + mode = part1 + elif self._is_mode(part0): + mode = part0 + if self._is_flags(part1): + flags = part1 + + elif len(split) >= 3: + part0 = split[0] + part1 = split[1] + part2 = split[2] + + if self._is_app(part0): + app = part0 + if self._is_flags(part1): + flags = part1 + if self._is_mode(part2): + mode = part2 + elif self._is_mode(part1): + mode = part1 + if self._is_flags(part2): + flags = part2 + elif self._is_flags(part0): + flags = part0 + if self._is_mode(part1): + mode = part1 + elif self._is_mode(part0): + mode = part0 + if self._is_flags(part1): + flags = part1 + + return app, flags, int(mode) + + def _get_tab(self): + line = parse(self.line) + data = line.rest(1) + if ' ' not in data: + all_apps = self.fm.apps.all() + if all_apps: + return (app for app in all_apps if app.startswith(data)) + + return None + + def _is_app(self, arg): + return self.fm.apps.has(arg) or \ + (not self._is_flags(arg) and arg in get_executables()) + + def _is_flags(self, arg): + return all(x in ALLOWED_FLAGS for x in arg) + + def _is_mode(self, arg): + return all(x in '0123456789' for x in arg) + + class find(Command): """ :find <string> @@ -115,9 +265,6 @@ class find(Command): tab = Command._tab_directory_content def execute(self): - if self.mode != cmode.COMMAND_QUICK: - self._search() - import re search = parse(self.line).rest(1) search = re.escape(search) @@ -281,7 +428,7 @@ class delete(Command): and len(os.listdir(cf.path)) > 0): # better ask for a confirmation, when attempting to # delete multiple files or a non-empty directory. - return self.fm.open_console(self.mode, DELETE_WARNING) + return self.fm.open_console(DELETE_WARNING) # no need for a confirmation, just delete self.fm.delete() diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 0806a494..e72f4c91 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -130,8 +130,8 @@ map('<F3>', fm.display_file()) map('<F4>', fm.edit_file()) map('<F5>', fm.copy()) map('<F6>', fm.cut()) -map('<F7>', fm.open_console(cmode.COMMAND, 'mkdir ')) -map('<F8>', fm.open_console(cmode.COMMAND, DELETE_WARNING)) +map('<F7>', fm.open_console('mkdir ')) +map('<F8>', fm.open_console(DELETE_WARNING)) map('<F10>', fm.exit()) # =================================================================== @@ -185,8 +185,7 @@ map('ud', 'uy', fm.uncut()) # ---------------------------------------------------- run programs map('S', fm.execute_command(os.environ['SHELL'])) map('E', fm.edit_file()) -map('du', fm.execute_console('p!du --max-depth=1 -h --apparent-size', - cmode.OPEN)) +map('du', fm.execute_console('shell -p du --max-depth=1 -h --apparent-size')) # -------------------------------------------------- toggle options map('z<bg>', fm.hint("[*cdfhimpPs*] show_*h*idden *p*review_files "\ @@ -199,7 +198,7 @@ map('zd', fm.toggle_boolean_option('sort_directories_first')) map('zc', fm.toggle_boolean_option('collapse_preview')) map('zs', fm.toggle_boolean_option('sort_case_insensitive')) map('zm', fm.toggle_boolean_option('mouse_enabled')) -map('zf', fm.open_console(cmode.COMMAND, 'filter ')) +map('zf', fm.open_console('filter ')) # ------------------------------------------------------------ sort map('o<bg>', 'O<bg>', fm.hint("*s*ize *b*ase*n*ame *m*time" \ @@ -225,19 +224,19 @@ map('or', 'Or', 'oR', 'OR', lambda arg: \ @map("A") def append_to_filename(arg): command = 'rename ' + arg.fm.env.cf.basename - arg.fm.open_console(cmode.COMMAND, command) + arg.fm.open_console(command) @map("I") def insert_before_filename(arg): - append_to_filename(arg) - arg.fm.ui.console.move(right=len('rename '), absolute=True) + command = 'rename ' + arg.fm.env.cf.basename + arg.fm.open_console(command, position=len('rename ')) -map('cw', fm.open_console(cmode.COMMAND, 'rename ')) -map('cd', fm.open_console(cmode.COMMAND, 'cd ')) -map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) +map('cw', fm.open_console('rename ')) +map('cd', fm.open_console('cd ')) +map('f', fm.open_console('find ')) map('d<bg>', fm.hint('d*u* (disk usage) d*d* (cut)')) -map('@', fm.open_console(cmode.OPEN, '@')) -map('#', fm.open_console(cmode.OPEN, 'p!')) +map('@', fm.open_console('shell %s', position=len('shell '))) +map('#', fm.open_console('shell -p ')) # --------------------------------------------- jump to directories map('gh', fm.cd('~')) @@ -268,7 +267,7 @@ for n in range(1, 10): map('<A-' + str(n) + '>', fm.tab_open(n)) # ------------------------------------------------------- searching -map('/', fm.open_console(cmode.SEARCH)) +map('/', fm.open_console('search ')) map('n', fm.search()) map('N', fm.search(forward=False)) @@ -307,11 +306,10 @@ def ctrl_c(arg): arg.fm.notify("Aborting: " + item.get_description()) arg.fm.loader.remove(index=0) -map(':', ';', fm.open_console(cmode.COMMAND)) -map('>', fm.open_console(cmode.COMMAND_QUICK)) -map('!', fm.open_console(cmode.OPEN, prompt='!')) -map('s', fm.open_console(cmode.OPEN, prompt='$')) -map('r', fm.open_console(cmode.OPEN_QUICK)) +map(':', ';', fm.open_console('')) +map('!', fm.open_console('shell ')) +map('s', fm.open_console('shell ')) +map('r', fm.open_console('open_with ')) # =================================================================== diff --git a/ranger/gui/defaultui.py b/ranger/gui/defaultui.py index 4baea756..434e6d45 100644 --- a/ranger/gui/defaultui.py +++ b/ranger/gui/defaultui.py @@ -92,8 +92,8 @@ class DefaultUI(UI): def close_embedded_pager(self): self.browser.close_pager() - def open_console(self, mode, string='', prompt=None): - if self.console.open(mode, string, prompt=prompt): + def open_console(self, string='', prompt=None, position=None): + if self.console.open(string, prompt=prompt, position=position): self.status.msg = None self.console.on_close = self.close_console self.console.visible = True diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 85b92548..9d0ea75f 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -18,38 +18,21 @@ The Console widget implements a vim-like console for entering commands, searching and executing files. """ -import string import curses import re from collections import deque from . import Widget -from ranger.gui.widgets.console_mode import is_valid_mode, mode_to_class from ranger import log, relpath_conf -from ranger.core.runner import ALLOWED_FLAGS -from ranger.ext.shell_escape import shell_quote from ranger.ext.utfwidth import uwid from ranger.container.keymap import CommandArgs -from ranger.ext.get_executables import get_executables from ranger.ext.direction import Direction from ranger.ext.utfwidth import uwid, uchars from ranger.container import History from ranger.container.history import HistoryEmptyException import ranger -DEFAULT_HISTORY = 0 -SEARCH_HISTORY = 1 -QUICKOPEN_HISTORY = 2 -OPEN_HISTORY = 3 - -class _CustomTemplate(string.Template): - """A string.Template subclass for use in the OpenConsole""" - delimiter = '%' - idpattern = '\d?[a-z]' - - class Console(Widget): - mode = None visible = False last_cursor_mode = None prompt = ':' @@ -57,29 +40,22 @@ class Console(Widget): tab_deque = None original_line = None history = None - histories = None override = None allow_close = False - historypaths = [] + historypath = None def __init__(self, win): Widget.__init__(self, win) self.clear() - self.histories = [] - # load histories from files - if ranger.arg.clean: - for i in range(4): - self.histories.append( - History(self.settings.max_console_history_size)) - else: - self.historypaths = [relpath_conf(x) for x in \ - ('history', 'history_search', 'history_qopen', 'history_open')] - for i, path in enumerate(self.historypaths): - hist = History(self.settings.max_console_history_size) - self.histories.append(hist) - if ranger.arg.clean: continue - try: f = open(path, 'r') - except: continue + self.history = History(self.settings.max_console_history_size) + # load history from files + if not ranger.arg.clean: + self.historypath = relpath_conf('history') + try: + f = open(path, 'r') + except: + pass + else: for line in f: hist.add(line[:-1]) f.close() @@ -88,20 +64,20 @@ class Console(Widget): # save histories from files if ranger.arg.clean or not self.settings.save_console_history: return - for i, path in enumerate(self.historypaths): - try: f = open(path, 'w') - except: continue - for entry in self.histories[i]: - f.write(entry + '\n') - f.close() + if self.historypath: + try: + f = open(self.historypath, 'w') + except: + pass + else: + for entry in self.histories[i]: + f.write(entry + '\n') + f.close() def init(self): """override this. Called directly after class change""" def draw(self): - if self.mode is None: - return - self.win.erase() self.addstr(0, 0, self.prompt) overflow = -self.wid + len(self.prompt) + uwid(self.line) + 1 @@ -118,25 +94,18 @@ class Console(Widget): except: pass - def open(self, mode, string='', prompt=None): - if not is_valid_mode(mode): - return False + def open(self, string='', prompt=None, position=None): if prompt is not None: assert isinstance(prompt, str) self.prompt = prompt elif 'prompt' in self.__dict__: del self.prompt - cls = mode_to_class(mode) - if self.last_cursor_mode is None: try: self.last_cursor_mode = curses.curs_set(1) except: pass - self.mode = mode - self.__class__ = cls - self.history = self.histories[DEFAULT_HISTORY] self.init() self.allow_close = False self.tab_deque = None @@ -144,6 +113,8 @@ class Console(Widget): self.visible = True self.line = string self.pos = len(string) + if position is not None: + self.pos = min(self.pos, position) self.history.add('') return True @@ -283,56 +254,6 @@ class Console(Widget): self.line = left_part + ''.join(uc[upos+1:]) self.on_line_change() - def execute(self): - pass - - def tab(self): - pass - - def on_line_change(self): - pass - - -class ConsoleWithTab(Console): - 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) - self.on_line_change() - - 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) - self.on_line_change() - - def _get_tab(self): - """ - Override this function in the subclass! - - It should return either a string, an iterable or None. - If a string is returned, tabbing will result in the line turning - into that string. - If another iterable is returned, each tabbing will cycle through - the elements of the iterable (which have to be strings). - If None is returned, nothing will happen. - """ - - return None - - -class CommandConsole(ConsoleWithTab): - prompt = ':' def execute(self, cmd=None): self.allow_close = True @@ -356,7 +277,7 @@ class CommandConsole(ConsoleWithTab): except: return None else: - return command_class(self.line, self.mode) + return command_class(self.line) def _get_cmd_class(self): return self.fm.commands.get_command(self.line.split()[0]) @@ -371,313 +292,34 @@ class CommandConsole(ConsoleWithTab): return self.fm.commands.command_generator(self.line) + 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) + self.on_line_change() + + 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) + self.on_line_change() -class QuickCommandConsole(CommandConsole): - """ - The QuickCommandConsole is essentially the same as the - CommandConsole, and includes one additional feature: - After each letter you type, it checks whether the command as it - stands there could be executed in a meaningful way, and if it does, - run it right away. - - Example: - >cd .. - As you type the last dot, The console will recognize what you mean - and enter the parent directory saving you the time of pressing enter. - """ - prompt = '>' def on_line_change(self): try: cls = self._get_cmd_class() except (KeyError, ValueError, IndexError): pass else: - cmd = cls(self.line, self.mode) + cmd = cls(self.line) if cmd and cmd.quick(): self.execute(cmd) - - -class SearchConsole(Console): - prompt = '/' - - def init(self): - self.history = self.histories[SEARCH_HISTORY] - - def execute(self): - self.fm.search_file(self.line, regexp=True) - self.close() - - -class OpenConsole(ConsoleWithTab): - """ - The Open Console allows you to execute shell commands: - !vim * will run vim and open all files in the directory. - - %f will be replaced with the basename of the highlighted file - %s will be selected with all files in the selection - - There is a special syntax for more control: - - !d! mplayer will run mplayer with flags (d means detached) - !@ mplayer will open the selected files with mplayer - (equivalent to !mplayer %s) - - Those two can be combinated: - - !d!@mplayer will open the selection with a detached mplayer - (again, this is equivalent to !d!mplayer %s) - - For a list of other flags than "d", check chapter 2.5 of the documentation - """ - prompt = '!' - - def init(self): - self.history = self.histories[OPEN_HISTORY] - - def execute(self): - command, flags = self._parse() - if not command and 'p' in flags: - command = 'cat %f' - if command: - if _CustomTemplate.delimiter in command: - command = self._substitute_metachars(command) - self.fm.execute_command(command, flags=flags) - self.close() - - def _get_tab(self): - try: - i = self.line.index('!')+1 - except ValueError: - line = self.line - start = '' - else: - line = self.line[i:] - start = self.line[:i] - - try: - position_of_last_space = line.rindex(" ") - except ValueError: - return (start + program + ' ' for program \ - in get_executables() if program.startswith(line)) - if position_of_last_space == len(line) - 1: - return self.line + '%s ' - else: - before_word, start_of_word = self.line.rsplit(' ', 1) - return (before_word + ' ' + file.shell_escaped_basename \ - for file in self.fm.env.cwd.files \ - if file.shell_escaped_basename.startswith(start_of_word)) - - def _substitute_metachars(self, command): - macros = {} - - if self.fm.env.cf: - macros['f'] = shell_quote(self.fm.env.cf.basename) - else: - macros['f'] = '' - - macros['s'] = ' '.join(shell_quote(fl.basename) \ - for fl in self.fm.env.get_selection()) - - macros['c'] = ' '.join(shell_quote(fl.path) - for fl in self.fm.env.copy) - - macros['t'] = ' '.join(shell_quote(fl.basename) - for fl in self.fm.env.cwd.files - if fl.realpath in self.fm.tags) - - if self.fm.env.cwd: - macros['d'] = shell_quote(self.fm.env.cwd.path) - else: - macros['d'] = '.' - - # define d/f/s macros for each tab - for i in range(1,10): - try: - tab_dir_path = self.fm.tabs[i] - except: - continue - tab_dir = self.fm.env.get_directory(tab_dir_path) - i = str(i) - macros[i + 'd'] = shell_quote(tab_dir_path) - if tab_dir.pointed_obj: - macros[i + 'f'] = shell_quote(tab_dir.pointed_obj.path) - macros[i + 's'] = ' '.join(shell_quote(fl.path) - for fl in tab_dir.get_selection()) - - # define D/F/S for the next tab - found_current_tab = False - next_tab_path = None - first_tab = None - for tab in self.fm.tabs: - if not first_tab: - first_tab = tab - if found_current_tab: - next_tab_path = self.fm.tabs[tab] - break - if self.fm.current_tab == tab: - found_current_tab = True - if found_current_tab and not next_tab_path: - next_tab_path = self.fm.tabs[first_tab] - next_tab = self.fm.env.get_directory(next_tab_path) - - macros['D'] = shell_quote(next_tab) - if next_tab.pointed_obj: - macros['F'] = shell_quote(next_tab.pointed_obj.path) - macros['S'] = ' '.join(shell_quote(fl.path) - for fl in next_tab.get_selection()) - - return _CustomTemplate(command).safe_substitute(macros) - - def _parse(self): - if '!' in self.line: - flags, cmd = self.line.split('!', 1) - else: - flags, cmd = '', self.line - - add_selection = False - if cmd.startswith('@'): - cmd = cmd[1:] - add_selection = True - elif flags.startswith('@'): - flags = flags[1:] - add_selection = True - - if add_selection: - cmd += ' ' + ' '.join(shell_quote(fl.basename) \ - for fl in self.env.get_selection()) - - return (cmd, flags) - - -class QuickOpenConsole(ConsoleWithTab): - """ - The Quick Open Console allows you to open files with predefined programs - and modes very quickly. By adding flags to the command, you can specify - precisely how the program is run, e.g. the d-flag will run it detached - from the file manager. - - For a list of other flags than "d", check chapter 2.5 of the documentation - - The syntax is "open with: <application> <mode> <flags>". - The parsing of the arguments is very flexible. You can leave out one or - more arguments (or even all of them) and it will fall back to default - values. You can switch the order as well. - There is just one rule: - - If you supply the <application>, it has to be the first argument. - - Examples: - - open with: mplayer D open the selection in mplayer, but not detached - open with: 1 open it with the default handler in mode 1 - open with: d open it detached with the default handler - open with: p open it as usual, but pipe the output to "less" - open with: totem 1 Ds open in totem in mode 1, will not detach the - process (flag D) but discard the output (flag s) - """ - - prompt = 'open with: ' - - def init(self): - self.history = self.histories[QUICKOPEN_HISTORY] - - def execute(self): - split = self.line.split() - app, flags, mode = self._get_app_flags_mode() - self.fm.execute_file( - files = [self.env.cf], - app = app, - flags = flags, - mode = mode ) - self.close() - - def _get_app_flags_mode(self): - """ - Extracts the application, flags and mode from - a string entered into the "openwith_quick" console. - """ - # examples: - # "mplayer d 1" => ("mplayer", "d", 1) - # "aunpack 4" => ("aunpack", "", 4) - # "p" => ("", "p", 0) - # "" => None - - app = '' - flags = '' - mode = 0 - split = self.line.split() - - if len(split) == 0: - pass - - elif len(split) == 1: - part = split[0] - if self._is_app(part): - app = part - elif self._is_flags(part): - flags = part - elif self._is_mode(part): - mode = part - - elif len(split) == 2: - part0 = split[0] - part1 = split[1] - - if self._is_app(part0): - app = part0 - if self._is_flags(part1): - flags = part1 - elif self._is_mode(part1): - mode = part1 - elif self._is_flags(part0): - flags = part0 - if self._is_mode(part1): - mode = part1 - elif self._is_mode(part0): - mode = part0 - if self._is_flags(part1): - flags = part1 - - elif len(split) >= 3: - part0 = split[0] - part1 = split[1] - part2 = split[2] - - if self._is_app(part0): - app = part0 - if self._is_flags(part1): - flags = part1 - if self._is_mode(part2): - mode = part2 - elif self._is_mode(part1): - mode = part1 - if self._is_flags(part2): - flags = part2 - elif self._is_flags(part0): - flags = part0 - if self._is_mode(part1): - mode = part1 - elif self._is_mode(part0): - mode = part0 - if self._is_flags(part1): - flags = part1 - - return app, flags, int(mode) - - def _get_tab(self): - if ' ' not in self.line: - all_apps = self.fm.apps.all() - if all_apps: - return (app for app in all_apps if app.startswith(self.line)) - - return None - - def _is_app(self, arg): - return self.fm.apps.has(arg) or \ - (not self._is_flags(arg) and arg in get_executables()) - - def _is_flags(self, arg): - return all(x in ALLOWED_FLAGS for x in arg) - - def _is_mode(self, arg): - return all(x in '0123456789' for x in arg) diff --git a/ranger/gui/widgets/console_mode.py b/ranger/gui/widgets/console_mode.py deleted file mode 100644 index c29f9959..00000000 --- a/ranger/gui/widgets/console_mode.py +++ /dev/null @@ -1,55 +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/>. - -DEFAULT = 0 -COMMAND = 1 -COMMAND_QUICK = 2 -OPEN = 3 -OPEN_QUICK = 4 -SEARCH = 5 - -def is_valid_mode(mode): - """ - Returns True or False depending on whether the mode is valid or not. - """ - return isinstance(mode, int) and mode >= 0 and mode <= 5 - -def all_modes(mode): - """ - Returns a generator containing all valid modes. - """ - return range(6) - -def mode_to_class(mode): - """ - Associates modes with the actual classes - from ranger.gui.widgets.console. - """ - from .console import Console, CommandConsole, OpenConsole, \ - QuickOpenConsole, QuickCommandConsole, SearchConsole - - if mode == DEFAULT: - return Console - if mode == COMMAND: - return CommandConsole - if mode == OPEN: - return OpenConsole - if mode == OPEN_QUICK: - return QuickOpenConsole - if mode == COMMAND_QUICK: - return QuickCommandConsole - if mode == SEARCH: - return SearchConsole - raise ValueError |