summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--INSTALL2
-rw-r--r--TODO1
-rw-r--r--ranger/__init__.py37
-rw-r--r--ranger/__main__.py106
-rw-r--r--ranger/api/apps.py4
-rw-r--r--ranger/colorschemes/__init__.py19
-rw-r--r--ranger/container/bookmarks.py8
-rw-r--r--ranger/container/tags.py4
-rw-r--r--ranger/core/__init__.py0
-rw-r--r--ranger/core/fm.py (renamed from ranger/fm.py)11
-rw-r--r--ranger/core/runner.py (renamed from ranger/runner.py)0
-rw-r--r--ranger/defaults/apps.py3
-rw-r--r--ranger/defaults/options.py41
-rw-r--r--ranger/ext/openstruct.py7
-rw-r--r--ranger/gui/widgets/browsercolumn.py2
-rw-r--r--ranger/gui/widgets/console.py2
-rw-r--r--ranger/help/starting.py4
-rw-r--r--ranger/shared/settings.py84
18 files changed, 202 insertions, 133 deletions
diff --git a/INSTALL b/INSTALL
index 4635478b..269140c6 100644
--- a/INSTALL
+++ b/INSTALL
@@ -27,4 +27,4 @@ To install ranger, follow this instructions:
    alias rng="source ranger ranger"
 
    (Unfortunately this feature is shell dependent.  It has been
-   successfully tested with BASH only.)
+   successfully tested with BASH and ZSH only.)
diff --git a/TODO b/TODO
index 41b5497e..81af21db 100644
--- a/TODO
+++ b/TODO
@@ -69,6 +69,7 @@ Bugs
    (X) #62  10/02/15  curs_set can raise an exception
    (X) #65  10/02/16  "source ranger ranger some/file.txt" shouldn't cd after exit
    ( ) #67  10/03/08  terminal title in tty
+   ( ) #69  10/03/11  tab-completion breaks with Apps subclass
 
 
 Ideas
diff --git a/ranger/__init__.py b/ranger/__init__.py
index f6913bb5..8bd978d7 100644
--- a/ranger/__init__.py
+++ b/ranger/__init__.py
@@ -18,12 +18,6 @@
 import os
 import sys
 
-LOGFILE = '/tmp/errorlog'
-
-__copyright__ = """
-Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-"""
-
 __license__ = 'GPL3'
 __version__ = '1.0.3'
 __credits__ = 'Roman Zimbelmann'
@@ -31,34 +25,41 @@ __author__ = 'Roman Zimbelmann'
 __maintainer__ = 'Roman Zimbelmann'
 __email__ = 'romanz@lavabit.com'
 
-debug = False
-
-CONFDIR = os.path.expanduser(os.path.join('~', '.config', 'ranger'))
-RANGERDIR = os.path.dirname(__file__)
-
-sys.path.append(CONFDIR)
+__copyright__ = """
+Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
+"""
 
 USAGE = '%prog [options] [path/filename]'
+DEFAULT_CONFDIR = '~/.ranger'
+RANGERDIR = os.path.dirname(__file__)
+LOGFILE = '/tmp/errorlog'
 
 #for python3-only versions, this could be replaced with:
-#
 #def log(*objects, start='ranger:', sep=' ', end='\n'):
 #	print(start, *objects, end=end, sep=sep, file=open(LOGFILE, 'a'))
 def log(*objects, **keywords):
-	"""Writes objects to a logfile.
-	Has the same arguments as print() in python3"""
+	"""
+	Writes objects to a logfile (for the purpose of debugging only.)
+	Has the same arguments as print() in python3.
+	"""
+	if LOGFILE is None or arg.clean:
+		return
 	start = 'start' in keywords and keywords['start'] or 'ranger:'
 	sep   =   'sep' in keywords and keywords['sep']   or ' '
 	_file =  'file' in keywords and keywords['file']  or open(LOGFILE, 'a')
 	end   =   'end' in keywords and keywords['end']   or '\n'
 	_file.write(sep.join(map(str, (start, ) + objects)) + end)
 
