From 1621a40c0e268929fff97c79537db709dd2b3fc6 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 24 Jun 2010 00:16:01 +0200 Subject: ranger.main: ranger dir1 dir2 [...] opens multiple tabs --- ranger/__main__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ranger/__main__.py b/ranger/__main__.py index ac6b2362..b3a41776 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -22,7 +22,7 @@ # convenient exception handling in ranger.py (ImportError) import locale -import os +import os.path import sys def parse_arguments(): @@ -181,8 +181,9 @@ def main(): SettingsAware._setup() + targets = arg.targets or ['.'] + target = targets[0] if arg.targets: - target = arg.targets[0] if target.startswith('file://'): target = target[7:] if not os.access(target, os.F_OK): @@ -195,16 +196,14 @@ def main(): 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) - else: - path = target - else: - path = '.' crash_traceback = None try: # Initialize objects - EnvironmentAware._assign(Environment(path)) + 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) FileManagerAware._assign(fm) fm.ui = UI() -- cgit 1.4.1-2-gfad0 From df2e1f7df4296e32f9597f6a78eac27f544ca12b Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 24 Jun 2010 20:31:41 +0200 Subject: Makefile: simplification --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 57db7b11..1b26e117 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # along with this program. If not, see . NAME = ranger -VERSION = $(shell cat README | grep -m 1 -o '[0-9][0-9.]\+') +VERSION = $(shell grep -m 1 -o '[0-9][0-9.]\+' README) SNAPSHOT_NAME ?= $(NAME)-$(VERSION)-$(shell git rev-parse HEAD | cut -b 1-8).tar.gz # Find suitable python version (need python >= 2.6 or 3.1): PYTHON ?= $(shell python -c 'import sys; sys.exit(sys.version < "2.6")' && \ -- cgit 1.4.1-2-gfad0 From 17fff39348d1797e7b0fc3ab1589a60546785f1d Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 24 Jun 2010 20:32:46 +0200 Subject: README: formatting, cleanup --- README | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index 562dfbdf..ee8b40d6 100644 --- a/README +++ b/README @@ -5,9 +5,10 @@ Ranger is a free console file manager that gives you greater flexibility and a good overview of your files without having to leave your *nix console. It visualizes the directory tree in two dimensions: the directory hierarchy on one, lists of files on the other, with a preview to the right so you know -where you'll be going. The default keys are similar to those of Vim, Emacs -and Midnight Commander, though Ranger is easily controllable with just the -arrow keys or the mouse. +where you'll be going. + +The default keys are similar to those of Vim, Emacs and Midnight Commander, +though Ranger is easily controllable with just the arrow keys or the mouse. The program is written in Python (2.6 or 3.1) and uses curses for the text-based user interface. @@ -19,7 +20,6 @@ About * Author: Roman Zimbelmann * Website: http://savannah.nongnu.org/projects/ranger * License: GNU General Public License Version 3 -* Version: 1.1.1 * Download URL of the newest stable version: http://git.savannah.gnu.org/cgit/ranger.git/snapshot/ranger-stable.tar.gz -- cgit 1.4.1-2-gfad0 From 20ab9343ae45320eb29f96ddb66b30148be2aa7f Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 24 Jun 2010 20:33:05 +0200 Subject: README: added design goals section --- README | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README b/README index ee8b40d6..120e7860 100644 --- a/README +++ b/README @@ -28,6 +28,15 @@ http://git.savannah.gnu.org/cgit/ranger.git/snapshot/ranger-stable.tar.gz git clone http://git.sv.gnu.org/r/ranger.git +Design Goals +------------ + +* An easily maintainable file manager in a high level language +* A quick way to switch directories and browse the file system +* Keep it small but useful, do one thing and do it well +* Console based, with smooth integration into the unix shell + + Features -------- -- cgit 1.4.1-2-gfad0 From 7bc8b3fc32b44a8db8bfb321423a1bb7718350ab Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 24 Jun 2010 22:41:20 +0200 Subject: ext.human_readable: more efficient implementation plus unit tests and benchmark. --- ranger/ext/human_readable.py | 53 ++++++++++++++++++++++++++++---------------- test/bm_human_readable.py | 45 +++++++++++++++++++++++++++++++++++++ test/tc_human_readable.py | 41 ++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 test/bm_human_readable.py create mode 100644 test/tc_human_readable.py diff --git a/ranger/ext/human_readable.py b/ranger/ext/human_readable.py index beeaf6d3..35dbc35e 100644 --- a/ranger/ext/human_readable.py +++ b/ranger/ext/human_readable.py @@ -13,24 +13,39 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import math - -ONE_KB = 1024 -UNITS = 'BKMGTP' -MAX_EXPONENT = len(UNITS) - 1 - def human_readable(byte, seperator=' '): - if not byte: - return '0' - - exponent = int(math.log(byte, 2) / 10) - flt = round(float(byte) / (1 << (10 * exponent)), 2) + """ + Convert a large number of bytes to an easily readable format. - if exponent > MAX_EXPONENT: - return '>9000' # off scale - - if int(flt) == flt: - return '%.0f%s%s' % (flt, seperator, UNITS[exponent]) - - else: - return '%.2f%s%s' % (flt, seperator, UNITS[exponent]) + >>> human_readable(54) + "54 B" + >>> human_readable(1500) + "1.46 K" + >>> human_readable(2 ** 20 * 1023) + "1023 M" + """ + if byte <= 0: + return '0' + if byte < 2**10: + return '%d%sB' % (byte, seperator) + if byte < 2**10 * 1000: + return '%.3g%sK' % (byte / 2**10.0, seperator) + if byte < 2**20: + return '%.4g%sK' % (byte / 2**10.0, seperator) + if byte < 2**20 * 1000: + return '%.3g%sM' % (byte / 2**20.0, seperator) + if byte < 2**30: + return '%.4g%sM' % (byte / 2**20.0, seperator) + if byte < 2**30 * 1000: + return '%.3g%sG' % (byte / 2**30.0, seperator) + if byte < 2**40: + return '%.4g%sG' % (byte / 2**30.0, seperator) + if byte < 2**40 * 1000: + return '%.3g%sT' % (byte / 2**40.0, seperator) + if byte < 2**50: + return '%.4g%sT' % (byte / 2**40.0, seperator) + if byte < 2**50 * 1000: + return '%.3g%sP' % (byte / 2**50.0, seperator) + if byte < 2**60: + return '%.4g%sP' % (byte / 2**50.0, seperator) + return '>9000' diff --git a/test/bm_human_readable.py b/test/bm_human_readable.py new file mode 100644 index 00000000..83f2a057 --- /dev/null +++ b/test/bm_human_readable.py @@ -0,0 +1,45 @@ +# 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.ext.human_readable import * + +# The version before 2010/06/24: +import math +UNITS = 'BKMGTP' +MAX_EXPONENT = len(UNITS) - 1 +def human_readable_old(byte, seperator=' '): + if not byte: + return '0' + + exponent = int(math.log(byte, 2) / 10) + flt = round(float(byte) / (1 << (10 * exponent)), 2) + + if exponent > MAX_EXPONENT: + return '>9000' # off scale + + if int(flt) == flt: + return '%.0f%s%s' % (flt, seperator, UNITS[exponent]) + + else: + return '%.2f%s%s' % (flt, seperator, UNITS[exponent]) + +class benchmark_human_readable(object): + def bm_current(self, n): + for i in range(n): + human_readable((128 * i) % 2**50) + + def bm_old(self, n): + for i in range(n): + human_readable_old((128 * i) % 2**50) diff --git a/test/tc_human_readable.py b/test/tc_human_readable.py new file mode 100644 index 00000000..50fc80a1 --- /dev/null +++ b/test/tc_human_readable.py @@ -0,0 +1,41 @@ +# 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 unittest +from ranger.ext.human_readable import human_readable as hr + +class HumanReadableTest(unittest.TestCase): + def test_basic(self): + self.assertEqual("0", hr(0)) + self.assertEqual("1 B", hr(1)) + self.assertEqual("1 K", hr(2 ** 10)) + self.assertEqual("1 M", hr(2 ** 20)) + self.assertEqual("1 G", hr(2 ** 30)) + self.assertEqual(">9000", hr(2 ** 100)) + + def test_big(self): + self.assertEqual("1023 G", hr(2 ** 30 * 1023)) + self.assertEqual("1024 G", hr(2 ** 40 - 1)) + self.assertEqual("1 T", hr(2 ** 40)) + + def test_small(self): + self.assertEqual("1000 B", hr(1000)) + self.assertEqual("1.66 M", hr(1.66 * 2 ** 20)) + self.assertEqual("1.46 K", hr(1500)) + self.assertEqual("1.5 K", hr(2 ** 10 + 2 ** 9)) + self.assertEqual("1.5 K", hr(2 ** 10 + 2 ** 9 - 1)) + +if __name__ == '__main__': + unittest.main() -- cgit 1.4.1-2-gfad0 From 40763055c6c1995271acffe6ba5928893940f88b Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 25 Jun 2010 14:57:12 +0200 Subject: tc_human_readable: additional testcase (which fails) --- test/tc_human_readable.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tc_human_readable.py b/test/tc_human_readable.py index 50fc80a1..b931ba21 100644 --- a/test/tc_human_readable.py +++ b/test/tc_human_readable.py @@ -37,5 +37,9 @@ class HumanReadableTest(unittest.TestCase): self.assertEqual("1.5 K", hr(2 ** 10 + 2 ** 9)) self.assertEqual("1.5 K", hr(2 ** 10 + 2 ** 9 - 1)) + def test_no_exponent(self): + for i in range(2 ** 10, 2 ** 20, 512): + self.assertTrue('e' not in hr(i), "%d => %s" % (i, hr(i))) + if __name__ == '__main__': unittest.main() -- cgit 1.4.1-2-gfad0 From 643c016cf21dbf8e8dd3ff168ae5c4d0da7b8e9b Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 25 Jun 2010 15:05:55 +0200 Subject: ext.human_readable: fixed --- ranger/ext/human_readable.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ranger/ext/human_readable.py b/ranger/ext/human_readable.py index 35dbc35e..40111b6d 100644 --- a/ranger/ext/human_readable.py +++ b/ranger/ext/human_readable.py @@ -28,23 +28,23 @@ def human_readable(byte, seperator=' '): return '0' if byte < 2**10: return '%d%sB' % (byte, seperator) - if byte < 2**10 * 1000: + if byte < 2**10 * 999: return '%.3g%sK' % (byte / 2**10.0, seperator) if byte < 2**20: return '%.4g%sK' % (byte / 2**10.0, seperator) - if byte < 2**20 * 1000: + if byte < 2**20 * 999: return '%.3g%sM' % (byte / 2**20.0, seperator) if byte < 2**30: return '%.4g%sM' % (byte / 2**20.0, seperator) - if byte < 2**30 * 1000: + if byte < 2**30 * 999: return '%.3g%sG' % (byte / 2**30.0, seperator) if byte < 2**40: return '%.4g%sG' % (byte / 2**30.0, seperator) - if byte < 2**40 * 1000: + if byte < 2**40 * 999: return '%.3g%sT' % (byte / 2**40.0, seperator) if byte < 2**50: return '%.4g%sT' % (byte / 2**40.0, seperator) - if byte < 2**50 * 1000: + if byte < 2**50 * 999: return '%.3g%sP' % (byte / 2**50.0, seperator) if byte < 2**60: return '%.4g%sP' % (byte / 2**50.0, seperator) -- cgit 1.4.1-2-gfad0 From fb05609637209fbbef26b09705378d03f335d770 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 30 Jun 2010 10:12:46 +0200 Subject: Fixed default keyword argument value "mode" for fm.open_console --- ranger/core/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 3c9f6135..008a89fe 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -69,7 +69,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): """Redraw the window""" self.ui.redraw_window() - def open_console(self, mode=':', string='', prompt=None): + def open_console(self, mode=cmode.COMMAND, string='', prompt=None): """Open the console if the current UI supports that""" if hasattr(self.ui, 'open_console'): self.ui.open_console(mode, string, prompt=prompt) -- cgit 1.4.1-2-gfad0 From c9f479ab1a2f8df056a27c388ec2a9791b8bca8f Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 30 Jun 2010 12:51:13 +0200 Subject: core.actions: added "execute_console" --- ranger/core/actions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 008a89fe..911f5ca4 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -74,6 +74,12 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): if hasattr(self.ui, 'open_console'): self.ui.open_console(mode, string, prompt=prompt) + def execute_console(self, string='', mode=cmode.COMMAND): + """Execute a command for the console""" + self.open_console(mode=mode, string=string) + self.ui.console.line = string + self.ui.console.execute() + def execute_file(self, files, **kw): """Execute a file. app is the name of a method in Applications, without the "app_" -- cgit 1.4.1-2-gfad0 From e0e8c588e3e3ee5b9c74f323573cb7e95ef74d9d Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 5 Jul 2010 01:54:12 +0200 Subject: defaults.keys: improved "du" keybinding --- ranger/defaults/keys.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index c95baed9..ce595be6 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -181,7 +181,8 @@ map('ud', 'uy', fm.uncut()) # ---------------------------------------------------- run programs map('S', fm.execute_command(os.environ['SHELL'])) map('E', fm.edit_file()) -map('du', fm.execute_command('du --max-depth=1 -h | less')) +map('du', fm.execute_console('p!du --max-depth=1 -h --apparent-size', + cmode.OPEN)) # -------------------------------------------------- toggle options map('z', fm.hint("[*cdfhimpPs*] show_*h*idden *p*review_files "\ -- cgit 1.4.1-2-gfad0 From 9001827456388e40f36393a9f44d6361e53210ed Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 12 Jul 2010 20:09:58 +0200 Subject: Why did the astrophysicist order three hamburgers? --- CHANGELOG | 6 ++++++ README | 2 +- doc/ranger.1 | 2 +- ranger/__init__.py | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 578717d4..3b2ee97a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +1.1.1 -> 1.1.2: +* Fix crash when using scrollwheel to scroll down in some cases +* The command "ranger dir1 dir2 ..." opens multiple directories in tabs +* Removed pydoc html documentation by default, re-create it with "make doc" +* Minor fixes + 1.1.0 -> 1.1.1: * New install script, "setup.py" * New flag for running programs: "w" (waits for enter press) diff --git a/README b/README index 120e7860..dad70262 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Ranger v.1.1.1 +Ranger v.1.1.2 ============== Ranger is a free console file manager that gives you greater flexibility diff --git a/doc/ranger.1 b/doc/ranger.1 index dd343f28..3db90285 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.TH RANGER 1 ranger-1.1.1 +.TH RANGER 1 ranger-1.1.2 .SH NAME ranger - visual file manager .\"----------------------------------------- diff --git a/ranger/__init__.py b/ranger/__init__.py index 4fcc2ecf..e0e8e1bf 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -20,7 +20,7 @@ import sys from ranger.ext.openstruct import OpenStruct __license__ = 'GPL3' -__version__ = '1.1.1' +__version__ = '1.1.2' __credits__ = 'Roman Zimbelmann' __author__ = 'Roman Zimbelmann' __maintainer__ = 'Roman Zimbelmann' -- cgit 1.4.1-2-gfad0 From 8b9099835d30fc155ba6e810e75b45fff027b630 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 17 Jul 2010 02:15:13 +0200 Subject: defaults.apps: make pager accept ansi color codes This fixes the :grep command on some OSes --- ranger/defaults/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 1f2f751c..48735196 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -94,7 +94,7 @@ class CustomApplications(Applications): # ----------------------------------------- application definitions # Note: Trivial applications are defined at the bottom def app_pager(self, c): - return tup('less', *c) + return tup('less', '-R', *c) def app_editor(self, c): try: -- cgit 1.4.1-2-gfad0 From bbe783498ca9afaa8ddcde21ae9327f476a24442 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 17 Jul 2010 02:45:58 +0200 Subject: help.console: typo --- ranger/help/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/help/console.py b/ranger/help/console.py index d1764841..3768a79a 100644 --- a/ranger/help/console.py +++ b/ranger/help/console.py @@ -186,7 +186,7 @@ There is a special syntax for more control: !@ mplayer will open the selected files with mplayer (equivalent to !mplayer %s) -Those two can be combinated: +Those two can be combined: !d!@mplayer will open the selection with a detached mplayer (again, this is equivalent to !d!mplayer %s) -- cgit 1.4.1-2-gfad0 From f54adde9099f08a8e15d8003a6a2cdf275593e4d Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 17 Jul 2010 02:50:43 +0200 Subject: gui.widgets.console: added % and % macros See also: http://lists.nongnu.org/archive/html/ranger-users/2010-07/msg00001.html --- ranger/gui/widgets/console.py | 36 +++++++++++++++++++++++++++++++++++- ranger/help/console.py | 6 ++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 1ee7ebc0..f02a4b01 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -44,7 +44,7 @@ OPEN_HISTORY = 3 class _CustomTemplate(string.Template): """A string.Template subclass for use in the OpenConsole""" delimiter = '%' - idpattern = '[a-z]' + idpattern = '\d?[a-z]' class Console(Widget): @@ -489,6 +489,40 @@ class OpenConsole(ConsoleWithTab): else: macros['d'] = '.' + # define d/f/s macros for each tab + for i in range(1,10): + try: + tab_dir_path = self.fm.tabs[i] + except: + continue + tab_dir = self.fm.env.get_directory(tab_dir_path) + i = str(i) + macros[i + 'd'] = shell_quote(tab_dir_path) + macros[i + 'f'] = shell_quote(tab_dir.pointed_obj.path) + macros[i + 's'] = ' '.join(shell_quote(fl.path) + for fl in tab_dir.get_selection()) + + # define D/F/S for the next tab + found_current_tab = False + next_tab_path = None + first_tab = None + for tab in self.fm.tabs: + if not first_tab: + first_tab = tab + if found_current_tab: + next_tab_path = self.fm.tabs[tab] + break + if self.fm.current_tab == tab: + found_current_tab = True + if found_current_tab and not next_tab_path: + next_tab_path = self.fm.tabs[first_tab] + next_tab = self.fm.env.get_directory(next_tab_path) + + macros['D'] = shell_quote(next_tab) + macros['F'] = shell_quote(next_tab.pointed_obj.path) + macros['S'] = ' '.join(shell_quote(fl.path) + for fl in next_tab.get_selection()) + return _CustomTemplate(command).safe_substitute(macros) def _parse(self): diff --git a/ranger/help/console.py b/ranger/help/console.py index 3768a79a..c830abfc 100644 --- a/ranger/help/console.py +++ b/ranger/help/console.py @@ -173,6 +173,12 @@ commands and they will be replaced with a list of files. %t all tagged files in the current directory %c the full paths of the currently copied/cut files +The macros %f, %d and %s also have upper case variants, %F, %D and %S, +which refer to the next tab. To refer to specific tabs, add a number in +between. Examples: + %D The path of the directory in the next tab + %7s The selection of the seventh tab + %c is the only macro which ranges out of the current directory. So you may "abuse" the copying function for other purposes, like diffing two files which are in different directories: -- cgit 1.4.1-2-gfad0 From bd088ce81a220cba9f30f9a1f209bc7b8a93a8d5 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 23 Jul 2010 15:03:53 +0200 Subject: defaults.keys: added keys da, dr, ya, yr + documentation --- ranger/core/actions.py | 16 +++++++++++----- ranger/defaults/keys.py | 4 ++++ ranger/help/fileop.py | 6 ++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 911f5ca4..a298b304 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -542,8 +542,9 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.env.cut = False self.ui.browser.main_column.request_redraw() - def copy(self, narg=None, dirarg=None): - """Copy the selected items""" + def copy(self, mode='set', narg=None, dirarg=None): + """Copy the selected items. Modes are: 'set', 'add', 'remove'.""" + assert mode in ('set', 'add', 'remove') cwd = self.env.cwd if not narg and not dirarg: selected = (f for f in self.env.get_selection() if f in cwd.files) @@ -559,12 +560,17 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): pagesize=self.env.termsize[0], offset=offset) cwd.pointer = pos cwd.correct_pointer() - self.env.copy = set(selected) + if mode == 'set': + self.env.copy = set(selected) + elif mode == 'add': + self.env.copy.update(set(selected)) + elif mode == 'remove': + self.env.copy.difference_update(set(selected)) self.env.cut = False self.ui.browser.main_column.request_redraw() - def cut(self, narg=None, dirarg=None): - self.copy(narg=narg, dirarg=dirarg) + def cut(self, mode='set', narg=None, dirarg=None): + self.copy(mode=mode, narg=narg, dirarg=dirarg) self.env.cut = True self.ui.browser.main_column.request_redraw() diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index ce595be6..75f6eb2e 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -168,7 +168,11 @@ map('u', fm.mark_in_direction(val=False)) # ------------------------------------------ file system operations map('yy', 'y', fm.copy()) +map('ya', fm.copy(mode='add')) +map('yr', fm.copy(mode='remove')) map('dd', 'd', fm.cut()) +map('da', fm.cut(mode='add')) +map('dr', fm.cut(mode='remove')) map('pp', fm.paste()) map('po', fm.paste(overwrite=True)) map('pl', fm.paste_symlink()) diff --git a/ranger/help/fileop.py b/ranger/help/fileop.py index 53ce9ff8..f8401800 100644 --- a/ranger/help/fileop.py +++ b/ranger/help/fileop.py @@ -60,6 +60,9 @@ The "highlighted file", or the "current file", is the one below the cursor. yy copy the selection dd cut the selection + ya, da add the selection to the copied/cut files + yr, dr remove the selection from the copied/cut files + pp paste the copied/cut files. No file will be overwritten. Instead, a "_" character will be appended to the new filename. po paste the copied/cut files. Existing files are overwritten. @@ -75,6 +78,9 @@ If renaming is not possible because the source and the destination are on separate devices, it will be copied and eventually the source is deleted. This implies that a file can only be cut + pasted once. +The files are either copied or cut, never mixed even if you mix "da" and "ya" +keys (in which case the last command is decisive about whether they are copied +or cut.) ============================================================================== 4.4. Task View -- cgit 1.4.1-2-gfad0 From 0fd09f7318210f6e4f8d91f7f39ecf9b83ce4879 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 12 Aug 2010 23:13:30 +0200 Subject: shared.mimetype: look for mimetypes in ~/.mime.types --- ranger/shared/mimetype.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ranger/shared/mimetype.py b/ranger/shared/mimetype.py index c6577056..da6fcd10 100644 --- a/ranger/shared/mimetype.py +++ b/ranger/shared/mimetype.py @@ -15,9 +15,12 @@ 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')) -- cgit 1.4.1-2-gfad0 From c2a8c862631cb7301e173795273a363aa5913834 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 13 Aug 2010 02:10:18 +0200 Subject: defaults.keys: map to ^H (since ^H is bugged sometimes) --- ranger/defaults/keys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 75f6eb2e..1f42b6ee 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -191,7 +191,7 @@ map('du', fm.execute_console('p!du --max-depth=1 -h --apparent-size', # -------------------------------------------------- toggle options map('z', fm.hint("[*cdfhimpPs*] show_*h*idden *p*review_files "\ "*P*review_dirs *f*ilter flush*i*nput *m*ouse")) -map('zh', '', fm.toggle_boolean_option('show_hidden')) +map('zh', '', '', fm.toggle_boolean_option('show_hidden')) map('zp', fm.toggle_boolean_option('preview_files')) map('zP', fm.toggle_boolean_option('preview_directories')) map('zi', fm.toggle_boolean_option('flushinput')) -- cgit 1.4.1-2-gfad0 From fd51fa3da2848859ba722bac45178c24845d17d0 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 16 Aug 2010 22:35:03 +0200 Subject: Fixed exception when using %f macros in special cases --- ranger/gui/widgets/console.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index f02a4b01..ff8d137a 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -498,7 +498,8 @@ class OpenConsole(ConsoleWithTab): tab_dir = self.fm.env.get_directory(tab_dir_path) i = str(i) macros[i + 'd'] = shell_quote(tab_dir_path) - macros[i + 'f'] = shell_quote(tab_dir.pointed_obj.path) + if tab_dir.pointed_obj: + macros[i + 'f'] = shell_quote(tab_dir.pointed_obj.path) macros[i + 's'] = ' '.join(shell_quote(fl.path) for fl in tab_dir.get_selection()) @@ -519,7 +520,8 @@ class OpenConsole(ConsoleWithTab): next_tab = self.fm.env.get_directory(next_tab_path) macros['D'] = shell_quote(next_tab) - macros['F'] = shell_quote(next_tab.pointed_obj.path) + if next_tab.pointed_obj: + macros['F'] = shell_quote(next_tab.pointed_obj.path) macros['S'] = ' '.join(shell_quote(fl.path) for fl in next_tab.get_selection()) -- cgit 1.4.1-2-gfad0 From 89f453db286f648dcaf498c32ac37d67ccd90e18 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 25 Aug 2010 03:10:58 +0200 Subject: defaults.keys: added keys gl and gL to resolve links --- ranger/defaults/keys.py | 4 +++- ranger/help/movement.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 1f42b6ee..0806a494 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -244,7 +244,9 @@ map('gh', fm.cd('~')) map('ge', fm.cd('/etc')) map('gu', fm.cd('/usr')) map('gd', fm.cd('/dev')) -map('gl', fm.cd('/lib')) +map('gl', lambda arg: arg.fm.cd(os.path.realpath(arg.fm.env.cwd.path))) +map('gL', lambda arg: arg.fm.cd( + os.path.dirname(os.path.realpath(arg.fm.env.cf.path)))) map('go', fm.cd('/opt')) map('gv', fm.cd('/var')) map('gr', 'g/', fm.cd('/')) diff --git a/ranger/help/movement.py b/ranger/help/movement.py index 3abec359..564b226b 100644 --- a/ranger/help/movement.py +++ b/ranger/help/movement.py @@ -74,6 +74,9 @@ This keys can be used to make movements beyond the current directory { traverse in the other direction. (not implemented yet, currently this only moves back in history) + gl move to the real path of the current directory (resolving symlinks) + gL move to the real path of the selected file or directory + ============================================================================== 1.2. Browser control -- cgit 1.4.1-2-gfad0 From e8b3ae51480ac217c2f064704902ff2f28b9f635 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 25 Aug 2010 03:11:41 +0200 Subject: widgets.console: improved delete_word() --- ranger/gui/widgets/console.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index ff8d137a..85b92548 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -20,6 +20,7 @@ commands, searching and executing files. import string import curses +import re from collections import deque from . import Widget @@ -258,15 +259,15 @@ class Console(Widget): self.on_line_change() def delete_word(self): - self.tab_deque = None - try: - i = self.line.rindex(' ', 0, self.pos - 1) + 1 - self.line = self.line[:i] + self.line[self.pos:] + if self.line: + self.tab_deque = None + i = len(self.line) - 2 + while i >= 0 and re.match(r'[\w\d]', self.line[i], re.U): + i -= 1 + self.copy = self.line[i + 1:] + self.line = self.line[:i + 1] self.pos = len(self.line) - except ValueError: - self.line = '' - self.pos = 0 - self.on_line_change() + self.on_line_change() def delete(self, mod): self.tab_deque = None -- cgit 1.4.1-2-gfad0 From c525589d6c6a7fd00be099d2e399f08113f4a33f Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Aug 2010 13:13:46 +0200 Subject: widgets/console: Simplified console --- ranger/api/commands.py | 4 +- ranger/api/keys.py | 1 - ranger/core/actions.py | 86 ++++++- ranger/defaults/commands.py | 155 ++++++++++++- ranger/defaults/keys.py | 36 ++- ranger/gui/defaultui.py | 4 +- ranger/gui/widgets/console.py | 448 ++++--------------------------------- ranger/gui/widgets/console_mode.py | 55 ----- 8 files changed, 295 insertions(+), 494 deletions(-) delete mode 100644 ranger/gui/widgets/console_mode.py diff --git a/ranger/api/commands.py b/ranger/api/commands.py index ca3f730d..f4e2ca76 100644 --- a/ranger/api/commands.py +++ b/ranger/api/commands.py @@ -17,7 +17,6 @@ import os from collections import deque from ranger.api import * from ranger.shared import FileManagerAware -from ranger.gui.widgets import console_mode as cmode from ranger.ext.command_parser import LazyParser as parse @@ -71,9 +70,8 @@ class Command(FileManagerAware): """Abstract command class""" name = None allow_abbrev = True - def __init__(self, line, mode): + def __init__(self, line): self.line = line - self.mode = mode def execute(self): """Override this""" diff --git a/ranger/api/keys.py b/ranger/api/keys.py index 13a4b07f..5812de39 100644 --- a/ranger/api/keys.py +++ b/ranger/api/keys.py @@ -20,7 +20,6 @@ from inspect import getargspec, ismethod from ranger import RANGERDIR from ranger.api import * -from ranger.gui.widgets import console_mode as cmode from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS from ranger.container.keymap import KeyMap, Direction, KeyMapWithDirections diff --git a/ranger/core/actions.py b/ranger/core/actions.py index a298b304..c93fa0a5 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -16,19 +16,25 @@ import os import re import shutil +import string from os.path import join, isdir from os import symlink, getcwd from inspect import cleandoc import ranger from ranger.ext.direction import Direction +from ranger.ext.shell_escape import shell_quote from ranger import fsobject from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware -from ranger.gui.widgets import console_mode as cmode from ranger.fsobject import File from ranger.ext import shutil_generatorized as shutil_g from ranger.core.loader import LoadableObject +class _MacroTemplate(string.Template): + """A template for substituting macros in commands""" + delimiter = '%' + idpattern = '\d?[a-z]' + class Actions(FileManagerAware, EnvironmentAware, SettingsAware): search_method = 'ctime' search_forward = False @@ -69,17 +75,80 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): """Redraw the window""" self.ui.redraw_window() - def open_console(self, mode=cmode.COMMAND, string='', prompt=None): + def open_console(self, string='', prompt=None, position=None): """Open the console if the current UI supports that""" if hasattr(self.ui, 'open_console'): - self.ui.open_console(mode, string, prompt=prompt) + self.ui.open_console(string, prompt=prompt, position=position) - def execute_console(self, string='', mode=cmode.COMMAND): + def execute_console(self, string=''): """Execute a command for the console""" - self.open_console(mode=mode, string=string) + self.open_console(string=string) self.ui.console.line = string self.ui.console.execute() + def substitute_metachars(self, string): + return _MacroTemplate(string).safe_substitute(self._get_macros()) + + def _get_macros(self): + macros = {} + + if self.fm.env.cf: + macros['f'] = shell_quote(self.fm.env.cf.basename) + else: + macros['f'] = '' + + macros['s'] = ' '.join(shell_quote(fl.basename) \ + for fl in self.fm.env.get_selection()) + + macros['c'] = ' '.join(shell_quote(fl.path) + for fl in self.fm.env.copy) + + macros['t'] = ' '.join(shell_quote(fl.basename) + for fl in self.fm.env.cwd.files + if fl.realpath in self.fm.tags) + + if self.fm.env.cwd: + macros['d'] = shell_quote(self.fm.env.cwd.path) + else: + macros['d'] = '.' + + # define d/f/s macros for each tab + for i in range(1,10): + try: + tab_dir_path = self.fm.tabs[i] + except: + continue + tab_dir = self.fm.env.get_directory(tab_dir_path) + i = str(i) + macros[i + 'd'] = shell_quote(tab_dir_path) + macros[i + 'f'] = shell_quote(tab_dir.pointed_obj.path) + macros[i + 's'] = ' '.join(shell_quote(fl.path) + for fl in tab_dir.get_selection()) + + # define D/F/S for the next tab + found_current_tab = False + next_tab_path = None + first_tab = None + for tab in self.fm.tabs: + if not first_tab: + first_tab = tab + if found_current_tab: + next_tab_path = self.fm.tabs[tab] + break + if self.fm.current_tab == tab: + found_current_tab = True + if found_current_tab and not next_tab_path: + next_tab_path = self.fm.tabs[first_tab] + next_tab = self.fm.env.get_directory(next_tab_path) + + macros['D'] = shell_quote(next_tab) + macros['F'] = shell_quote(next_tab.pointed_obj.path) + macros['S'] = ' '.join(shell_quote(fl.path) + for fl in next_tab.get_selection()) + + return macros + + def execute_file(self, files, **kw): """Execute a file. app is the name of a method in Applications, without the "app_" @@ -134,7 +203,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): selection = self.env.get_selection() if not self.env.enter_dir(cf) and selection: if self.execute_file(selection, mode=mode) is False: - self.open_console(cmode.OPEN_QUICK) + self.open_console('open_with ') elif direction.vertical(): newpos = direction.move( direction=direction.down(), @@ -297,7 +366,10 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def search_file(self, text, regexp=True): if isinstance(text, str) and regexp: - text = re.compile(text, re.L | re.U | re.I) + try: + text = re.compile(text, re.L | re.U | re.I) + except: + return False self.env.last_search = text self.search(order='search') diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index 8728f9be..acfbfffb 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -55,6 +55,8 @@ For a list of all actions, check /ranger/core/actions.py. ''' from ranger.api.commands import * +from ranger.ext.get_executables import get_executables +from ranger.core.runner import ALLOWED_FLAGS alias('e', 'edit') alias('q', 'quit') @@ -100,6 +102,154 @@ class cd(Command): return rel_dest != '.' and isdir(abs_dest) +class search(Command): + def execute(self): + self.fm.search_file(parse(self.line).rest(1), regexp=True) + + +class shell(Command): + def execute(self): + line = parse(self.line) + if line.chunk(1)[0] == '-': + flags = line.chunk(1)[1:] + command = line.rest(2) + else: + flags = '' + command = line.rest(1) + + if not command and 'p' in flags: command = 'cat %f' + if command: + if '%' in command: + command = self.fm.substitute_metachars(command) + self.fm.execute_command(command, flags=flags) + + def tab(self): + line = parse(self.line) + if line.chunk(1)[0] == '-': + flags = line.chunk(1)[1:] + command = line.rest(2) + else: + flags = '' + command = line.rest(1) + start = self.line[0:len(self.line) - len(command)] + + try: + position_of_last_space = command.rindex(" ") + except ValueError: + return (start + program + ' ' for program \ + in get_executables() if program.startswith(command)) + if position_of_last_space == len(command) - 1: + return self.line + '%s ' + else: + before_word, start_of_word = self.line.rsplit(' ', 1) + return (before_word + ' ' + file.shell_escaped_basename \ + for file in self.fm.env.cwd.files \ + if file.shell_escaped_basename.startswith(start_of_word)) + +class open_with(Command): + def execute(self): + line = parse(self.line) + app, flags, mode = self._get_app_flags_mode(line.rest(1)) + self.fm.execute_file( + files = [self.fm.env.cf], + app = app, + flags = flags, + mode = mode) + + def _get_app_flags_mode(self, string): + """ + Extracts the application, flags and mode from a string. + + examples: + "mplayer d 1" => ("mplayer", "d", 1) + "aunpack 4" => ("aunpack", "", 4) + "p" => ("", "p", 0) + "" => None + """ + + app = '' + flags = '' + mode = 0 + split = string.split() + + if len(split) == 0: + pass + + elif len(split) == 1: + part = split[0] + if self._is_app(part): + app = part + elif self._is_flags(part): + flags = part + elif self._is_mode(part): + mode = part + + elif len(split) == 2: + part0 = split[0] + part1 = split[1] + + if self._is_app(part0): + app = part0 + if self._is_flags(part1): + flags = part1 + elif self._is_mode(part1): + mode = part1 + elif self._is_flags(part0): + flags = part0 + if self._is_mode(part1): + mode = part1 + elif self._is_mode(part0): + mode = part0 + if self._is_flags(part1): + flags = part1 + + elif len(split) >= 3: + part0 = split[0] + part1 = split[1] + part2 = split[2] + + if self._is_app(part0): + app = part0 + if self._is_flags(part1): + flags = part1 + if self._is_mode(part2): + mode = part2 + elif self._is_mode(part1): + mode = part1 + if self._is_flags(part2): + flags = part2 + elif self._is_flags(part0): + flags = part0 + if self._is_mode(part1): + mode = part1 + elif self._is_mode(part0): + mode = part0 + if self._is_flags(part1): + flags = part1 + + return app, flags, int(mode) + + def _get_tab(self): + line = parse(self.line) + data = line.rest(1) + if ' ' not in data: + all_apps = self.fm.apps.all() + if all_apps: + return (app for app in all_apps if app.startswith(data)) + + return None + + def _is_app(self, arg): + return self.fm.apps.has(arg) or \ + (not self._is_flags(arg) and arg in get_executables()) + + def _is_flags(self, arg): + return all(x in ALLOWED_FLAGS for x in arg) + + def _is_mode(self, arg): + return all(x in '0123456789' for x in arg) + + class find(Command): """ :find @@ -115,9 +265,6 @@ class find(Command): tab = Command._tab_directory_content def execute(self): - if self.mode != cmode.COMMAND_QUICK: - self._search() - import re search = parse(self.line).rest(1) search = re.escape(search) @@ -281,7 +428,7 @@ class delete(Command): and len(os.listdir(cf.path)) > 0): # better ask for a confirmation, when attempting to # delete multiple files or a non-empty directory. - return self.fm.open_console(self.mode, DELETE_WARNING) + return self.fm.open_console(DELETE_WARNING) # no need for a confirmation, just delete self.fm.delete() diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 0806a494..e72f4c91 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -130,8 +130,8 @@ map('', fm.display_file()) map('', fm.edit_file()) map('', fm.copy()) map('', fm.cut()) -map('', fm.open_console(cmode.COMMAND, 'mkdir ')) -map('', fm.open_console(cmode.COMMAND, DELETE_WARNING)) +map('', fm.open_console('mkdir ')) +map('', fm.open_console(DELETE_WARNING)) map('', fm.exit()) # =================================================================== @@ -185,8 +185,7 @@ map('ud', 'uy', fm.uncut()) # ---------------------------------------------------- run programs map('S', fm.execute_command(os.environ['SHELL'])) map('E', fm.edit_file()) -map('du', fm.execute_console('p!du --max-depth=1 -h --apparent-size', - cmode.OPEN)) +map('du', fm.execute_console('shell -p du --max-depth=1 -h --apparent-size')) # -------------------------------------------------- toggle options map('z', fm.hint("[*cdfhimpPs*] show_*h*idden *p*review_files "\ @@ -199,7 +198,7 @@ map('zd', fm.toggle_boolean_option('sort_directories_first')) map('zc', fm.toggle_boolean_option('collapse_preview')) map('zs', fm.toggle_boolean_option('sort_case_insensitive')) map('zm', fm.toggle_boolean_option('mouse_enabled')) -map('zf', fm.open_console(cmode.COMMAND, 'filter ')) +map('zf', fm.open_console('filter ')) # ------------------------------------------------------------ sort map('o', 'O', fm.hint("*s*ize *b*ase*n*ame *m*time" \ @@ -225,19 +224,19 @@ map('or', 'Or', 'oR', 'OR', lambda arg: \ @map("A") def append_to_filename(arg): command = 'rename ' + arg.fm.env.cf.basename - arg.fm.open_console(cmode.COMMAND, command) + arg.fm.open_console(command) @map("I") def insert_before_filename(arg): - append_to_filename(arg) - arg.fm.ui.console.move(right=len('rename '), absolute=True) + command = 'rename ' + arg.fm.env.cf.basename + arg.fm.open_console(command, position=len('rename ')) -map('cw', fm.open_console(cmode.COMMAND, 'rename ')) -map('cd', fm.open_console(cmode.COMMAND, 'cd ')) -map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) +map('cw', fm.open_console('rename ')) +map('cd', fm.open_console('cd ')) +map('f', fm.open_console('find ')) map('d', fm.hint('d*u* (disk usage) d*d* (cut)')) -map('@', fm.open_console(cmode.OPEN, '@')) -map('#', fm.open_console(cmode.OPEN, 'p!')) +map('@', fm.open_console('shell %s', position=len('shell '))) +map('#', fm.open_console('shell -p ')) # --------------------------------------------- jump to directories map('gh', fm.cd('~')) @@ -268,7 +267,7 @@ for n in range(1, 10): map('', fm.tab_open(n)) # ------------------------------------------------------- searching -map('/', fm.open_console(cmode.SEARCH)) +map('/', fm.open_console('search ')) map('n', fm.search()) map('N', fm.search(forward=False)) @@ -307,11 +306,10 @@ def ctrl_c(arg): arg.fm.notify("Aborting: " + item.get_description()) arg.fm.loader.remove(index=0) -map(':', ';', fm.open_console(cmode.COMMAND)) -map('>', fm.open_console(cmode.COMMAND_QUICK)) -map('!', fm.open_console(cmode.OPEN, prompt='!')) -map('s', fm.open_console(cmode.OPEN, prompt='$')) -map('r', fm.open_console(cmode.OPEN_QUICK)) +map(':', ';', fm.open_console('')) +map('!', fm.open_console('shell ')) +map('s', fm.open_console('shell ')) +map('r', fm.open_console('open_with ')) # =================================================================== diff --git a/ranger/gui/defaultui.py b/ranger/gui/defaultui.py index 4baea756..434e6d45 100644 --- a/ranger/gui/defaultui.py +++ b/ranger/gui/defaultui.py @@ -92,8 +92,8 @@ class DefaultUI(UI): def close_embedded_pager(self): self.browser.close_pager() - def open_console(self, mode, string='', prompt=None): - if self.console.open(mode, string, prompt=prompt): + def open_console(self, string='', prompt=None, position=None): + if self.console.open(string, prompt=prompt, position=position): self.status.msg = None self.console.on_close = self.close_console self.console.visible = True diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 85b92548..9d0ea75f 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -18,38 +18,21 @@ The Console widget implements a vim-like console for entering commands, searching and executing files. """ -import string import curses import re from collections import deque from . import Widget -from ranger.gui.widgets.console_mode import is_valid_mode, mode_to_class from ranger import log, relpath_conf -from ranger.core.runner import ALLOWED_FLAGS -from ranger.ext.shell_escape import shell_quote from ranger.ext.utfwidth import uwid from ranger.container.keymap import CommandArgs -from ranger.ext.get_executables import get_executables from ranger.ext.direction import Direction from ranger.ext.utfwidth import uwid, uchars from ranger.container import History from ranger.container.history import HistoryEmptyException import ranger -DEFAULT_HISTORY = 0 -SEARCH_HISTORY = 1 -QUICKOPEN_HISTORY = 2 -OPEN_HISTORY = 3 - -class _CustomTemplate(string.Template): - """A string.Template subclass for use in the OpenConsole""" - delimiter = '%' - idpattern = '\d?[a-z]' - - class Console(Widget): - mode = None visible = False last_cursor_mode = None prompt = ':' @@ -57,29 +40,22 @@ class Console(Widget): tab_deque = None original_line = None history = None - histories = None override = None allow_close = False - historypaths = [] + historypath = None def __init__(self, win): Widget.__init__(self, win) self.clear() - self.histories = [] - # load histories from files - if ranger.arg.clean: - for i in range(4): - self.histories.append( - History(self.settings.max_console_history_size)) - else: - self.historypaths = [relpath_conf(x) for x in \ - ('history', 'history_search', 'history_qopen', 'history_open')] - for i, path in enumerate(self.historypaths): - hist = History(self.settings.max_console_history_size) - self.histories.append(hist) - if ranger.arg.clean: continue - try: f = open(path, 'r') - except: continue + self.history = History(self.settings.max_console_history_size) + # load history from files + if not ranger.arg.clean: + self.historypath = relpath_conf('history') + try: + f = open(path, 'r') + except: + pass + else: for line in f: hist.add(line[:-1]) f.close() @@ -88,20 +64,20 @@ class Console(Widget): # save histories from files if ranger.arg.clean or not self.settings.save_console_history: return - for i, path in enumerate(self.historypaths): - try: f = open(path, 'w') - except: continue - for entry in self.histories[i]: - f.write(entry + '\n') - f.close() + if self.historypath: + try: + f = open(self.historypath, 'w') + except: + pass + else: + for entry in self.histories[i]: + f.write(entry + '\n') + f.close() def init(self): """override this. Called directly after class change""" def draw(self): - if self.mode is None: - return - self.win.erase() self.addstr(0, 0, self.prompt) overflow = -self.wid + len(self.prompt) + uwid(self.line) + 1 @@ -118,25 +94,18 @@ class Console(Widget): except: pass - def open(self, mode, string='', prompt=None): - if not is_valid_mode(mode): - return False + def open(self, string='', prompt=None, position=None): if prompt is not None: assert isinstance(prompt, str) self.prompt = prompt elif 'prompt' in self.__dict__: del self.prompt - cls = mode_to_class(mode) - if self.last_cursor_mode is None: try: self.last_cursor_mode = curses.curs_set(1) except: pass - self.mode = mode - self.__class__ = cls - self.history = self.histories[DEFAULT_HISTORY] self.init() self.allow_close = False self.tab_deque = None @@ -144,6 +113,8 @@ class Console(Widget): self.visible = True self.line = string self.pos = len(string) + if position is not None: + self.pos = min(self.pos, position) self.history.add('') return True @@ -283,56 +254,6 @@ class Console(Widget): self.line = left_part + ''.join(uc[upos+1:]) self.on_line_change() - def execute(self): - pass - - def tab(self): - pass - - def on_line_change(self): - pass - - -class ConsoleWithTab(Console): - def tab(self, n=1): - if self.tab_deque is None: - tab_result = self._get_tab() - - if isinstance(tab_result, str): - self.line = tab_result - self.pos = len(tab_result) - self.on_line_change() - - elif tab_result == None: - pass - - elif hasattr(tab_result, '__iter__'): - self.tab_deque = deque(tab_result) - self.tab_deque.appendleft(self.line) - - if self.tab_deque is not None: - self.tab_deque.rotate(-n) - self.line = self.tab_deque[0] - self.pos = len(self.line) - self.on_line_change() - - def _get_tab(self): - """ - Override this function in the subclass! - - It should return either a string, an iterable or None. - If a string is returned, tabbing will result in the line turning - into that string. - If another iterable is returned, each tabbing will cycle through - the elements of the iterable (which have to be strings). - If None is returned, nothing will happen. - """ - - return None - - -class CommandConsole(ConsoleWithTab): - prompt = ':' def execute(self, cmd=None): self.allow_close = True @@ -356,7 +277,7 @@ class CommandConsole(ConsoleWithTab): except: return None else: - return command_class(self.line, self.mode) + return command_class(self.line) def _get_cmd_class(self): return self.fm.commands.get_command(self.line.split()[0]) @@ -371,313 +292,34 @@ class CommandConsole(ConsoleWithTab): return self.fm.commands.command_generator(self.line) + def tab(self, n=1): + if self.tab_deque is None: + tab_result = self._get_tab() + + if isinstance(tab_result, str): + self.line = tab_result + self.pos = len(tab_result) + self.on_line_change() + + elif tab_result == None: + pass + + elif hasattr(tab_result, '__iter__'): + self.tab_deque = deque(tab_result) + self.tab_deque.appendleft(self.line) + + if self.tab_deque is not None: + self.tab_deque.rotate(-n) + self.line = self.tab_deque[0] + self.pos = len(self.line) + self.on_line_change() -class QuickCommandConsole(CommandConsole): - """ - The QuickCommandConsole is essentially the same as the - CommandConsole, and includes one additional feature: - After each letter you type, it checks whether the command as it - stands there could be executed in a meaningful way, and if it does, - run it right away. - - Example: - >cd .. - As you type the last dot, The console will recognize what you mean - and enter the parent directory saving you the time of pressing enter. - """ - prompt = '>' def on_line_change(self): try: cls = self._get_cmd_class() except (KeyError, ValueError, IndexError): pass else: - cmd = cls(self.line, self.mode) + cmd = cls(self.line) if cmd and cmd.quick(): self.execute(cmd) - - -class SearchConsole(Console): - prompt = '/' - - def init(self): - self.history = self.histories[SEARCH_HISTORY] - - def execute(self): - self.fm.search_file(self.line, regexp=True) - self.close() - - -class OpenConsole(ConsoleWithTab): - """ - The Open Console allows you to execute shell commands: - !vim * will run vim and open all files in the directory. - - %f will be replaced with the basename of the highlighted file - %s will be selected with all files in the selection - - There is a special syntax for more control: - - !d! mplayer will run mplayer with flags (d means detached) - !@ mplayer will open the selected files with mplayer - (equivalent to !mplayer %s) - - Those two can be combinated: - - !d!@mplayer will open the selection with a detached mplayer - (again, this is equivalent to !d!mplayer %s) - - For a list of other flags than "d", check chapter 2.5 of the documentation - """ - prompt = '!' - - def init(self): - self.history = self.histories[OPEN_HISTORY] - - def execute(self): - command, flags = self._parse() - if not command and 'p' in flags: - command = 'cat %f' - if command: - if _CustomTemplate.delimiter in command: - command = self._substitute_metachars(command) - self.fm.execute_command(command, flags=flags) - self.close() - - def _get_tab(self): - try: - i = self.line.index('!')+1 - except ValueError: - line = self.line - start = '' - else: - line = self.line[i:] - start = self.line[:i] - - try: - position_of_last_space = line.rindex(" ") - except ValueError: - return (start + program + ' ' for program \ - in get_executables() if program.startswith(line)) - if position_of_last_space == len(line) - 1: - return self.line + '%s ' - else: - before_word, start_of_word = self.line.rsplit(' ', 1) - return (before_word + ' ' + file.shell_escaped_basename \ - for file in self.fm.env.cwd.files \ - if file.shell_escaped_basename.startswith(start_of_word)) - - def _substitute_metachars(self, command): - macros = {} - - if self.fm.env.cf: - macros['f'] = shell_quote(self.fm.env.cf.basename) - else: - macros['f'] = '' - - macros['s'] = ' '.join(shell_quote(fl.basename) \ - for fl in self.fm.env.get_selection()) - - macros['c'] = ' '.join(shell_quote(fl.path) - for fl in self.fm.env.copy) - - macros['t'] = ' '.join(shell_quote(fl.basename) - for fl in self.fm.env.cwd.files - if fl.realpath in self.fm.tags) - - if self.fm.env.cwd: - macros['d'] = shell_quote(self.fm.env.cwd.path) - else: - macros['d'] = '.' - - # define d/f/s macros for each tab - for i in range(1,10): - try: - tab_dir_path = self.fm.tabs[i] - except: - continue - tab_dir = self.fm.env.get_directory(tab_dir_path) - i = str(i) - macros[i + 'd'] = shell_quote(tab_dir_path) - if tab_dir.pointed_obj: - macros[i + 'f'] = shell_quote(tab_dir.pointed_obj.path) - macros[i + 's'] = ' '.join(shell_quote(fl.path) - for fl in tab_dir.get_selection()) - - # define D/F/S for the next tab - found_current_tab = False - next_tab_path = None - first_tab = None - for tab in self.fm.tabs: - if not first_tab: - first_tab = tab - if found_current_tab: - next_tab_path = self.fm.tabs[tab] - break - if self.fm.current_tab == tab: - found_current_tab = True - if found_current_tab and not next_tab_path: - next_tab_path = self.fm.tabs[first_tab] - next_tab = self.fm.env.get_directory(next_tab_path) - - macros['D'] = shell_quote(next_tab) - if next_tab.pointed_obj: - macros['F'] = shell_quote(next_tab.pointed_obj.path) - macros['S'] = ' '.join(shell_quote(fl.path) - for fl in next_tab.get_selection()) - - return _CustomTemplate(command).safe_substitute(macros) - - def _parse(self): - if '!' in self.line: - flags, cmd = self.line.split('!', 1) - else: - flags, cmd = '', self.line - - add_selection = False - if cmd.startswith('@'): - cmd = cmd[1:] - add_selection = True - elif flags.startswith('@'): - flags = flags[1:] - add_selection = True - - if add_selection: - cmd += ' ' + ' '.join(shell_quote(fl.basename) \ - for fl in self.env.get_selection()) - - return (cmd, flags) - - -class QuickOpenConsole(ConsoleWithTab): - """ - The Quick Open Console allows you to open files with predefined programs - and modes very quickly. By adding flags to the command, you can specify - precisely how the program is run, e.g. the d-flag will run it detached - from the file manager. - - For a list of other flags than "d", check chapter 2.5 of the documentation - - The syntax is "open with: ". - The parsing of the arguments is very flexible. You can leave out one or - more arguments (or even all of them) and it will fall back to default - values. You can switch the order as well. - There is just one rule: - - If you supply the , it has to be the first argument. - - Examples: - - open with: mplayer D open the selection in mplayer, but not detached - open with: 1 open it with the default handler in mode 1 - open with: d open it detached with the default handler - open with: p open it as usual, but pipe the output to "less" - open with: totem 1 Ds open in totem in mode 1, will not detach the - process (flag D) but discard the output (flag s) - """ - - prompt = 'open with: ' - - def init(self): - self.history = self.histories[QUICKOPEN_HISTORY] - - def execute(self): - split = self.line.split() - app, flags, mode = self._get_app_flags_mode() - self.fm.execute_file( - files = [self.env.cf], - app = app, - flags = flags, - mode = mode ) - self.close() - - def _get_app_flags_mode(self): - """ - Extracts the application, flags and mode from - a string entered into the "openwith_quick" console. - """ - # examples: - # "mplayer d 1" => ("mplayer", "d", 1) - # "aunpack 4" => ("aunpack", "", 4) - # "p" => ("", "p", 0) - # "" => None - - app = '' - flags = '' - mode = 0 - split = self.line.split() - - if len(split) == 0: - pass - - elif len(split) == 1: - part = split[0] - if self._is_app(part): - app = part - elif self._is_flags(part): - flags = part - elif self._is_mode(part): - mode = part - - elif len(split) == 2: - part0 = split[0] - part1 = split[1] - - if self._is_app(part0): - app = part0 - if self._is_flags(part1): - flags = part1 - elif self._is_mode(part1): - mode = part1 - elif self._is_flags(part0): - flags = part0 - if self._is_mode(part1): - mode = part1 - elif self._is_mode(part0): - mode = part0 - if self._is_flags(part1): - flags = part1 - - elif len(split) >= 3: - part0 = split[0] - part1 = split[1] - part2 = split[2] - - if self._is_app(part0): - app = part0 - if self._is_flags(part1): - flags = part1 - if self._is_mode(part2): - mode = part2 - elif self._is_mode(part1): - mode = part1 - if self._is_flags(part2): - flags = part2 - elif self._is_flags(part0): - flags = part0 - if self._is_mode(part1): - mode = part1 - elif self._is_mode(part0): - mode = part0 - if self._is_flags(part1): - flags = part1 - - return app, flags, int(mode) - - def _get_tab(self): - if ' ' not in self.line: - all_apps = self.fm.apps.all() - if all_apps: - return (app for app in all_apps if app.startswith(self.line)) - - return None - - def _is_app(self, arg): - return self.fm.apps.has(arg) or \ - (not self._is_flags(arg) and arg in get_executables()) - - def _is_flags(self, arg): - return all(x in ALLOWED_FLAGS for x in arg) - - def _is_mode(self, arg): - return all(x in '0123456789' for x in arg) diff --git a/ranger/gui/widgets/console_mode.py b/ranger/gui/widgets/console_mode.py deleted file mode 100644 index c29f9959..00000000 --- a/ranger/gui/widgets/console_mode.py +++ /dev/null @@ -1,55 +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 . - -DEFAULT = 0 -COMMAND = 1 -COMMAND_QUICK = 2 -OPEN = 3 -OPEN_QUICK = 4 -SEARCH = 5 - -def is_valid_mode(mode): - """ - Returns True or False depending on whether the mode is valid or not. - """ - return isinstance(mode, int) and mode >= 0 and mode <= 5 - -def all_modes(mode): - """ - Returns a generator containing all valid modes. - """ - return range(6) - -def mode_to_class(mode): - """ - Associates modes with the actual classes - from ranger.gui.widgets.console. - """ - from .console import Console, CommandConsole, OpenConsole, \ - QuickOpenConsole, QuickCommandConsole, SearchConsole - - if mode == DEFAULT: - return Console - if mode == COMMAND: - return CommandConsole - if mode == OPEN: - return OpenConsole - if mode == OPEN_QUICK: - return QuickOpenConsole - if mode == COMMAND_QUICK: - return QuickCommandConsole - if mode == SEARCH: - return SearchConsole - raise ValueError -- cgit 1.4.1-2-gfad0 From f20fc829c48b2ac4076e388a320fd82580356658 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Aug 2010 15:40:46 +0200 Subject: Updated documentation for the console --- ranger/defaults/commands.py | 11 +-- ranger/help/console.py | 188 ++++++++++++++------------------------------ 2 files changed, 61 insertions(+), 138 deletions(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index acfbfffb..8005100c 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -20,7 +20,7 @@ Each command is a subclass of `Command'. Several methods are defined to interface with the console: execute: call this method when the command is executed. tab: call this method when tab is pressed. - quick: call this method after each keypress in the QuickCommandConsole. + quick: call this method after each keypress. The return values for tab() can be either: None: There is no tab completion @@ -69,9 +69,6 @@ class cd(Command): The cd command changes the directory. The command 'cd -' is equivalent to typing ``. - - In the quick console, the directory will be entered without the - need to press enter, as soon as there is one unambiguous match. """ def execute(self): @@ -255,10 +252,8 @@ class find(Command): :find The find command will attempt to find a partial, case insensitive - match in the filenames of the current directory. - - In the quick command console, once there is one unambiguous match, - the file will be run automatically. + match in the filenames of the current directory and execute the + file automatically. """ count = 0 diff --git a/ranger/help/console.py b/ranger/help/console.py index c830abfc..bc69f7fb 100644 --- a/ranger/help/console.py +++ b/ranger/help/console.py @@ -13,59 +13,33 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -3. Basic movement and browsing +3. The Console -3.1. Overview of all the Console Modes +3.1. General Information 3.2. List of Commands -3.3. The (Quick) Command Console -3.4. The Open Console -3.5. The Quick Open Console - +3.3. Macros +3.4. The more complicated Commands in Detail ============================================================================== -3.1. Overview of all the Console Modes: - -There are, as of now, five different types of consoles: - -3.1.1. The Command Console, opened by pressing ":" - Usable for entering commands like you know it from vim. - Use the tab key to cycle through all available commands - and press F1 to view the docstring of the current command. - -3.1.2. The Quick Command Console, opened with ">" - Works just like 3.1.1, but it saves you pressing by checking - whether the current input can be executed in a meaningful way - and doing so automatically. (works with commands like cd, find.) - -3.1.3. The Search Console, opened with "/" - Very much like the console you get in vim after pressing "/". - The current directory will be searched for files whose names - match the given regular expression. +3.1. General Information -3.1.4. The Open Console, activated with "!" - Similar to ":!..." in vim. The given command will be executed, - "%s" will be replaced with all the files in the selection, - "%f" is replaced with only the current highlighted file. - There are further options to tweak how the command is executed. +The console is opened by pressing ":". Press to cycle through all +available commands and press to view help about the current command. -3.1.5. The Quick Open Console, opened by pressing "r" - Rather than having to enter the full command, this console gives - you a higher level interface to running files. - You can define the application, the mode and the flags separated - with spaces. +All commands are defined in the file ranger/defaults/commands.py, which +also contains a detailed specification. ============================================================================== 3.2. List of Commands -This is a list of the few commands which are implemented by default. -All commands except for ":delete" can be abbreviated to the shortest +All commands except for ":delete" can be abbreviated with the shortest unambiguous name, e.g. ":chmod" can be written as ":ch" but not as ":c" since it conflicts with ":cd". :cd - Changes the directory to * + Changes the directory to :chmod Sets the permissions of the selection to the octal number. @@ -86,10 +60,9 @@ it conflicts with ":cd". :filter Displays only files which contain in their basename. -:find - Look for a partial, case insensitive match in the filenames - of the current directory and execute it if there is only - one match. * +:find + Quickly find files that match the regexp and execute the first + unambiguous match. :grep Looks for a string in all marked files or directory. @@ -104,65 +77,34 @@ it conflicts with ":cd". :mkdir Creates a directory with the name +:open_with [] [] [] + Open the current file with the program, flags and mode. |24?| |25?| + All arguments are optional. If none is given, its equivalent to + pressing + :quit Exits ranger :rename Changes the name of the currently highlighted file to +:search + Search for a regexp in all file names, like the / key in vim. + +:shell [-] + Run the command, optionally with some flags. |25?| + Example: shell -d firefox -safe-mode %s + opens (detached from ranger) the selection in firefox' safe-mode + :terminal Spawns "x-terminal-emulator" starting in the current directory. :touch Creates a file with the name -* implements handler for the Quick Command Console. - - -============================================================================== -3.3. The (Quick) Command Console - -Open these consoles by pressing ":" or ">" - -The Command Console and the "Quick" Command Console are mostly identical -since they share the commands. As explained in 3.1.2, the point in using -the Quick Command Console is that the command is executed without the -need to press as soon as ranger thinks you want it executed. - -Take the "find" command, for example. It will attempt to find a file -or directory which matches the given input, and if there is one unambiguous -match, that file will be executed or that directory will be entered. -If you use the "find" command in the quick console, as soon as there is -one unambiguous match, will be pressed for you, giving you a -very fast way to browse your files. - - -All commands are defined in ranger/defaults/commands.py. You can refer to this -file for a list of commands. Implementing new commands should be intuitive: -Create a new class, a subclass of Command, and define the execute method -is usually enough. For parsing command input, the command parser in -ranger/ext/command_parser.py is used. The tab method should return None, -a string or an iterable sequence containing the strings which should be -cycled through by pressing tab. - -Only those commands which implement the quick() method will be specially -treated by the Quick Command Console. For the rest, both consoles are equal. -quick() is called after each key press and if it returns True, the -command will be executed immediately. - - -Pressing F1 inside the console displays the docstring of the current command -in the pager if docstrings are available (i.e. unless python is run with -the flag "-OO" which removes all docstrings.) - ============================================================================== -3.4. The Open Console - -Open this console by pressing "!" or "s" - -The Open Console allows you to execute shell commands: -!vim * will run vim and open all files in the directory. +3.3. Macros Like in similar filemanagers there are some macros. Use them in commands and they will be replaced with a list of files. @@ -184,56 +126,42 @@ between. Examples: are in different directories: Yank the file A (type yy), move to the file B and use: - !p!diff %c %f - -There is a special syntax for more control: - -!d! mplayer will run mplayer with flags (d means detached) -!@ mplayer will open the selected files with mplayer - (equivalent to !mplayer %s) - -Those two can be combined: - -!d!@mplayer will open the selection with a detached mplayer - (again, this is equivalent to !d!mplayer %s) - -These keys open the console with a predefined text: - @ "!@" Suffixes %s. Good for things like "@mount" - # "!p!" Pipes output through a pager. For commands with output. - Note: A plain "!p!" will be translated to "!p!cat %f" - -For a list of other flags than "d", check chapter 2.5 of the documentation + :shell -p diff %c %f ============================================================================== -3.5. The Quick Open Console - -Open this console by pressing "r" - -The Quick Open Console allows you to open files with predefined programs -and modes very quickly. By adding flags to the command, you can specify -precisely how the program is run, e.g. the d-flag will run it detached -from the file manager. - -For a list of other flags than "d", check chapter 2.5 of the documentation - -The syntax is "open with: ". -The parsing of the arguments is very flexible. You can leave out one or -more arguments (or even all of them) and it will fall back to default -values. You can switch the order as well. -There is just one rule: - -If you supply the , it has to be the first argument. +3.4. The more complicated Commands in Detail + +3.3.1. "find" +The find command is different than others: it doesn't require you to +press . To speed things up, it tries to guess when you're +done typing and executes the command right away. +The key "f" opens the console with ":find " + +3.3.2. "shell" +The shell command accepts flags |25?| as the first argument if it starts +with a "-". Example: ":shell -p cat somefile.txt" will use the "p"-flag, +which pipes the output to the pager. +There are some keys which open the console with the shell command: + "!" opens ":shell " + "@" opens ":shell %s" + "#" opens ":shell -p " + +3.3.3. "open_with" +The open_with command opens the current file with the specified program, +mode |24?| and flags |25?|. +The programs and the meaning of modes can be defined in the apps.py, +giving you a high level interface for running files. +Pressing "r" will open the console with ":open_with " Examples: -open with: mplayer D open the selection in mplayer, but not detached -open with: 1 open it with the default handler in mode 1 -open with: d open it detached with the default handler -open with: p open it as usual, but pipe the output to "less" -open with: totem 1 Ds open in totem in mode 1, will not detach the - process (flag D) but discard the output (flag s) - +:open_with mplayer D open the selection in mplayer, but not detached +:open_with 1 open it with the default handler in mode 1 +:open_with d open it detached with the default handler +:open_with p open it as usual, but pipe the output to "less" +:open_with totem 1 Ds open in totem in mode 1, will not detach the + process (flag D) but discard the output (flag s) ============================================================================== """ -- cgit 1.4.1-2-gfad0 From 245e62ce8ce1db96ee053f89e15c68f717b616f9 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Aug 2010 16:02:46 +0200 Subject: core.actions: 12? now opens help chapter 1 subchapter 2 --- ranger/core/actions.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index c93fa0a5..6b8fc13c 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -524,15 +524,29 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): from ranger.help import get_help, get_help_by_index + scroll_to_line = 0 if narg is not None: - help_text = get_help_by_index(narg) + chapter, subchapter = int(str(narg)[0]), str(narg)[1:] + help_text = get_help_by_index(chapter) + lines = help_text.split('\n') + if chapter: + chapternumber = str(chapter) + '.' + subchapter + '. ' + skip_to_content = True + for line_number, line in enumerate(lines): + if skip_to_content: + if line[:10] == '==========': + skip_to_content = False + else: + if line.startswith(chapternumber): + scroll_to_line = line_number else: help_text = get_help(topic) + lines = help_text.split('\n') pager = self.ui.open_pager() pager.markup = 'help' - lines = help_text.split('\n') pager.set_source(lines) + pager.move(down=scroll_to_line) def display_log(self): if not hasattr(self.ui, 'open_pager'): -- cgit 1.4.1-2-gfad0 From 8d1e92bdf6604eaca15e935f7073ee9a5d129abf Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Aug 2010 16:16:44 +0200 Subject: more documentation updates --- ranger/help/starting.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/ranger/help/starting.py b/ranger/help/starting.py index 99cfc45e..1796f83d 100644 --- a/ranger/help/starting.py +++ b/ranger/help/starting.py @@ -17,7 +17,7 @@ 2. Running Files 2.1. How to run files -2.2. The "open with" prompt +2.2. The "open_with" command 2.2. Programs 2.4. Modes 2.5. Flags @@ -30,40 +30,47 @@ While highlighting a file, press the "l" key to fire up the automatic filetype detection mechanism and attempt to start the file. l run the selection - r open the "open with" prompt + r open the console with ":open_with" Note: The selection means, if there are marked files in this directory, use them. Otherwise use the file under the cursor. ============================================================================== -2.2. The "open with" prompt +2.2. The "open_with" command If the automatic filetype detection fails or starts the file in a wrong way, you can press "r" to manually tell ranger how to run it. -Syntax: open with: +The programs and modes can be defined in the apps.py, giving you a +high level interface for running files. + +Syntax: :open_with +You can leave out parameters or change the order. Examples: Open this file with vim: - open with: vim + :open_with vim Run this file like with "./file": - open with: self + :open_with self +Open this file as usual but pipe the output to "less" + :open_with p Open this file with mplayer with the "detached" flag: - open with: mplayer d + :open_with mplayer d +Open this file with totem in mode 1, will not detach the process (flag D) +but discard the output (flag s). + :open_with totem 1 Ds The parameters , and are explained in the following paragraphs -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 in the class CustomApplications which starts with "app_" can be used -as a program in the "open with" prompt. +as a program in the "open_with" command. You're encouraged to add your own program definitions to the list. Refer to the existing examples in the apps.py, it should be easy to adapt it for your @@ -81,8 +88,8 @@ gives you 2 ways of opening a video (by default): By specifying a mode, you can select one of those. The "l" key will 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" +You can specify a mode in the "open_with" command 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 @@ -97,11 +104,11 @@ Flags give you a way to modify the behaviour of the spawned process. p Redirect output to the pager w Wait for an enter-press when the process is done -For example, "open with: p" will pipe the output of that process into +For example, ":open_with p" will pipe the output of that process into the pager. An uppercase flag has the opposite effect. If a program will be detached by -default, use "open with: D" to not detach it. +default, use ":open_with D" to not detach it. Note: Some combinations don't make sense, eg: "vim d" would open the file in vim and detach it. Since vim is a console application, you loose grip -- cgit 1.4.1-2-gfad0 From 6b141b1ba4e9dca835d3a16044ad2a24a1e16f3e Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Aug 2010 16:26:16 +0200 Subject: widgets.console: removed unused Console.init() method --- ranger/gui/widgets/console.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 9d0ea75f..53853978 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -74,9 +74,6 @@ class Console(Widget): f.write(entry + '\n') f.close() - def init(self): - """override this. Called directly after class change""" - def draw(self): self.win.erase() self.addstr(0, 0, self.prompt) @@ -106,7 +103,6 @@ class Console(Widget): self.last_cursor_mode = curses.curs_set(1) except: pass - self.init() self.allow_close = False self.tab_deque = None self.focused = True -- cgit 1.4.1-2-gfad0 From 0fb328f92a939dd342b070a386bb717bd9c645b0 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Aug 2010 16:28:18 +0200 Subject: widgets.console: removed duplicate import --- ranger/gui/widgets/console.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 53853978..636073fc 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -24,7 +24,6 @@ from collections import deque from . import Widget from ranger import log, relpath_conf -from ranger.ext.utfwidth import uwid from ranger.container.keymap import CommandArgs from ranger.ext.direction import Direction from ranger.ext.utfwidth import uwid, uchars -- cgit 1.4.1-2-gfad0 From 0a8001b2a141b141e6e34b1ebcce316288ef7ab7 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Aug 2010 17:25:50 +0200 Subject: Fixed history --- ranger/container/history.py | 21 ++++++++++++++++++++- ranger/core/actions.py | 2 +- ranger/defaults/commands.py | 2 +- ranger/defaults/options.py | 2 +- ranger/gui/widgets/console.py | 13 ++++++++----- ranger/help/console.py | 24 ++++++------------------ test/tc_history.py | 19 +++++++++++++++++++ 7 files changed, 56 insertions(+), 27 deletions(-) diff --git a/ranger/container/history.py b/ranger/container/history.py index ba13775d..5d4da07a 100644 --- a/ranger/container/history.py +++ b/ranger/container/history.py @@ -63,7 +63,7 @@ class History(object): self.history_forward.appendleft( self.history.pop() ) return self.current() - def move(self, n): + def move(self, n, pattern=None): if n > 0: return self.forward() if n < 0: @@ -84,3 +84,22 @@ class History(object): if self.history_forward: self.history.extend(self.history_forward) self.history_forward.clear() + + def unique(self): + found = [] + i = len(self.history) + while i: + i -= 1 + item = self.history[i] + if item in found: + del self.history[i] + else: + found.append(item) + i = len(self.history_forward) + while i: + i -= 1 + item = self.history_forward[i] + if item in found: + del self.history_forward[i] + else: + found.append(item) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 6b8fc13c..d12d9c6c 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -86,7 +86,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.ui.console.line = string self.ui.console.execute() - def substitute_metachars(self, string): + def substitute_macros(self, string): return _MacroTemplate(string).safe_substitute(self._get_macros()) def _get_macros(self): diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index 8005100c..b6e77697 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -117,7 +117,7 @@ class shell(Command): if not command and 'p' in flags: command = 'cat %f' if command: if '%' in command: - command = self.fm.substitute_metachars(command) + command = self.fm.substitute_macros(command) self.fm.execute_command(command, flags=flags) def tab(self): diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 23752d97..d3c420af 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -85,7 +85,7 @@ tilde_in_titlebar = True # How many directory-changes or console-commands should be kept in history? max_history_size = 20 -max_console_history_size = 20 +max_console_history_size = 50 # Try to keep so much space between the top/bottom border when scrolling: scroll_offset = 8 diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 636073fc..2efb059d 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -34,6 +34,7 @@ import ranger class Console(Widget): visible = False last_cursor_mode = None + history_search_pattern = None prompt = ':' copy = '' tab_deque = None @@ -51,16 +52,16 @@ class Console(Widget): if not ranger.arg.clean: self.historypath = relpath_conf('history') try: - f = open(path, 'r') + f = open(self.historypath, 'r') except: pass else: for line in f: - hist.add(line[:-1]) + self.history.add(line[:-1]) f.close() def destroy(self): - # save histories from files + # save history to files if ranger.arg.clean or not self.settings.save_console_history: return if self.historypath: @@ -69,7 +70,7 @@ class Console(Widget): except: pass else: - for entry in self.histories[i]: + for entry in self.history: f.write(entry + '\n') f.close() @@ -173,6 +174,7 @@ class Console(Widget): self.pos += len(key) self.on_line_change() + self.history_search_pattern = self.line def history_move(self, n): try: @@ -182,7 +184,7 @@ class Console(Widget): else: if self.line != current and self.line != self.history.top(): self.history.modify(self.line) - self.history.move(n) + self.history.move(n, self.history_search_pattern) current = self.history.current() if self.line != current: self.line = self.history.current() @@ -191,6 +193,7 @@ class Console(Widget): def add_to_history(self): self.history.fast_forward() self.history.modify(self.line) + self.history.unique() def move(self, **keywords): direction = Direction(keywords) diff --git a/ranger/help/console.py b/ranger/help/console.py index bc69f7fb..f03491db 100644 --- a/ranger/help/console.py +++ b/ranger/help/console.py @@ -139,29 +139,17 @@ done typing and executes the command right away. The key "f" opens the console with ":find " 3.3.2. "shell" -The shell command accepts flags |25?| as the first argument if it starts -with a "-". Example: ":shell -p cat somefile.txt" will use the "p"-flag, -which pipes the output to the pager. -There are some keys which open the console with the shell command: +The shell command accepts flags |25?| as the first argument. This example +will use the "p"-flag, which pipes the output to the pager: + :shell -p cat somefile.txt + +There are some shortcuts which open the console with the shell command: "!" opens ":shell " "@" opens ":shell %s" "#" opens ":shell -p " 3.3.3. "open_with" -The open_with command opens the current file with the specified program, -mode |24?| and flags |25?|. -The programs and the meaning of modes can be defined in the apps.py, -giving you a high level interface for running files. -Pressing "r" will open the console with ":open_with " - -Examples: - -:open_with mplayer D open the selection in mplayer, but not detached -:open_with 1 open it with the default handler in mode 1 -:open_with d open it detached with the default handler -:open_with p open it as usual, but pipe the output to "less" -:open_with totem 1 Ds open in totem in mode 1, will not detach the - process (flag D) but discard the output (flag s) +The open_with command is explained in detail in chapter 2.2. |22?| ============================================================================== """ diff --git a/test/tc_history.py b/test/tc_history.py index 33784e14..b1161f2a 100644 --- a/test/tc_history.py +++ b/test/tc_history.py @@ -54,4 +54,23 @@ class Test(TestCase): self.assertEqual(4, hist.bottom()) self.assertEqual([4,5,6], list(hist)) + hist.back() + hist.fast_forward() + self.assertEqual([4,5,6], list(hist)) + hist.back() + hist.back() + hist.fast_forward() + self.assertEqual([4,5,6], list(hist)) + hist.back() + hist.back() + hist.back() + hist.fast_forward() + self.assertEqual([4,5,6], list(hist)) + hist.back() + hist.back() + hist.back() + hist.back() + hist.fast_forward() + self.assertEqual([4,5,6], list(hist)) + if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From a02baa3311a469a021ed0f801d3a743ecb1ace41 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 28 Aug 2010 08:01:22 +0200 Subject: Changed implementation of container.history --- ranger/container/history.py | 131 ++++++++++++++++++++++++-------------------- test/tc_history.py | 16 +++--- 2 files changed, 80 insertions(+), 67 deletions(-) diff --git a/ranger/container/history.py b/ranger/container/history.py index 5d4da07a..9a3d6c4a 100644 --- a/ranger/container/history.py +++ b/ranger/container/history.py @@ -13,93 +13,106 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from collections import deque - class HistoryEmptyException(Exception): pass class History(object): - def __init__(self, maxlen = None): - self.history = deque(maxlen = maxlen) - self.history_forward = deque(maxlen = maxlen) + def __init__(self, maxlen=None): + self._history = [] + self._index = 0 + self.maxlen = maxlen def add(self, item): - if len(self.history) == 0 or self.history[-1] != item: - self.history.append(item) - self.history_forward.clear() - - def modify(self, item): + # Remove Duplicates + try: + self._history.remove(item) + except: + pass + # Remove first if list is too long + if len(self._history) > self.maxlen - 1: + del self._history[0] + # Append the item and fast forward + self._history.append(item) + self._index = len(self._history) - 1 + + def modify(self, item, unique=False): + if self._history and unique: + try: + self._history.remove(item) + self._index -= 1 + except: + pass try: - self.history[-1] = item + self._history[self._index] = item except IndexError: - raise HistoryEmptyException + self.add(item) def __len__(self): - return len(self.history) + return len(self._history) def current(self): - try: - return self.history[-1] - except IndexError: - raise HistoryEmptyException() + if self._history: + return self._history[self._index] + else: + raise HistoryEmptyException def top(self): try: - return self.history_forward[-1] + return self._history[-1] except IndexError: - try: - return self.history[-1] - except IndexError: - raise HistoryEmptyException() + raise HistoryEmptyException() def bottom(self): try: - return self.history[0] + return self._history[0] except IndexError: raise HistoryEmptyException() def back(self): - if len(self.history) > 1: - self.history_forward.appendleft( self.history.pop() ) - return self.current() - - def move(self, n, pattern=None): - if n > 0: - return self.forward() - if n < 0: - return self.back() + self._index -= 1 + if self._index < 0: + self._index = 0 + + def move(self, n): + self._index += n + if self._index > len(self._history) - 1: + self._index = len(self._history) - 1 + if self._index < 0: + self._index = 0 + + def search(self, string, n): + if n != 0 and string: + step = n > 0 and 1 or -1 + i = self._index + steps_left = steps_left_at_start = int(abs(n)) + while steps_left: + i += step + if i >= len(self._history) or i < 0: + break + if self._history[i].startswith(string): + steps_left -= 1 + if steps_left != steps_left_at_start: + self._index = i def __iter__(self): - return self.history.__iter__() + return self._history.__iter__() def next(self): - return self.history.next() + return self._history.next() def forward(self): - if len(self.history_forward) > 0: - self.history.append( self.history_forward.popleft() ) - return self.current() + if self._history: + self._index += 1 + if self._index > len(self._history) - 1: + self._index = len(self._history) - 1 + else: + self._index = 0 def fast_forward(self): - if self.history_forward: - self.history.extend(self.history_forward) - self.history_forward.clear() - - def unique(self): - found = [] - i = len(self.history) - while i: - i -= 1 - item = self.history[i] - if item in found: - del self.history[i] - else: - found.append(item) - i = len(self.history_forward) - while i: - i -= 1 - item = self.history_forward[i] - if item in found: - del self.history_forward[i] - else: - found.append(item) + if self._history: + self._index = len(self._history) - 1 + else: + self._index = 0 + + def _left(self): # used for unit test + return self._history[0:self._index+1] diff --git a/test/tc_history.py b/test/tc_history.py index b1161f2a..301ebedd 100644 --- a/test/tc_history.py +++ b/test/tc_history.py @@ -27,13 +27,13 @@ class Test(TestCase): hist.back() self.assertEqual(4, hist.current()) - self.assertEqual([3,4], list(hist)) + self.assertEqual([3,4], list(hist._left())) self.assertEqual(5, hist.top()) hist.back() self.assertEqual(3, hist.current()) - self.assertEqual([3], list(hist)) + self.assertEqual([3], list(hist._left())) # no change if current == bottom self.assertEqual(hist.current(), hist.bottom()) @@ -46,31 +46,31 @@ class Test(TestCase): hist.forward() hist.forward() self.assertEqual(5, hist.current()) - self.assertEqual([3,4,5], list(hist)) + self.assertEqual([3,4,5], list(hist._left())) self.assertEqual(3, hist.bottom()) hist.add(6) self.assertEqual(4, hist.bottom()) - self.assertEqual([4,5,6], list(hist)) + self.assertEqual([4,5,6], list(hist._left())) hist.back() hist.fast_forward() - self.assertEqual([4,5,6], list(hist)) + self.assertEqual([4,5,6], list(hist._left())) hist.back() hist.back() hist.fast_forward() - self.assertEqual([4,5,6], list(hist)) + self.assertEqual([4,5,6], list(hist._left())) hist.back() hist.back() hist.back() hist.fast_forward() - self.assertEqual([4,5,6], list(hist)) + self.assertEqual([4,5,6], list(hist._left())) hist.back() hist.back() hist.back() hist.back() hist.fast_forward() - self.assertEqual([4,5,6], list(hist)) + self.assertEqual([4,5,6], list(hist._left())) if __name__ == '__main__': main() -- cgit 1.4.1-2-gfad0 From b80a91f6fc0f8fb7af50fff9956d54da20304c9c Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 28 Aug 2010 08:01:42 +0200 Subject: Integrated new container.history implementation --- ranger/defaults/commands.py | 4 ++-- ranger/gui/widgets/console.py | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index b6e77697..adc166df 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -107,7 +107,7 @@ class search(Command): class shell(Command): def execute(self): line = parse(self.line) - if line.chunk(1)[0] == '-': + if line.chunk(1) and line.chunk(1)[0] == '-': flags = line.chunk(1)[1:] command = line.rest(2) else: @@ -122,7 +122,7 @@ class shell(Command): def tab(self): line = parse(self.line) - if line.chunk(1)[0] == '-': + if line.chunk(1) and line.chunk(1)[0] == '-': flags = line.chunk(1)[1:] command = line.rest(2) else: diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 2efb059d..89bd4a3a 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -108,6 +108,7 @@ class Console(Widget): self.focused = True self.visible = True self.line = string + self.history_search_pattern = self.line self.pos = len(string) if position is not None: self.pos = min(self.pos, position) @@ -174,7 +175,6 @@ class Console(Widget): self.pos += len(key) self.on_line_change() - self.history_search_pattern = self.line def history_move(self, n): try: @@ -184,7 +184,10 @@ class Console(Widget): else: if self.line != current and self.line != self.history.top(): self.history.modify(self.line) - self.history.move(n, self.history_search_pattern) + if self.history_search_pattern: + self.history.search(self.history_search_pattern, n) + else: + self.history.move(n) current = self.history.current() if self.line != current: self.line = self.history.current() @@ -192,8 +195,7 @@ class Console(Widget): def add_to_history(self): self.history.fast_forward() - self.history.modify(self.line) - self.history.unique() + self.history.modify(self.line, unique=True) def move(self, **keywords): direction = Direction(keywords) @@ -313,6 +315,7 @@ class Console(Widget): self.on_line_change() def on_line_change(self): + self.history_search_pattern = self.line try: cls = self._get_cmd_class() except (KeyError, ValueError, IndexError): -- cgit 1.4.1-2-gfad0 From 56f380c61f62a18b39375da7e7cd93d443be8aef Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 28 Aug 2010 08:10:34 +0200 Subject: cleaned up and fine tuned :find command --- ranger/defaults/commands.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index adc166df..f1c92274 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -260,22 +260,13 @@ class find(Command): tab = Command._tab_directory_content def execute(self): - import re - search = parse(self.line).rest(1) - search = re.escape(search) - self.fm.env.last_search = re.compile(search, re.IGNORECASE) - self.fm.search_method = 'search' - if self.count == 1: self.fm.move(right=1) self.fm.block_input(0.5) + else: + self.fm.cd(parse(self.line).rest(1)) def quick(self): - self._search() - if self.count == 1: - return True - - def _search(self): self.count = 0 line = parse(self.line) cwd = self.fm.env.cwd @@ -284,6 +275,11 @@ class find(Command): except IndexError: return False + if arg == '.': + return False + if arg == '..': + return True + deq = deque(cwd.files) deq.rotate(-cwd.pointer) i = 0 -- cgit 1.4.1-2-gfad0 From a2853ab67f2e471d9a34b4c528db6ee2024ef874 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 28 Aug 2010 08:30:52 +0200 Subject: Changed default config dir to $XDG_CONFIG_HOME/ranger --- HACKING | 4 ++-- README | 2 +- doc/colorschemes.txt | 4 ++-- doc/ranger.1 | 9 +++++---- ranger.py | 6 +++++- ranger/__init__.py | 5 ++++- ranger/__main__.py | 2 +- ranger/core/actions.py | 2 +- ranger/defaults/apps.py | 4 ++-- ranger/defaults/commands.py | 2 +- ranger/defaults/keys.py | 2 +- ranger/defaults/options.py | 2 +- ranger/gui/colorscheme.py | 6 +++--- 13 files changed, 29 insertions(+), 21 deletions(-) diff --git a/HACKING b/HACKING index 9c114e89..0424b047 100644 --- a/HACKING +++ b/HACKING @@ -56,13 +56,13 @@ assuming is a "SettingsAware" object. * Changing commands, adding aliases: ranger/defaults/commands.py -or ~/.ranger/commands.py +or ~/.config/ranger/commands.py * Adding colorschemes: Copy ranger/colorschemes/default.py to ranger/colorschemes/myscheme.py and modify it according to your needs. Alternatively, mimic the jungle colorscheme. It subclasses the default scheme and just modifies a few things. -In ranger/defaults/options.py (or ~/.ranger/options.py), change +In ranger/defaults/options.py (or ~/.config/ranger/options.py), change colorscheme = 'default' to: colorscheme = 'myscheme' diff --git a/README b/README index dad70262..babcfbbb 100644 --- a/README +++ b/README @@ -75,7 +75,7 @@ parent directories and to the right there's a preview of the object you're pointing at. Now use the Arrow Keys to navigate, Enter to open a file or type Q to quit. -To customize ranger, copy the files from ranger/defaults/ to ~/.ranger/ +To customize ranger, copy the files from ranger/defaults/ to ~/.config/ranger/ and modify them according to your wishes. diff --git a/doc/colorschemes.txt b/doc/colorschemes.txt index 905c7a3e..e7bc2c0a 100644 --- a/doc/colorschemes.txt +++ b/doc/colorschemes.txt @@ -65,7 +65,7 @@ Specify a Colorscheme --------------------- Colorschemes are searched for in these directories: -~/.ranger/colorschemes/ +~/.config/ranger/colorschemes/ /ranger/colorschemes/ To specify which colorscheme to use, define the variable "colorscheme" @@ -73,7 +73,7 @@ in your options.py: colorscheme = colorschemes.default This means, use the (one) colorscheme contained in -either ~/.ranger/colorschemes/default.py or /ranger/colorschemes/default.py. +either ~/.config/ranger/colorschemes/default.py or /ranger/colorschemes/default.py. You can define more than one colorscheme in a colorscheme file. The one named "Scheme" will be chosen in that case. If there is no colorscheme diff --git a/doc/ranger.1 b/doc/ranger.1 index 3db90285..fab42496 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -38,7 +38,8 @@ Return the exit code 1 if ranger is used to run a file, for example with `ranger --fail-unless-cd filename`. This can be useful for scripts. .TP -r \fIdir\fR, --confdir=\fIdir\fR -Define a different configuration directory. The default is $HOME/.ranger. +Define a different configuration directory. The default is +$XDG_CONFIG_HOME/ranger (which defaults to ~/.config/ranger) .TP -m \fIn\fR, --mode=\fIn\fR When a filename is supplied, make it run in mode \fIn\fR. Check the @@ -177,17 +178,17 @@ of your parent shell after exiting ranger: ranger() { command ranger --fail-unless-cd $@ && - cd "$(grep \\^\\' ~/.ranger/bookmarks | cut -b3-)" + cd "$(grep \\^\\' ~/.config/ranger/bookmarks | cut -b3-)" } .\"----------------------------------------- .SH CONFIGURATION The files in .B ranger/defaults/ can be copied into your configuration directory (by default, this is -$HOME/.ranger) and customized according to your wishes. +~/.config/ranger) and customized according to your wishes. Most files don't have to be copied completely though: Just define those settings you want to add or change and they will override the defauls. -Colorschemes can be placed in $HOME/.ranger/colorschemes. +Colorschemes can be placed in ~/.config/ranger/colorschemes. .P All configuration is done in Python. Each configuration file should contain sufficient documentation. diff --git a/ranger.py b/ranger.py index f290d796..cc7f14ed 100755 --- a/ranger.py +++ b/ranger.py @@ -23,7 +23,11 @@ # after you exit ranger by starting it with: source ranger ranger """": if [ $1 ]; then - $@ --fail-unless-cd && cd "$(grep \^\' ~/.ranger/bookmarks | cut -b3-)" + if [ -z $XDG_CONFIG_HOME ]; then + $@ --fail-unless-cd && cd "$(grep \^\' ~/.config/ranger/bookmarks | cut -b3-)" + else + $@ --fail-unless-cd && cd "$(grep \^\' "$XDG_CONFIG_HOME"/ranger/bookmarks | cut -b3-)" + fi else echo "usage: source path/to/ranger.py path/to/ranger.py" fi diff --git a/ranger/__init__.py b/ranger/__init__.py index e0e8e1bf..57e0e5a2 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -31,7 +31,10 @@ Copyright (C) 2009, 2010 Roman Zimbelmann """ USAGE = '%prog [options] [path/filename]' -DEFAULT_CONFDIR = '~/.ranger' +if 'XDG_CONFIG_HOME' in os.environ and os.environ['XDG_CONFIG_HOME']: + DEFAULT_CONFDIR = os.environ['XDG_CONFIG_HOME'] + '/ranger' +else: + DEFAULT_CONFDIR = '~/.config/ranger' RANGERDIR = os.path.dirname(__file__) LOGFILE = '/tmp/errorlog' arg = OpenStruct( diff --git a/ranger/__main__.py b/ranger/__main__.py index b3a41776..28284ef5 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -113,7 +113,7 @@ def load_settings(fm, clean): pass # COMPAT WARNING if hasattr(keys, 'initialize_commands'): - print("Warning: the syntax for ~/.ranger/keys.py has changed.") + 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) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index d12d9c6c..a5ee0d4d 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -423,7 +423,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): # -------------------------- # -- Tags # -------------------------- - # Tags are saved in ~/.ranger/tagged and simply mark if a + # Tags are saved in ~/.config/ranger/tagged and simply mark if a # file is important to you in any context. def tag_toggle(self, movedown=None): diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 48735196..47eff0c9 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -17,13 +17,13 @@ This is the default ranger configuration file for filetype detection and application handling. -You can place this file in your ~/.ranger/ directory and it will be used +You can place this file in your ~/.config/ranger/ directory and it will be used instead of this one. Though, to minimize your effort when upgrading ranger, you may want to subclass CustomApplications rather than making a full copy. This example modifies the behaviour of "feh" and adds a custom media player: -#### start of the ~/.ranger/apps.py example +#### start of the ~/.config/ranger/apps.py example from ranger.defaults.apps import CustomApplications as DefaultApps from ranger.api.apps import * diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index f1c92274..b6bf7e2b 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -32,7 +32,7 @@ The return value for quick() can be: The return value for execute() doesn't matter. If you want to add custom commands, you can create a file -~/.ranger/commands.py, add the line: +~/.config/ranger/commands.py, add the line: from ranger.api.commands import * and write some command definitions, for example: diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index e72f4c91..7d8ccc4a 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -58,7 +58,7 @@ dgg => fm.cut(foo=bar, dirarg=Direction(to=0)) 5dgg => fm.cut(foo=bar, narg=5, dirarg=Direction(to=0)) 5d3gg => fm.cut(foo=bar, narg=5, dirarg=Direction(to=3)) -Example ~/.ranger/keys.py +Example ~/.config/ranger/keys.py ------------------------- from ranger.api.keys import * diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index d3c420af..126d4bad 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -17,7 +17,7 @@ This is the default configuration file of ranger. There are two ways of customizing ranger. The first and recommended -method is creating a file at ~/.ranger/options.py and adding +method is creating a file at ~/.config/ranger/options.py and adding those lines you want to change. It might look like this: from ranger.api.options import * diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py index 501b8788..5b317acb 100644 --- a/ranger/gui/colorscheme.py +++ b/ranger/gui/colorscheme.py @@ -24,7 +24,7 @@ The values are specified in ranger.gui.color. A colorscheme must... 1. be inside either of these directories: -~/.ranger/colorschemes/ +~/.config/ranger/colorschemes/ path/to/ranger/colorschemes/ 2. be a subclass of ranger.gui.colorscheme.ColorScheme @@ -121,7 +121,7 @@ class ColorScheme(SettingsAware): return fg, -1, attr def _colorscheme_name_to_class(signal): - # Find the colorscheme. First look for it at ~/.ranger/colorschemes, + # Find the colorscheme. First look for it at ~/.config/ranger/colorschemes, # then at RANGERDIR/colorschemes. If the file contains a class # named Scheme, it is used. Otherwise, an arbitrary other class # is picked. @@ -139,7 +139,7 @@ def _colorscheme_name_to_class(signal): except: return False - # create ~/.ranger/colorschemes/__init__.py if it doesn't exist + # 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') -- cgit 1.4.1-2-gfad0 From bd33933dd1cb708a5e708b361c1842190a1c9e4b Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 28 Aug 2010 08:52:02 +0200 Subject: Fixed new history implementation --- ranger/container/history.py | 22 +++++++++++++++++----- ranger/core/environment.py | 4 ++-- ranger/fsobject/directory.py | 4 ++-- ranger/gui/widgets/console.py | 1 + 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/ranger/container/history.py b/ranger/container/history.py index 9a3d6c4a..d7a45500 100644 --- a/ranger/container/history.py +++ b/ranger/container/history.py @@ -17,17 +17,25 @@ class HistoryEmptyException(Exception): pass class History(object): - def __init__(self, maxlen=None): + def __init__(self, maxlen=None, unique=True): self._history = [] self._index = 0 self.maxlen = maxlen + self.unique = unique def add(self, item): + # Remove everything after index + if self._index < len(self._history) - 2: + del self._history[:self._index+1] # Remove Duplicates - try: - self._history.remove(item) - except: - pass + if self.unique: + try: + self._history.remove(item) + except: + pass + else: + if self._history and self._history[-1] == item: + del self._history[-1] # Remove first if list is too long if len(self._history) > self.maxlen - 1: del self._history[0] @@ -72,6 +80,7 @@ class History(object): self._index -= 1 if self._index < 0: self._index = 0 + return self.current() def move(self, n): self._index += n @@ -79,6 +88,7 @@ class History(object): self._index = len(self._history) - 1 if self._index < 0: self._index = 0 + return self.current() def search(self, string, n): if n != 0 and string: @@ -93,6 +103,7 @@ class History(object): steps_left -= 1 if steps_left != steps_left_at_start: self._index = i + return self.current() def __iter__(self): return self._history.__iter__() @@ -107,6 +118,7 @@ class History(object): self._index = len(self._history) - 1 else: self._index = 0 + return self.current() def fast_forward(self): if self._history: diff --git a/ranger/core/environment.py b/ranger/core/environment.py index fca2168b..61db8694 100644 --- a/ranger/core/environment.py +++ b/ranger/core/environment.py @@ -54,7 +54,7 @@ class Environment(SettingsAware, SignalDispatcher): self.keybuffer = KeyBuffer(None, None) self.keymanager = KeyManager(self.keybuffer, ALLOWED_CONTEXTS) self.copy = set() - self.history = History(self.settings.max_history_size) + self.history = History(self.settings.max_history_size, unique=False) try: self.username = pwd.getpwuid(os.geteuid()).pw_name @@ -166,7 +166,7 @@ class Environment(SettingsAware, SignalDispatcher): def history_go(self, relative): """Move relative in history""" if self.history: - self.history.move(relative).go() + self.history.move(relative).go(history=False) def enter_dir(self, path, history = True): """Enter given path""" diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index f1bcd9a3..05c07009 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -420,10 +420,10 @@ class Directory(FileSystemObject, Accumulator, SettingsAware): return True return self.last_used + seconds < time() - def go(self): + def go(self, history=True): """enter the directory if the filemanager is running""" if self.fm: - return self.fm.enter_dir(self.path) + return self.fm.enter_dir(self.path, history=history) return False def empty(self): diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 89bd4a3a..57264292 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -112,6 +112,7 @@ class Console(Widget): self.pos = len(string) if position is not None: self.pos = min(self.pos, position) + self.history.fast_forward() self.history.add('') return True -- cgit 1.4.1-2-gfad0 From 0df1d653deb7a9a8ca30f012475891e339b4449f Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 28 Aug 2010 21:27:43 +0200 Subject: Changed version number to 1.2 (testing) to adhere with versioning scheme --- HACKING | 2 +- README | 4 ++-- doc/ranger.1 | 2 +- ranger/__init__.py | 2 +- ranger/__main__.py | 9 ++++++++- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/HACKING b/HACKING index 0424b047..1794c8e5 100644 --- a/HACKING +++ b/HACKING @@ -92,4 +92,4 @@ X.Y.Z, where: * X: Major version, milestone * Y: Minor version, odd number => stable version -* Z: Revision +* Z: Revision, may be omitted if zero diff --git a/README b/README index babcfbbb..912b8718 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -Ranger v.1.1.2 -============== +Ranger v.1.2 +============ Ranger is a free console file manager that gives you greater flexibility and a good overview of your files without having to leave your *nix console. diff --git a/doc/ranger.1 b/doc/ranger.1 index fab42496..10f78d47 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.TH RANGER 1 ranger-1.1.2 +.TH RANGER 1 ranger-1.2 .SH NAME ranger - visual file manager .\"----------------------------------------- diff --git a/ranger/__init__.py b/ranger/__init__.py index 57e0e5a2..282fd2b1 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -20,7 +20,7 @@ import sys from ranger.ext.openstruct import OpenStruct __license__ = 'GPL3' -__version__ = '1.1.2' +__version__ = '1.2' __credits__ = 'Roman Zimbelmann' __author__ = 'Roman Zimbelmann' __maintainer__ = 'Roman Zimbelmann' diff --git a/ranger/__main__.py b/ranger/__main__.py index 28284ef5..5a8411d6 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -30,7 +30,14 @@ def parse_arguments(): from optparse import OptionParser, SUPPRESS_HELP from ranger import __version__, USAGE, DEFAULT_CONFDIR from ranger.ext.openstruct import OpenStruct - parser = OptionParser(usage=USAGE, version='ranger ' + __version__) + + 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 == 1 else ' (testing)' + 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") -- cgit 1.4.1-2-gfad0 From fd29d7e6bb22d6176717ac5dc9e3c8f88009732b Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 28 Aug 2010 22:36:48 +0200 Subject: Tuned versioning scheme to be more intuitive, back to 1.1.2 --- HACKING | 2 +- README | 4 ++-- doc/ranger.1 | 2 +- ranger/__init__.py | 2 +- ranger/__main__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/HACKING b/HACKING index 1794c8e5..dd384758 100644 --- a/HACKING +++ b/HACKING @@ -91,5 +91,5 @@ Version Numbering X.Y.Z, where: * X: Major version, milestone -* Y: Minor version, odd number => stable version +* Y: Minor version, even number => stable version * Z: Revision, may be omitted if zero diff --git a/README b/README index 912b8718..babcfbbb 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -Ranger v.1.2 -============ +Ranger v.1.1.2 +============== Ranger is a free console file manager that gives you greater flexibility and a good overview of your files without having to leave your *nix console. diff --git a/doc/ranger.1 b/doc/ranger.1 index 10f78d47..fab42496 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.TH RANGER 1 ranger-1.2 +.TH RANGER 1 ranger-1.1.2 .SH NAME ranger - visual file manager .\"----------------------------------------- diff --git a/ranger/__init__.py b/ranger/__init__.py index 282fd2b1..57e0e5a2 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -20,7 +20,7 @@ import sys from ranger.ext.openstruct import OpenStruct __license__ = 'GPL3' -__version__ = '1.2' +__version__ = '1.1.2' __credits__ = 'Roman Zimbelmann' __author__ = 'Roman Zimbelmann' __maintainer__ = 'Roman Zimbelmann' diff --git a/ranger/__main__.py b/ranger/__main__.py index 5a8411d6..39e7f1f3 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -34,7 +34,7 @@ def parse_arguments(): 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 == 1 else ' (testing)' + version_tag = ' (stable)' if int(minor_version) % 2 == 0 else ' (testing)' version_string = 'ranger ' + __version__ + version_tag parser = OptionParser(usage=USAGE, version=version_string) -- cgit 1.4.1-2-gfad0 From ec823be00aa7175ebcb836a240a20f6389159c2e Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 29 Aug 2010 19:16:57 +0200 Subject: Removed symlink in test/ --- test/all_benchmarks.py | 8 ++++++-- test/all_tests.py | 6 +++++- test/bm_human_readable.py | 6 ++++++ test/bm_loader.py | 6 ++++++ test/ranger | 1 - test/tc_bookmarks.py | 6 ++++++ test/tc_colorscheme.py | 6 ++++++ test/tc_direction.py | 6 ++++++ test/tc_directory.py | 5 +++++ test/tc_displayable.py | 6 ++++++ test/tc_ext.py | 6 ++++++ test/tc_history.py | 6 ++++++ test/tc_human_readable.py | 6 ++++++ test/tc_keyapi.py | 6 ++++++ test/tc_loader.py | 6 ++++++ test/tc_newkeys.py | 10 +++++++--- test/tc_signal.py | 6 ++++++ test/tc_ui.py | 6 ++++++ test/tc_utfwidth.py | 8 +++++++- 19 files changed, 108 insertions(+), 8 deletions(-) delete mode 120000 test/ranger diff --git a/test/all_benchmarks.py b/test/all_benchmarks.py index 20f11ad8..a3612701 100755 --- a/test/all_benchmarks.py +++ b/test/all_benchmarks.py @@ -19,9 +19,13 @@ Run all the benchmarks inside this directory. Usage: ./all_benchmarks.py [count] [regexp-filters...] """ -import os -import re +import os.path import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + +import re import time if __name__ == '__main__': diff --git a/test/all_tests.py b/test/all_tests.py index 7cfc855f..0c184df5 100755 --- a/test/all_tests.py +++ b/test/all_tests.py @@ -19,8 +19,12 @@ Run all the tests inside this directory as a test suite. Usage: ./all_tests.py [verbosity] """ -import os +import os.path import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest if __name__ == '__main__': diff --git a/test/bm_human_readable.py b/test/bm_human_readable.py index 83f2a057..ef400774 100644 --- a/test/bm_human_readable.py +++ b/test/bm_human_readable.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + from ranger.ext.human_readable import * # The version before 2010/06/24: diff --git a/test/bm_loader.py b/test/bm_loader.py index 968640a5..552954a7 100644 --- a/test/bm_loader.py +++ b/test/bm_loader.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + from ranger.core.loader import Loader from ranger.fsobject import Directory, File from ranger.ext.openstruct import OpenStruct diff --git a/test/ranger b/test/ranger deleted file mode 120000 index 5459458c..00000000 --- a/test/ranger +++ /dev/null @@ -1 +0,0 @@ -../ranger \ No newline at end of file diff --git a/test/tc_bookmarks.py b/test/tc_bookmarks.py index 9b41f1c6..59435f06 100644 --- a/test/tc_bookmarks.py +++ b/test/tc_bookmarks.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + from os.path import realpath, join, dirname import unittest import os diff --git a/test/tc_colorscheme.py b/test/tc_colorscheme.py index 8d6adee6..eefb1e4f 100644 --- a/test/tc_colorscheme.py +++ b/test/tc_colorscheme.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + from unittest import TestCase, main import random import ranger.colorschemes diff --git a/test/tc_direction.py b/test/tc_direction.py index 5c1776cf..16c26dab 100644 --- a/test/tc_direction.py +++ b/test/tc_direction.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest from ranger.ext.direction import Direction from ranger.ext.openstruct import OpenStruct diff --git a/test/tc_directory.py b/test/tc_directory.py index a702c4db..754253b3 100644 --- a/test/tc_directory.py +++ b/test/tc_directory.py @@ -13,7 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + from os.path import realpath, join, dirname from ranger import fsobject diff --git a/test/tc_displayable.py b/test/tc_displayable.py index 1c66a40e..72e0507d 100644 --- a/test/tc_displayable.py +++ b/test/tc_displayable.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest import curses from random import randint diff --git a/test/tc_ext.py b/test/tc_ext.py index 919f35a2..495591a1 100644 --- a/test/tc_ext.py +++ b/test/tc_ext.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest from collections import deque diff --git a/test/tc_history.py b/test/tc_history.py index 301ebedd..02a8bb9f 100644 --- a/test/tc_history.py +++ b/test/tc_history.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + from ranger.container import History from unittest import TestCase, main import unittest diff --git a/test/tc_human_readable.py b/test/tc_human_readable.py index b931ba21..493e6d3a 100644 --- a/test/tc_human_readable.py +++ b/test/tc_human_readable.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest from ranger.ext.human_readable import human_readable as hr diff --git a/test/tc_keyapi.py b/test/tc_keyapi.py index 48282a7d..79d89fa5 100644 --- a/test/tc_keyapi.py +++ b/test/tc_keyapi.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + from unittest import TestCase, main class Test(TestCase): diff --git a/test/tc_loader.py b/test/tc_loader.py index 9f7f7322..5a2e5a68 100644 --- a/test/tc_loader.py +++ b/test/tc_loader.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest import os from os.path import realpath, join, dirname diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py index fd856f17..c9597201 100644 --- a/test/tc_newkeys.py +++ b/test/tc_newkeys.py @@ -12,7 +12,13 @@ # 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 os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] +sys.path[1:1] = ['..'] from unittest import TestCase, main @@ -22,8 +28,6 @@ from ranger.container.keymap import * from ranger.container.keybuffer import KeyBuffer from ranger.ext.keybinding_parser import parse_keybinding -import sys - def simulate_press(self, string): for char in parse_keybinding(string): self.add(char) diff --git a/test/tc_signal.py b/test/tc_signal.py index f31681f4..3b1bac40 100644 --- a/test/tc_signal.py +++ b/test/tc_signal.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest import gc from ranger.ext.signal_dispatcher import * diff --git a/test/tc_ui.py b/test/tc_ui.py index dc8d669d..fa2bdcac 100644 --- a/test/tc_ui.py +++ b/test/tc_ui.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + import unittest import curses diff --git a/test/tc_utfwidth.py b/test/tc_utfwidth.py index 2aa5fa6d..0288c17b 100644 --- a/test/tc_utfwidth.py +++ b/test/tc_utfwidth.py @@ -12,7 +12,13 @@ # 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 os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] +sys.path[1:1] = ['..'] from unittest import TestCase, main from ranger.ext.utfwidth import * -- cgit 1.4.1-2-gfad0 From a1032643b32f446bccce1253aab7c470c1484d31 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 29 Aug 2010 19:58:28 +0200 Subject: defaults.keys: key pL to create relative symlinks --- ranger/core/actions.py | 8 +++++-- ranger/defaults/keys.py | 5 +++-- ranger/ext/relative_symlink.py | 39 +++++++++++++++++++++++++++++++++++ ranger/help/fileop.py | 3 ++- test/tc_relative_symlink.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 ranger/ext/relative_symlink.py create mode 100644 test/tc_relative_symlink.py diff --git a/ranger/core/actions.py b/ranger/core/actions.py index a5ee0d4d..a93344a4 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -23,6 +23,7 @@ from inspect import cleandoc import ranger 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 @@ -660,11 +661,14 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.env.cut = True self.ui.browser.main_column.request_redraw() - def paste_symlink(self): + def paste_symlink(self, relative=False): copied_files = self.env.copy for f in copied_files: try: - symlink(f.path, join(getcwd(), f.basename)) + if relative: + relative_symlink(f.path, join(getcwd(), f.basename)) + else: + symlink(f.path, join(getcwd(), f.basename)) except Exception as x: self.notify(x) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index 7d8ccc4a..9f0c78cb 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -175,9 +175,10 @@ map('da', fm.cut(mode='add')) map('dr', fm.cut(mode='remove')) map('pp', fm.paste()) map('po', fm.paste(overwrite=True)) -map('pl', fm.paste_symlink()) +map('pl', fm.paste_symlink(relative=False)) +map('pL', fm.paste_symlink(relative=True)) map('p', fm.hint('press *p* to confirm pasting' \ - ', *o* to overwrite or *l* to create symlinks')) + ', *o*verwrite, create sym*l*inks, relative sym*L*inks')) map('u', fm.hint("un*y*ank, unbook*m*ark, unselect:*v*")) map('ud', 'uy', fm.uncut()) diff --git a/ranger/ext/relative_symlink.py b/ranger/ext/relative_symlink.py new file mode 100644 index 00000000..bba00e39 --- /dev/null +++ b/ranger/ext/relative_symlink.py @@ -0,0 +1,39 @@ +# 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 os import symlink, sep +from os.path import dirname, join + +def relative_symlink(src, dst): + common_base = get_common_base(src, dst) + symlink(get_relative_source_file(src, dst, common_base), dst) + +def get_relative_source_file(src, dst, common_base=None): + if common_base is None: + common_base = get_common_base(src, dst) + return '../' * dst.count('/', len(common_base)) + src[len(common_base):] + +def get_common_base(src, dst): + if not src or not dst: + return '/' + i = 0 + while True: + new_i = src.find(sep, i + 1) + if new_i == -1: + break + if not dst.startswith(src[:new_i + 1]): + break + i = new_i + return src[:i + 1] diff --git a/ranger/help/fileop.py b/ranger/help/fileop.py index f8401800..ac23c6d4 100644 --- a/ranger/help/fileop.py +++ b/ranger/help/fileop.py @@ -31,7 +31,7 @@ harm your files: :chmod Change the rights of the selection :delete DELETES ALL FILES IN THE SELECTION :rename Change the name of the current file -pp, pl, po Pastes the copied files in different ways +pp, pl, pL, po Pastes the copied files in different ways Think twice before using these commands or key combinations. @@ -67,6 +67,7 @@ The "highlighted file", or the "current file", is the one below the cursor. Instead, a "_" character will be appended to the new filename. po paste the copied/cut files. Existing files are overwritten. pl create symbolic links to the copied/cut files. + pL create relative symbolic links to the copied/cut files. The difference between copying and cutting should be intuitive: diff --git a/test/tc_relative_symlink.py b/test/tc_relative_symlink.py new file mode 100644 index 00000000..a202513d --- /dev/null +++ b/test/tc_relative_symlink.py @@ -0,0 +1,47 @@ +# 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 os.path +import sys +rangerpath = os.path.join(os.path.dirname(__file__), '..') +if sys.path[1] != rangerpath: + sys.path[1:1] = [rangerpath] + +import unittest +from ranger.ext.relative_symlink import * +rel = get_relative_source_file + +class Test(unittest.TestCase): + def test_foo(self): + self.assertEqual('../foo', rel('/foo', '/x/bar')) + self.assertEqual('../../foo', rel('/foo', '/x/y/bar')) + self.assertEqual('../../a/b/foo', rel('/a/b/foo', '/x/y/bar')) + self.assertEqual('../../x/b/foo', rel('/x/b/foo', '/x/y/bar', + common_base='/')) + self.assertEqual('../b/foo', rel('/x/b/foo', '/x/y/bar')) + self.assertEqual('../b/foo', rel('/x/b/foo', '/x/y/bar')) + + def test_get_common_base(self): + self.assertEqual('/', get_common_base('', '')) + self.assertEqual('/', get_common_base('', '/')) + self.assertEqual('/', get_common_base('/', '')) + self.assertEqual('/', get_common_base('/', '/')) + self.assertEqual('/', get_common_base('/bla/bar/x', '/foo/bar/a')) + self.assertEqual('/foo/bar/', get_common_base('/foo/bar/x', '/foo/bar/a')) + self.assertEqual('/foo/', get_common_base('/foo/bar/x', '/foo/baz/a')) + self.assertEqual('/foo/', get_common_base('/foo/bar/x', '/foo/baz/a')) + self.assertEqual('/', get_common_base('//foo/bar/x', '/foo/baz/a')) + +if __name__ == '__main__': unittest.main() -- cgit 1.4.1-2-gfad0 From c9dd8d5e0641fecc73e6cb8aa6fe2ffaa6dec4a6 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 29 Aug 2010 19:59:16 +0200 Subject: removed test/__init__.py (not needed) --- test/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/__init__.py diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29b..00000000 -- cgit 1.4.1-2-gfad0 From ff64e3730b653fb23f0b6a4688437ab141efb19d Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 30 Aug 2010 14:28:42 +0200 Subject: Sort settings in ranger/shared/settings.py --- ranger/shared/settings.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index f5d8614f..3a178882 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -20,35 +20,35 @@ from ranger.ext.signal_dispatcher import SignalDispatcher from ranger.ext.openstruct import OpenStruct ALLOWED_SETTINGS = { - 'show_hidden': bool, - 'show_hidden_bookmarks': bool, - 'show_cursor': bool, 'autosave_bookmarks': bool, - 'save_console_history': 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_borders': bool, 'draw_bookmark_borders': bool, - 'dirname_in_tabs': bool, - 'sort': str, - 'sort_reverse': 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, - 'update_title': bool, - 'shorten_title': int, # Note: False is an instance of int + 'sort_reverse': bool, + 'sort': str, 'tilde_in_titlebar': bool, - 'max_history_size': (int, type(None)), - 'max_console_history_size': (int, type(None)), - 'scroll_offset': int, - 'preview_files': bool, - 'preview_directories': bool, - 'mouse_enabled': bool, - 'flushinput': bool, - 'colorscheme': str, - 'colorscheme_overlay': (type(None), type(lambda:0)), - 'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'), + 'update_title': bool, 'xterm_alt_key': bool, } -- cgit 1.4.1-2-gfad0 From b1c1e1d88bc511306e4d786dea4234f1cbc6f041 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 11 Sep 2010 14:26:31 +0200 Subject: defaults.commands: Removed "quick" method from :cd --- ranger/defaults/commands.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index b6bf7e2b..d3c05023 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -86,18 +86,6 @@ class cd(Command): def tab(self): return self._tab_only_directories() - def quick(self): - from os.path import isdir, join, normpath - line = parse(self.line) - cwd = self.fm.env.cwd.path - - rel_dest = line.rest(1) - if not rel_dest: - return False - - abs_dest = normpath(join(cwd, rel_dest)) - return rel_dest != '.' and isdir(abs_dest) - class search(Command): def execute(self): -- cgit 1.4.1-2-gfad0 From 547173247b8ce5dc40fa4f8b9fcf822270661c0f Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 11 Sep 2010 20:49:05 +0200 Subject: README: added ranger starting function example --- README | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README b/README index babcfbbb..80331dcb 100644 --- a/README +++ b/README @@ -78,6 +78,20 @@ or type Q to quit. To customize ranger, copy the files from ranger/defaults/ to ~/.config/ranger/ and modify them according to your wishes. +The author of ranger uses this function (in ~/.bashrc) to start ranger: +function ranger-cd { + before="$(pwd)" + python2.6 /the/path/to/ranger/ranger.py --fail-unless-cd "$@" || return 0 + after="$(grep \^\' ~/.config/ranger/bookmarks | cut -b3-)" + if [[ "$before" != "$after" ]]; then + cd "$after" + fi +} +bind '"\C-o":"ranger-cd\C-m"' + +This changes the directory after you close ranger and adds the shortcut + for starting ranger. + Troubleshooting, Getting Help ----------------------------- -- cgit 1.4.1-2-gfad0 From 73e3ab44213174e1f01a06cf51ca9fd53ee35ea6 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 11 Sep 2010 21:02:41 +0200 Subject: chmod --- doc/print_keys.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 doc/print_keys.py diff --git a/doc/print_keys.py b/doc/print_keys.py old mode 100644 new mode 100755 -- cgit 1.4.1-2-gfad0 From 471fc68047418ec7af877598b19696837ed7750c Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 11 Sep 2010 21:02:53 +0200 Subject: ranger.py: fixed escape in embedded shellscript --- ranger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger.py b/ranger.py index cc7f14ed..010ca04b 100755 --- a/ranger.py +++ b/ranger.py @@ -23,7 +23,7 @@ # after you exit ranger by starting it with: source ranger ranger """": if [ $1 ]; then - if [ -z $XDG_CONFIG_HOME ]; then + if [ -z "$XDG_CONFIG_HOME" ]; then $@ --fail-unless-cd && cd "$(grep \^\' ~/.config/ranger/bookmarks | cut -b3-)" else $@ --fail-unless-cd && cd "$(grep \^\' "$XDG_CONFIG_HOME"/ranger/bookmarks | cut -b3-)" -- cgit 1.4.1-2-gfad0 From 27eec7c6b53303acfd7713f7cc696ea50894a6b1 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 11 Sep 2010 22:56:00 +0200 Subject: Makefile, setup.py: Fixed `make doc` --- Makefile | 1 - setup.py | 47 ++++++++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index 1b26e117..fd525721 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,6 @@ doc: cleandoc $(PYTHON) -c 'import pydoc, sys; \ sys.path[0] = "$(CWD)"; \ pydoc.writedocs("$(CWD)")' - rm $(DOCDIR)/test* find . -name \*.html -exec sed -i 's|'$(CWD)'|../..|g' -- {} \; cleandoc: diff --git a/setup.py b/setup.py index 17cfc660..587b52c0 100755 --- a/setup.py +++ b/setup.py @@ -17,26 +17,27 @@ import distutils.core import ranger -distutils.core.setup( - name='ranger', - description='Vim-like file manager', - version=ranger.__version__, - author=ranger.__author__, - author_email=ranger.__email__, - license=ranger.__license__, - url='http://savannah.nongnu.org/projects/ranger', - scripts=['scripts/ranger'], - data_files=[('share/man/man1', ['doc/ranger.1'])], - package_data={'ranger': ['data/*']}, - packages=('ranger', - 'ranger.api', - 'ranger.colorschemes', - 'ranger.container', - 'ranger.core', - 'ranger.defaults', - 'ranger.ext', - 'ranger.fsobject', - 'ranger.gui', - 'ranger.gui.widgets', - 'ranger.help', - 'ranger.shared')) +if __name__ == '__main__': + distutils.core.setup( + name='ranger', + description='Vim-like file manager', + version=ranger.__version__, + author=ranger.__author__, + author_email=ranger.__email__, + license=ranger.__license__, + url='http://savannah.nongnu.org/projects/ranger', + scripts=['scripts/ranger'], + data_files=[('share/man/man1', ['doc/ranger.1'])], + package_data={'ranger': ['data/*']}, + packages=('ranger', + 'ranger.api', + 'ranger.colorschemes', + 'ranger.container', + 'ranger.core', + 'ranger.defaults', + 'ranger.ext', + 'ranger.fsobject', + 'ranger.gui', + 'ranger.gui.widgets', + 'ranger.help', + 'ranger.shared')) -- cgit 1.4.1-2-gfad0 From 63a9639d62c208f78d9e5c8740f10cce74a4e967 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 11 Sep 2010 23:47:35 +0200 Subject: ranger.py Fixed embedded shellscript (quotes for bash) --- ranger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger.py b/ranger.py index 010ca04b..5652ba69 100755 --- a/ranger.py +++ b/ranger.py @@ -24,9 +24,9 @@ """": if [ $1 ]; then if [ -z "$XDG_CONFIG_HOME" ]; then - $@ --fail-unless-cd && cd "$(grep \^\' ~/.config/ranger/bookmarks | cut -b3-)" + "$@" --fail-unless-cd && cd "$(grep \^\' ~/.config/ranger/bookmarks | cut -b3-)" else - $@ --fail-unless-cd && cd "$(grep \^\' "$XDG_CONFIG_HOME"/ranger/bookmarks | cut -b3-)" + "$@" --fail-unless-cd && cd "$(grep \^\' "$XDG_CONFIG_HOME"/ranger/bookmarks | cut -b3-)" fi else echo "usage: source path/to/ranger.py path/to/ranger.py" -- cgit 1.4.1-2-gfad0 From eb8737ec4473b10e2c4d7152f4b8320d3a62c6d5 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 13 Sep 2010 12:00:03 +0200 Subject: fsobject.file: Don't preview sockets --- ranger/fsobject/file.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ranger/fsobject/file.py b/ranger/fsobject/file.py index 0debb44c..5e79c2d1 100644 --- a/ranger/fsobject/file.py +++ b/ranger/fsobject/file.py @@ -76,7 +76,9 @@ class File(FileSystemObject): def has_preview(self): if not self.fm.settings.preview_files: return False - if not self.accessible or self.is_fifo or self.is_device: + if self.is_socket or self.is_fifo or self.is_device: + return False + if not self.accessible: return False if PREVIEW_WHITELIST.search(self.basename): return True -- cgit 1.4.1-2-gfad0 From 0dd0765eb2ad3e33c3ad844a664fcd99db45c7b0 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 13 Sep 2010 12:02:26 +0200 Subject: fsobject.directory: removed unused attribute --- ranger/fsobject/directory.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index 05c07009..9bdb4caa 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -63,7 +63,6 @@ class Directory(FileSystemObject, Accumulator, SettingsAware): filter = None marked_items = None scroll_begin = 0 - scroll_offset = 0 mount_path = '/' disk_usage = 0 @@ -213,7 +212,6 @@ class Directory(FileSystemObject, Accumulator, SettingsAware): yield self.disk_usage = disk_usage - self.scroll_offset = 0 self.filenames = filenames self.files = files -- cgit 1.4.1-2-gfad0 From 6df94b8b2ae44d3c44558204ba7483775f230d12 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 13 Sep 2010 12:49:28 +0200 Subject: README: Usage Tips section --- README | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README b/README index 80331dcb..4fd9876f 100644 --- a/README +++ b/README @@ -78,6 +78,10 @@ or type Q to quit. To customize ranger, copy the files from ranger/defaults/ to ~/.config/ranger/ and modify them according to your wishes. + +Usage Tips +---------- + The author of ranger uses this function (in ~/.bashrc) to start ranger: function ranger-cd { before="$(pwd)" @@ -92,6 +96,8 @@ bind '"\C-o":"ranger-cd\C-m"' This changes the directory after you close ranger and adds the shortcut for starting ranger. +To change back to the previous directory, you can type: cd - + Troubleshooting, Getting Help ----------------------------- -- cgit 1.4.1-2-gfad0 From 414b01ecfc75d2c977b52c93ab4045f959246914 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 13 Sep 2010 12:54:32 +0200 Subject: ranger.main: no file previews when user == root --- ranger/__main__.py | 2 ++ ranger/shared/settings.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ranger/__main__.py b/ranger/__main__.py index 39e7f1f3..7db82a57 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -212,6 +212,8 @@ def main(): 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() diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py index 3a178882..7604af12 100644 --- a/ranger/shared/settings.py +++ b/ranger/shared/settings.py @@ -66,7 +66,9 @@ class SettingObject(SignalDispatcher): if name[0] == '_': self.__dict__[name] = value else: - assert name in self._settings, "No such setting: {0}!".format(name) + 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]) -- cgit 1.4.1-2-gfad0 From 81f5ac9e8b9dbcbccbd01ad8f046ccf26fb53430 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 13 Sep 2010 13:06:48 +0200 Subject: Beware of low-flying butterflies --- CHANGELOG | 18 ++++++++++++++++++ README | 2 +- doc/ranger.1 | 2 +- ranger/__init__.py | 2 +- ranger/__main__.py | 5 ++++- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3b2ee97a..f1417fee 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,21 @@ +This log only documents changes between stable versions. + +From 1.2 on, odd minor version numbers (1.3, 1.5, 1.7,..) are assigned to the +fresh git snapshots while stable versions will have even minor numbers. + +1.1.2 -> 1.2: +* !!! Changed the default configuration directory to ~/.config/ranger !!! +* Removed "Console Modes", each old mode is now a simple command +* Disabled file previews by default if ranger is used by root +* Allow to jump to specific help sections by typing two numbers, e.g. 13? +* Added keys: da, dr, ya, yr for adding and removing files from copy buffer +* Added keys: gl and gL to resolve links, see 11? +* Added key: pL to create a relative symlink +* Added % and % macros for the console, see 33? +* Fixed ansi codes for colors in the pager +* Use the file ~/.mime.types for mime type detection +* Several clean-ups and fixes + 1.1.1 -> 1.1.2: * Fix crash when using scrollwheel to scroll down in some cases * The command "ranger dir1 dir2 ..." opens multiple directories in tabs diff --git a/README b/README index 4fd9876f..85b7e857 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Ranger v.1.1.2 +Ranger v.1.2.0 ============== Ranger is a free console file manager that gives you greater flexibility diff --git a/doc/ranger.1 b/doc/ranger.1 index fab42496..d81db6d4 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.TH RANGER 1 ranger-1.1.2 +.TH RANGER 1 ranger-1.2.0 .SH NAME ranger - visual file manager .\"----------------------------------------- diff --git a/ranger/__init__.py b/ranger/__init__.py index 57e0e5a2..c2a695c9 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -20,7 +20,7 @@ import sys from ranger.ext.openstruct import OpenStruct __license__ = 'GPL3' -__version__ = '1.1.2' +__version__ = '1.2.0' __credits__ = 'Roman Zimbelmann' __author__ = 'Roman Zimbelmann' __maintainer__ = 'Roman Zimbelmann' diff --git a/ranger/__main__.py b/ranger/__main__.py index 7db82a57..0ded8c95 100644 --- a/ranger/__main__.py +++ b/ranger/__main__.py @@ -35,7 +35,10 @@ def parse_arguments(): if '.' in minor_version: minor_version = minor_version[:minor_version.find('.')] version_tag = ' (stable)' if int(minor_version) % 2 == 0 else ' (testing)' - version_string = 'ranger ' + __version__ + version_tag + 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) -- cgit 1.4.1-2-gfad0 From ddc294bf3ab0f12e956496f23f51609ce343dda6 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 16 Sep 2010 17:46:33 +0200 Subject: core.actions: Fixed yy/pp bug when yanking multiple directories --- README | 2 +- doc/ranger.1 | 2 +- ranger/__init__.py | 2 +- ranger/core/actions.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index 85b7e857..f0ee7a06 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Ranger v.1.2.0 +Ranger v.1.2.1 ============== Ranger is a free console file manager that gives you greater flexibility diff --git a/doc/ranger.1 b/doc/ranger.1 index d81db6d4..03cc3d56 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.TH RANGER 1 ranger-1.2.0 +.TH RANGER 1 ranger-1.2.1 .SH NAME ranger - visual file manager .\"----------------------------------------- diff --git a/ranger/__init__.py b/ranger/__init__.py index c2a695c9..4f223b7d 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -20,7 +20,7 @@ import sys from ranger.ext.openstruct import OpenStruct __license__ = 'GPL3' -__version__ = '1.2.0' +__version__ = '1.2.1' __credits__ = 'Roman Zimbelmann' __author__ = 'Roman Zimbelmann' __maintainer__ = 'Roman Zimbelmann' diff --git a/ranger/core/actions.py b/ranger/core/actions.py index a93344a4..b88849ef 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -709,7 +709,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): for f in self.env.copy: if isdir(f.path): for _ in shutil_g.copytree(src=f.path, - dst=join(self.env.cwd.path, f.basename), + dst=join(original_path, f.basename), symlinks=True, overwrite=overwrite): yield -- cgit 1.4.1-2-gfad0 From d3bcb234bf5776da7d9b66e73107e906342eeb7d Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 16 Sep 2010 22:56:36 +0200 Subject: CHANGELOG updated --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f1417fee..8078305d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,9 @@ This log only documents changes between stable versions. From 1.2 on, odd minor version numbers (1.3, 1.5, 1.7,..) are assigned to the fresh git snapshots while stable versions will have even minor numbers. +1.2 -> 1.2.1: +* Fixed yy/pp bug when yanking multiple directories + 1.1.2 -> 1.2: * !!! Changed the default configuration directory to ~/.config/ranger !!! * Removed "Console Modes", each old mode is now a simple command -- cgit 1.4.1-2-gfad0