Commits

Anonymous committed 62a40be

Handling break and continue in extract method
Handling variable wirtes followd by reads in extract method

  • Participants
  • Parent commits 2d1cd0d

Comments (0)

Files changed (9)

 
 Features added in this release:
 
-* Basic support for builtin types
-* Find occurrences; ``C-G``
-* Removing imports from the same module
-* Goto last edit location; ``C-q``
-* Trying ``utf-8`` if default don't work
-* Comment line and region; ``C-c c``, ``C-c C-c``
-* Ignoring ``*.pyc``, ``*~`` and ``.svn``
+* 
 
 
 Getting Started

File docs/dev/done.txt

 ===========
 
 
+> Public Release 0.4m4 : December 31, 2006
+
+
 - Basic support for builtin types : December 29, 2006
 
 

File docs/dev/issues.txt

 
 Now that rope supports many basic refactorings it seems necessary to
 put more time on enhancing old refactorings and finishing remaining
-stories.  So from till ``0.4`` we will:
+stories.  So till ``0.4`` we will:
 
 * Enhance old refactorings
 * Do remaining stories
 
   * human readable?
   * python (like emacs and lisp)
+  
+    - push or pull model for configurations
+  
   * simple equals
 
 * types

File docs/dev/stories.txt

 * Adding ignored file patterns in projects; ``*.pyc *~ .svn ...``
 
 
+> Public Release 0.4m5 : January 7, 2007
+
+
 * Commanding buffer
 
 
 
 
 * Transforming to ``from w.x.y import z`` for long imports
-
-
-> Public Release 0.4m4 : December 31, 2006

File docs/dev/workingon.txt

+Small Stories
+=============
+
+- ``break`` and ``continue`` in extract method
+- Handling writes followed by reads in extract method
+
+* Exploring module running
+
+
 Remaining Stories
 =================
 
 * Allowing running code to call rope functions while rope is running?
 * Importing star and removing self imports; stack overflow
-* Definition location for variables inside loops
 * Extract constant
 * What to do if a file is removed while editing
 * Allowing non-existent resources?
 * Better `ropetest` package structure
 * Decide when to use `difflib` in `Editor.set_text`
 * Handling `AssList` for inline variable and encapsulate field
-* ``break`` and ``continue`` in extract method
 
 * Considering logical lines in `rope.codeanalyze`
 * Reporting unhandled exceptions as error dialogs in the GUI

File docs/user/overview.txt

 C-x C-s        save
 C-x s          save all
 -------------  --------------------------
-M-/            code-assist
+M-/            code-assist(auto-complete)
 F3             go to definition location
 F2             show doc
 C-o            show quick outline

File rope/__init__.py

 """rope, a python refactoring library and IDE"""
 
-VERSION = '0.4m4'
+VERSION = '0.4m5'

File rope/refactor/extract.py

         try:
             if _ReturnOrYieldFinder.does_it_return(
                 self.source_code[self.parts.region[0]:self.parts.region[1]]):
-                raise RefactoringException('Extracted piece should not contain return statements')
+                raise RefactoringException('Extracted piece should not contain return statements.')
+            if _UnmatchedBreakOrContinueFinder.has_errors(
+                self.source_code[self.parts.region[0]:self.parts.region[1]]):
+                raise RefactoringException(
+                    'A break/continue without matching having a for/while loop.')
         except SyntaxError:
             raise RefactoringException('Extracted piece should contain complete statements.')
 
         self.written = set()
         self.read = set()
         self.postread = set()
+        self.postwritten = set()
         self.host_function = True
     
     def _read_variable(self, name, lineno):
         if self.start <= lineno <= self.end:
-            self.read.add(name)
+            if name not in self.written:
+                self.read.add(name)
         if self.end < lineno:
-            self.postread.add(name)
+            if name not in self.postwritten:
+                self.postread.add(name)
     
     def _written_variable(self, name, lineno):
         if self.start <= lineno <= self.end:
             self.written.add(name)
         if self.start > lineno:
             self.prewritten.add(name)
