Anonymous avatar Anonymous committed f9fcbdd

Adding Change Occurrences refactoring
Better status bar

Comments (0)

Files changed (26)

docs/dev/done.txt

 ===========
 
 
+- Better status bar : February 23, 2007
+
+
+- Change occurrences : February 23, 2007
+
+
 - Moving methods : February 21, 2007
 
 

docs/dev/issues.txt

 Now that we handle many kinds of refactorings and we have achieved
 many of rope's initial goals we can think of extending it.
 
-There are many places to extend rope.  Some of them are:
+There are many places to extend rope.  There are two main places
+that can be enhanced.  One is occurrence finding mechanisms and the
+other is object inference.  Among the features that are going to be
+added implicit interfaces and changing structure refactorings are the
+most promising ones.
 
-* More advanced refactorings
-* Enhancing old refactorings
-* Refactoring scripts
-* Dynamic type inference(faster, new ways)
-* Static type inference(new, better ways)
-* Exclusion principle for occurrence finding
-* Changing structure refactorings; for example: ``a = b -> a(b.c)``
+* Occurrence finding
+* DOI
+* SOI
+* Implicit interfaces
+* Changing strucutre refactorings; for example: ``a = b -> a.set(b)``
+* Advanced refactorings
 
 
 Hot Topics
 * `Python's implicit interfaces`_
 * `Having virtual PyModules`_
 * `Redefining function/class definitions`_
-* `Better status bar`_
 * Indexing source files for faster occurrence finding
 * Faster module running
 * Saving hard to compute information like class hierarchies to files
 Change Occurrences
 ==================
 
-We want to customize these parameters when performing rename
-refactoring:
-
-* The region rename
-* Should we replace primaries
-* Performing only on calls
-* It is completely side-effect free
-* Only reads or writes?
-* Change imports?
-* Change definitions?
-
-Refactorings that can be done:
-
-* Introduce parameter
-* Local variable to field
-* Handle long imports
-* Change references to a name to another name
-* Changing imported module
-* Encapsulate field
-* Introduce factory
-* ...
-
-Issues:
-
-* Removing local rename?
 * How specify a region in a file
 
   * Always use the current scope
   * ``C-u # C-r r o`` where # is the number of scopes outside
     current scope
 
-* The defaults should be local rename?
-* Do we rename modules and packages, too
-* What about defined objects
-* Using another name
-
-  * ``Extended Rename``; ``C-c r e``
-  * ``Alternative Rename``; ``C-c r a``
-  * ``Other Rename``; ``C-c r o``
-  * ``Local Rename``; ``C-c r e``
-  * ``Fix Occurrences``; ``C-c r x``
-  * ``Alter Occurrences``; ``C-c r a``
-  * ``Change Occurrences``; ``C-c r o``
-  * ``Modify Occurrences``; ``C-c r o``
-  * ``Replace Occurrences``; ``C-c r o``
-  * ``Switch Occurrences``; ``C-c r w``
-
 
 Checking Variable And Function Usages In Their Defining Scopes
 ==============================================================
 * Maybe we can think of a way for issuing warnings rope encounters
 
   The warnings seem to belong to the IDE rather than the base.  But
-  the problem is that many warnings are seen in the base package.
+  the problem is that most warnings are encountered in the base package.
 
 
 Better Occurrence Finding
 ==========================================
 
 
-Better Status Bar
------------------
+Version Control Commands
+------------------------
 
-* line
-* mode
-* file
-* readonly status
-* changed status
-
-Temporaries:
-
-* search
-* keys
+* Commit; ``C-x v c``
+* Update; ``C-x v u``
+* Diff; ``C-x v d``
+* Log; ``C-x v l``
+* Revert; ``C-x v r``
+* Remove; ``C-x v v``
+* Status; ``C-x v s``
+* Add; ``C-x v a``
 
 
 Better Dialogs

docs/dev/stories.txt

 * Pull up method
 * Push down method
 * Introduce redirection
-* Moving fields/methods to attribute classes
 
 
 IDE And UI Stories
 
 
 * Performing import actions only on one import statement
-
-
-* Change occurrences

