Commits

Anonymous committed 7b9dff6

Caching Project.get_files()

Comments (0)

Files changed (6)

docs/dev/issues.txt

 * Finding available refactorings
 
 
-Better Resource Observing
-=========================
-
-I propose to have separate methods for handling different file
-operations::
-
-  class ResourceObserver(object):
-
-      def resource_changed(self, resource):
-          pass
-
-      def resource_moved(self, source, destination):
-          pass
-
-      def resource_removed(self, resource):
-          pass
-
-      def resource_created(self, resource):
-          pass
-
-
-In one extreme we have to separate every change.  Some observers are
-interested in details and some are not.  Reducing the number of
-messages requires more checks on both interested and uninterested
-observers.
-
-The other problem that we might face is that messages might overlap
-each other.  For example when we move a resource a new resource would
-be created, too;  Should we call `resource_created`?
-
-
 Memory Management
 =================
 

docs/dev/workingon.txt

 Small Stories
 =============
 
-- Renaming `ResourceObserver.resource_removed()` to `resource_moved`
-- Reporting resource_created in validate_resource; use mtime
-- Changing parents after creations
-- Multiple calls to validate
-- Reporting resource_created in resource_moved
-
-* Caching `Project.get_all_files()`
-* `Project.get_files()` does not care about ignored resources
-* Caching `PyCore.get_python_files()`?
-* Caching `PyCore.get_source_folders()`?
+- Caching `Project.get_files()`
+- Using set for caching
+- `Project.get_files()` should ignore ignored resources
 
 * Codeassist on function header with pydocs!
 
 
 * Renaming normal files, folder, modules, and packages in project tree view
 * Spell checking on python source
-* Faster find file in large projects; caching `Project.get_all_files()`
 * Completion in module entries
 * A completing text widget for dialogs; `Completing(name, list, handle)`
 * Goto definition for ``"# comment.\na_var"``

rope/base/oi/shelvedb.py

     def _get_name_for_path(self, path):
         base_name = os.path.basename(path)
         def to_hex(i):
-            return hex(i).replace('0x', '', 1)
+            return hex(i).replace('0x', '', 1).replace('L', '')
         hashed = to_hex(hash(path))
         if not hashed.startswith('-'):
             hashed = '-' + hashed

rope/base/project.py

             fscommands = rope.base.fscommands.create_fscommands(self._address)
         super(Project, self).__init__(fscommands)
         self.ignored = _IgnoredResources()
+        self.file_list = _FileListCacher(self)
         self.prefs.add_callback('ignored_resources', self.ignored.set_ignored)
         self.prefs['ignored_resources'] = ['*.pyc', '.svn', '*~', '.ropeproject']
         self._init_rope_folder(ropefolder)
         self._init_prefs(prefs)
 
     def get_files(self):
-        return self._get_files_recursively(self.root)
+        return self.file_list.get_files()
 
     def _get_resource_path(self, name):
         return os.path.join(self._address, *name.split('/'))
 
-    def _get_files_recursively(self, folder):
-        result = []
-        for file in folder.get_files():
-            result.append(file)
-        for folder in folder.get_folders():
-            if not folder.name.startswith('.'):
-                result.extend(self._get_files_recursively(folder))
-        return result
-
     def _init_rope_folder(self, ropefolder):
         self._ropefolder = None
         if ropefolder is not None:
 
     """
 
-    def __init__(self, changed=None, moved=None, created=None, removed=None):
+    def __init__(self, changed=None, moved=None, created=None,
+                 removed=None, validate=None):
         self.changed = changed
         self.moved = moved
         self.created = created
         self.removed = removed
+        self._validate = validate
 
     def resource_changed(self, resource):
         """It is called when the resource changes"""
 
     def resource_moved(self, resource, new_resource):
         """It is called when a resource is moved"""
-        if self.removed is not None:
-            self.removed(resource, new_resource)
+        if self.moved is not None:
+            self.moved(resource, new_resource)
 
     def resource_created(self, resource):
         """Is called when a new resource is created"""
         by other processes.
 
         """
+        if self._removed is not None:
+            self._removed(resource)
 
 
 class FilteredResourceObserver(object):
     def add_created(self, resource):
         self.creations.add(resource)
 
+
 class _IgnoredResources(object):
 
     def __init__(self):
     ignored_patterns = property(_get_compiled_patterns)
 
 
