Anonymous avatar Anonymous committed 244be5b

Better error conditions for extract refactorings

Comments (0)

Files changed (7)

 
 * `Undo Unification`_
 * `Python's Implicit Interfaces`_
-* Code completions inside uncompleted ``try`` blocks
 
 
 To Be Discussed
   hard to compute information like class hierarchies
 * Using a modification of `compiler` AST for simplifying refactorings
 * Indexing source files for faster occurance finding
-* Ways for eliminating `NoRefactoring`
+* Undoing/redoing refactorings should confirm
 
 
 Undo Unification
 > Public Release 0.3m5 : October 15, 2006
 
 
+* Code completions inside uncompleted ``try`` blocks
+
+
 * Single line extract method and variable @10h
 
 

docs/workingon.txt

 Single line extract method and variable
 =======================================
 
-- Exceptional conditions for extract method/variable
+- Holding complete lines for multi-line extractions
 
 * Refactor `extract` module
-* More tests for extract variable
-
 * Refactor `rope.ui.refactor` dialogs
-* Changing sourcetools.* to use logical lines?
 * Refactor `importutils` module
 
 

rope/codeanalyze.py

     
     def _find_first_non_space_char(self, offset):
         if offset >= len(self.source_code):
-            return len(offset)
+            return len(self.source_code)
         current_offset = offset
         while current_offset < len(self.source_code) and\
               self.source_code[current_offset] in ' \t\n':
         return self._find_import_pair_end(from_names) >= offset
 
     def is_from_statement_module(self, offset):
+        if offset >= len(self.source_code) - 1:
+            return False
         stmt_start = self._find_primary_start(offset)
         line_start = self._get_line_start(stmt_start)
         prev_word = self.source_code[line_start:stmt_start].strip()

rope/refactor/extract.py

         changes = ChangeSet()
         changes.add_change(ChangeFileContents(resource, new_contents))
         return changes