+def relpath_conf(*paths):
+	"""returns the path relative to rangers configuration directory"""
+	if arg.clean:
+		assert 0, "Should not access relpath_conf in clean mode!"
+	else:
+		return os.path.join(arg.confdir, *paths)
+
 def relpath(*paths):
 	"""returns the path relative to rangers library directory"""
 	return os.path.join(RANGERDIR, *paths)
 
-def relpath_conf(*paths):
-	"""returns the path relative to rangers configuration directory"""
-	return os.path.join(CONFDIR, *paths)
 
 from ranger.__main__ import main
diff --git a/ranger/__main__.py b/ranger/__main__.py
index 72b4adf8..dba229bc 100644
--- a/ranger/__main__.py
+++ b/ranger/__main__.py
@@ -19,6 +19,65 @@
 import os
 import sys
 
+
+def parse_arguments():
+	"""Parse the program arguments"""
+
+	from optparse import OptionParser, SUPPRESS_HELP
+	from ranger.ext.openstruct import OpenStruct
+	from ranger import __version__, USAGE, DEFAULT_CONFDIR
+
+	parser = OptionParser(usage=USAGE, version='ranger ' + __version__)
+
+	# Instead of using this directly, use the embedded
+	# shell script by running ranger with:
+	# source /path/to/ranger /path/to/ranger
+	parser.add_option('--cd-after-exit',
+			action='store_true',
+			help=SUPPRESS_HELP)
+
+	parser.add_option('-d', '--debug', action='store_true',
+			help="activate debug mode")
+
+	parser.add_option('-c', '--clean', action='store_true',
+			help="don't touch/require any config files. " \
+				"This will disable certain features. (tagging, bookmarks)")
+
+	parser.add_option('-r', '--confdir', dest='confdir', type='string',
+			default=DEFAULT_CONFDIR,
+			help="the configuration directory. (%default)")
+
+	parser.add_option('-m', '--mode', type='int', dest='mode', default=0,
+			help="if a filename is supplied, run it with this mode")
+
+	parser.add_option('-f', '--flags', type='string', dest='flags', default='',
+			help="if a filename is supplied, run it with these flags.")
+
+	options, positional = parser.parse_args()
+
+	arg = OpenStruct(options.__dict__, targets=positional)
+
+	arg.confdir = os.path.expanduser(arg.confdir)
+
+	if arg.cd_after_exit:
+		sys.stderr = sys.__stdout__
+
+	try:
+		os.makedirs(arg.confdir)
+	except OSError as err:
+		if err.errno != 17:  # 17 means it already exists
+			print("This configuration directory could not be created:")
+			print(arg.confdir)
+			print("To run ranger without the need for configuration files")
+			print("use the --clean option (not implemented yet)")
+			raise SystemExit()
+
+	if not arg.clean:
+#		sys.path[0:0] = (arg.confdir, )
+		sys.path.append(arg.confdir)
+
+	return arg
+
 def main():
 	"""initialize objects and run the filemanager"""
 	try:
@@ -30,12 +89,10 @@ def main():
 
 	from signal import signal, SIGINT
 	from locale import setlocale, LC_ALL
-	from optparse import OptionParser, SUPPRESS_HELP
 
 	import ranger
 	from ranger.ext import curses_interrupt_handler
-	from ranger import __version__, USAGE, CONFDIR
-	from ranger.fm import FM
+	from ranger.core.fm import FM
 	from ranger.container.environment import Environment
 	from ranger.shared.settings import SettingsAware
 	from ranger.gui.defaultui import DefaultUI as UI
@@ -45,50 +102,23 @@ def main():
 		setlocale(LC_ALL, 'en_US.utf8')
 	except:
 		pass
-	os.stat_float_times(True)
-	curses_interrupt_handler.install_interrupt_handler()
-
-	if not os.path.exists(CONFDIR):
-		os.mkdir(CONFDIR)
 
+	curses_interrupt_handler.install_interrupt_handler()
 