+        if self.end < lineno:
+            self.postwritten.add(name)
         
     def visitFunction(self, node):
         if not self.is_global and self.host_function:
         self.written.add(node.name)
     
     def visitName(self, node):
-        self.read.add(node.name)
+        if node.name not in self.written:
+            self.read.add(node.name)
     
     def visitFunction(self, node):
         self.written.add(node.name)
     
     def __init__(self):
         self.returns = False
+        self.loop_count = 0
+    
+    def check_loop(self):
+        if self.loop_count < 1:
+            self.error = True
 
     def visitReturn(self, node):
         self.returns = True
         compiler.walk(ast, visitor)
         return visitor.returns
 
+
+class _UnmatchedBreakOrContinueFinder(object):
+    
+    def __init__(self):
+        self.error = False
+        self.loop_count = 0
+    
+    def visitFor(self, node):
+        self.loop_encountered(node)
+
+    def visitWhile(self, node):
+        self.loop_encountered(node)
+    
+    def loop_encountered(self, node):
+        self.loop_count += 1
+        compiler.walk(node.body, self)
+        self.loop_count -= 1
+        if node.else_:
+            compiler.walk(node.else_, self)
+    
+    def visitBreak(self, node):
+        self.check_loop()
+    
+    def visitContinue(self, node):
+        self.check_loop()
+
+    def check_loop(self):
+        if self.loop_count < 1:
+            self.error = True
+
+    def visitFunction(self, node):
+        pass
+    
+    def visitClass(self, node):
+        pass
+    
+    @staticmethod
+    def has_errors(code):
+        if code.strip() == '':
+            return False
+        min_indents = sourceutils.find_minimum_indents(code)
+        indented_code = sourceutils.indent_lines(code, -min_indents)
+        ast = _parse_text(indented_code)
+        visitor = _UnmatchedBreakOrContinueFinder()
+        compiler.walk(ast, visitor)
+        return visitor.error
+
+
 def _parse_text(body):
     if isinstance(body, unicode):
         body = body.encode('utf-8')

File ropetest/refactor/extracttest.py

                    '    def new_func(self):\n        return 2\n'
         self.assertEquals(expected, refactored)
 
+    def test_breaks_and_continues_inside_loops(self):
+        code = 'def a_func():\n    for i in range(10):\n        continue\n'
+        start = code.index('for')
+        end = len(code) - 1
+        refactored = self.do_extract_method(code, start, end, 'new_func')
+        expected = 'def a_func():\n    new_func()\n\n' \
+                   'def new_func():\n    for i in range(10):\n        continue\n'
+        self.assertEquals(expected, refactored)
+
+    @testutils.assert_raises(rope.base.exceptions.RefactoringException)
+    def test_breaks_and_continues_outside_loops(self):
+        code = 'def a_func():\n    for i in range(10):\n        a = i\n        continue\n'
+        start = code.index('a = i')
+        end = len(code) - 1
+        refactored = self.do_extract_method(code, start, end, 'new_func')
+
+    def test_variable_writes_followed_by_variable_reads_after_extraction(self):
+        code = 'def a_func():\n    a = 1\n    a = 2\n    b = a\n'
+        start = code.index('a = 1')
+        end = code.index('a = 2') - 1
+        refactored = self.do_extract_method(code, start, end, 'new_func')
+        expected = 'def a_func():\n    new_func()\n    a = 2\n    b = a\n\n' \
+                   'def new_func():\n    a = 1\n'
+        self.assertEquals(expected, refactored)
+
+    def test_variable_writes_followed_by_variable_reads_inside_extraction(self):
+        code = 'def a_func():\n    a = 1\n    a = 2\n    b = a\n'
+        start = code.index('a = 2')
+        end = len(code) - 1
+        refactored = self.do_extract_method(code, start, end, 'new_func')
+        expected = 'def a_func():\n    a = 1\n    new_func()\n\n' \
+                   'def new_func():\n    a = 2\n    b = a\n'
+        self.assertEquals(expected, refactored)
+
     def test_extract_variable(self):
         code = 'a_var = 10 + 20\n'
         start = code.index('10')