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. --- test/bm_human_readable.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ test/tc_human_readable.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 test/bm_human_readable.py create mode 100644 test/tc_human_readable.py (limited to 'test') 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(+) (limited to 'test') 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 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(-) (limited to 'test') 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(-) (limited to 'test') 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 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 (limited to 'test') 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 (limited to 'test') 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 (limited to 'test') diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29b..00000000 -- cgit 1.4.1-2-gfad0