-	# Parse options
-	parser = OptionParser(usage=USAGE, version='ranger ' + __version__)
-
-	# Instead of using this directly, use the embedded
-	# shell script by running ranger with:
-	# source /path/to/ranger /path/to/ranger
-	parser.add_option('--cd-after-exit',
-			action='store_true',
-			help=SUPPRESS_HELP)
-
-	parser.add_option('-m', type='int', dest='mode', default=0,
-			help="if a filename is supplied, run it with this mode")
-
-	parser.add_option('-f', type='string', dest='flags', default='',
-			help="if a filename is supplied, run it with these flags.")
-
-	parser.add_option('-d', '--debug', action='store_true',
-			help="activate debug mode")
-
-	args, rest = parser.parse_args()
-
-	if args.cd_after_exit:
-		sys.stderr = sys.__stdout__
-
-	ranger.debug = args.debug
+	arg = parse_arguments()
+	ranger.arg = arg
 
 	SettingsAware._setup()
 
 	# Initialize objects
-	target = ' '.join(rest)
-	if target:
+	if arg.targets:
+		target = arg.target[0]
 		if not os.access(target, os.F_OK):
 			print("File or directory doesn't exist: %s" % target)
 			sys.exit(1)
 		elif os.path.isfile(target):
 			thefile = File(target)
-			FM().execute_file(thefile, mode=args.mode, flags=args.flags)
+			FM().execute_file(thefile, mode=arg.mode, flags=arg.flags)
 			sys.exit(0)
 		else:
 			path = target
@@ -100,7 +130,7 @@ def main():
 	try:
 		my_ui = UI()
 		my_fm = FM(ui=my_ui)
-		my_fm.stderr_to_out = args.cd_after_exit
+		my_fm.stderr_to_out = arg.cd_after_exit
 
 		# Run the file manager
 		my_fm.initialize()
@@ -110,7 +140,7 @@ def main():
 		# Finish, clean up
 		if 'my_ui' in vars():
 			my_ui.destroy()
-		if args.cd_after_exit:
+		if arg.cd_after_exit:
 			try: sys.__stderr__.write(my_fm.env.pwd.path)
 			except: pass
 
diff --git a/ranger/api/apps.py b/ranger/api/apps.py
index 743fa248..eadc0839 100644
--- a/ranger/api/apps.py
+++ b/ranger/api/apps.py
@@ -14,7 +14,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-This module provides helper functions/classes for ranger.defaults.apps.
+This module provides helper functions/classes for ranger.apps.
 """
 
 import os, sys, re
@@ -26,7 +26,7 @@ from ranger.shared import FileManagerAware
 class Applications(FileManagerAware):
 	"""
 	This class contains definitions on how to run programs and should
-	be extended in ranger.defaults.apps
+	be extended in ranger.apps
 
 	The user can decide what program to run, and if he uses eg. 'vim', the
 	function app_vim() will be called.  However, usually the user
diff --git a/ranger/colorschemes/__init__.py b/ranger/colorschemes/__init__.py
index ed5413d8..422c6598 100644
--- a/ranger/colorschemes/__init__.py
+++ b/ranger/colorschemes/__init__.py
@@ -22,22 +22,15 @@ from os.path import expanduser, dirname, exists, join
 
 __all__ = get_all_modules(dirname(__file__))
 