docs/dev/workingon.txt

 Small Stories
 =============
 
-- Moving Methods
+- Change occurrences
 
-  - Adding to the UI
-  - Separating `MoveModule` and `MoveGlobal`
-  - Nonexistent attribute
-  - Unknown attribute type
-  - Adding imports? Only when moving to another module
-  - Adding methods to a class with ``pass`` body
+  - Find file and entering when nothing is selected
+  - Renaming `FilteredOccurrenceFinder` to `FilteredFinder`
+  - Considering ``*`` to be block starts in reST mode
+  - `get_old_name()` should return a primary
+  - Only calls
+  - Reads and writes
 
-- Default name for new method
-- Inlining the last method of a class
-- In the ``README.txt`` mention how to change the keybinding
+- ``--  docs/dev/workingon.txt  (rest)``
 
 * A completing text widget for dialogs; `Completing(name, list, handle)`
-* Do `get_body()` and `get_body_region()` belong to `PyFunction`?
-* Does `get_definition_info()` belong to `PyFunction`?
+* Version control commands support; commiting, updating, ...
 * Moving static and class methods
 * Inlining methods with decorators
 * Decorators and method refactorings
 * Unifying ``x.get_type() == get_base_type('y')``, ``isinstance(x, PyY)``
 * Finding unused variables
 * Document how to move fields
-* Adding codeassist templates in ``~/.rope``?
+* Adding codeassist templates in ``~/.rope``? Should we access
+  codeassist directly or use core
 
 
 Remaining Small Stories

rope/ide/codeassist.py

         name = rope.base.codeanalyze.get_name_at(resource, offset)
         pyname = rope.base.codeanalyze.get_pyname_at(self.project.get_pycore(),
                                                      resource, offset)
