summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/actions.py4
-rw-r--r--ranger/commands.py110
-rw-r--r--ranger/defaults/keys.py8
-rw-r--r--ranger/gui/defaultui.py4
-rw-r--r--ranger/gui/widgets/console.py64
5 files changed, 180 insertions, 10 deletions
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 <relative> or to <absolute>"""
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)