summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2010-08-26 13:13:46 +0200
committerhut <hut@lavabit.com>2010-08-26 13:58:15 +0200
commitc525589d6c6a7fd00be099d2e399f08113f4a33f (patch)
tree427a6915b57764ac5987612135befa295d9609c7
parente8b3ae51480ac217c2f064704902ff2f28b9f635 (diff)
downloadranger-c525589d6c6a7fd00be099d2e399f08113f4a33f.tar.gz
widgets/console: Simplified console
-rw-r--r--ranger/api/commands.py4
-rw-r--r--ranger/api/keys.py1
-rw-r--r--ranger/core/actions.py86
-rw-r--r--ranger/defaults/commands.py155
-rw-r--r--ranger/defaults/keys.py36
-rw-r--r--ranger/gui/defaultui.py4
-rw-r--r--ranger/gui/widgets/console.py448
-rw-r--r--ranger/gui/widgets/console_mode.py55
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