diff options
author | Christian Zangl <laktak@cdak.net> | 2019-07-18 14:33:30 +0200 |
---|---|---|
committer | Christian Zangl <laktak@cdak.net> | 2019-07-18 15:05:11 +0200 |
commit | c51e5af03f06b8a126a40a1a5af4f2b03277374a (patch) | |
tree | df02a7dffd6711dba35bc919aa521dd6728c89b8 | |
parent | 5003eb7ef0486f6563fa7e57864d51e3aa5b28b0 (diff) | |
download | ranger-c51e5af03f06b8a126a40a1a5af4f2b03277374a.tar.gz |
Added a make_safe_path parameter to paste
It allows you to specify your own function to generate the path for conflicting files.
-rw-r--r-- | doc/ranger.pod | 4 | ||||
-rw-r--r-- | examples/rc_emacs.conf | 4 | ||||
-rwxr-xr-x | ranger/config/commands.py | 31 | ||||
-rw-r--r-- | ranger/config/rc.conf | 6 | ||||
-rw-r--r-- | ranger/core/actions.py | 22 | ||||
-rw-r--r-- | ranger/core/loader.py | 16 | ||||
-rw-r--r-- | ranger/ext/safe_path.py | 41 | ||||
-rw-r--r-- | ranger/ext/shutil_generatorized.py | 53 |
8 files changed, 87 insertions, 90 deletions
diff --git a/doc/ranger.pod b/doc/ranger.pod index 17a9fdc8..3a98df86 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -527,8 +527,8 @@ also "da", "dr" and "dt" shortcuts equivalent to "ya", "yr" and "yt".) Paste the files which were previously copied or cut, like pressing Ctrl+V in modern GUI programs. -Conflicts will be renamed by appending an _ to the file extension. To change -this see the paste command. +Conflicts will be renamed by appending an _ to the full file name. To change +this and insert a _ before the file extension see commands.py. =item po diff --git a/examples/rc_emacs.conf b/examples/rc_emacs.conf index 583fce5c..0462282e 100644 --- a/examples/rc_emacs.conf +++ b/examples/rc_emacs.conf @@ -335,8 +335,8 @@ map <A-d> console rename%space map <C-e> eval fm.open_console('rename ' + fm.thisfile.relative_path) map <C-a> eval fm.open_console('rename ' + fm.thisfile.relative_path, position=7) -map <C-y>y paste conflict=rename_ext -map <C-y>o paste conflict=overwrite +map <C-y>y paste +map <C-y>o paste overwrite=True map <C-y>l paste_symlink relative=False map <C-y>L paste_symlink relative=True map <C-y>hl paste_hardlink diff --git a/ranger/config/commands.py b/ranger/config/commands.py index 2b1f4940..1eb740f8 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -1859,3 +1859,34 @@ class yank(Command): in sorted(self.modes.keys()) if mode ) + + +class paste_ext(Command): + """ + :paste_ext + + Like paste but renames conflicting files so that the + file extension stays intact. + """ + + @staticmethod + def make_safe_path(dst): + if not os.path.exists(dst): + return dst + + dst_name, dst_ext = os.path.splitext(dst) + + if not dst_name.endswith("_"): + dst_name += "_" + if not os.path.exists(dst_name + dst_ext): + return dst_name + dst_ext + n = 0 + test_dst = dst_name + str(n) + while os.path.exists(test_dst + dst_ext): + n += 1 + test_dst = dst_name + str(n) + + return test_dst + dst_ext + + def execute(self): + return self.fm.paste(make_safe_path=paste_ext.make_safe_path) diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index 2efcccae..f559290d 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -470,10 +470,10 @@ map a rename_append map A eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%")) map I eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%"), position=7) -map pp paste conflict=rename_ext -map po paste conflict=overwrite +map pp paste +map po paste overwrite=True map pP paste append=True -map pO paste conflict=overwrite append=True +map pO paste overwrite=True append=True map pl paste_symlink relative=False map pL paste_symlink relative=True map phl paste_hardlink diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 8adc075a..de441733 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -24,7 +24,6 @@ import ranger from ranger.ext.direction import Direction from ranger.ext.relative_symlink import relative_symlink from ranger.ext.keybinding_parser import key_to_string, construct_keybinding -from ranger.ext.safe_path import get_safe_path, get_safe_path_classic from ranger.ext.shell_escape import shell_quote from ranger.ext.next_available_filename import next_available_filename from ranger.ext.rifle import squash_flags, ASK_COMMAND @@ -1592,30 +1591,15 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m link(source_path, next_available_filename(target_path)) - def paste(self, conflict='rename_ext', append=False, dest=None, overwrite=False): + def paste(self, overwrite=False, append=False, dest=None, make_safe_path=None): """:paste Paste the selected items into the current directory or to dest if provided. """ - # overwrite is obsolete, it was replaced by conflict - if overwrite: - conflict = 'overwrite' - - if conflict == 'overwrite': - resolve_conflict = None - elif conflict == 'rename': - resolve_conflict = get_safe_path - elif conflict == 'rename_ext': - resolve_conflict = get_safe_path_classic - else: - self.notify('Failed to paste. The conflict parameter ' + conflict - + ' is invalid.', bad=True) - return - if dest is None or isdir(dest): - loadable = CopyLoader(self.copy_buffer, self.do_cut, resolve_conflict, - dest) + loadable = CopyLoader(self.copy_buffer, self.do_cut, overwrite, + dest, make_safe_path) self.loader.add(loadable, append=append) self.do_cut = False else: diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 04ff279a..fadff787 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -21,7 +21,6 @@ except ImportError: from ranger.core.shared import FileManagerAware from ranger.ext.signals import SignalDispatcher from ranger.ext.human_readable import human_readable -from ranger.ext.safe_path import get_safe_path class Loadable(object): @@ -52,12 +51,14 @@ class Loadable(object): class CopyLoader(Loadable, FileManagerAware): # pylint: disable=too-many-instance-attributes progressbar_supported = True - def __init__(self, copy_buffer, do_cut=False, resolve_conflict=get_safe_path, dest=None): + def __init__(self, copy_buffer, do_cut=False, overwrite=False, dest=None, + make_safe_path=None): self.copy_buffer = tuple(copy_buffer) self.do_cut = do_cut self.original_copy_buffer = copy_buffer self.original_path = dest if dest is not None else self.fm.thistab.path - self.resolve_conflict = resolve_conflict + self.overwrite = overwrite + self.make_safe_path = make_safe_path self.percent = 0 if self.copy_buffer: self.one_file = self.copy_buffer[0] @@ -109,7 +110,8 @@ class CopyLoader(Loadable, FileManagerAware): # pylint: disable=too-many-instan self.fm.tags.dump() n = 0 for n in shutil_g.move(src=fobj.path, dst=self.original_path, - resolve_conflict=self.resolve_conflict): + overwrite=self.overwrite, + make_safe_path=self.make_safe_path): self.percent = ((done + n) / size) * 100. yield done += n @@ -125,7 +127,8 @@ class CopyLoader(Loadable, FileManagerAware): # pylint: disable=too-many-instan src=fobj.path, dst=os.path.join(self.original_path, fobj.basename), symlinks=True, - resolve_conflict=self.resolve_conflict, + overwrite=self.overwrite, + make_safe_path=self.make_safe_path, ): self.percent = ((done + n) / size) * 100. yield @@ -133,7 +136,8 @@ class CopyLoader(Loadable, FileManagerAware): # pylint: disable=too-many-instan else: n = 0 for n in shutil_g.copy2(fobj.path, self.original_path, - symlinks=True, resolve_conflict=self.resolve_conflict): + symlinks=True, overwrite=self.overwrite, + make_safe_path=self.make_safe_path): self.percent = ((done + n) / size) * 100. yield done += n diff --git a/ranger/ext/safe_path.py b/ranger/ext/safe_path.py deleted file mode 100644 index 22ab42ee..00000000 --- a/ranger/ext/safe_path.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file is part of ranger, the console file manager. -# License: GNU GPL version 3, see the file "AUTHORS" for details. - -import os - -APPENDIX = '_' - - -def get_safe_path_classic(dst): - if not os.path.exists(dst): - return dst - if not dst.endswith(APPENDIX): - dst += APPENDIX - if not os.path.exists(dst): - return dst - n = 0 - test_dst = dst + str(n) - while os.path.exists(test_dst): - n += 1 - test_dst = dst + str(n) - - return test_dst - - -def get_safe_path(dst): - if not os.path.exists(dst): - return dst - - dst_name, dst_ext = os.path.splitext(dst) - - if not dst_name.endswith(APPENDIX): - dst_name += APPENDIX - if not os.path.exists(dst_name + dst_ext): - return dst_name + dst_ext - n = 0 - test_dst = dst_name + str(n) - while os.path.exists(test_dst + dst_ext): - n += 1 - test_dst = dst_name + str(n) - - return test_dst + dst_ext diff --git a/ranger/ext/shutil_generatorized.py b/ranger/ext/shutil_generatorized.py index 6f736174..bcd381ea 100644 --- a/ranger/ext/shutil_generatorized.py +++ b/ranger/ext/shutil_generatorized.py @@ -7,11 +7,11 @@ import os import stat import sys from shutil import (_samefile, rmtree, _basename, _destinsrc, Error, SpecialFileError) -from ranger.ext.safe_path import get_safe_path __all__ = ["copyfileobj", "copyfile", "copystat", "copy2", "BLOCK_SIZE", "copytree", "move", "rmtree", "Error", "SpecialFileError"] +APPENDIX = '_' BLOCK_SIZE = 16 * 1024 @@ -103,6 +103,22 @@ else: pass +def get_safe_path(dst): + if not os.path.exists(dst): + return dst + if not dst.endswith(APPENDIX): + dst += APPENDIX + if not os.path.exists(dst): + return dst + n = 0 + test_dst = dst + str(n) + while os.path.exists(test_dst): + n += 1 + test_dst = dst + str(n) + + return test_dst + + def copyfileobj(fsrc, fdst, length=BLOCK_SIZE): """copy data from file-like object fsrc to file-like object fdst""" done = 0 @@ -137,7 +153,7 @@ def copyfile(src, dst): yield done -def copy2(src, dst, resolve_conflict=get_safe_path, symlinks=False): +def copy2(src, dst, overwrite=False, symlinks=False, make_safe_path=None): """Copy data and all stat info ("cp -p src dst"). The destination may be a directory. @@ -145,11 +161,11 @@ def copy2(src, dst, resolve_conflict=get_safe_path, symlinks=False): """ if os.path.isdir(dst): dst = os.path.join(dst, os.path.basename(src)) - if resolve_conflict: - dst = resolve_conflict(dst) + if not overwrite: + dst = (make_safe_path or get_safe_path)(dst) if symlinks and os.path.islink(src): linkto = os.readlink(src) - if not resolve_conflict and os.path.lexists(dst): + if overwrite and os.path.lexists(dst): os.unlink(dst) os.symlink(linkto, dst) else: @@ -159,7 +175,7 @@ def copy2(src, dst, resolve_conflict=get_safe_path, symlinks=False): def copytree(src, dst, # pylint: disable=too-many-locals,too-many-branches - symlinks=False, ignore=None, resolve_conflict=get_safe_path): + symlinks=False, ignore=None, overwrite=False, make_safe_path=None): """Recursively copy a directory tree using copy2(). The destination directory must not already exist. @@ -194,8 +210,8 @@ def copytree(src, dst, # pylint: disable=too-many-locals,too-many-branches try: os.makedirs(dst) except OSError: - if resolve_conflict: - dst = resolve_conflict(dst) + if not overwrite: + dst = (make_safe_path or get_safe_path)(dst) os.makedirs(dst) errors = [] done = 0 @@ -207,20 +223,21 @@ def copytree(src, dst, # pylint: disable=too-many-locals,too-many-branches try: if symlinks and os.path.islink(srcname): linkto = os.readlink(srcname) - if not resolve_conflict and os.path.lexists(dstname): + if overwrite and os.path.lexists(dstname): os.unlink(dstname) os.symlink(linkto, dstname) copystat(srcname, dstname) elif os.path.isdir(srcname): n = 0 - for n in copytree(srcname, dstname, symlinks, ignore, resolve_conflict): + for n in copytree(srcname, dstname, symlinks, ignore, overwrite, + make_safe_path): yield done + n done += n else: # Will raise a SpecialFileError for unsupported file types n = 0 - for n in copy2(srcname, dstname, resolve_conflict=resolve_conflict, - symlinks=symlinks): + for n in copy2(srcname, dstname, overwrite=overwrite, symlinks=symlinks, + make_safe_path=make_safe_path): yield done + n done += n # catch the Error from the recursive copytree so that we can @@ -241,7 +258,7 @@ def copytree(src, dst, # pylint: disable=too-many-locals,too-many-branches raise Error(errors) -def move(src, dst, resolve_conflict=get_safe_path): +def move(src, dst, overwrite=False, make_safe_path=None): """Recursively move a file or directory to another location. This is similar to the Unix "mv" command. @@ -267,18 +284,20 @@ def move(src, dst, resolve_conflict=get_safe_path): return real_dst = os.path.join(dst, _basename(src)) - if resolve_conflict: - real_dst = resolve_conflict(real_dst) + if not overwrite: + real_dst = (make_safe_path or get_safe_path)(real_dst) try: os.rename(src, real_dst) except OSError: if os.path.isdir(src): if _destinsrc(src, dst): raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) - for done in copytree(src, real_dst, symlinks=True, resolve_conflict=resolve_conflict): + for done in copytree(src, real_dst, symlinks=True, overwrite=overwrite, + make_safe_path=make_safe_path): yield done rmtree(src) else: - for done in copy2(src, real_dst, symlinks=True, resolve_conflict=resolve_conflict): + for done in copy2(src, real_dst, symlinks=True, overwrite=overwrite, + make_safe_path=None): yield done os.unlink(src) |