-from ranger.colorschemes import *
-from ranger import relpath_conf
-
-if exists(relpath_conf('colorschemes')):
-	initpy = relpath_conf('colorschemes', '__init__.py')
-	if not exists(initpy):
-		open(initpy, 'w').write("""# Automatically generated:
+if not ranger.arg.clean:
+	if exists(ranger.relpath_conf('colorschemes')):
+		initpy = ranger.relpath_conf('colorschemes', '__init__.py')
+		if not exists(initpy):
+			open(initpy, 'w').write("""# Automatically generated:
 from ranger.ext.get_all_modules import get_all_modules
 from os.path import dirname
 
 __all__ = get_all_modules(dirname(__file__))
 """)
 
-	try:
-		import sys
-		sys.path[0:0] = [ranger.CONFDIR]
-		from colorschemes import *
-	except ImportError:
-		pass
+from ranger.colorschemes import *
diff --git a/ranger/container/bookmarks.py b/ranger/container/bookmarks.py
index a0c757ca..d4e12f62 100644
--- a/ranger/container/bookmarks.py
+++ b/ranger/container/bookmarks.py
@@ -150,6 +150,8 @@ class Bookmarks(object):
 		This is done automatically after every modification if autosave is True."""
 		import os
 		self.update()
+		if self.path is None:
+			return
 		if os.access(self.path, os.W_OK):
 			f = open(self.path, 'w')
 			for key, value in self.dct.items():
@@ -163,6 +165,10 @@ class Bookmarks(object):
 	def _load_dict(self):
 		import os
 		dct = {}
+
+		if self.path is None:
+			return dct
+
 		if not os.path.exists(self.path):
 			try:
 				f = open(self.path, 'w')
@@ -193,6 +199,8 @@ class Bookmarks(object):
 
 	def _get_mtime(self):
 		import os
+		if self.path is None:
+			return None
 		try:
 			return os.stat(self.path).st_mtime
 		except OSError:
diff --git a/ranger/container/tags.py b/ranger/container/tags.py
index 70a4aa3d..11ac3a5d 100644
--- a/ranger/container/tags.py
+++ b/ranger/container/tags.py
@@ -81,3 +81,7 @@ class Tags(object):
 		for line in f:
 			result.add(line.strip())
 		return result
+
+	def __nonzero__(self):
+		return True
+	__bool__ = __nonzero__
diff --git a/ranger/core/__init__.py b/ranger/core/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ranger/core/__init__.py
diff --git a/ranger/fm.py b/ranger/core/fm.py
index a10e9af6..83426a04 100644
--- a/ranger/fm.py
+++ b/ranger/core/fm.py
@@ -16,9 +16,10 @@
 from time import time
 from collections import deque
 
+import ranger
 from ranger.actions import Actions
 from ranger.container import Bookmarks
-from ranger.runner import Runner
+from ranger.core.runner import Runner
 from ranger import relpath_conf
 from ranger.ext.get_executables import get_executables
 from ranger import __version__
@@ -61,8 +62,12 @@ class FM(Actions):
 		from ranger.fsobject.directory import Directory
 
 		if self.bookmarks is None:
+			if ranger.arg.clean:
+				bookmarkfile = None
+			else:
+				bookmarkfile = relpath_conf('bookmarks')
 			self.bookmarks = Bookmarks(
-					bookmarkfile=relpath_conf('bookmarks'),
+					bookmarkfile=bookmarkfile,
 					bookmarktype=Directory,
 					autosave=self.settings.autosave_bookmarks)
 			self.bookmarks.load()
@@ -71,7 +76,7 @@ class FM(Actions):
 			self.bookmarks = bookmarks
 
 		from ranger.container.tags import Tags
-		if self.tags is None:
+		if not ranger.arg.clean and self.tags is None:
 			self.tags = Tags(relpath_conf('tagged'))
 
 		if self.ui is None:
diff --git a/ranger/runner.py b/ranger/core/runner.py
index 26424881..26424881 100644
--- a/ranger/runner.py
+++ b/ranger/core/runner.py
diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py
index a19df7a9..37099d04 100644
--- a/ranger/defaults/apps.py
+++ b/ranger/defaults/apps.py
@@ -78,7 +78,7 @@ class CustomApplications(Applications):
 			return self.either(c, 'mplayer', 'totem')
 
 		if f.image:
-			return self.app_feh(c)
+			return self.either(c, 'feh', 'mirage')
 
 		if f.document:
 			return self.app_editor(c)
@@ -130,7 +130,6 @@ class CustomApplications(Applications):
 	@depends_on('mirage')
 	def app_mirage(self, c):
 		c.flags += 'd'
-
 		return tup('mirage', *c)
 
 	@depends_on('feh')
diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py
index 139cda88..8711f737 100644
--- a/ranger/defaults/options.py
+++ b/ranger/defaults/options.py
@@ -21,26 +21,45 @@ intact and the type of the value stays the same.
 
 from ranger.api.options import *
 
-one_kb = 1024
-
-colorscheme = colorschemes.default
+# Which colorscheme to use?  There are these by default:
+# colorschemes.texas
+# colorschemes.jungle
+# colorschemes.default
+# colorschemes.snow
+# Texas uses 88 colors. If they are not supported, it will fall back
+# to the default scheme.
+colorscheme = colorschemes.texas
 
 max_history_size = 20
-max_filesize_for_preview = 300 * one_kb
 scroll_offset = 2
-preview_files = True
-flushinput = True
 
-sort = 'basename'
-reverse = False
-directories_first = True
+# Flush the input after each key hit?  (Noticable when ranger lags)
+flushinput = True
 
-show_hidden = False
+# Preview files on the rightmost column?
+# And collapse the last column if there is nothing to preview?
+preview_files = True
+max_filesize_for_preview = 300 * 1024  # 300kb
 collapse_preview = True
+
+# Save bookmarks (used with mX and `X) instantly?
+# this helps to synchronize bookmarks between multiple ranger
+# instances but leads to slight performance loss.
+# When false, bookmarks are saved when ranger is exited.
 autosave_bookmarks = True
+
+# Specify a title for the window? Some terminals don't support this:
 update_title = False
 
+# Makes sense for screen readers:
 show_cursor = False
 
+# One of: size, basename, mtime, type
+sort = 'basename'
+reverse = False
+directories_first = True
+
+# Which files are hidden if show_hidden is False?
 hidden_filter = regexp(
-		r'lost\+found|^\.|~$|\.(:?pyc|pyo|bak|swp)$')
+	r'lost\+found|^\.|~$|\.(:?pyc|pyo|bak|swp)$')
+show_hidden = False
diff --git a/ranger/ext/openstruct.py b/ranger/ext/openstruct.py
index 11363127..a94c3031 100644
--- a/ranger/ext/openstruct.py
+++ b/ranger/ext/openstruct.py
@@ -13,8 +13,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+# prepend __ to arguments because one might use "args"
+# or "keywords" as a keyword argument.
+
 class OpenStruct(dict):
 	"""The fusion of dict and struct"""
