From d4900452fca51685349966d527d173fdefe83f08 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 30 Sep 2010 00:42:24 +0200 Subject: ext.utfwidth: stuff --- test/tc_utfwidth.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/tc_utfwidth.py b/test/tc_utfwidth.py index 0288c17b..67ff609e 100644 --- a/test/tc_utfwidth.py +++ b/test/tc_utfwidth.py @@ -42,5 +42,7 @@ class Test(TestCase): self.assertEqual(4, uwid("asdf")) self.assertEqual(5, uwid("löööl")) self.assertEqual(6, uwid("バババ")) + self.assertEqual(1, uwid("äsdf", count=1)) + self.assertEqual(2, uwid("バババ", count=1)) if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From 512f386be8753775ec824a6d9cbaf6527d50eda4 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 30 Sep 2010 01:08:40 +0200 Subject: ext.utfwidth: updated algorithms --- ranger/ext/utfwidth.py | 45 +++++++++++++----------------------------- ranger/gui/curses_shortcuts.py | 9 ++++++--- test/tc_utfwidth.py | 5 ----- 3 files changed, 20 insertions(+), 39 deletions(-) (limited to 'test') diff --git a/ranger/ext/utfwidth.py b/ranger/ext/utfwidth.py index 364db757..762f3894 100644 --- a/ranger/ext/utfwidth.py +++ b/ranger/ext/utfwidth.py @@ -28,20 +28,25 @@ WIDE = 2 def uwid(string, count=maxint): """Return the width of a string""" - end = len(string) - i = 0 + try: + string = string.decode('utf8', 'replace') + except AttributeError: + pass width = 0 - while i < end and count: - bytelen = utf_byte_length(string[i:]) - width += utf_char_width(string[i:i+bytelen]) - i += bytelen + for c in string: + width += utf_char_width(c) count -= 1 + if not count: + break return width def uchars(string): """Return a list with one string for each character""" - end = len(string) - i = 0 + try: + string = string.decode('utf-8', 'replace') + except AttributeError: + pass + return list(string) result = [] while i < end: bytelen = utf_byte_length(string[i:]) @@ -60,24 +65,9 @@ def uwidslice(string, start=0, end=maxint): chars.append(c) return "".join(chars[start:end]) -def utf_byte_length(string): - """Return the byte length of one utf character""" - firstord = ord(string[0]) - if firstord < 0b01111111: - return 1 - if firstord < 0b10111111: - return 1 # invalid - if firstord < 0b11011111: - return 2 - if firstord < 0b11101111: - return 3 - if firstord < 0b11110100: - return 4 - return 1 # invalid - def utf_char_width(string): """Return the width of a single character""" - u = _utf_char_to_int(string) + u = ord(string) if u < 0x1100: return NARROW # Hangul Jamo init. constonants @@ -117,10 +107,3 @@ def utf_char_width(string): if u >= 0x30000 and u <= 0x3FFFD: return WIDE return NARROW # invalid (?) - -def _utf_char_to_int(string): - # Squash the last 6 bits of each byte together to an integer - u = 0 - for c in string: - u = (u << 6) | (ord(c) & 0b00111111) - return u diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py index 3df45700..65886d7e 100644 --- a/ranger/gui/curses_shortcuts.py +++ b/ranger/gui/curses_shortcuts.py @@ -51,9 +51,12 @@ class CursesShortcuts(SettingsAware): pass except UnicodeEncodeError: try: - self.win.addstr(*(ascii_only(obj) for obj in args)) - except (_curses.error, TypeError): - pass + self.win.addstr(*(obj.encode('utf8') for obj in args)) + except UnicodeEncodeError: + try: + self.win.addstr(*(ascii_only(obj) for obj in args)) + except (_curses.error, TypeError): + pass def addnstr(self, *args): try: diff --git a/test/tc_utfwidth.py b/test/tc_utfwidth.py index 67ff609e..fba9f783 100644 --- a/test/tc_utfwidth.py +++ b/test/tc_utfwidth.py @@ -29,11 +29,6 @@ a_katakana = "ア" # width = 2, bytes = 3 # need one with width = 1 & bytes = 3 class Test(TestCase): - def test_utf_byte_length(self): - self.assertEqual(1, utf_byte_length(a_ascii)) - self.assertEqual(2, utf_byte_length(a_umlaut)) - self.assertEqual(3, utf_byte_length(a_katakana)) - def test_uwid(self): self.assertEqual(1, uwid(a_ascii)) self.assertEqual(1, uwid(a_umlaut)) -- cgit 1.4.1-2-gfad0 From 473daebb6870a54fe4b47529e77be698a2edfdb3 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 30 Sep 2010 04:51:29 +0200 Subject: Revert 5 commits concerning utf (due to very poor performance) This reverts commits: 8c8e7282b3b4238a3b7cf981d9e5715b11076419 5cb67eeb96d337b55deea20131fc44a3d5447251 512f386be8753775ec824a6d9cbaf6527d50eda4 d4900452fca51685349966d527d173fdefe83f08 e6dda13a71168f9ec4a1e4844edad5a3257803e9 --- ranger/ext/utfwidth.py | 121 ++++++++++++------------------------ ranger/gui/bar.py | 15 +++-- ranger/gui/curses_shortcuts.py | 30 ++++++--- ranger/gui/widgets/browsercolumn.py | 8 +-- test/tc_utfwidth.py | 7 ++- 5 files changed, 77 insertions(+), 104 deletions(-) (limited to 'test') diff --git a/ranger/ext/utfwidth.py b/ranger/ext/utfwidth.py index 5c850607..a506c676 100644 --- a/ranger/ext/utfwidth.py +++ b/ranger/ext/utfwidth.py @@ -18,80 +18,49 @@ # ---- # This file contains portions of code from cmus (uchar.c). -""" -This module provides functions that operate with the width of characters -and strings rather than characters or bytes. -""" - -import sys - NARROW = 1 WIDE = 2 -if sys.version > '3': - def uwid(string, count=-1): - """Return the width of a string""" - width = 0 - for c in string: - width += utf_char_width(c) - count -= 1 - if not count: - break - return width - - def uchars(string): - """Return a list with one string for each character""" - return list(string) +def uwid(string): + """Return the width of a string""" + end = len(string) + i = 0 + width = 0 + while i < end: + bytelen = utf_byte_length(string[i:]) + width += utf_char_width(string[i:i+bytelen]) + i += bytelen + return width - utf_ord = ord -else: - def uwid(string, count=-1): - """Return the width of a string""" - end = len(string) - i = 0 - width = 0 - while i < end and count: - bytelen = _utf_byte_length(string[i:]) - width += utf_char_width(string[i:i+bytelen]) - i += bytelen - count -= 1 - return width +def uchars(string): + """Return a list with one string for each character""" + end = len(string) + i = 0 + result = [] + while i < end: + bytelen = utf_byte_length(string[i:]) + result.append(string[i:i+bytelen]) + i += bytelen + return result - def uchars(string): - """Return a list with one string for each character""" - end = len(string) - i = 0 - result = [] - while i < end: - bytelen = _utf_byte_length(string[i:]) - result.append(string[i:i+bytelen]) - i += bytelen - return result - - def _utf_byte_length(string): - """Return the byte length of one utf character""" - firstord = ord(string[0]) - if firstord < 0b01111111: - return 1 - if firstord < 0b10111111: - return 1 # invalid - if firstord < 0b11011111: - return 2 - if firstord < 0b11101111: - return 3 - if firstord < 0b11110100: - return 4 +def utf_byte_length(string): + """Return the byte length of one utf character""" + firstord = ord(string[0]) + if firstord < 0b01111111: + return 1 + if firstord < 0b10111111: return 1 # invalid - - def utf_ord(char): - value = 0 - for byte in char: - value = (value << 6) | (ord(byte) & 0b00111111) - return value + if firstord < 0b11011111: + return 2 + if firstord < 0b11101111: + return 3 + if firstord < 0b11110100: + return 4 + return 1 # invalid def utf_char_width(string): """Return the width of a single character""" - u = utf_ord(string) + u = _utf_char_to_int(string) if u < 0x1100: return NARROW # Hangul Jamo init. constonants @@ -132,19 +101,9 @@ def utf_char_width(string): return WIDE return NARROW # invalid (?) -def uslice(string, start=0, end=1000000000): - """ - Returns a sliced string. - - Works like string[start:end] except that one step represents - one narrow character in a monospaced character grid. - """ - chars = [] - for c in uchars(string): - c_wid = utf_char_width(c) - if c_wid == NARROW: - chars.append(c) - elif c_wid == WIDE: - chars.append("") - chars.append(c) - return "".join(chars[start:end]) +def _utf_char_to_int(string): + # Squash the last 6 bits of each byte together to an integer + u = 0 + for c in string: + u = (u << 6) | (ord(c) & 0b00111111) + return u diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py index 42e1f1c4..41cc8133 100644 --- a/ranger/gui/bar.py +++ b/ranger/gui/bar.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ranger.ext.utfwidth import uwid, uslice, utf_char_width +from ranger.ext.utfwidth import uwid class Bar(object): left = None @@ -75,11 +75,10 @@ class Bar(object): # Shrink items to a minimum size of 1 until there is enough room. for item in self.left: if not item.fixed: - itemlen = uwid(item.string) - minimal_width = uwid(item.string, count=1) - if oversize > itemlen - minimal_width: - item.cut_off_to(minimal_width) - oversize -= (itemlen - minimal_width) + itemlen = len(item) + if oversize > itemlen - 1: + item.cut_off_to(1) + oversize -= (itemlen - 1) else: item.cut_off(oversize) break @@ -133,10 +132,10 @@ class ColoredString(object): def cut_off(self, n): if n >= 1: - self.string = uslice(self.string, 0, -n) + self.string = self.string[:-n] def cut_off_to(self, n): - self.string = uslice(self.string, 0, n) + self.string = self.string[:n] def __len__(self): return uwid(self.string) diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py index e5683b66..3df45700 100644 --- a/ranger/gui/curses_shortcuts.py +++ b/ranger/gui/curses_shortcuts.py @@ -44,26 +44,38 @@ class CursesShortcuts(SettingsAware): addstr(*args) -- failsafe version of self.win.addstr(*args) """ - def _addxyz_wrapper(self, function, args): + def addstr(self, *args): try: - function(*args) + self.win.addstr(*args) except (_curses.error, TypeError): pass except UnicodeEncodeError: try: - function(*(obj.encode('utf8') if hasattr(obj, 'encode') \ - else obj for obj in args)) + self.win.addstr(*(ascii_only(obj) for obj in args)) except (_curses.error, TypeError): pass - def addstr(self, *args): - self._addxyz_wrapper(self.win.addstr, args) - def addnstr(self, *args): - self._addxyz_wrapper(self.win.addnstr, args) + try: + self.win.addnstr(*args) + except (_curses.error, TypeError): + pass + except UnicodeEncodeError: + try: + self.win.addnstr(*(ascii_only(obj) for obj in args)) + except (_curses.error, TypeError): + pass def addch(self, *args): - self._addxyz_wrapper(self.win.addch, args) + try: + self.win.addch(*args) + except (_curses.error, TypeError): + pass + except UnicodeEncodeError: + try: + self.win.addch(*(ascii_only(obj) for obj in args)) + except (_curses.error, TypeError): + pass def color(self, *keys): """Change the colors from now on.""" diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index 6021d622..d617e64e 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -20,7 +20,6 @@ from time import time from . import Widget from .pager import Pager from ranger.fsobject import BAD_INFO -from ranger.ext.utfwidth import uslice class BrowserColumn(Pager): main_column = False @@ -249,13 +248,14 @@ class BrowserColumn(Pager): this_color.append('link') this_color.append(drawn.exists and 'good' or 'bad') + string = drawn.basename if self.main_column: if tagged: - self.addstr(line, 0, uslice(text, 0, self.wid - 2)) + self.addnstr(line, 0, text, self.wid - 2) elif self.wid > 1: - self.addstr(line, 1, uslice(text, 0, self.wid - 2)) + self.addnstr(line, 1, text, self.wid - 2) else: - self.addstr(line, 0, uslice(text, 0, self.wid)) + self.addnstr(line, 0, text, self.wid) if self.display_infostring and drawn.infostring \ and self.settings.display_size_in_main_column: diff --git a/test/tc_utfwidth.py b/test/tc_utfwidth.py index fba9f783..0288c17b 100644 --- a/test/tc_utfwidth.py +++ b/test/tc_utfwidth.py @@ -29,6 +29,11 @@ a_katakana = "ア" # width = 2, bytes = 3 # need one with width = 1 & bytes = 3 class Test(TestCase): + def test_utf_byte_length(self): + self.assertEqual(1, utf_byte_length(a_ascii)) + self.assertEqual(2, utf_byte_length(a_umlaut)) + self.assertEqual(3, utf_byte_length(a_katakana)) + def test_uwid(self): self.assertEqual(1, uwid(a_ascii)) self.assertEqual(1, uwid(a_umlaut)) @@ -37,7 +42,5 @@ class Test(TestCase): self.assertEqual(4, uwid("asdf")) self.assertEqual(5, uwid("löööl")) self.assertEqual(6, uwid("バババ")) - self.assertEqual(1, uwid("äsdf", count=1)) - self.assertEqual(2, uwid("バババ", count=1)) if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From a457053815d72ef5d7ccfedbfda02101665bdec6 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 2 Oct 2010 05:51:17 +0200 Subject: a little restructuration --- ranger/__init__.py | 49 +++----- ranger/__main__.py | 236 -------------------------------------- ranger/api/apps.py | 2 +- ranger/api/commands.py | 2 +- ranger/container/settingobject.py | 128 +++++++++++++++++++++ ranger/core/actions.py | 3 +- ranger/core/environment.py | 2 +- ranger/core/fm.py | 21 +++- ranger/core/helper.py | 167 +++++++++++++++++++++++++++ ranger/core/loader.py | 2 +- ranger/core/main.py | 98 ++++++++++++++++ ranger/core/shared.py | 73 ++++++++++++ ranger/fsobject/directory.py | 2 +- ranger/fsobject/fsobject.py | 8 +- ranger/gui/colorscheme.py | 12 +- ranger/gui/curses_shortcuts.py | 2 +- ranger/gui/displayable.py | 2 +- ranger/gui/widgets/console.py | 3 +- ranger/help/console.py | 1 + ranger/help/invocation.py | 1 + ranger/shared/__init__.py | 35 ------ ranger/shared/mimetype.py | 26 ----- ranger/shared/settings.py | 162 -------------------------- setup.py | 4 +- test/tc_directory.py | 8 +- test/tc_loader.py | 2 +- 26 files changed, 525 insertions(+), 526 deletions(-) delete mode 100644 ranger/__main__.py create mode 100644 ranger/container/settingobject.py create mode 100644 ranger/core/helper.py create mode 100644 ranger/core/main.py create mode 100644 ranger/core/shared.py delete mode 100644 ranger/shared/__init__.py delete mode 100644 ranger/shared/mimetype.py delete mode 100644 ranger/shared/settings.py (limited to 'test') diff --git a/ranger/__init__.py b/ranger/__init__.py index 6e96b779..0245988e 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -13,13 +13,21 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -"""Ranger - file browser for the unix terminal""" +""" +Console-based visual file manager. + +Ranger is a file manager with an ncurses frontend written in Python. +It is designed to give you a broader overview of the file system by +displaying previews and backviews, dividing the screen into columns. + +The keybindings are similar to those of other console programs like +vim, mutt or ncmpcpp so the usage will be intuitive and efficient. +""" from os import path, environ -from os.path import join as _join from ranger.ext.openstruct import OpenStruct from sys import argv -from .__main__ import main +from ranger.core.main import main # Information __license__ = 'GPL3' @@ -40,40 +48,9 @@ DEBUG = ('-d' in argv or '--debug' in argv) and ('--' not in argv or ('--debug' in argv and argv.index('--debug') < argv.index('--')))) # Get some valid arguments before actually parsing them in main() -arg = OpenStruct(debug=DEBUG, clean=False, confdir=DEFAULT_CONFDIR, - mode=0, flags='', targets=[]) - -# Debugging features. These will be activated when run with --debug. -# Example usage in the code: -# import ranger; ranger.log("hello world") -def log(*objects, **keywords): - """ - Writes objects to a logfile (for the purpose of debugging only.) - Has the same arguments as print() in python3. - """ - if LOGFILE is None or not arg.debug 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 log_traceback(): - if LOGFILE is None or not arg.debug or arg.clean: return - import traceback - traceback.print_stack(file=open(LOGFILE, 'a')) - -# Handy functions -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 _join(arg.confdir, *paths) +#arg = OpenStruct(debug=DEBUG, clean=False, confdir=DEFAULT_CONFDIR, +# mode=0, flags='', targets=[]) -def relpath(*paths): - """returns the path relative to rangers library directory""" - return _join(RANGERDIR, *paths) # Clean up del environ, OpenStruct, argv diff --git a/ranger/__main__.py b/ranger/__main__.py deleted file mode 100644 index 2efec4df..00000000 --- a/ranger/__main__.py +++ /dev/null @@ -1,236 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann -# -# 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 . - -# Most import statements in this module are inside the functions. -# This enables more convenient exception handling in ranger.py -# (ImportError will imply that this module can't be found) - -import locale -import os.path -import sys - -def parse_arguments(): - """Parse the program arguments""" - from optparse import OptionParser, SUPPRESS_HELP - from ranger import __version__, USAGE, DEFAULT_CONFDIR - from ranger.ext.openstruct import OpenStruct - - minor_version = __version__[2:] # assumes major version number is <10 - if '.' in minor_version: - minor_version = minor_version[:minor_version.find('.')] - version_tag = ' (stable)' if int(minor_version) % 2 == 0 else ' (testing)' - if __version__.endswith('.0'): - version_string = 'ranger ' + __version__[:-2] + version_tag - else: - version_string = 'ranger ' + __version__ + version_tag - - parser = OptionParser(usage=USAGE, version=version_string) - - 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. ") - parser.add_option('--fail-if-run', action='store_true', # COMPAT - help=SUPPRESS_HELP) - parser.add_option('--fail-unless-cd', action='store_true', - help="experimental: return the exit code 1 if ranger is" \ - "used to run a file (with `ranger filename`)") - parser.add_option('-r', '--confdir', type='string', - metavar='dir', default=DEFAULT_CONFDIR, - help="the configuration directory. (%default)") - parser.add_option('-m', '--mode', type='int', default=0, metavar='n', - help="if a filename is supplied, run it with this mode") - parser.add_option('-f', '--flags', type='string', default='', - metavar='string', - 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.fail_if_run: - arg.fail_unless_cd = arg.fail_if_run - del arg['fail_if_run'] - - return arg - - -def allow_access_to_confdir(confdir, allow): - if allow: - try: - os.makedirs(confdir) - except OSError as err: - if err.errno != 17: # 17 means it already exists - print("This configuration directory could not be created:") - print(confdir) - print("To run ranger without the need for configuration") - print("files, use the --clean option.") - raise SystemExit() - if not confdir in sys.path: - sys.path[0:0] = [confdir] - else: - if sys.path[0] == confdir: - del sys.path[0] - - -def load_settings(fm, clean): - import ranger.shared - import ranger.api.commands - import ranger.api.keys - if not clean: - allow_access_to_confdir(ranger.arg.confdir, True) - - # Load commands - comcont = ranger.api.commands.CommandContainer() - ranger.api.commands.alias = comcont.alias - try: - import commands - comcont.load_commands_from_module(commands) - except ImportError: - pass - from ranger.defaults import commands - comcont.load_commands_from_module(commands) - commands = comcont - - # Load apps - try: - import apps - except ImportError: - from ranger.defaults import apps - - # Load keys - keymanager = ranger.shared.EnvironmentAware.env.keymanager - ranger.api.keys.keymanager = keymanager - from ranger.defaults import keys - try: - import keys - except ImportError: - pass - # COMPAT WARNING - if hasattr(keys, 'initialize_commands'): - print("Warning: the syntax for ~/.config/ranger/keys.py has changed.") - print("Your custom keys are not loaded."\ - " Please update your configuration.") - allow_access_to_confdir(ranger.arg.confdir, False) - else: - comcont = ranger.api.commands.CommandContainer() - ranger.api.commands.alias = comcont.alias - from ranger.api import keys - keymanager = ranger.shared.EnvironmentAware.env.keymanager - ranger.api.keys.keymanager = keymanager - from ranger.defaults import commands, keys, apps - comcont.load_commands_from_module(commands) - commands = comcont - fm.commands = commands - fm.keys = keys - fm.apps = apps.CustomApplications() - - -def load_apps(fm, clean): - import ranger - if not clean: - allow_access_to_confdir(ranger.arg.confdir, True) - try: - import apps - except ImportError: - from ranger.defaults import apps - allow_access_to_confdir(ranger.arg.confdir, False) - else: - from ranger.defaults import apps - fm.apps = apps.CustomApplications() - - -def main(): - """initialize objects and run the filemanager""" - try: - import curses - except ImportError as errormessage: - print(errormessage) - print('ranger requires the python curses module. Aborting.') - sys.exit(1) - - try: locale.setlocale(locale.LC_ALL, '') - except: print("Warning: Unable to set locale. Expect encoding problems.") - - if not 'SHELL' in os.environ: - os.environ['SHELL'] = 'bash' - - arg = parse_arguments() - - import ranger - from ranger.ext import curses_interrupt_handler - from ranger.core.runner import Runner - from ranger.core.fm import FM - from ranger.core.environment import Environment - from ranger.gui.defaultui import DefaultUI as UI - from ranger.fsobject import File - from ranger.shared import (EnvironmentAware, FileManagerAware, - SettingsAware) - - if not arg.debug: - curses_interrupt_handler.install_interrupt_handler() - ranger.arg = arg - - SettingsAware._setup() - - targets = arg.targets or ['.'] - target = targets[0] - if arg.targets: - if target.startswith('file://'): - target = target[7:] - 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): - def print_function(string): - print(string) - runner = Runner(logfunc=print_function) - load_apps(runner, ranger.arg.clean) - runner(files=[File(target)], mode=arg.mode, flags=arg.flags) - sys.exit(1 if arg.fail_unless_cd else 0) - - crash_traceback = None - try: - # Initialize objects - EnvironmentAware._assign(Environment(target)) - fm = FM() - fm.tabs = dict((n+1, os.path.abspath(path)) for n, path \ - in enumerate(targets[:9])) - load_settings(fm, ranger.arg.clean) - if fm.env.username == 'root': - fm.settings.preview_files = False - FileManagerAware._assign(fm) - fm.ui = UI() - - # Run the file manager - fm.initialize() - fm.ui.initialize() - fm.loop() - except Exception: - import traceback - crash_traceback = traceback.format_exc() - except SystemExit as error: - return error.args[0] - finally: - try: - fm.ui.destroy() - except (AttributeError, NameError): - pass - if crash_traceback: - print(crash_traceback) - print("Ranger crashed. " \ - "Please report this (including the traceback) at:") - print("http://savannah.nongnu.org/bugs/?group=ranger&func=additem") - return 1 - return 0 diff --git a/ranger/api/apps.py b/ranger/api/apps.py index 91aae357..45432705 100644 --- a/ranger/api/apps.py +++ b/ranger/api/apps.py @@ -17,7 +17,7 @@ import os, sys, re from ranger.api import * from ranger.ext.iter_tools import flatten from ranger.ext.get_executables import get_executables -from ranger.shared import FileManagerAware +from ranger.core.shared import FileManagerAware class Applications(FileManagerAware): diff --git a/ranger/api/commands.py b/ranger/api/commands.py index f4e2ca76..08751b70 100644 --- a/ranger/api/commands.py +++ b/ranger/api/commands.py @@ -16,7 +16,7 @@ import os from collections import deque from ranger.api import * -from ranger.shared import FileManagerAware +from ranger.core.shared import FileManagerAware from ranger.ext.command_parser import LazyParser as parse diff --git a/ranger/container/settingobject.py b/ranger/container/settingobject.py new file mode 100644 index 00000000..008b846d --- /dev/null +++ b/ranger/container/settingobject.py @@ -0,0 +1,128 @@ +# Copyright (C) 2009, 2010 Roman Zimbelmann +# +# 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 . + +from inspect import isfunction +from ranger.ext.signal_dispatcher import SignalDispatcher +from ranger.core.shared import FileManagerAware + +ALLOWED_SETTINGS = { + 'autosave_bookmarks': bool, + 'collapse_preview': bool, + 'colorscheme_overlay': (type(None), type(lambda:0)), + 'colorscheme': str, + 'column_ratios': (tuple, list, set), + 'dirname_in_tabs': bool, + 'display_size_in_main_column': bool, + 'display_size_in_status_bar': bool, + 'draw_bookmark_borders': bool, + 'draw_borders': bool, + 'flushinput': bool, + 'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'), + 'max_console_history_size': (int, type(None)), + 'max_history_size': (int, type(None)), + 'mouse_enabled': bool, + 'preview_directories': bool, + 'preview_files': bool, + 'save_console_history': bool, + 'scroll_offset': int, + 'shorten_title': int, # Note: False is an instance of int + 'show_cursor': bool, + 'show_hidden_bookmarks': bool, + 'show_hidden': bool, + 'sort_case_insensitive': bool, + 'sort_directories_first': bool, + 'sort_reverse': bool, + 'sort': str, + 'tilde_in_titlebar': bool, + 'update_title': bool, + 'xterm_alt_key': bool, +} + + +class SettingObject(SignalDispatcher, FileManagerAware): + def __init__(self): + SignalDispatcher.__init__(self) + self.__dict__['_settings'] = dict() + self.__dict__['_setting_sources'] = list() + for name in ALLOWED_SETTINGS: + self.signal_bind('setopt.'+name, + self._raw_set_with_signal, priority=0.2) + + def __setattr__(self, name, value): + if name[0] == '_': + self.__dict__[name] = value + else: + assert name in ALLOWED_SETTINGS, "No such setting: {0}!".format(name) + if name not in self._settings: + getattr(self, name) + assert self._check_type(name, value) + kws = dict(setting=name, value=value, + previous=self._settings[name], fm=self.fm) + self.signal_emit('setopt', **kws) + self.signal_emit('setopt.'+name, **kws) + + def __getattr__(self, name): + assert name in ALLOWED_SETTINGS or name in self._settings, \ + "No such setting: {0}!".format(name) + try: + return self._settings[name] + except: + for struct in self._setting_sources: + try: value = getattr(struct, name) + except: pass + else: break + else: + raise Exception("The option `{0}' was not defined" \ + " in the defaults!".format(name)) + assert self._check_type(name, value) + self._raw_set(name, value) + self.__setattr__(name, value) + return self._settings[name] + + def __iter__(self): + for x in self._settings: + yield x + + def types_of(self, name): + try: + typ = ALLOWED_SETTINGS[name] + except KeyError: + return tuple() + else: + if isinstance(typ, tuple): + return typ + else: + return (typ, ) + + + def _check_type(self, name, value): + typ = ALLOWED_SETTINGS[name] + if isfunction(typ): + assert typ(value), \ + "The option `" + name + "' has an incorrect type!" + else: + assert isinstance(value, typ), \ + "The option `" + name + "' has an incorrect type!"\ + " Got " + str(type(value)) + ", expected " + str(typ) + "!" + return True + + __getitem__ = __getattr__ + __setitem__ = __setattr__ + + def _raw_set(self, name, value): + self._settings[name] = value + + def _raw_set_with_signal(self, signal): + self._settings[signal.setting] = signal.value diff --git a/ranger/core/actions.py b/ranger/core/actions.py index a19927a4..59902bab 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -26,7 +26,8 @@ from ranger.ext.direction import Direction from ranger.ext.relative_symlink import relative_symlink from ranger.ext.shell_escape import shell_quote from ranger import fsobject -from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware +from ranger.core.shared import FileManagerAware, EnvironmentAware, \ + SettingsAware from ranger.fsobject import File from ranger.ext import shutil_generatorized as shutil_g from ranger.core.loader import LoadableObject diff --git a/ranger/core/environment.py b/ranger/core/environment.py index 8d4c60df..655054d7 100644 --- a/ranger/core/environment.py +++ b/ranger/core/environment.py @@ -22,7 +22,7 @@ from os.path import abspath, normpath, join, expanduser, isdir from ranger.fsobject import Directory from ranger.container import KeyBuffer, KeyManager, History from ranger.ext.signal_dispatcher import SignalDispatcher -from ranger.shared import SettingsAware +from ranger.core.shared import SettingsAware ALLOWED_CONTEXTS = ('browser', 'pager', 'embedded_pager', 'taskview', 'console') diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 05b3e52b..7f655aa6 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -19,6 +19,7 @@ The File Manager, putting the pieces together from time import time from collections import deque +import mimetypes import os import sys @@ -28,7 +29,6 @@ from ranger.container.tags import Tags from ranger.gui.defaultui import DefaultUI from ranger.container import Bookmarks from ranger.core.runner import Runner -from ranger import relpath_conf from ranger.ext.get_executables import get_executables from ranger.fsobject import Directory from ranger.ext.signal_dispatcher import SignalDispatcher @@ -69,7 +69,7 @@ class FM(Actions, SignalDispatcher): if ranger.arg.clean: bookmarkfile = None else: - bookmarkfile = relpath_conf('bookmarks') + bookmarkfile = self.confpath('bookmarks') self.bookmarks = Bookmarks( bookmarkfile=bookmarkfile, bookmarktype=Directory, @@ -80,7 +80,7 @@ class FM(Actions, SignalDispatcher): self.bookmarks = bookmarks if not ranger.arg.clean and self.tags is None: - self.tags = Tags(relpath_conf('tagged')) + self.tags = Tags(self.confpath('tagged')) if self.ui is None: self.ui = DefaultUI() @@ -93,6 +93,10 @@ class FM(Actions, SignalDispatcher): self.env.signal_bind('cd', self._update_current_tab) + mimetypes.knownfiles.append(os.path.expanduser('~/.mime.types')) + mimetypes.knownfiles.append(self.relpath('data/mime.types')) + self.mimetypes = mimetypes.MimeTypes() + def block_input(self, sec=0): self.input_blocked = sec != 0 self.input_blocked_until = time() + sec @@ -102,6 +106,17 @@ class FM(Actions, SignalDispatcher): self.input_blocked = False return self.input_blocked + def confpath(self, *paths): + """returns the path relative to rangers configuration directory""" + if ranger.arg.clean: + assert 0, "Should not access relpath_conf in clean mode!" + else: + return os.path.join(ranger.arg.confdir, *paths) + + def relpath(self, *paths): + """returns the path relative to rangers library directory""" + return os.path.join(ranger.RANGERDIR, *paths) + def loop(self): """ The main loop consists of: diff --git a/ranger/core/helper.py b/ranger/core/helper.py new file mode 100644 index 00000000..c7ac3702 --- /dev/null +++ b/ranger/core/helper.py @@ -0,0 +1,167 @@ +# Copyright (C) 2009, 2010 Roman Zimbelmann +# +# 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 . + +"""Helper functions""" + +import os.path +import sys +from ranger import * + +def parse_arguments(): + """Parse the program arguments""" + from optparse import OptionParser, SUPPRESS_HELP + from ranger import __version__, USAGE, DEFAULT_CONFDIR + from ranger.ext.openstruct import OpenStruct + from os.path import expanduser + + minor_version = __version__[2:] # assumes major version number is <10 + if '.' in minor_version: + minor_version = minor_version[:minor_version.find('.')] + version_tag = ' (stable)' if int(minor_version) % 2 == 0 else ' (testing)' + if __version__.endswith('.0'): + version_string = 'ranger ' + __version__[:-2] + version_tag + else: + version_string = 'ranger ' + __version__ + version_tag + + parser = OptionParser(usage=USAGE, version=version_string) + + 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. ") + parser.add_option('--fail-if-run', action='store_true', # COMPAT + help=SUPPRESS_HELP) + parser.add_option('--fail-unless-cd', action='store_true', + help="experimental: return the exit code 1 if ranger is" \ + "used to run a file (with `ranger filename`)") + parser.add_option('-r', '--confdir', type='string', + metavar='dir', default=DEFAULT_CONFDIR, + help="the configuration directory. (%default)") + parser.add_option('-m', '--mode', type='int', default=0, metavar='n', + help="if a filename is supplied, run it with this mode") + parser.add_option('-f', '--flags', type='string', default='', + metavar='string', + help="if a filename is supplied, run it with these flags.") + + options, positional = parser.parse_args() + arg = OpenStruct(options.__dict__, targets=positional) + arg.confdir = expanduser(arg.confdir) + if arg.fail_if_run: + arg.fail_unless_cd = arg.fail_if_run + del arg['fail_if_run'] + + return arg + + +def load_settings(fm, clean): + import ranger.core.shared + import ranger.api.commands + import ranger.api.keys + if not clean: + allow_access_to_confdir(ranger.arg.confdir, True) + + # Load commands + comcont = ranger.api.commands.CommandContainer() + ranger.api.commands.alias = comcont.alias + try: + import commands + comcont.load_commands_from_module(commands) + except ImportError: + pass + from ranger.defaults import commands + comcont.load_commands_from_module(commands) + commands = comcont + + # Load apps + try: + import apps + except ImportError: + from ranger.defaults import apps + + # Load keys + keymanager = ranger.core.shared.EnvironmentAware.env.keymanager + ranger.api.keys.keymanager = keymanager + from ranger.defaults import keys + try: + import keys + except ImportError: + pass + allow_access_to_confdir(ranger.arg.confdir, False) + else: + comcont = ranger.api.commands.CommandContainer() + ranger.api.commands.alias = comcont.alias + from ranger.api import keys + keymanager = ranger.core.shared.EnvironmentAware.env.keymanager + ranger.api.keys.keymanager = keymanager + from ranger.defaults import commands, keys, apps + comcont.load_commands_from_module(commands) + commands = comcont + fm.commands = commands + fm.keys = keys + fm.apps = apps.CustomApplications() + + +def load_apps(fm, clean): + import ranger + if not clean: + allow_access_to_confdir(ranger.arg.confdir, True) + try: + import apps + except ImportError: + from ranger.defaults import apps + allow_access_to_confdir(ranger.arg.confdir, False) + else: + from ranger.defaults import apps + fm.apps = apps.CustomApplications() + + +def allow_access_to_confdir(confdir, allow): + if allow: + try: + os.makedirs(confdir) + except OSError as err: + if err.errno != 17: # 17 means it already exists + print("This configuration directory could not be created:") + print(confdir) + print("To run ranger without the need for configuration") + print("files, use the --clean option.") + raise SystemExit() + if not confdir in sys.path: + sys.path[0:0] = [confdir] + else: + if sys.path[0] == confdir: + del sys.path[0] + + +# Debugging functions. These will be activated when run with --debug. +# Example usage in the code: +# import ranger; ranger.log("hello world") +def log(*objects, **keywords): + """ + Writes objects to a logfile (for the purpose of debugging only.) + Has the same arguments as print() in python3. + """ + if LOGFILE is None or not arg.debug 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 log_traceback(): + if LOGFILE is None or not arg.debug or arg.clean: return + import traceback + traceback.print_stack(file=open(LOGFILE, 'a')) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 4f4424e4..ae6436a1 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -15,7 +15,7 @@ from collections import deque from time import time -from ranger.shared import FileManagerAware +from ranger.core.shared import FileManagerAware import math def status_generator(): diff --git a/ranger/core/main.py b/ranger/core/main.py new file mode 100644 index 00000000..42118516 --- /dev/null +++ b/ranger/core/main.py @@ -0,0 +1,98 @@ +# Copyright (C) 2009, 2010 Roman Zimbelmann +# +# 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 . + +""" +The main function responsible to initialize the FM object and stuff. +""" + +from ranger.core.helper import * + +def main(): + """initialize objects and run the filemanager""" + import locale + import os.path + import ranger + from ranger.ext import curses_interrupt_handler + from ranger.core.runner import Runner + from ranger.core.fm import FM + from ranger.core.environment import Environment + from ranger.gui.defaultui import DefaultUI as UI + from ranger.fsobject import File + from ranger.core.shared import (EnvironmentAware, FileManagerAware, + SettingsAware) + + try: + locale.setlocale(locale.LC_ALL, '') + except: + print("Warning: Unable to set locale. Expect encoding problems.") + + if not 'SHELL' in os.environ: + os.environ['SHELL'] = 'bash' + + ranger.arg = arg = parse_arguments() + SettingsAware._setup(clean=arg.clean) + + targets = arg.targets or ['.'] + target = targets[0] + if arg.targets: + if target.startswith('file://'): + target = target[7:] + 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): + def print_function(string): + print(string) + runner = Runner(logfunc=print_function) + load_apps(runner, arg.clean) + runner(files=[File(target)], mode=arg.mode, flags=arg.flags) + sys.exit(1 if arg.fail_unless_cd else 0) + + crash_traceback = None + try: + # Initialize objects + EnvironmentAware.env = Environment(target) + fm = FM() + fm.tabs = dict((n+1, os.path.abspath(path)) for n, path \ + in enumerate(targets[:9])) + load_settings(fm, arg.clean) + if fm.env.username == 'root': + fm.settings.preview_files = False + FileManagerAware.fm = fm + fm.ui = UI() + if not arg.debug: + curses_interrupt_handler.install_interrupt_handler() + + # Run the file manager + fm.initialize() + fm.ui.initialize() + fm.loop() + except Exception: + import traceback + crash_traceback = traceback.format_exc() + except SystemExit as error: + return error.args[0] + finally: + try: + fm.ui.destroy() + except (AttributeError, NameError): + pass + if crash_traceback: + print(crash_traceback) + print("Ranger crashed. " \ + "Please report this (including the traceback) at:") + print("http://savannah.nongnu.org/bugs/?group=ranger&func=additem") + return 1 + return 0 diff --git a/ranger/core/shared.py b/ranger/core/shared.py new file mode 100644 index 00000000..b91445a3 --- /dev/null +++ b/ranger/core/shared.py @@ -0,0 +1,73 @@ +# Copyright (C) 2009, 2010 Roman Zimbelmann +# +# 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 . + +"""Shared objects contain singleton variables which can be +inherited, essentially acting like global variables.""" + +from ranger.ext.lazy_property import lazy_property + +class Awareness(object): + pass + +class EnvironmentAware(Awareness): + # This creates an instance implicitly, mainly for unit tests + @lazy_property + def env(self): + from ranger.core.environment import Environment + return Environment(".") + +class FileManagerAware(Awareness): + # This creates an instance implicitly, mainly for unit tests + @lazy_property + def fm(self): + from ranger.core.fm import FM + return FM() + +class SettingsAware(Awareness): + # This creates an instance implicitly, mainly for unit tests + @lazy_property + def settings(self): + from ranger.ext.openstruct import OpenStruct + return OpenStruct() + + @staticmethod + def _setup(clean=True): + from ranger.container.settingobject import SettingObject, \ + ALLOWED_SETTINGS + import ranger + import sys + settings = SettingObject() + + from ranger.gui.colorscheme import _colorscheme_name_to_class + settings.signal_bind('setopt.colorscheme', + _colorscheme_name_to_class, priority=1) + + if not clean: + # add the custom options to the list of setting sources + sys.path[0:0] = [ranger.arg.confdir] + try: + import options as my_options + except ImportError: + pass + else: + settings._setting_sources.append(my_options) + del sys.path[0] + + from ranger.defaults import options as default_options + settings._setting_sources.append(default_options) + assert all(hasattr(default_options, setting) \ + for setting in ALLOWED_SETTINGS), \ + "Ensure that all options are defined in the defaults!" + SettingsAware.settings = settings diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index ac628ff0..e52b84d7 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -23,7 +23,7 @@ from time import time from ranger.ext.mount_path import mount_path from ranger.fsobject import BAD_INFO, File, FileSystemObject -from ranger.shared import SettingsAware +from ranger.core.shared import SettingsAware from ranger.ext.accumulator import Accumulator import ranger.fsobject diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index a52dac91..fd886275 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -22,7 +22,7 @@ from os import access, listdir, lstat, readlink, stat from time import time from os.path import abspath, basename, dirname, realpath, splitext, extsep from . import BAD_INFO -from ranger.shared import MimeTypeAware, FileManagerAware +from ranger.core.shared import FileManagerAware from ranger.ext.shell_escape import shell_escape from ranger.ext.spawn import spawn from ranger.ext.lazy_property import lazy_property @@ -30,7 +30,7 @@ from ranger.ext.human_readable import human_readable _extract_number_re = re.compile(r'([^0-9]?)(\d*)') -class FileSystemObject(MimeTypeAware, FileManagerAware): +class FileSystemObject(FileManagerAware): (basename, basename_lower, dirname, @@ -70,8 +70,6 @@ class FileSystemObject(MimeTypeAware, FileManagerAware): def __init__(self, path, preload=None, path_is_abs=False): - MimeTypeAware.__init__(self) - if not path_is_abs: path = abspath(path) self.path = path @@ -123,7 +121,7 @@ class FileSystemObject(MimeTypeAware, FileManagerAware): basename = self.basename if self.extension == 'part': basename = basename[0:-5] - self._mimetype = self.mimetypes.guess_type(basename, False)[0] + self._mimetype = self.fm.mimetypes.guess_type(basename, False)[0] if self._mimetype is None: self._mimetype = '' diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py index 5b317acb..ae6aac0b 100644 --- a/ranger/gui/colorscheme.py +++ b/ranger/gui/colorscheme.py @@ -47,8 +47,8 @@ from curses import color_pair import ranger from ranger.gui.color import get_color from ranger.gui.context import Context -from ranger.__main__ import allow_access_to_confdir -from ranger.shared.settings import SettingsAware +from ranger.core.helper import allow_access_to_confdir +from ranger.core.shared import SettingsAware # ColorScheme is not SettingsAware but it will gain access # to the settings during the initialization. We can't import @@ -141,15 +141,15 @@ def _colorscheme_name_to_class(signal): # create ~/.config/ranger/colorschemes/__init__.py if it doesn't exist if usecustom: - if os.path.exists(ranger.relpath_conf('colorschemes')): - initpy = ranger.relpath_conf('colorschemes', '__init__.py') + if os.path.exists(signal.fm.confpath('colorschemes')): + initpy = signal.fm.confpath('colorschemes', '__init__.py') if not os.path.exists(initpy): open(initpy, 'a').close() if usecustom and \ - exists(ranger.relpath_conf('colorschemes', scheme_name)): + exists(signal.fm.confpath('colorschemes', scheme_name)): scheme_supermodule = 'colorschemes' - elif exists(ranger.relpath('colorschemes', scheme_name)): + elif exists(signal.fm.relpath('colorschemes', scheme_name)): scheme_supermodule = 'ranger.colorschemes' usecustom = False else: diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py index 3df45700..4ed348fd 100644 --- a/ranger/gui/curses_shortcuts.py +++ b/ranger/gui/curses_shortcuts.py @@ -16,7 +16,7 @@ import _curses from ranger.ext.iter_tools import flatten -from ranger.shared import SettingsAware +from ranger.core.shared import SettingsAware def ascii_only(string): # Some python versions have problems with invalid unicode strings. diff --git a/ranger/gui/displayable.py b/ranger/gui/displayable.py index 9ca72b13..70455b35 100644 --- a/ranger/gui/displayable.py +++ b/ranger/gui/displayable.py @@ -15,7 +15,7 @@ import _curses -from ranger.shared import FileManagerAware, EnvironmentAware +from ranger.core.shared import FileManagerAware, EnvironmentAware from ranger.gui.curses_shortcuts import CursesShortcuts class Displayable(EnvironmentAware, FileManagerAware, CursesShortcuts): diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 57264292..9f7d2405 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -23,7 +23,6 @@ import re from collections import deque from . import Widget -from ranger import log, relpath_conf from ranger.container.keymap import CommandArgs from ranger.ext.direction import Direction from ranger.ext.utfwidth import uwid, uchars @@ -50,7 +49,7 @@ class Console(Widget): self.history = History(self.settings.max_console_history_size) # load history from files if not ranger.arg.clean: - self.historypath = relpath_conf('history') + self.historypath = self.fm.confpath('history') try: f = open(self.historypath, 'r') except: diff --git a/ranger/help/console.py b/ranger/help/console.py index f03491db..716740b9 100644 --- a/ranger/help/console.py +++ b/ranger/help/console.py @@ -12,6 +12,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + """ 3. The Console diff --git a/ranger/help/invocation.py b/ranger/help/invocation.py index 26cffd4a..27ab5a67 100644 --- a/ranger/help/invocation.py +++ b/ranger/help/invocation.py @@ -12,6 +12,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + """ 5. Ranger invocation diff --git a/ranger/shared/__init__.py b/ranger/shared/__init__.py deleted file mode 100644 index 048b9e7a..00000000 --- a/ranger/shared/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann -# -# 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 . - -"""Shared objects contain singleton variables which can be -inherited, essentially acting like global variables.""" -class Awareness(object): - pass - -class EnvironmentAware(Awareness): - env = None - @staticmethod - def _assign(instance): - EnvironmentAware.env = instance - - -class FileManagerAware(Awareness): - fm = None - @staticmethod - def _assign(instance): - FileManagerAware.fm = instance - -from .mimetype import MimeTypeAware -from .settings import SettingsAware diff --git a/ranger/shared/mimetype.py b/ranger/shared/mimetype.py deleted file mode 100644 index da6fcd10..00000000 --- a/ranger/shared/mimetype.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann -# -# 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 . - -from ranger import relpath -import mimetypes -import os.path - -class MimeTypeAware(object): - mimetypes = {} - def __init__(self): - MimeTypeAware.__init__ = lambda _: None # refuse multiple inits - mimetypes.knownfiles.append(os.path.expanduser('~/.mime.types')) - MimeTypeAware.mimetypes = mimetypes.MimeTypes() - MimeTypeAware.mimetypes.read(relpath('data/mime.types')) diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py deleted file mode 100644 index 7604af12..00000000 --- a/ranger/shared/settings.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright (C) 2009, 2010 Roman Zimbelmann -# -# 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 . - -import sys -from inspect import isfunction -import ranger -from ranger.ext.signal_dispatcher import SignalDispatcher -from ranger.ext.openstruct import OpenStruct - -ALLOWED_SETTINGS = { - 'autosave_bookmarks': bool, - 'collapse_preview': bool, - 'colorscheme_overlay': (type(None), type(lambda:0)), - 'colorscheme': str, - 'column_ratios': (tuple, list, set), - 'dirname_in_tabs': bool, - 'display_size_in_main_column': bool, - 'display_size_in_status_bar': bool, - 'draw_bookmark_borders': bool, - 'draw_borders': bool, - 'flushinput': bool, - 'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'), - 'max_console_history_size': (int, type(None)), - 'max_history_size': (int, type(None)), - 'mouse_enabled': bool, - 'preview_directories': bool, - 'preview_files': bool, - 'save_console_history': bool, - 'scroll_offset': int, - 'shorten_title': int, # Note: False is an instance of int - 'show_cursor': bool, - 'show_hidden_bookmarks': bool, - 'show_hidden': bool, - 'sort_case_insensitive': bool, - 'sort_directories_first': bool, - 'sort_reverse': bool, - 'sort': str, - 'tilde_in_titlebar': bool, - 'update_title': bool, - 'xterm_alt_key': bool, -} - - -class SettingObject(SignalDispatcher): - def __init__(self): - SignalDispatcher.__init__(self) - self.__dict__['_settings'] = dict() - self.__dict__['_setting_sources'] = list() - for name in ALLOWED_SETTINGS: - self.signal_bind('setopt.'+name, - self._raw_set_with_signal, priority=0.2) - - def __setattr__(self, name, value): - if name[0] == '_': - self.__dict__[name] = value - else: - assert name in ALLOWED_SETTINGS, "No such setting: {0}!".format(name) - if name not in self._settings: - getattr(self, name) - assert self._check_type(name, value) - kws = dict(setting=name, value=value, - previous=self._settings[name]) - self.signal_emit('setopt', **kws) - self.signal_emit('setopt.'+name, **kws) - - def __getattr__(self, name): - assert name in ALLOWED_SETTINGS or name in self._settings, \ - "No such setting: {0}!".format(name) - try: - return self._settings[name] - except: - for struct in self._setting_sources: - try: value = getattr(struct, name) - except: pass - else: break - else: - raise Exception("The option `{0}' was not defined" \ - " in the defaults!".format(name)) - assert self._check_type(name, value) - self._raw_set(name, value) - self.__setattr__(name, value) - return self._settings[name] - - def __iter__(self): - for x in self._settings: - yield x - - def types_of(self, name): - try: - typ = ALLOWED_SETTINGS[name] - except KeyError: - return tuple() - else: - if isinstance(typ, tuple): - return typ - else: - return (typ, ) - - - def _check_type(self, name, value): - typ = ALLOWED_SETTINGS[name] - if isfunction(typ): - assert typ(value), \ - "The option `" + name + "' has an incorrect type!" - else: - assert isinstance(value, typ), \ - "The option `" + name + "' has an incorrect type!"\ - " Got " + str(type(value)) + ", expected " + str(typ) + "!" - return True - - __getitem__ = __getattr__ - __setitem__ = __setattr__ - - def _raw_set(self, name, value): - self._settings[name] = value - - def _raw_set_with_signal(self, signal): - self._settings[signal.setting] = signal.value - - -# -- globalize the settings -- -class SettingsAware(object): - settings = OpenStruct() - - @staticmethod - def _setup(): - settings = SettingObject() - - from ranger.gui.colorscheme import _colorscheme_name_to_class - settings.signal_bind('setopt.colorscheme', - _colorscheme_name_to_class, priority=1) - - if not ranger.arg.clean: - # overwrite single default options with custom options - sys.path[0:0] = [ranger.arg.confdir] - try: - import options as my_options - except ImportError: - pass - else: - settings._setting_sources.append(my_options) - del sys.path[0] - - from ranger.defaults import options as default_options - settings._setting_sources.append(default_options) - assert all(hasattr(default_options, setting) \ - for setting in ALLOWED_SETTINGS), \ - "Ensure that all options are defined in the defaults!" - - SettingsAware.settings = settings diff --git a/setup.py b/setup.py index 587b52c0..e63e28d2 100755 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ if __name__ == '__main__': distutils.core.setup( name='ranger', description='Vim-like file manager', + long_description=ranger.__doc__, version=ranger.__version__, author=ranger.__author__, author_email=ranger.__email__, @@ -39,5 +40,4 @@ if __name__ == '__main__': 'ranger.fsobject', 'ranger.gui', 'ranger.gui.widgets', - 'ranger.help', - 'ranger.shared')) + 'ranger.help')) diff --git a/test/tc_directory.py b/test/tc_directory.py index 754253b3..a43ac89d 100644 --- a/test/tc_directory.py +++ b/test/tc_directory.py @@ -24,7 +24,7 @@ from os.path import realpath, join, dirname from ranger import fsobject from ranger.fsobject.file import File from ranger.fsobject.directory import Directory -from ranger.shared.settings import SettingsAware +from ranger.core.shared import SettingsAware SettingsAware._setup() @@ -49,7 +49,7 @@ class Test1(unittest.TestCase): import os # Check whether the directory has the correct list of filenames. dir = Directory(TESTDIR) - dir.load_content() + dir.load_content(schedule=False) self.assertTrue(dir.exists) self.assertEqual(type(dir.filenames), list) @@ -78,8 +78,8 @@ class Test1(unittest.TestCase): def test_nonexistant_dir(self): dir = Directory(NONEXISTANT_DIR) - dir.load_content() - + dir.load_content(schedule=False) + self.assertTrue(dir.content_loaded) self.assertFalse(dir.exists) self.assertFalse(dir.accessible) diff --git a/test/tc_loader.py b/test/tc_loader.py index 5a2e5a68..a679a629 100644 --- a/test/tc_loader.py +++ b/test/tc_loader.py @@ -24,7 +24,7 @@ import os from os.path import realpath, join, dirname from testlib import Fake -from ranger.shared import FileManagerAware, SettingsAware +from ranger.core.shared import FileManagerAware, SettingsAware from ranger.core.loader import Loader from ranger.fsobject import Directory, File from ranger.ext.openstruct import OpenStruct -- cgit 1.4.1-2-gfad0