From b6aff4c3bfa284df76d055c665b3b0675a7ef26c Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 25 Jan 2010 00:30:24 +0100 Subject: made configuration more simple --- README | 8 +-- ranger/api/__init__.py | 4 ++ ranger/api/apps.py | 130 ++++++++++++++++++++++++++++++++++++++++++++ ranger/api/keys.py | 132 +++++++++++++++++++++++++++++++++++++++++++++ ranger/api/options.py | 17 ++++++ ranger/applications.py | 130 -------------------------------------------- ranger/defaults/apps.py | 2 +- ranger/defaults/keys.py | 2 +- ranger/defaults/options.py | 7 +-- ranger/keyapi.py | 132 --------------------------------------------- ranger/shared/settings.py | 16 +++++- 11 files changed, 303 insertions(+), 277 deletions(-) create mode 100644 ranger/api/__init__.py create mode 100644 ranger/api/apps.py create mode 100644 ranger/api/keys.py create mode 100644 ranger/api/options.py delete mode 100644 ranger/applications.py delete mode 100644 ranger/keyapi.py diff --git a/README b/README index 764ab57e..352fbaee 100644 --- a/README +++ b/README @@ -83,12 +83,8 @@ apps.py defines how files are run, keys.py defines keybindings. The files in ranger/defaults/ can be copied into ~/.ranger/ for per-user modifications. Colorschemes can be placed in ~/.ranger/colorschemes. -The options.py defines the import path of "apps", "keys" and the -colorscheme. To use the files you placed in ~/.ranger/, you may need -to make this changes in the options.py: - -"from ranger.defaults import apps, keys" => "import apps, keys" -"from ranger import colorschemes" => "import colorschemes" +The configuration files should be self-explanatory. If you need more +information, check out the source code. == Guidelines for developers: diff --git a/ranger/api/__init__.py b/ranger/api/__init__.py new file mode 100644 index 00000000..cc64a7c0 --- /dev/null +++ b/ranger/api/__init__.py @@ -0,0 +1,4 @@ +""" +Files in this module contain helper functions used in +configuration files. +""" diff --git a/ranger/api/apps.py b/ranger/api/apps.py new file mode 100644 index 00000000..c01e13a7 --- /dev/null +++ b/ranger/api/apps.py @@ -0,0 +1,130 @@ +# Copyright (c) 2009, 2010 hut +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +This module provides helper functions/classes for ranger.defaults.apps. +""" + +import os, sys +from subprocess import Popen, PIPE +from ranger.ext.iter_tools import flatten +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 + + 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 + simply wants to "start" the file without specific instructions. + In such a case, app_default() is called, where you should examine + the context and decide which program to use. + + All app functions have a name starting with app_ and return a string + containing the whole command or a tuple containing a list of the + arguments. They are supplied with one argument, which is the + AppContext instance. + + You should define at least app_default, app_pager and app_editor since + internal functions depend on those. Here are sample implementations: + + def app_default(self, context): + if context.file.media: + if context.file.video: + # detach videos from the filemanager + context.flags += 'd' + return self.app_mplayer(context) + else: + return self.app_editor(context) + + def app_pager(self, context): + return ('less', ) + tuple(context) + + def app_editor(self, context): + return ('vim', ) + tuple(context) + """ + + def _meets_dependencies(self, fnc): + try: + deps = fnc.dependencies + except AttributeError: + return True + + for dep in deps: + if hasattr(dep, 'dependencies') \ + and not self._meets_dependencies(dep): + return False + if dep not in self.fm.executables: + return False + + return True + + def either(self, context, *args): + for app in args: + try: + application_handler = getattr(self, 'app_' + app) + except AttributeError: + continue + if self._meets_dependencies(application_handler): + return application_handler(context) + + def app_self(self, context): + """Run the file itself""" + return "./" + context.file.basename + + def get(self, app): + """Looks for an application, returns app_default if it doesn't exist""" + try: + return getattr(self, 'app_' + app) + except AttributeError: + return self.app_default + + def apply(self, app, context): + if not app: + app = 'default' + try: + handler = getattr(self, 'app_' + app) + except AttributeError: + handler = self.app_default + return handler(context) + + def has(self, app): + """Returns whether an application is defined""" + return hasattr(self, 'app_' + app) + + def all(self): + """Returns a list with all application functions""" + methods = self.__class__.__dict__ + return [meth[4:] for meth in methods if meth.startswith('app_')] + + +def tup(*args): + """ + This helper function creates a tuple out of the arguments. + + ('a', ) + tuple(some_iterator) + is equivalent to: + tup('a', *some_iterator) + """ + return args + + +def depends_on(*args): + args = tuple(flatten(args)) + def decorator(fnc): + fnc.dependencies = args + return fnc + return decorator diff --git a/ranger/api/keys.py b/ranger/api/keys.py new file mode 100644 index 00000000..308fab2b --- /dev/null +++ b/ranger/api/keys.py @@ -0,0 +1,132 @@ +# Copyright (c) 2009, 2010 hut +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import os +from curses import * +from curses.ascii import * +from inspect import getargspec, ismethod + +from ranger import RANGERDIR +from ranger.gui.widgets import console_mode as cmode +from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS + +def make_abbreviations(command_list): + def bind(*args): + lastarg = args[-1] + if hasattr(lastarg, '__call__'): + # do the binding + command_list.bind(lastarg, *args[:-1]) + else: + # act as a decorator. eg: + # @bind('a') + # def do_stuff(arg): + # arg.fm.ui.do_stuff() + # + # is equivalent to: + # bind('a', lambda arg: arg.fm.ui.do_stuff()) + return lambda fnc: command_list.bind(fnc, *args) + + def hint(*args): + command_list.hint(args[-1], *args[:-1]) + + def alias(*args): + command_list.alias(*args) + + return bind, hint, alias + +class Wrapper(object): + def __init__(self, firstattr): + self.__firstattr__ = firstattr + + def __getattr__(self, attr): + if attr.startswith('_'): + raise AttributeError + def wrapper(*real_args, **real_keywords): + def function(command_argument): + args, kws = real_args, real_keywords + number = command_argument.n + obj = getattr(command_argument, self.__firstattr__) + fnc = getattr(obj, attr) + if number is not None: + args, kws = replace_narg(number, fnc, args, kws) + return fnc(*args, **kws) + return function + return wrapper + +# fm.enter_dir('~') is translated into lambda arg: arg.fm.enter_dir('~') +# this makes things like this possible: +# bind('gh', fm.enter_dir('~')) +# +# but NOT: (note the 2 dots) +# bind('H', fm.history.go(-1)) +# +# for something like that, use the long version: +# bind('H', lambda arg: arg.fm.history.go(-1)) +# +# If the method has an argument named "narg", pressing a number before +# the key will pass that number as the narg argument. If you want the +# same behaviour in a custom lambda function, you can write: +# bind('gg', fm.move_pointer(absolute=0)) +# as: +# bind('gg', lambda arg: narg(arg.n, arg.fm.move_pointer, absolute=0)) + +fm = Wrapper('fm') +wdg = Wrapper('wdg') + + +NARG_KEYWORD = 'narg' + +def narg(number_, function_, *args_, **keywords_): + """ + This applies the replace_narg function to the arguments and keywords + and directly runs this function. + + Example: + def foo(xyz, narg): return hash((xyz, narg)) + + narg(50, foo, 123) == foo(123, narg=50) + """ + args, keywords = replace_narg(number_, function_, args_, keywords_) + print(args, keywords) + return function_(*args, **keywords) + +def replace_narg(number, function, args, keywords): + """ + This function returns (args, keywords) with one little change: + if has a named argument called "narg", args and keywords + will be modified so that the value of "narg" will be . + + def foo(xyz, narg): pass + + replace_narg(666, foo, (), {'narg': 10, 'xyz': 5}) + => (), {'narg': 666, 'xyz': 5} + + replace_narg(666, foo, (1, 2), {}) + => (1, 666), {} + """ + argspec = getargspec(function).args + if NARG_KEYWORD in argspec: + try: + # is narg in args? + args = list(args) + index = argspec.index(NARG_KEYWORD) + if ismethod(function): + index -= 1 # because of 'self' + args[index] = number + except (ValueError, IndexError): + # is narg in keywords? + keywords = dict(keywords) + keywords[NARG_KEYWORD] = number + return args, keywords + diff --git a/ranger/api/options.py b/ranger/api/options.py new file mode 100644 index 00000000..210e7f61 --- /dev/null +++ b/ranger/api/options.py @@ -0,0 +1,17 @@ +# Copyright (c) 2009, 2010 hut +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import re +from re import compile as regexp +from ranger import colorschemes diff --git a/ranger/applications.py b/ranger/applications.py deleted file mode 100644 index c01e13a7..00000000 --- a/ranger/applications.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) 2009, 2010 hut -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -""" -This module provides helper functions/classes for ranger.defaults.apps. -""" - -import os, sys -from subprocess import Popen, PIPE -from ranger.ext.iter_tools import flatten -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 - - 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 - simply wants to "start" the file without specific instructions. - In such a case, app_default() is called, where you should examine - the context and decide which program to use. - - All app functions have a name starting with app_ and return a string - containing the whole command or a tuple containing a list of the - arguments. They are supplied with one argument, which is the - AppContext instance. - - You should define at least app_default, app_pager and app_editor since - internal functions depend on those. Here are sample implementations: - - def app_default(self, context): - if context.file.media: - if context.file.video: - # detach videos from the filemanager - context.flags += 'd' - return self.app_mplayer(context) - else: - return self.app_editor(context) - - def app_pager(self, context): - return ('less', ) + tuple(context) - - def app_editor(self, context): - return ('vim', ) + tuple(context) - """ - - def _meets_dependencies(self, fnc): - try: - deps = fnc.dependencies - except AttributeError: - return True - - for dep in deps: - if hasattr(dep, 'dependencies') \ - and not self._meets_dependencies(dep): - return False - if dep not in self.fm.executables: - return False - - return True - - def either(self, context, *args): - for app in args: - try: - application_handler = getattr(self, 'app_' + app) - except AttributeError: - continue - if self._meets_dependencies(application_handler): - return application_handler(context) - - def app_self(self, context): - """Run the file itself""" - return "./" + context.file.basename - - def get(self, app): - """Looks for an application, returns app_default if it doesn't exist""" - try: - return getattr(self, 'app_' + app) - except AttributeError: - return self.app_default - - def apply(self, app, context): - if not app: - app = 'default' - try: - handler = getattr(self, 'app_' + app) - except AttributeError: - handler = self.app_default - return handler(context) - - def has(self, app): - """Returns whether an application is defined""" - return hasattr(self, 'app_' + app) - - def all(self): - """Returns a list with all application functions""" - methods = self.__class__.__dict__ - return [meth[4:] for meth in methods if meth.startswith('app_')] - - -def tup(*args): - """ - This helper function creates a tuple out of the arguments. - - ('a', ) + tuple(some_iterator) - is equivalent to: - tup('a', *some_iterator) - """ - return args - - -def depends_on(*args): - args = tuple(flatten(args)) - def decorator(fnc): - fnc.dependencies = args - return fnc - return decorator diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 9c1ae5c3..3d54b2c1 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -14,7 +14,7 @@ import os from re import compile, VERBOSE -from ranger.applications import * +from ranger.api.apps import * INTERPRETED_LANGUAGES = compile(r''' ^(text|application)\/x-( diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 211c0d2d..7f508d03 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -32,7 +32,7 @@ arg.keybuffer: the keybuffer instance Check ranger.keyapi for more information """ -from ranger.keyapi import * +from ranger.api.keys import * def _vimlike_aliases(command_list): bind, hint, alias = make_abbreviations(command_list) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index e5ba8679..d2df0b05 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -12,9 +12,7 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -from ranger.defaults import apps, keys -from ranger import colorschemes -import re +from ranger.api.options import * colorscheme = colorschemes.default @@ -32,5 +30,4 @@ show_hidden = False collapse_preview = True autosave_bookmarks = True -hidden_filter = re.compile( \ - r'^\.|~$|\.(:?pyc|pyo|bak|swp)$') +hidden_filter = regexp(r'^\.|~$|\.(:?pyc|pyo|bak|swp)$') diff --git a/ranger/keyapi.py b/ranger/keyapi.py deleted file mode 100644 index 308fab2b..00000000 --- a/ranger/keyapi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) 2009, 2010 hut -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import os -from curses import * -from curses.ascii import * -from inspect import getargspec, ismethod - -from ranger import RANGERDIR -from ranger.gui.widgets import console_mode as cmode -from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS - -def make_abbreviations(command_list): - def bind(*args): - lastarg = args[-1] - if hasattr(lastarg, '__call__'): - # do the binding - command_list.bind(lastarg, *args[:-1]) - else: - # act as a decorator. eg: - # @bind('a') - # def do_stuff(arg): - # arg.fm.ui.do_stuff() - # - # is equivalent to: - # bind('a', lambda arg: arg.fm.ui.do_stuff()) - return lambda fnc: command_list.bind(fnc, *args) - - def hint(*args): - command_list.hint(args[-1], *args[:-1]) - - def alias(*args): - command_list.alias(*args) - - return bind, hint, alias - -class Wrapper(object): - def __init__(self, firstattr): - self.__firstattr__ = firstattr - - def __getattr__(self, attr): - if attr.startswith('_'): - raise AttributeError - def wrapper(*real_args, **real_keywords): - def function(command_argument): - args, kws = real_args, real_keywords - number = command_argument.n - obj = getattr(command_argument, self.__firstattr__) - fnc = getattr(obj, attr) - if number is not None: - args, kws = replace_narg(number, fnc, args, kws) - return fnc(*args, **kws) - return function - return wrapper - -# fm.enter_dir('~') is translated into lambda arg: arg.fm.enter_dir('~') -# this makes things like this possible: -# bind('gh', fm.enter_dir('~')) -# -# but NOT: (note the 2 dots) -# bind('H', fm.history.go(-1)) -# -# for something like that, use the long version: -# bind('H', lambda arg: arg.fm.history.go(-1)) -# -# If the method has an argument named "narg", pressing a number before -# the key will pass that number as the narg argument. If you want the -# same behaviour in a custom lambda function, you can write: -# bind('gg', fm.move_pointer(absolute=0)) -# as: -# bind('gg', lambda arg: narg(arg.n, arg.fm.move_pointer, absolute=0)) - -fm = Wrapper('fm') -wdg = Wrapper('wdg') - - -NARG_KEYWORD = 'narg' - -def narg(number_, function_, *args_, **keywords_): - """ - This applies the replace_narg function to the arguments and keywords - and directly runs this function. - - Example: - def foo(xyz, narg): return hash((xyz, narg)) - - narg(50, foo, 123) == foo(123, narg=50) - """ - args, keywords = replace_narg(number_, function_, args_, keywords_) - print(args, keywords) - return function_(*args, **keywords) - -def replace_narg(number, function, args, keywords): - """ - This function returns (args, keywords) with one little change: - if has a named argument called "narg", args and keywords - will be modified so that the value of "narg" will be . - - def foo(xyz, narg): pass - - replace_narg(666, foo, (), {'narg': 10, 'xyz': 5}) - => (), {'narg': 666, 'xyz': 5} - - replace_narg(666, foo, (1, 2), {}) - => (1, 666), {} - """ - argspec = getargspec(function).args - if NARG_KEYWORD in argspec: - try: - # is narg in args? - args = list(args) - index = argspec.index(NARG_KEYWORD) - if ismethod(function): - index -= 1 # because of 'self' - args[index] = number - except (ValueError, IndexError): - # is narg in keywords? - keywords = dict(keywords) - keywords[NARG_KEYWORD] = number - return args, keywords - diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index ac024494..2a86e052 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -19,7 +19,6 @@ preview_files max_history_size colorscheme collapse_preview hidden_filter flushinput max_dirsize_for_autopreview autosave_bookmarks -apps keys """.split() # -- globalize the settings -- @@ -41,10 +40,21 @@ class SettingsAware(object): if hasattr(custom_options, setting): setattr(options, setting, getattr(custom_options, setting)) elif not hasattr(options, setting): - raise Exception("Following option was not defined: " + setting) + raise Exception("This option was not defined: " + setting) except ImportError: pass + try: + import apps + except ImportError: + from ranger.defaults import apps + + try: + import keys + except ImportError: + from ranger.defaults import keys + + # If a module is specified as the colorscheme, replace it with one # valid colorscheme inside that module. @@ -68,3 +78,5 @@ class SettingsAware(object): for setting in ALLOWED_SETTINGS: SettingsAware.settings[setting] = getattr(options, setting) + SettingsAware.settings.keys = keys + SettingsAware.settings.apps = apps -- cgit 1.4.1-2-gfad0