-	def __init__(self, *args, **keywords):
-		dict.__init__(self, *args, **keywords)
+	def __init__(self, *__args, **__keywords):
+		dict.__init__(self, *__args, **__keywords)
 		self.__dict__ = self
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 2550062f..b98005b5 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -207,7 +207,7 @@ class BrowserColumn(Pager, Widget):
 
 			this_color = base_color + list(drawed.mimetype_tuple)
 			text = drawed.basename
-			tagged = drawed.realpath in self.fm.tags
+			tagged = self.fm.tags and drawed.realpath in self.fm.tags
 
 			if i == selected_i:
 				this_color.append('selected')
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 87e5a7b5..a331c66d 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -580,7 +580,7 @@ class QuickOpenConsole(ConsoleWithTab):
 		return self.fm.apps.has(arg)
 
 	def _is_flags(self, arg):
-		from ranger.runner import ALLOWED_FLAGS
+		from ranger.core.runner import ALLOWED_FLAGS
 		return all(x in ALLOWED_FLAGS for x in arg)
 
 	def _is_mode(self, arg):
diff --git a/ranger/help/starting.py b/ranger/help/starting.py
index 29921ffc..f5517c78 100644
--- a/ranger/help/starting.py
+++ b/ranger/help/starting.py
@@ -61,7 +61,7 @@ Note: The "open with" console is named QuickOpenConsole in the source code.
 ==============================================================================
 2.3. Programs
 
-Programs have to be defined in ranger/defaults/apps.py.  Each function
+Programs have to be defined in ranger/apps.py.  Each function
 in the class CustomApplications which starts with "app_" can be used
 as a program in the "open with" prompt.
 
@@ -83,7 +83,7 @@ start a file in mode 0. "4l" will start the file in mode 4 etc.
 You can specify a mode in the "open with" console by simply adding
 the number.  Eg: "open with: mplayer 1" or "open with: 1"
 
