summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG13
-rw-r--r--ranger/api/commands.py41
-rw-r--r--ranger/defaults/commands.py89
-rw-r--r--ranger/ext/command_parser.py105
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