-        
+    
 
 class _ExtractPerformer(object):
     """Perform extract method/variable refactoring
     
     We devide program source code into these parts::
+      
       [...]
         scope_start
             [before_line]
 
         self.start_line = self.lines.get_line_start(start_line)
         self.end_line = self.lines.get_line_end(end_line)
-                
-        start_line = self.lines.get_line_number(start_offset)
+        
         self.first_line_indents = self._get_indents(start_line)
-        self.scope = self.refactoring.pycore.get_string_scope(source_code,
-                                                              resource)
+        self.scope = self.refactoring.pycore.get_string_scope(source_code, resource)
         self.holding_scope = self._find_holding_scope(start_line)
         self.scope_start = self.lines.get_line_start(self.holding_scope.get_start())
         self.scope_end = self.lines.get_line_end(self.holding_scope.get_end()) + 1
         
         self.is_one_line = self._is_one_line_extract(start_line, end_line)
-        if extract_variable:
-            self.extract_info = _ExtractVariableInfo(self)
-        else:
-            self.extract_info = _ExtractInfo(self)
-        if self._is_global():
-            self.scope_indents = 0
-        else:
-            self.scope_indents = self._get_indents(self.holding_scope.get_start()) + 4
+        self.extract_info = self._create_extract_info()
+        
         self._check_exceptional_conditions()
         self.info_collector = self._create_info_collector()
+    
+    def _create_extract_info(self):
+        if self.extract_variable:
+            return _ExtractVariableInfo(self)
+        else:
+            return _ExtractInfo(self)
 
     def _find_holding_scope(self, start_line):
         holding_scope = self.scope.get_inner_scope_for_line(start_line)
             raise RefactoringException('Extracted piece should contain complete statements.')
         if self.is_one_line or self.extract_variable:
             self._check_exceptional_conditions_for_one_liners()
+        else:
+            self._check_exceptional_conditions_for_multi_liners()
+    
+    def _check_exceptional_conditions_for_multi_liners(self):
+        if self.start != self.start_line or self.end != self.end_line:
+            raise RefactoringException('Extracted piece should contain complete statements.')
     
     def _check_exceptional_conditions_for_one_liners(self):
         if (self.start > 0 and self._is_on_a_word(self.start - 1)) or \
            (self.end < len(self.source_code) and self._is_on_a_word(self.end - 1)):
             raise RefactoringException('Should extract complete statements.')
         start_line = self.lines.get_line_number(self.start)
-        end_line = self.lines.get_line_number(self.end) - 1
+        end_line = self.lines.get_line_number(self.end)
         if self.extract_variable and not self._is_one_line_extract(start_line, end_line):
             raise RefactoringException('Extract variable should not span multiple lines.')
     
             return ' ' * self.first_line_indents + call_prefix + \
                    self._get_function_call(args)
     
+    def _get_scope_indents(self):
+        if self._is_global():
+            return 0
+        else:
+            return self._get_indents(self.holding_scope.get_start()) + 4
+    
+    def _get_function_indents(self):
+        if self._is_global():
+            return 4
+        else:
+            return self._get_scope_indents()
+    
     def _get_function_definition(self):
         args = self._find_function_arguments()
         returns = self._find_function_returns()
-        if not self._is_global():
-            function_indents = self.scope_indents
-        else:
-            function_indents = 4
+        function_indents = self._get_function_indents()
         result = []
         result.append('%sdef %s:\n' %
                       (' ' * self._get_indents(self.holding_scope.get_start()),
         self.performer = performer
     
     def get_before_line(self):
-        result = ' ' * self.performer.scope_indents + \
+        result = ' ' * self.performer._get_scope_indents() + \
                  self.performer.extracted_name + ' = ' + \
                  self.performer._get_one_line_definition() + '\n'
         return result

ropetest/refactor/extracttest.py

         start, end = self._convert_line_range_to_offset(code, 2, 2)
         self.do_extract_method(code, start, end, 'new_func')
 
+    @testutils.assert_raises(rope.exceptions.RefactoringException)
+    def test_extract_method_containing_uncomplete_lines(self):
+        code = 'a_var = 20\nanother_var = 30\n'
+        start = code.index('20')
+        end = code.index('30') + 2
+        self.do_extract_method(code, start, end, 'new_func')
+
+    @testutils.assert_raises(rope.exceptions.RefactoringException)
+    def test_extract_method_containing_uncomplete_lines2(self):
+        code = 'a_var = 20\nanother_var = 30\n'
+        start = code.index('20')
+        end = code.index('another') + 5
+        self.do_extract_method(code, start, end, 'new_func')
+
     def test_extract_function_and_argument_as_paramenter(self):
         code = 'def a_func(arg):\n    print arg\n'
         start, end = self._convert_line_range_to_offset(code, 2, 2)
         expected = 'c = 1\na = c + 2\n'
         self.assertEquals(expected, refactored)
 
+    def test_extract_variable_for_a_tuple(self):
+        code = 'a = 1, 2\n'
+        start = code.index('1')
+        end = code.index('2') + 1
+        refactored = self.do_extract_variable(code, start, end, 'c')
+        expected = 'c = 1, 2\na = c\n'
+        self.assertEquals(expected, refactored)
+
+    def test_extract_variable_for_a_string(self):
+        code = 'def a_func():\n    a = "hey!"\n'
+        start = code.index('"')
+        end = code.rindex('"') + 1
+        refactored = self.do_extract_variable(code, start, end, 'c')
+        expected = 'def a_func():\n    c = "hey!"\n    a = c\n'
+        self.assertEquals(expected, refactored)
+
     @testutils.assert_raises(rope.exceptions.RefactoringException)
     def test_raising_exception_when_on_incomplete_variables(self):
         code = 'a_var = 10 + 20\n'
         end = code.rindex('+') + 1
         refactored = self.do_extract_method(code, start, end, 'new_func')
 
+    # FIXME: Extract method should be more intelligent about bad ranges
+    @testutils.assert_raises(rope.exceptions.RefactoringException)
+    def xxx_test_raising_exception_on_function_parens(self):
+        code = 'a = range(10)'
+        start = code.index('(')
+        end = code.rindex(')') + 1
+        refactored = self.do_extract_method(code, start, end, 'new_func')
+
 
 if __name__ == '__main__':
     unittest.main()

ropetest/refactor/inlinetest.py

         refactored = self._inline_local_variable(code, code.index('a_var') + 1)
         self.assertEquals('another_var = 10 + 10\n', refactored)        
 
+    def test_inlining_at_the_end_of_input(self):
+        code = 'a = 1\nb = a'
+        refactored = self._inline_local_variable(code, code.index('a') + 1)
+        self.assertEquals('b = 1', refactored)
+
     @ropetest.testutils.assert_raises(rope.exceptions.RefactoringException)
     def test_on_classes(self):
         code = 'def AClass(object):\n    pass\n'
     def test_tuple_assignments(self):
         code = 'a_var, another_var = (20, 30)\n'
         refactored = self._inline_local_variable(code, code.index('a_var') + 1)
+
+
+if __name__ == '__main__':
+    unittest.main()
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.