-        finder = occurrences.FilteredOccurrenceFinder(
+        finder = occurrences.FilteredFinder(
             self.project.get_pycore(), name, [pyname])
         result = []
         for resource in self.project.get_pycore().get_python_files():

rope/refactor/change_signature.py

             pynames = rename.get_all_methods_in_hierarchy(pyclass, self.name)
         else:
             pynames = [self.pyname]
-        finder = rope.refactor.occurrences.FilteredOccurrenceFinder(
+        finder = rope.refactor.occurrences.FilteredFinder(
             self.pycore, self.name, pynames)
         for file in self.pycore.get_python_files():
             change_calls = _ChangeCallsInModule(

rope/refactor/encapsulate_field.py

         self.pycore = pycore
         self.name = name
         self.occurrences_finder = rope.refactor.occurrences.\
-                                  FilteredOccurrenceFinder(pycore, name, pynames)
+                                  FilteredFinder(pycore, name, pynames)
         self.getter = 'get_' + name
         self.setter = 'set_' + name
 

rope/refactor/importutils/__init__.py

             imported = name
             if alias is not None:
                 imported = alias
-            occurrence_finder = occurrences.FilteredOccurrenceFinder(
+            occurrence_finder = occurrences.FilteredFinder(
                 self.pycore, imported, [pymodule.get_attribute(imported)],
                 imports=False)
             source = rename.rename_in_module(
         old_name = name.split('.')[-1]
         old_pyname = rope.base.evaluate.get_string_result(
             pymodule.get_scope(), name)
-        occurrence_finder = rope.refactor.occurrences.FilteredOccurrenceFinder(
+        occurrence_finder = rope.refactor.occurrences.FilteredFinder(
             self.pycore, old_name, [old_pyname], imports=False)
         changes = rope.refactor.sourceutils.ChangeCollector(pymodule.source_code)
         for occurrence in occurrence_finder.find_occurrences(pymodule=pymodule):

rope/refactor/inline.py

         self.pyfunction = self.pyname.get_object()
         self.pymodule = self.pyfunction.get_module()
         self.resource = self.pyfunction.get_module().get_resource()
-        self.occurrence_finder = rope.refactor.occurrences.FilteredOccurrenceFinder(
+        self.occurrence_finder = rope.refactor.occurrences.FilteredFinder(
             self.pycore, self.name, [self.pyname])
         self.definition_generator = _DefinitionGenerator(self.pycore, self.pyfunction)
 
         definition = definition_with_assignment[definition_with_assignment.\
                                                 index('=') + 1:].strip()
 
-        occurrence_finder = occurrences.FilteredOccurrenceFinder(
+        occurrence_finder = occurrences.FilteredFinder(
             self.pycore, self.name, [self.pyname])
         changed_source = rename.rename_in_module(
             occurrence_finder, definition, pymodule=self.pymodule, replace_primary=True)

rope/refactor/introduce_factory.py

                 changes.add_change(ChangeContents(file_, changed_code))
 
     def _rename_occurrences(self, file_, changed_name, global_factory):
-        occurrence_finder = occurrences.FilteredOccurrenceFinder(
+        occurrence_finder = occurrences.FilteredFinder(
             self.pycore, self.old_name, [self.old_pyname], only_calls=True)
         changed_code = rename.rename_in_module(
             occurrence_finder, changed_name, resource=file_,

rope/refactor/introduce_parameter.py

 
     def _change_function_occurances(self, change_collector, function_start,
                                     function_end, new_name):
-        finder = occurrences.FilteredOccurrenceFinder(self.pycore, self.name,
+        finder = occurrences.FilteredFinder(self.pycore, self.name,
                                                       [self.pyname])
         for occurrence in finder.find_occurrences(resource=self.resource):
             start, end = occurrence.get_primary_range()

rope/refactor/method_object.py

             body = param + ' = None\n' + body
             pymod = self.pycore.get_string_module(body, self.resource)
             pyname = pymod.get_attribute(param)
-            finder = occurrences.FilteredOccurrenceFinder(
+            finder = occurrences.FilteredFinder(
                 self.pycore, param, [pyname])
             result = rename.rename_in_module(finder, 'self.' + param,
                                              pymodule=pymod)

rope/refactor/move.py

         self_name = self._get_self_name()
         body = self_name + ' = None\n' + self._get_unchanged_body()
         pymodule = self.pycore.get_string_module(body)
-        finder = occurrences.FilteredOccurrenceFinder(
+        finder = occurrences.FilteredFinder(
             self.pycore, self_name, [pymodule.get_attribute(self_name)])
         result = rename.rename_in_module(finder, host, pymodule=pymodule)
         return result[result.index('\n') + 1:]
         return pymodule, can_select.changed
 
     def _rename_in_module(self, pymodule, new_name, imports=False):
-        occurrence_finder = occurrences.FilteredOccurrenceFinder(
+        occurrence_finder = occurrences.FilteredFinder(
             self.pycore, self.old_name, [self.old_pyname], imports=imports)
         source = rename.rename_in_module(occurrence_finder, new_name,
                                          pymodule=pymodule, replace_primary=True)

rope/refactor/occurrences.py

         return self.tools.word_finder.is_assigned_here(self.offset)
 
 
-class FilteredOccurrenceFinder(object):
+class FilteredFinder(object):
 
     def __init__(self, pycore, name, pynames, only_calls=False, imports=True):
         self.pycore = pycore

rope/refactor/rename.py

-import rope.base.codeanalyze
-import rope.base.exceptions
-import rope.base.pynames
-import rope.base.pyobjects
+from rope.base import exceptions, codeanalyze, pyobjects, pynames
 from rope.base.change import ChangeSet, ChangeContents, MoveResource
 from rope.refactor import occurrences, sourceutils
 
 
 class Rename(object):
+    """A class for performing rename refactoring
+
+    It can rename everything: classes, functions, modules, packages,
+    methods, variables and keyword arguments.
+
+    """
 
     def __init__(self, project, resource, offset=None):
         """If `offset` is None, the `resource` itself will be renamed"""
         self.pycore = project.pycore
         self.resource = resource
         if offset is not None:
-            self.old_name = rope.base.codeanalyze.get_name_at(self.resource,
-                                                              offset)
-            self.old_pyname = rope.base.codeanalyze.get_pyname_at(
-                self.pycore, resource, offset)
+            self.old_name = codeanalyze.get_name_at(self.resource, offset)
+            self.old_pyname = codeanalyze.get_pyname_at(self.pycore,
+                                                        resource, offset)
             if self.old_pyname is None:
-                raise rope.base.exceptions.RefactoringError(
+                raise exceptions.RefactoringError(
                     'Rename refactoring should be performed on python identifiers.')
         else:
             if not resource.is_folder() and resource.name == '__init__.py':
                 resource = resource.parent
             dummy_pymodule = self.pycore.get_string_module('')
-            self.old_pyname = rope.base.pynames.ImportedModule(
-                dummy_pymodule, resource=resource)
+            self.old_pyname = pynames.ImportedModule(dummy_pymodule,
+                                                     resource=resource)
             if resource.is_folder():
                 self.old_name = resource.name
             else:
 
         """
         old_pynames = self._get_old_pynames(in_file, in_hierarchy)
-        if not old_pynames:
-            return None
         if not in_file and len(old_pynames) == 1 and \
            self._is_renaming_a_function_local_name():
             in_file = True
         files = self._get_interesting_files(in_file)
         changes = ChangeSet('Renaming <%s> to <%s>' %
                             (self.old_name, new_name))
+        finder = occurrences.FilteredFinder(self.pycore, self.old_name,
+                                            old_pynames)
         for file_ in files:
-            occurance_finder = occurrences.FilteredOccurrenceFinder(
-                self.pycore, self.old_name, old_pynames)
-            new_content = rename_in_module(occurance_finder, new_name,
-                                           resource=file_)
+            new_content = rename_in_module(finder, new_name, resource=file_)
             if new_content is not None:
                 changes.add_change(ChangeContents(file_, new_content))
 
         if lineno is None:
             return False
         scope = module.get_scope().get_inner_scope_for_line(lineno)
-        if isinstance(self.old_pyname, rope.base.pynames.DefinedName) and \
+        if isinstance(self.old_pyname, pynames.DefinedName) and \
            scope.get_kind() in ('Function', 'Class'):
             scope = scope.parent
         return scope.get_kind() == 'Function' and \
                self.old_pyname in scope.get_names().values() and \
-               isinstance(self.old_pyname, rope.base.pynames.AssignedName)
+               isinstance(self.old_pyname, pynames.AssignedName)
 
     def _is_renaming_a_module(self):
-        if self.old_pyname.get_object().get_type() == rope.base.pycore.get_base_type('Module'):
+        if self.old_pyname.get_object().get_type() == pyobjects.get_base_type('Module'):
             return True
         return False
 
 
     def is_method(self):
         pyname = self.old_pyname
-        return isinstance(pyname, rope.base.pynames.DefinedName) and \
-               pyname.get_object().get_type() == rope.base.pyobjects.get_base_type('Function') and \
-               pyname.get_object().parent.get_type() == rope.base.pyobjects.get_base_type('Type')
+        return isinstance(pyname, pynames.DefinedName) and \
+               pyname.get_object().get_type() == pyobjects.get_base_type('Function') and \
+               pyname.get_object().parent.get_type() == pyobjects.get_base_type('Type')
 
     def _rename_module(self, pyobject, new_name):
         resource = pyobject.get_resource()
         return MoveResource(resource, new_location)
 
 
-def rename_in_module(occurrences_finder, new_name, resource=None,
-                     pymodule=None, replace_primary=False):
+class ChangeOccurrences(object):
+    """A class for changing the occurrences of a name in a scope
+
+    This class replaces the occurrences of a name.  Note that it only
+    changes the scope containing the offset passed to the constructor.
+    What's more it does not have any side-effects.  That is for
+    example changing occurrences of a module does not rename the 
+    module; it merely replaces the occurrences of that module in a
+    scope with the given expression.  This class is useful for
+    performing many custom refactorings.
+
+    """
+
+    def __init__(self, project, resource, offset):
+        self.pycore = project.pycore
+        self.resource = resource
+        self.offset = offset
+        self.old_name = codeanalyze.get_name_at(resource, offset)
+        self.old_pyname = codeanalyze.get_pyname_at(self.pycore,
+                                                    resource, offset)
+        self.pymodule = self.pycore.resource_to_pyobject(self.resource)
+
+    def get_old_name(self):
+        word_finder = codeanalyze.WordRangeFinder(self.resource.read())
+        return word_finder.get_primary_at(self.offset)
+
+    def _get_scope_offset(self):
+        lines = self.pymodule.lines
+        scope = self.pymodule.get_scope().\
+                get_inner_scope_for_line(lines.get_line_number(self.offset))
+        start = lines.get_line_start(scope.get_start())
+        end = lines.get_line_end(scope.get_end())
+        return start, end
+
+    def get_changes(self, new_name, only_calls=False, reads=True, writes=True):
+        changes = ChangeSet('Changing <%s> occurrences to <%s>' %
+                            (self.old_name, new_name))
+        scope_start, scope_end = self._get_scope_offset()
+        finder = occurrences.FilteredFinder(
+            self.pycore, self.old_name, [self.old_pyname],
+            imports=False, only_calls=only_calls)
+        new_contents = rename_in_module(
+            finder, new_name, pymodule=self.pymodule, replace_primary=True,
+            region=(scope_start, scope_end), reads=reads, writes=writes)
+        if new_contents is not None:
+            changes.add_change(ChangeContents(self.resource, new_contents))
+        return changes
+
+
+def rename_in_module(occurrences_finder, new_name, resource=None, pymodule=None,
+                     replace_primary=False, region=None, reads=True, writes=True):
     if resource is not None:
         source_code = resource.read()
     else:
             start, end = occurrence.get_primary_range()
         else:
             start, end = occurrence.get_word_range()
-        change_collector.add_change(start, end, new_name)
+        if (not reads and not occurrence.is_written()) or \
+           (not writes and occurrence.is_written()):
+            continue
+        if region is None or region[0] <= start < region[1]:
+            change_collector.add_change(start, end, new_name)
     return change_collector.get_changed()
 
 
 
         self.main = Frame(self.root, height='13c', width='26c', relief=RIDGE, bd=2)
         self.editor_panel = Frame(self.main, borderwidth=0)
-        self.editor_manager = rope.ui.editorpile.EditorPile(self.editor_panel, self)
+        self.status_bar = Frame(self.main, borderwidth=1, relief=RIDGE)
 
-        self.status_bar = Frame(self.main, borderwidth=1, relief=RIDGE)
         self.status_bar_manager = rope.ui.statusbar.StatusBarManager(self.status_bar)
+        buffer_status = self.status_bar_manager.create_status('buffer')
+        buffer_status.set_width(40)
+        self.editor_manager = rope.ui.editorpile.EditorPile(self.editor_panel, self,
+                                                            buffer_status)
+
         line_status = self.status_bar_manager.create_status('line')
         line_status.set_width(8)
 

rope/ui/editorpile.py

 
 class EditorPile(object):
 
-    def __init__(self, editor_panel, core):
+    def __init__(self, editor_panel, core, status):
         self.core = core
+        self.status = status
         self.editor_list = Frame(editor_panel, borderwidth=0)
         self.editor_frame = Frame(editor_panel, borderwidth=0, relief=RIDGE)
         self.editors = []
         if not editor.get_file().exists():
             new_title = '! ' + new_title
         self.buttons[editor]['text'] = new_title
+        self._update_buffer_status()
 
     def _editor_was_changed(self, resource, offset):
         self.last_edited_location = (resource, offset)
         self.active_editor = editor
         self.editors.remove(editor)
         self.editors.insert(0, editor)
+        self._update_buffer_status()
         self.core._editor_changed()
 
     def get_resource_editor(self, file_, readonly=False, mode=None):
         if self.editors:
             self.buttons[self.editors[0]].invoke()
         else:
+            self._update_buffer_status()
             self.core._editor_changed()
 
     def get_editor_list(self):
         if self.last_edited_location is not None:
             self.get_resource_editor(self.last_edited_location[0])
             self.active_editor.get_editor().set_insert(self.last_edited_location[1])
+
+    def _update_buffer_status(self):
+        if self.active_editor is not None:
+            editor = self.active_editor
+            mode1 = '-'
+            if editor.get_editor().is_modified():
+                mode1 = '*'
+            mode2 = mode1
+            if editor.readonly:
+                mode2 = '%'
+            if not editor.get_file().exists():
+                mode2 = '!'
+            text = '%s%s  %s  (%s) ' % (
+                mode1, mode2, editor.get_file().path,
+                editor.get_editor().get_editing_context().name)
+            self.status.set_text(text)
+        else:
+            self.status.set_text('')

rope/ui/highlighter.py

         while index > 0:
             new_index = self._get_line_start(text, index - 1)
             line = text[new_index:index]
-            if line.strip() == '':
+            if line.strip() == '' or self._is_list(line):
                 return new_index
             index = new_index
         return 0
         index = self._get_line_end(text, index)
         while index < len(text) - 1:
             new_index = self._get_line_end(text, index + 1)
-            line = text[index:new_index]
+            line = text[index + 1:new_index]
             if line.strip() == '':
                 return new_index
+            if self._is_list(line):
+                return index
             index = new_index
         return len(text)
+
+    def _is_list(self, line):
+        for mark in '*-+':
+            if line.startswith('%s ' % mark):
+                return True
+        return False

rope/ui/keybinder.py

         def call_back(event=None):
             try:
                 status_text = self.status_bar.create_status('key')
-                status_text.set_width(6)
+                status_text.set_width(8)
             except statusbar.StatusBarException:
                 status_text = self.status_bar.get_status('key')
             status_text.set_text(key)

rope/ui/refactor.py

         self.toplevel.bind('<Escape>', lambda event: self._cancel())
         self.toplevel.bind('<Control-g>', lambda event: self._cancel())
         frame = self._get_dialog_frame()
+        frame['border'] = 2
+        frame['relief'] = Tkinter.GROOVE
 
         ok_button = Tkinter.Button(self.toplevel, text='Done', command=self._ok)
         preview_button = Tkinter.Button(self.toplevel, text='Preview', command=self._preview)
     MethodObjectDialog(context).show()
 
 
+class ChangeOccurrencesDialog(RefactoringDialog):
+
+    def __init__(self, context):
+        resource = context.resource
+        editor = context.get_active_editor().get_editor()
+        super(ChangeOccurrencesDialog, self).__init__(context.project,
+                                                      'Change Occurrences')
+        offset = editor.get_current_offset()
+        self.renamer = rope.refactor.rename.ChangeOccurrences(
+            context.project, resource, offset)
+
+    def _get_changes(self):
+        new_name = self.new_name_entry.get()
+        return self.renamer.get_changes(
+            new_name, only_calls=self.only_calls.get(),
+            reads=self.reads.get(), writes=self.writes.get())
+
+    def _get_dialog_frame(self):
+        frame = Tkinter.Frame(self.toplevel)
+        label = Tkinter.Label(frame, text='Replace Occurrences With :')
+        label.grid(row=0, column=0)
+        self.new_name_entry = Tkinter.Entry(frame)
+        self.new_name_entry.insert(0, self.renamer.get_old_name())
+        self.new_name_entry.select_range(0, Tkinter.END)
+        self.new_name_entry.grid(row=0, column=1, columnspan=2)
+        self.new_name_entry.bind('<Return>', lambda event: self._ok())
+        self.only_calls = Tkinter.IntVar(frame, value=0)
+        self.reads = Tkinter.IntVar(frame, value=1)
+        self.writes = Tkinter.IntVar(frame, value=1)
+        only_calls_button = Tkinter.Checkbutton(
+            frame, text='Do only for calls', variable=self.only_calls)
+        reads_button = Tkinter.Checkbutton(
+            frame, text='Reads', variable=self.reads)
+        writes_button = Tkinter.Checkbutton(
+            frame, text='Writes', variable=self.writes)
+        only_calls_button.grid(row=1, column=0, sticky=Tkinter.W)
+        reads_button.grid(row=2, column=0, sticky=Tkinter.W)
+        writes_button.grid(row=2, column=1, sticky=Tkinter.W)
+        self.new_name_entry.focus_set()
+        return frame
+
+
+def change_occurrences(context):
+    ChangeOccurrencesDialog(context).show()
+
+
 actions = []
 actions.append(SimpleAction('rename', ConfirmEditorsAreSaved(rename), 'C-c r r',
                             MenuAddress(['Refactor', 'Rename'], 'r'), ['python']))
                             ConfirmEditorsAreSaved(method_object), 'C-c r j',
                             MenuAddress(['Refactor', 'Method To Method Object'], 'j', 1),
                             ['python']))
+actions.append(SimpleAction('change_occurrences',
+                            ConfirmEditorsAreSaved(change_occurrences, all=False), 'C-c r o',
+                            MenuAddress(['Refactor', 'Change Occurrences'], None, 1),
+                            ['python']))
 actions.append(SimpleAction('local_to_field',
                             ConfirmEditorsAreSaved(convert_local_to_field), None,
                             MenuAddress(['Refactor', 'Convert Local Variable to Field'], 'b', 1),

rope/ui/statusbar.py

 
     def set_width(self, width):
         self.width = width
+        self.set_text(self.get_text())
         #        self.label['width'] = width
 
     def set_text(self, text):

rope/ui/uihelpers.py

 
     def selected(self, obj):
         self.toplevel.destroy()
-        self.handle.selected(obj)
+        if obj is not None:
+            self.handle.selected(obj)
 
     def canceled(self):
         self.toplevel.destroy()

ropetest/refactor/__init__.py

 
 def suite():
     result = unittest.TestSuite()
-    result.addTests(unittest.makeSuite(ropetest.refactor.renametest.RenameRefactoringTest))
+    result.addTests(ropetest.refactor.renametest.suite())
     result.addTests(unittest.makeSuite(ropetest.refactor.extracttest.ExtractMethodTest))
     result.addTests(unittest.makeSuite(IntroduceFactoryTest))
     result.addTests(unittest.makeSuite(ropetest.refactor.movetest.MoveRefactoringTest))

ropetest/refactor/movetest.py

 import unittest
 
-import rope.base.exceptions
 import rope.base.project
-import ropetest
+from rope.base import exceptions
 from rope.refactor import move
 from ropetest import testutils
 
     def setUp(self):
         super(MoveRefactoringTest, self).setUp()
         self.project_root = 'sampleproject'
-        ropetest.testutils.remove_recursively(self.project_root)
+        testutils.remove_recursively(self.project_root)
         self.project = rope.base.project.Project(self.project_root)
         self.pycore = self.project.get_pycore()
         self.mod1 = self.pycore.create_module(self.project.root, 'mod1')
         self.mod5 = self.pycore.create_module(self.pkg, 'mod5')
 
     def tearDown(self):
-        ropetest.testutils.remove_recursively(self.project_root)
+        testutils.remove_recursively(self.project_root)
         super(MoveRefactoringTest, self).tearDown()
 
     def _move(self, resource, offset, dest_resource):
         self.assertEquals('class AClass(object):\n    pass\na_var = AClass()\n',
                           self.mod2.read())
 
-    @ropetest.testutils.assert_raises(rope.base.exceptions.RefactoringError)
+    @testutils.assert_raises(exceptions.RefactoringError)
     def test_folder_destination(self):
         folder = self.project.root.create_folder('folder')
         self.mod1.write('class AClass(object):\n    pass\n')
         self._move(self.mod1, self.mod1.read().index('AClass') + 1, folder)
 
-    @ropetest.testutils.assert_raises(rope.base.exceptions.RefactoringError)
+    @testutils.assert_raises(exceptions.RefactoringError)
     def test_raising_exception_for_moving_non_global_elements(self):
         self.mod1.write('def a_func():\n    class AClass(object):\n        pass\n')
         self._move(self.mod1, self.mod1.read().index('AClass') + 1,
             '    def a_method(self):\n        return self.attr.new_method()\n',
             self.mod1.read())
 
-    @testutils.assert_raises(rope.base.exceptions.RefactoringError)
+    @testutils.assert_raises(exceptions.RefactoringError)
     def test_moving_methods_and_nonexistent_attributes(self):
         code = 'class A(object):\n' \
                '    def a_method(self):\n        return 1\n'
                                  code.index('a_method'))
         mover.get_changes('x', 'new_method')
 
-    @testutils.assert_raises(rope.base.exceptions.RefactoringError)
+    @testutils.assert_raises(exceptions.RefactoringError)
     def test_unknown_attribute_type(self):
         code = 'class A(object):\n    attr = 1\n' \
                '    def a_method(self):\n        return 1\n'

ropetest/refactor/renametest.py

         mod1.write('a = 10\nprint (1+a)\n')
         pymod = self.pycore.get_module('mod1')
         old_pyname = pymod.get_attribute('a')
-        finder = rope.refactor.occurrences.FilteredOccurrenceFinder(
+        finder = rope.refactor.occurrences.FilteredFinder(
             self.pycore, 'a', [old_pyname])
         refactored = rename.rename_in_module(
             finder, 'new_var', pymodule=pymod, replace_primary=True)
             refactored)
 
 
+class ChangeOccurrencesTest(unittest.TestCase):
+
+    def setUp(self):
+        super(ChangeOccurrencesTest, self).setUp()
+        ropetest.testutils.remove_recursively('sample_project')
+        self.project = rope.base.project.Project('sample_project')
+        self.mod = self.project.get_pycore().create_module(
+            self.project.root, 'mod')
+
+    def tearDown(self):
+        ropetest.testutils.remove_recursively('sample_project')
+        super(ChangeOccurrencesTest, self).tearDown()
+
+    def test_simple_case(self):
+        self.mod.write('a_var = 1\nprint(a_var)\n')
+        changer = rename.ChangeOccurrences(self.project, self.mod,
+                                           self.mod.read().index('a_var'))
+        changer.get_changes('new_var').do()
+        self.assertEquals('new_var = 1\nprint(new_var)\n', self.mod.read())
+
+    def test_only_performing_inside_scopes(self):
+        self.mod.write('a_var = 1\nnew_var = 2\ndef f():\n    print(a_var)\n')
+        changer = rename.ChangeOccurrences(self.project, self.mod,
+                                           self.mod.read().rindex('a_var'))
+        changer.get_changes('new_var').do()
+        self.assertEquals('a_var = 1\nnew_var = 2\ndef f():\n    print(new_var)\n',
+                          self.mod.read())
+
+    def test_only_performing_on_calls(self):
+        self.mod.write('def f1():\n    pass\ndef f2():\n    pass\ng = f1\na = f1()\n')
+        changer = rename.ChangeOccurrences(self.project, self.mod,
+                                           self.mod.read().rindex('f1'))
+        changer.get_changes('f2', only_calls=True).do()
+        self.assertEquals('def f1():\n    pass\ndef f2():\n    pass\ng = f1\na = f2()\n',
+                          self.mod.read())
+
+    def test_only_performing_on_reads(self):
+        self.mod.write('a = 1\nb = 2\nprint(a)\n')
+        changer = rename.ChangeOccurrences(self.project, self.mod,
+                                           self.mod.read().rindex('a'))
+        changer.get_changes('b', writes=False).do()
+        self.assertEquals('a = 1\nb = 2\nprint(b)\n', self.mod.read())
+
+
+def suite():
+    result = unittest.TestSuite()
+    result.addTests(unittest.makeSuite(RenameRefactoringTest))
+    result.addTests(unittest.makeSuite(ChangeOccurrencesTest))
+    return result
+
+
 if __name__ == '__main__':
     unittest.main()

ropetest/ui/highlightertest.py

                                                   code.rindex(']') + 2,
                                                   'footnote')))
 
+    def test_suspected_region_lists(self):
+        text = '* 0\n* 1\n* 2\n* 3\n'
+        suspected = self.highlighting.get_suspected_region_after_change(
+            text, text.index('2'), text.index('2') + 1)
+        self.assertEquals((text.index('* 1'), text.index('* 3') - 1), suspected)
+
 
 def suite():
     result = unittest.TestSuite()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.