From 9d817dc755285f10e556e7a100fa22f8480397ce Mon Sep 17 00:00:00 2001 From: Christian Zangl Date: Sun, 14 Jul 2019 14:47:13 +0200 Subject: 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. --- doc/ranger.pod | 3 +++ 1 file changed, 3 insertions(+) (limited to 'doc') diff --git a/doc/ranger.pod b/doc/ranger.pod index fadd4cba..17a9fdc8 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -527,6 +527,9 @@ 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. + =item po Paste the copied/cut files, overwriting existing files. -- cgit 1.4.1-2-gfad0 From c51e5af03f06b8a126a40a1a5af4f2b03277374a Mon Sep 17 00:00:00 2001 From: Christian Zangl Date: Thu, 18 Jul 2019 14:33:30 +0200 Subject: Added a make_safe_path parameter to paste It allows you to specify your own function to generate the path for conflicting files. --- doc/ranger.pod | 4 +-- examples/rc_emacs.conf | 4 +-- ranger/config/commands.py | 31 ++++++++++++++++++++++ ranger/config/rc.conf | 6 ++--- ranger/core/actions.py | 22 +++------------- ranger/core/loader.py | 16 +++++++----- ranger/ext/safe_path.py | 41 ----------------------------- ranger/ext/shutil_generatorized.py | 53 ++++++++++++++++++++++++++------------ 8 files changed, 87 insertions(+), 90 deletions(-) delete mode 100644 ranger/ext/safe_path.py (limited to 'doc') 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 console rename%space map eval fm.open_console('rename ' + fm.thisfile.relative_path) map eval fm.open_console('rename ' + fm.thisfile.relative_path, position=7) -map y paste conflict=rename_ext -map o paste conflict=overwrite +map y paste +map o paste overwrite=True map l paste_symlink relative=False map L paste_symlink relative=True map 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) -- cgit 1.4.1-2-gfad0 From 65f95287d42869952e3bc0ae9ffefa94221f7faa Mon Sep 17 00:00:00 2001 From: Christian Zangl Date: Fri, 19 Jul 2019 23:21:30 +0200 Subject: fix docs --- doc/ranger.pod | 5 +++-- ranger/config/commands.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'doc') diff --git a/doc/ranger.pod b/doc/ranger.pod index 3a98df86..55944cdd 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -527,8 +527,9 @@ 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 full file name. To change -this and insert a _ before the file extension see commands.py. +Conflicts will be renamed by appending an '_' (and a counter if necessary), +resulting in F. If you prefer F you can use the +I command from F. =item po diff --git a/ranger/config/commands.py b/ranger/config/commands.py index 1eb740f8..9cbf5f2e 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -1865,8 +1865,8 @@ class paste_ext(Command): """ :paste_ext - Like paste but renames conflicting files so that the - file extension stays intact. + Like paste but tries to rename conflicting files so that the + file extension stays intact (e.g. file_.ext). """ @staticmethod -- cgit 1.4.1-2-gfad0 From b31658573c603b0a795c58810a53796faf48685a Mon Sep 17 00:00:00 2001 From: toonn Date: Sun, 22 Dec 2019 16:40:07 +0100 Subject: Add example and remove reference from docs --- doc/ranger.1 | 8 ++++++-- doc/ranger.pod | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'doc') diff --git a/doc/ranger.1 b/doc/ranger.1 index 3521d762..d0ed2e7b 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) +.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.9.2" "2019-10-02" "ranger manual" +.TH RANGER 1 "ranger-1.9.2" "2019-12-22" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -620,6 +620,10 @@ also \*(L"da\*(R", \*(L"dr\*(R" and \*(L"dt\*(R" shortcuts equivalent to \*(L"ya .IX Item "pp" Paste the files which were previously copied or cut, like pressing Ctrl+V in modern \s-1GUI\s0 programs. +.Sp +Conflicts will be renamed by appending an '_' (and a counter if necessary), +resulting in \fIfile.ext_\fR, \fIfile.ext_0\fR, etc. If you prefer \fIfile_.ext\fR you +can use the \f(CW\*(C`paste_ext\*(C'\fR command. .IP "po" 14 .IX Item "po" Paste the copied/cut files, overwriting existing files. diff --git a/doc/ranger.pod b/doc/ranger.pod index 260ab3e3..53f4933c 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -542,8 +542,8 @@ Paste the files which were previously copied or cut, like pressing Ctrl+V in modern GUI programs. Conflicts will be renamed by appending an '_' (and a counter if necessary), -resulting in F. If you prefer F you can use the -I command from F. +resulting in F, F, etc. If you prefer F you +can use the C command. =item po -- cgit 1.4.1-2-gfad0 From f6c4d5f802b1b3a4c26393d28a11e5b098e802a0 Mon Sep 17 00:00:00 2001 From: toonn Date: Sun, 22 Dec 2019 16:44:46 +0100 Subject: Use readable formatting for the examples `F` is rendered using underline in a man page, which makes the underscore hard to see. So I switched the file name examples for `pp` to use `C` which is rendered using "double quotation marks." --- doc/ranger.1 | 2 +- doc/ranger.pod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'doc') diff --git a/doc/ranger.1 b/doc/ranger.1 index d0ed2e7b..f64c19d7 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -622,7 +622,7 @@ Paste the files which were previously copied or cut, like pressing Ctrl+V in modern \s-1GUI\s0 programs. .Sp Conflicts will be renamed by appending an '_' (and a counter if necessary), -resulting in \fIfile.ext_\fR, \fIfile.ext_0\fR, etc. If you prefer \fIfile_.ext\fR you +resulting in \f(CW\*(C`file.ext_\*(C'\fR, \f(CW\*(C`file.ext_0\*(C'\fR, etc. If you prefer \f(CW\*(C`file_.ext\*(C'\fR you can use the \f(CW\*(C`paste_ext\*(C'\fR command. .IP "po" 14 .IX Item "po" diff --git a/doc/ranger.pod b/doc/ranger.pod index 53f4933c..8e9f261a 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -542,7 +542,7 @@ Paste the files which were previously copied or cut, like pressing Ctrl+V in modern GUI programs. Conflicts will be renamed by appending an '_' (and a counter if necessary), -resulting in F, F, etc. If you prefer F you +resulting in C, C, etc. If you prefer C you can use the C command. =item po -- cgit 1.4.1-2-gfad0