summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorChristian Zangl <laktak@cdak.net>2019-07-18 14:33:30 +0200
committerChristian Zangl <laktak@cdak.net>2019-07-18 15:05:11 +0200
commitc51e5af03f06b8a126a40a1a5af4f2b03277374a (patch)
treedf02a7dffd6711dba35bc919aa521dd6728c89b8
parent5003eb7ef0486f6563fa7e57864d51e3aa5b28b0 (diff)
downloadranger-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.pod4
-rw-r--r--examples/rc_emacs.conf4
-rwxr-xr-xranger/config/commands.py31
-rw-r--r--ranger/config/rc.conf6
-rw-r--r--ranger/core/actions.py22
-rw-r--r--ranger/core/loader.py16
-rw-r--r--ranger/ext/safe_path.py41
-rw-r--r--ranger/ext/shutil_generatorized.py53
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)