about summary refs log tree commit diff stats
path: root/ranger
diff options
context:
space:
mode:
author5hir0kur0 <12101162+5hir0kur0@users.noreply.github.com>2020-03-06 16:09:11 +0100
committer5hir0kur0 <12101162+5hir0kur0@users.noreply.github.com>2020-03-06 16:25:33 +0100
commitefa32996173ee50cb2dbf4eb3d0cc2af998d6f3a (patch)
tree9e3bb98991041fa1f3043b514fe32b02aeee01af /ranger
parentf65e6f08bcf63b7d915d2a2e98d1f6f891cd30de (diff)
downloadranger-efa32996173ee50cb2dbf4eb3d0cc2af998d6f3a.tar.gz
trash: Don't call the File() constructor
Previously the File() constructor was called for every path (if the
paths to be moved to trash were supplied after the command instead of
deleting the selection, e.g. ":trash a b c").
This commit adds a method paths_to_filesystem_objects() to find the
existing objects that ranger has in memory and use those instead.
Diffstat (limited to 'ranger')
-rwxr-xr-xranger/config/commands.py42
1 files changed, 40 insertions, 2 deletions
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index a2d13542..ecbb2fe1 100755
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -722,14 +722,15 @@ class trash(Command):
     def execute(self):
         import shlex
         from functools import partial
-        from ranger.container.file import File
 
         def is_directory_with_files(path):
             return os.path.isdir(path) and not os.path.islink(path) and len(os.listdir(path)) > 0
 
         if self.rest(1):
             file_names = shlex.split(self.rest(1))
-            files = [File(name) for name in file_names]
+            files = self.paths_to_filesystem_objects(file_names)
+            if files is None:
+                return
             many_files = (len(files) > 1 or is_directory_with_files(files[0].path))
         else:
             cwd = self.fm.thisdir
@@ -754,6 +755,43 @@ class trash(Command):
             # no need for a confirmation, just delete
             self.fm.execute_file(files, label='trash')
 
+    @staticmethod
+    def group_paths_by_dirname(paths):
+        """
+        Groups the paths into a dictionary with their dirnames as keys and a set of
+        basenames as entries.
+        """
+        groups = dict()
+        for path in paths:
+            abspath = os.path.abspath(os.path.expanduser(path))
+            dirname, basename = os.path.split(abspath)
+            groups.setdefault(dirname, set()).add(basename)
+        return groups
+
+    def paths_to_filesystem_objects(self, paths):
+        """
+        Find FileSystemObjects corresponding to the paths if they are already in
+        memory and load those that are not.
+        """
+        result = []
+        # Grouping the files by dirname avoids the potentially quadratic running time of doing
+        # a linear search in the directory for each entry name that is supposed to be deleted.
+        groups = trash.group_paths_by_dirname(paths)
+        for dirname, basenames in groups.items():
+            directory = self.fm.get_directory(dirname)
+            directory.load_content_if_outdated()
+            for entry in directory.files_all:
+                if entry.basename in basenames:
+                    result.append(entry)
+                    basenames.remove(entry.basename)
+            if basenames != set():
+                # Abort the operation with an error message if there are entries
+                # that weren't found.
+                names = ', '.join(basenames)
+                self.fm.notify('Error: No such file or directory: {}'.format(names), bad=True)
+                return None
+        return result
+
     def tab(self, tabnum):
         return self._tab_directory_content()