+class _FileListCacher(object):
+
+    def __init__(self, project):
+        self.project = project
+        self._list = None
+        self.observer = None
+
+    def get_files(self):
+        if self._list is None:
+            if self.observer is None:
+                self._init_observer()
+            self._list = self._get_files_recursively(self.project.root)
+        return self._list
+
+    def _get_files_recursively(self, folder):
+        result = set()
+        for file in folder.get_files():
+            if not self.project.is_ignored(file):
+                result.add(file)
+        for child in folder.get_folders():
+            if not self.project.is_ignored(child):
+                result.update(self._get_files_recursively(child))
+        return result
+
+    def _init_observer(self):
+        if self.observer is None:
+            self.observer = ResourceObserver(
+                self._changed, self._moved, self._created,
+                self._removed, self._validate)
+            self.project.add_observer(self.observer)
+
+    def _changed(self, resource):
+        if resource.is_folder():
+            self._list = None
+
+    def _moved(self, resource, new_resource):
+        if resource.is_folder():
+            self._list = None
+        elif self._list is not None:
+            self._removed(resource)
+            self._created(new_resource)
+
+    def _created(self, resource):
+        if not resource.is_folder() and self._list is not None:
+            if not self.project.is_ignored(resource):
+                self._list.add(resource)
+
+    def _removed(self, resource):
+        if resource.is_folder():
+            self._list = None
+        else:
+            if self._list is not None and resource in self._list:
+                self._list.remove(resource)
+
+    def _validate(self, resource):
+        self._list = None
+
+
 _DEFAULT_CONFIG_PY = '''# The default ``config.py``
 
 

rope/ui/fileactions.py

             files = self.last_result
         else:
             if self.all_files is None:
-                self.all_files = self.project.get_files()
+                self.all_files = list(self.project.get_files())
+                self.all_files.sort(cmp=self._compare_files)
             files = self.all_files
-            files.sort(cmp=self._compare_files)
         result = []
         selector = self._create_selector(starting)
         for file_ in files:

ropetest/projecttest.py

         self.assertEquals('', root_folder.name)
 
     def test_get_all_files(self):
-        files = self.project.get_files()
+        files = tuple(self.project.get_files())
         self.assertEquals(1, len(files))
         self.assertEquals(self.sample_file, files[0].name)
 
+    def test_get_all_files_after_changing(self):
+        self.assertEquals(1, len(self.project.get_files()))
+        myfile = self.project.root.create_file('myfile.txt')
+        self.assertEquals(2, len(self.project.get_files()))
+        myfile.move('newfile.txt')
+        self.assertEquals(2, len(self.project.get_files()))
+        self.project.get_file('newfile.txt').remove()
+        self.assertEquals(1, len(self.project.get_files()))
+
     def test_multifile_get_all_files(self):
         fileName = 'nestedFile.txt'
         parent = self.project.get_resource(self.sample_folder)
         parent.create_file(fileName)
-        files = self.project.get_files()
+        files = list(self.project.get_files())
         self.assertEquals(2, len(files))
         self.assertTrue(fileName == files[0].name or fileName == files[1].name)
 
-    def test_ignoring_dot_star_folders_in_get_files(self):
-        root = self.project.address
-        dot_test = os.path.join(root, '.test')
-        os.mkdir(dot_test)
-        test_py = os.path.join(dot_test, 'test.py')
-        file(test_py, 'w').close()
-        for x in self.project.get_files():
-            self.assertNotEquals('.test/test.py', x.path)
-
     def test_ignoring_dot_pyc_files_in_get_files(self):
         root = self.project.address
         src_folder = os.path.join(root, 'src')
         myfile = myfolder.create_file('myfile.txt')
         self.assertTrue(self.project.is_ignored(myfile))
 
+    def test_ignored_resources_and_get_files(self):
+        self.project = testutils.sample_project(
+            ignored_resources=['myfile.txt'], ropefolder=None)
+        myfile = self.project.get_file('myfile.txt')
+        self.assertEquals(0, len(self.project.get_files()))
+        myfile.create()
+        self.assertEquals(0, len(self.project.get_files()))
+
+    def test_ignored_resources_and_get_files2(self):
+        self.project = testutils.sample_project(
+            ignored_resources=['myfile.txt'], ropefolder=None)
+        myfile = self.project.root.create_file('myfile.txt')
+        self.assertEquals(0, len(self.project.get_files()))
+
     def test_setting_ignored_resources_patterns(self):
         self.project = testutils.sample_project(ignored_resources=['m?file.*'])
         myfile = self.project.get_file('myfile.txt')