diff options
-rw-r--r-- | ranger/core/actions.py | 8 | ||||
-rw-r--r-- | ranger/defaults/keys.py | 5 | ||||
-rw-r--r-- | ranger/ext/relative_symlink.py | 39 | ||||
-rw-r--r-- | ranger/help/fileop.py | 3 | ||||
-rw-r--r-- | test/tc_relative_symlink.py | 47 |
5 files changed, 97 insertions, 5 deletions
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<bg>', 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<bg>', 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 <romanz@lavabit.com> +# +# 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 <http://www.gnu.org/licenses/>. + +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 <number> Change the rights of the selection :delete DELETES ALL FILES IN THE SELECTION :rename <newname> 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 <romanz@lavabit.com> +# +# 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 <http://www.gnu.org/licenses/>. + +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() |