diff options
author | Christian Zangl <laktak@cdak.net> | 2019-07-14 14:47:13 +0200 |
---|---|---|
committer | Christian Zangl <laktak@cdak.net> | 2019-07-14 15:05:43 +0200 |
commit | 9d817dc755285f10e556e7a100fa22f8480397ce (patch) | |
tree | 3a6aa62ced5c2e9d0f14076ae915becd65ccdec2 /ranger | |
parent | 669100aad83e3a51f140b6085d836a8260eda8d0 (diff) | |
download | ranger-9d817dc755285f10e556e7a100fa22f8480397ce.tar.gz |
Added a conflict parameter to the paste action
It allows you to specify 'overwrite', 'rename', or 'rename_ext' (=default) in case of a file name conflict. This obsoletes the overwrite parameter.
Diffstat (limited to 'ranger')
-rw-r--r-- | ranger/config/rc.conf | 6 | ||||
-rw-r--r-- | ranger/core/actions.py | 20 | ||||
-rw-r--r-- | ranger/core/loader.py | 10 | ||||
-rw-r--r-- | ranger/ext/safe_path.py | 41 | ||||
-rw-r--r-- | ranger/ext/shutil_generatorized.py | 49 |
5 files changed, 84 insertions, 42 deletions
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index f559290d..2efcccae 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 -map po paste overwrite=True +map pp paste conflict=rename_ext +map po paste conflict=overwrite map pP paste append=True -map pO paste overwrite=True append=True +map pO paste conflict=overwrite 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 435fcf13..8adc075a 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -24,6 +24,7 @@ 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 @@ -1591,14 +1592,29 @@ class Actions( # pylint: disable=too-many-instance-attributes,too-many-public-m link(source_path, next_available_filename(target_path)) - def paste(self, overwrite=False, append=False, dest=None): + def paste(self, conflict='rename_ext', append=False, dest=None, overwrite=False): """: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, overwrite, + loadable = CopyLoader(self.copy_buffer, self.do_cut, resolve_conflict, dest) self.loader.add(loadable, append=append) self.do_cut = False diff --git a/ranger/core/loader.py b/ranger/core/loader.py index 26b729b6..bf4c780b 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -51,12 +51,12 @@ class Loadable(object): class CopyLoader(Loadable, FileManagerAware): # pylint: disable=too-many-instance-attributes progressbar_supported = True - def __init__(self, copy_buffer, do_cut=False, overwrite=False, dest=None): + def __init__(self, copy_buffer, do_cut=False, resolve_conflict=False, dest=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.overwrite = overwrite + self.resolve_conflict = resolve_conflict self.percent = 0 if self.copy_buffer: self.one_file = self.copy_buffer[0] @@ -108,7 +108,7 @@ 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, - overwrite=self.overwrite): + resolve_conflict=self.resolve_conflict): self.percent = ((done + n) / size) * 100. yield done += n @@ -124,7 +124,7 @@ class CopyLoader(Loadable, FileManagerAware): # pylint: disable=too-many-instan src=fobj.path, dst=os.path.join(self.original_path, fobj.basename), symlinks=True, - overwrite=self.overwrite, + resolve_conflict=self.resolve_conflict, ): self.percent = ((done + n) / size) * 100. yield @@ -132,7 +132,7 @@ 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, overwrite=self.overwrite): + symlinks=True, resolve_conflict=self.resolve_conflict): self.percent = ((done + n) / size) * 100. yield done += n diff --git a/ranger/ext/safe_path.py b/ranger/ext/safe_path.py new file mode 100644 index 00000000..22ab42ee --- /dev/null +++ b/ranger/ext/safe_path.py @@ -0,0 +1,41 @@ +# 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 fe3cf068..6f736174 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,22 +103,6 @@ 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 @@ -153,7 +137,7 @@ def copyfile(src, dst): yield done -def copy2(src, dst, overwrite=False, symlinks=False): +def copy2(src, dst, resolve_conflict=get_safe_path, symlinks=False): """Copy data and all stat info ("cp -p src dst"). The destination may be a directory. @@ -161,11 +145,11 @@ def copy2(src, dst, overwrite=False, symlinks=False): """ if os.path.isdir(dst): dst = os.path.join(dst, os.path.basename(src)) - if not overwrite: - dst = get_safe_path(dst) + if resolve_conflict: + dst = resolve_conflict(dst) if symlinks and os.path.islink(src): linkto = os.readlink(src) - if overwrite and os.path.lexists(dst): + if not resolve_conflict and os.path.lexists(dst): os.unlink(dst) os.symlink(linkto, dst) else: @@ -175,7 +159,7 @@ def copy2(src, dst, overwrite=False, symlinks=False): def copytree(src, dst, # pylint: disable=too-many-locals,too-many-branches - symlinks=False, ignore=None, overwrite=False): + symlinks=False, ignore=None, resolve_conflict=get_safe_path): """Recursively copy a directory tree using copy2(). The destination directory must not already exist. @@ -210,8 +194,8 @@ def copytree(src, dst, # pylint: disable=too-many-locals,too-many-branches try: os.makedirs(dst) except OSError: - if not overwrite: - dst = get_safe_path(dst) + if resolve_conflict: + dst = resolve_conflict(dst) os.makedirs(dst) errors = [] done = 0 @@ -223,19 +207,20 @@ 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 overwrite and os.path.lexists(dstname): + if not resolve_conflict 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, overwrite): + for n in copytree(srcname, dstname, symlinks, ignore, resolve_conflict): yield done + n done += n else: # Will raise a SpecialFileError for unsupported file types n = 0 - for n in copy2(srcname, dstname, overwrite=overwrite, symlinks=symlinks): + for n in copy2(srcname, dstname, resolve_conflict=resolve_conflict, + symlinks=symlinks): yield done + n done += n # catch the Error from the recursive copytree so that we can @@ -256,7 +241,7 @@ def copytree(src, dst, # pylint: disable=too-many-locals,too-many-branches raise Error(errors) -def move(src, dst, overwrite=False): +def move(src, dst, resolve_conflict=get_safe_path): """Recursively move a file or directory to another location. This is similar to the Unix "mv" command. @@ -282,18 +267,18 @@ def move(src, dst, overwrite=False): return real_dst = os.path.join(dst, _basename(src)) - if not overwrite: - real_dst = get_safe_path(real_dst) + if resolve_conflict: + real_dst = resolve_conflict(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, overwrite=overwrite): + for done in copytree(src, real_dst, symlinks=True, resolve_conflict=resolve_conflict): yield done rmtree(src) else: - for done in copy2(src, real_dst, symlinks=True, overwrite=overwrite): + for done in copy2(src, real_dst, symlinks=True, resolve_conflict=resolve_conflict): yield done os.unlink(src) |