-For a list of all programs and modes, see ranger/defaults/apps.py
+For a list of all programs and modes, see ranger/apps.py
 
 
 ==============================================================================
diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py
index b549bd20..6df5241f 100644
--- a/ranger/shared/settings.py
+++ b/ranger/shared/settings.py
@@ -14,6 +14,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import types
+from inspect import isclass, ismodule
+import ranger
 from ranger.ext.openstruct import OpenStruct
 from ranger.gui.colorscheme import ColorScheme
 
@@ -41,54 +43,52 @@ class SettingsAware(object):
 
 	@staticmethod
 	def _setup():
-		from inspect import isclass, ismodule
-		from ranger.gui.colorscheme import ColorScheme
+		settings = OpenStruct()
 
-		# overwrite single default options with custom options
 		from ranger.defaults import options
-		try:
-			import options as custom_options
-			for setting in ALLOWED_SETTINGS:
-				if hasattr(custom_options, setting):
-					setattr(options, setting, getattr(custom_options, setting))
-				elif not hasattr(options, setting):
-					raise Exception("This option was not defined: " + setting)
-		except ImportError:
-			pass
-
-		assert check_option_types(options)
-
-		try:
-			import apps
-		except ImportError:
-			from ranger.defaults import apps
-
-		try:
-			import keys
-		except ImportError:
-			from ranger.defaults import keys
+		for setting in ALLOWED_SETTINGS:
+			try:
+				settings[setting] = getattr(options, setting)
+			except AttributeError:
+				raise Exception("The option `{0}' was not defined" \
+						" in the defaults!".format(setting))
+
+		import sys
+		if not ranger.arg.clean:
+			# overwrite single default options with custom options
+			try:
+				import rangerrc
+			except ImportError:
+				pass
+			else:
+				for setting in ALLOWED_SETTINGS:
+					try:
+						settings[setting] = getattr(rangerrc, setting)
+					except AttributeError:
+						pass
 
+		assert check_option_types(settings)
 
 		# If a module is specified as the colorscheme, replace it with one
 		# valid colorscheme inside that module.
 
-		all_content = options.colorscheme.__dict__.items()
+		all_content = settings.colorscheme.__dict__.items()
 
-		if isclass(options.colorscheme) and \
-				issubclass(options.colorscheme, ColorScheme):
-			options.colorscheme = options.colorscheme()
+		if isclass(settings.colorscheme) and \
+				issubclass(settings.colorscheme, ColorScheme):
+			settings.colorscheme = settings.colorscheme()
 
-		elif ismodule(options.colorscheme):
+		elif ismodule(settings.colorscheme):
 			def is_scheme(x):
 				return isclass(x) and issubclass(x, ColorScheme)
 
-			if hasattr(options.colorscheme, 'Scheme') \
-					and is_scheme(options.colorscheme.Scheme):
-				options.colorscheme = options.colorscheme.Scheme()
+			if hasattr(settings.colorscheme, 'Scheme') \
+					and is_scheme(settings.colorscheme.Scheme):
+				settings.colorscheme = settings.colorscheme.Scheme()
 			else:
-				for name, var in options.colorscheme.__dict__.items():
+				for name, var in settings.colorscheme.__dict__.items():
 					if var != ColorScheme and is_scheme(var):
-						options.colorscheme = var()
+						settings.colorscheme = var()
 						break
 				else:
 					raise Exception("The module contains no " \
@@ -96,12 +96,18 @@ class SettingsAware(object):
 		else:
 			raise Exception("Cannot locate colorscheme!")
 
-		for setting in ALLOWED_SETTINGS:
-			SettingsAware.settings[setting] = getattr(options, setting)
-
-		SettingsAware.settings.keys = keys
-		SettingsAware.settings.apps = apps
+		try:
+			import apps
+		except ImportError:
+			from ranger.defaults import apps
+		settings.apps = apps
+		try:
+			import keys
+		except ImportError:
+			from ranger.defaults import keys
+		settings.keys = keys
 
+		SettingsAware.settings = settings
 
 def check_option_types(opt):
 	import inspect