summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2009-12-27 02:23:23 +0100
committerhut <hut@lavabit.com>2009-12-27 02:23:23 +0100
commit34266423a7bf6969a7df3faccc9c73db01c35d82 (patch)
treeb365e9c9779cc51f141684c3ab455f0cec22112f
parent582c9ff2c211a5dcf7ff9f042dbec791541d34b6 (diff)
downloadranger-34266423a7bf6969a7df3faccc9c73db01c35d82.tar.gz
improved tab completion capabilities
-rw-r--r--ranger/applications.py2
-rw-r--r--ranger/commands.py75
-rw-r--r--ranger/gui/widgets/console.py67
3 files changed, 122 insertions, 22 deletions
diff --git a/ranger/applications.py b/ranger/applications.py
index d5c0f5fb..ed5f282e 100644
--- a/ranger/applications.py
+++ b/ranger/applications.py
@@ -22,7 +22,7 @@ class Applications(object):
 
 	def all(self):
 		"""Returns a list with all application functions"""
-		return [x for x in self.__dict__ if x.startswith('app_')]
+		return [x[4:] for x in self.__class__.__dict__ if x.startswith('app_')]
 
 import os, sys
 null = open(os.devnull, 'a')
diff --git a/ranger/commands.py b/ranger/commands.py
index 2c0593a3..07fa1679 100644
--- a/ranger/commands.py
+++ b/ranger/commands.py
@@ -19,7 +19,7 @@ class Command(FileManagerAware):
 	def quick_open(self):
 		"""Override this"""
 
-	def _tab_through_directories(self):
+	def _tab_only_directories(self):
 		from os.path import dirname, basename, expanduser, join, isdir
 
 		line = parse(self.line)
@@ -30,33 +30,94 @@ class Command(FileManagerAware):
 		except IndexError:
 			rel_dest = ''
 
+		# expand the tilde into the user directory
 		if rel_dest.startswith('~'):
 			return line + expanduser(rel_dest) + '/'
 
+		# define some shortcuts
 		abs_dest = join(pwd, rel_dest)
 		abs_dirname = dirname(abs_dest)
 		rel_basename = basename(rel_dest)
 		rel_dirname = dirname(rel_dest)
 		
 		try:
+			# are we after a directory?
 			if rel_dest.endswith('/') or rel_dest == '':
 				_, dirnames, _ = os.walk(abs_dest).next()
+
+			# are we in the middle of the filename?
 			else:
 				_, dirnames, _ = os.walk(abs_dirname).next()
 				dirnames = [dn for dn in dirnames \
 						if dn.startswith(rel_basename)]
 		except (OSError, StopIteration):
+			# os.walk found nothing
 			pass
 		else:
 			dirnames.sort()
 
+			# no results, return None
 			if len(dirnames) == 0:
 				return
 
+			# one result. since it must be a directory, append a slash.
 			if len(dirnames) == 1:
 				return line + 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 + join(rel_dirname, dirname) for dirname in dirnames)
+	
+	def _tab_directory_content(self):
+		from os.path import dirname, basename, expanduser, join, isdir
+
+		line = parse(self.line)
+		pwd = self.fm.env.pwd.path
+
+		try:
+			rel_dest = line.rest(1)
+		except IndexError:
+			rel_dest = ''
+
+		# expand the tilde into the user directory
+		if rel_dest.startswith('~'):
+			return line + expanduser(rel_dest) + '/'
+
+		# define some shortcuts
+		abs_dest = join(pwd, rel_dest)
+		abs_dirname = dirname(abs_dest)
+		rel_basename = basename(rel_dest)
+		rel_dirname = dirname(rel_dest)
+		
+		try:
+			# are we after a directory?
+			if rel_dest.endswith('/') or rel_dest == '':
+				_, dirnames, filenames = os.walk(abs_dest).next()
+				names = dirnames + filenames
+
+			# are we in the middle of the filename?
+			else:
+				_, dirnames, filenames = os.walk(abs_dirname).next()
+				names = [name for name in (dirnames + filenames) \
+						if name.startswith(rel_basename)]
+		except (OSError, StopIteration):
+			# os.walk found nothing
+			pass
+		else:
+			names.sort()
+
+			# no results, return None
+			if len(names) == 0:
+				return
+
+			# one result. since it must be a directory, append a slash.
+			if len(names) == 1:
+				return line + 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 + join(rel_dirname, name) for name in names)
+
 
 # -------------------------------- definitions
 
@@ -81,7 +142,7 @@ class cd(Command):
 			self.fm.enter_dir(destination)
 
 	def tab(self):
-		return self._tab_through_directories()
+		return self._tab_only_directories()
 	
 	def quick_open(self):
 		from os.path import isdir, join, normpath
@@ -120,6 +181,9 @@ class find(Command):
 			self.fm.block_input(0.5)
 			return True
 
+	def tab(self):
+		return self._tab_directory_content()
+
 	def _search(self):
 		self.count = 0
 		line = parse(self.line)
@@ -168,6 +232,9 @@ class rename(Command):
 	def execute(self):
 		line = parse(self.line)
 		self.fm.rename(self.fm.env.cf, line.rest(1))
+
+	def tab(self):
+		return self._tab_directory_content()
 	
 
 # -------------------------------- rest
@@ -185,3 +252,7 @@ def alias(**kw):
 		by_name[key] = value
 
 alias(q=quit)
+
+def command_generator(start):
+	return (cmd + ' ' for cmd in by_name if cmd.startswith(start))
+
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 75963829..1e0f0b06 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -191,18 +191,7 @@ class Console(Widget):
 		pass
 
 
-class CommandConsole(Console):
-	prompt = ':'
-
-	def execute(self, cmd=None):
-		if cmd is None:
-			cmd = self._get_cmd()
-
-		if cmd:
-			cmd.execute()
-
-		Console.execute(self)
-	
+class ConsoleWithTab(Console):
 	def tab(self, n=1):
 		if self.tab_deque is None:
 			tab_result = self._get_tab()
@@ -224,11 +213,38 @@ class CommandConsole(Console):
 			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):
+		if cmd is None:
+			cmd = self._get_cmd()
+
+		if cmd:
+			cmd.execute()
+
+		Console.execute(self)
 
 	def _get_cmd(self):
 		try:
 			command_name = self.line.split()[0]
-		except:
+		except IndexError:
 			return None
 
 		try:
@@ -239,11 +255,14 @@ class CommandConsole(Console):
 		return command_class(self.line, self.mode)
 	
 	def _get_tab(self):
-		cmd = self._get_cmd()
-		if cmd:
-			return cmd.tab()
-		else:
-			return None
+		if ' ' in self.line:
+			cmd = self._get_cmd()
+			if cmd:
+				return cmd.tab()
+			else:
+				return None
+
+		return commands.command_generator(self.line)
 
 
 class QuickCommandConsole(CommandConsole):
@@ -338,7 +357,7 @@ class OpenConsole(Console):
 		return "'" + str(string).replace("'","'\"'\"'") + "'"
 
 
-class QuickOpenConsole(Console):
+class QuickOpenConsole(ConsoleWithTab):
 	"""
 	The QuickOpenConsole allows you to open files with
 	pre-defined programs and modes very quickly. By adding flags
@@ -434,6 +453,16 @@ class QuickOpenConsole(Console):
 
 		return app, flags, int(mode)
 
+	def _get_tab(self):
+		if ' ' not in self.line:
+			all_apps = self.fm.apps.all()
+			log(all_apps)
+			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)