Commits

Ali Gholami Rudi  committed 5b8e016

Changing PythonIndenter to use StatmentRangeFinder

  • Participants
  • Parent commits 54dc337

Comments (0)

Files changed (9)

File docs/workingon.txt

 *** Ignoring string contents while indenting @ 1 ***
 
-* Ignoring comment contents
-* Ignoring string contents
-? Use StatementRangeFinder
 - Add codeanalyze module
 - Adding LineEditor to the Editor class
 
+! StatementRangeFinder reports all elements with indentation from current
+  statment
+* Add CachedLines
+* Ignoring comment contents
+* Ignoring string contents
+? Add StatementRangeFinderTest to runtests
 * Hold the list of project classes and import them automatically
   in codeassist
 
 
 --- Before 0.2 Release ---
 * Next/prev word should consider underline and capitals as spaces; make it configurable
-* GUI testing redux
+* GUI testing redux; make a ropefunctest direction; ? rename ropetest to ropeunittest
 * Better editor changing dialog; use uihelpers module
 * More builtin templates
+* Highlighting enhancement
 
 
 

File rope/codeanalyze.py

+
+
+class Lines(object):
+
+    def get_line(self, line_number):
+        pass
+
+    def length(self):
+        pass
+
+class ArrayLinesAdapter(object):
+    def __init__(self, lines):
+        self.lines = lines
+    
+    def get_line(self, line_number):
+        return self.lines[line_number - 1]
+    
+    def length(self):
+        return len(self.lines)
 
 
 class StatementRangeFinder(object):
         self.in_string = ''
         self.open_parens = 0
         self.explicit_continuation = False
+        self.parens_openings = []
 
-    def _analyze_line(self, current_line):
+    def _analyze_line(self, current_line_number):
+        current_line = self.lines.get_line(current_line_number)
         for i in range(len(current_line)):
             char = current_line[i]
             if char in '\'"':
                 break
             if char in '([{':
                 self.open_parens += 1
+                self.parens_openings.append((current_line_number, i))
             if char in ')]}':
                 self.open_parens -= 1
+                if self.parens_openings:
+                    self.parens_openings.pop()
         if current_line.rstrip().endswith('\\'):
             self.explicit_continuation = True
         else:
             self.explicit_continuation = False
 
 
-    def get_range(self):
+    def analyze(self):
         last_statement = 0
-        for current_line_number in range(0, self.lineno + 1):
+        for current_line_number in range(1, self.lineno + 1):
             if not self.explicit_continuation and self.open_parens == 0 and self.in_string == '':
                 last_statement = current_line_number
-            current_line = self.lines[current_line_number]
-            self._analyze_line(current_line)
+            self._analyze_line(current_line_number)
         last_indents = self.get_line_indents(last_statement)
         end_line = self.lineno
-        if True or self.lines[self.lineno].rstrip().endswith(':'):
-            for i in range(self.lineno + 1, len(self.lines)):
+        if True or self.lines.get_line(self.lineno).rstrip().endswith(':'):
+            for i in range(self.lineno + 1, self.lines.length() + 1):
                 if self.get_line_indents(i) >= last_indents:
                     end_line = i
                 else:
                     break
-        return (last_statement, end_line)
+        self.scope_end = end_line
+        self.statement_start = last_statement
+
+    def get_statement_start(self):
+        return self.statement_start
+
+    def get_scope_end(self):
+        return self.scope_end
+
+    def last_open_parens(self):
+        if not self.parens_openings:
+            return None
+        return self.parens_openings[-1]
+
+    def is_line_continued(self):
+        return self.open_parens != 0 or self.explicit_continuation
 
     def get_line_indents(self, line_number):
         indents = 0
-        for char in self.lines[line_number]:
+        for char in self.lines.get_line(line_number):
             if char == ' ':
                 indents += 1
             else:

File rope/codeassist.py

 import re
 
 from rope.exceptions import RopeException
-from rope.codeanalyze import StatementRangeFinder
+from rope.codeanalyze import StatementRangeFinder, ArrayLinesAdapter
 
 
 class RopeSyntaxError(RopeException):
         return current_offset + 1
 
     def _comment_current_statement(self, lines, lineno):
-        range_finder = StatementRangeFinder(lines, lineno)
-        start, end = range_finder.get_range()
-        last_indents = range_finder.get_line_indents(start)
+        range_finder = StatementRangeFinder(ArrayLinesAdapter(lines), lineno + 1)
+        range_finder.analyze()
+        start = range_finder.get_statement_start() - 1
+        end = range_finder.get_scope_end() - 1
+        last_indents = self._get_line_indents(lines, start)
         lines[start] = last_indents * ' ' + 'pass'
         for line in range(start + 1, end + 1):
             lines[line] = '#' # + lines[line]

File rope/editor.py

     def get_line(self, line_number):
         pass
     
-    def set_line(self, line_number, string):
+    def length(self):
+        pass
+    
+    def indent_line(self, line_number, count):
         pass
 
 
     def get_line(self, line_number):
         return self.editor.text.get('%d.0' % line_number, '%d.0 lineend' % line_number)
 
-    def set_line(self, line_number, contents):
-        old_contents = self.get_line(line_number)
-        if old_contents == contents:
+    def length(self):
+        result = self.editor._get_line_from_index(END) - 1
+        return result
+
+    def indent_line(self, line_number, count):
+        if count == 0:
             return
-        if old_contents.endswith(contents):
+        if count > 0:
+            self.editor.text.insert('%d.0' % line_number, count * ' ')
+        else:
             self.editor.text.delete('%d.0' % line_number,
-                                    '%d.%d' % (line_number, len(old_contents) - len(contents)))
-            return
-        if contents.endswith(old_contents):
-            self.editor.text.insert('%d.0' % line_number, contents[0:len(contents) - len(old_contents)])
-            return
-        self.editor.text.delete('%d.0' % line_number, '%d.0 lineend' % line_number)
-        self.editor.text.insert('%d.0' % line_number, contents)
+                                    '%d.%d' % (line_number, -count))
 
 
 class GraphicalEditor(TextEditor):

File rope/indenter.py

 import re
 
+from rope.codeanalyze import StatementRangeFinder
+
+
 class TextIndenter(object):
     '''A class for formatting texts'''
 
     def _set_line_indents(self, lineno, indents):
         old_indents = self._count_line_indents(lineno)
         indent_diffs = indents - old_indents
-        if indent_diffs == 0:
-            return
-        old_line = self.line_editor.get_line(lineno)
-        new_line = ''
-        if indent_diffs > 0:
-            new_line = ' ' * indent_diffs + old_line
-        else:
-            new_line = old_line[-indent_diffs:]
-        self.line_editor.set_line(lineno, new_line)
+        self.line_editor.indent_line(lineno, indent_diffs)
 
     def _count_line_indents(self, lineno):
         contents = self.line_editor.get_line(lineno)
     def _get_correct_indentation(self, lineno):
         if lineno == 1:
             return 0
-        new_indent = self._get_base_indentation(lineno)
+        new_indent = self._get_base_indentation_old(lineno)
 
         prev_lineno = self._get_last_non_empty_line(lineno)
         prev_line = self.line_editor.get_line(prev_lineno)
         new_indent += self._get_indentation_changes_caused_by_current_line(current_line)
         return new_indent
 
-    def _get_base_indentation(self, lineno):
+    def _get_base_indentation_old(self, lineno):
         current_line = self.line_editor.get_line(lineno)
         current_lineno = self._get_last_non_empty_line(lineno)
 
                 return self._count_line_indents(real_lineno)
         return self._count_line_indents(current_lineno)
 
+    def _get_base_indentation_old(self, lineno):
+        range_finder = StatementRangeFinder(self.line_editor, lineno - 1)
+        range_finder.analyze()
+        start = range_finder.get_statement_start()
+        if not range_finder.is_line_continued():
+            return self._count_line_indents(self._get_last_non_empty_line(start + 1))
+
+        if range_finder.last_open_parens():
+            return range_finder.last_open_parens()[1] + 1
+
+        start_line = self.line_editor.get_line(start)
+        if start == lineno - 1:
+            try:
+                return start_line.index(' = ') + 3
+            except ValueError:
+                match = re.search('\\b ', start_line)
+                if match:
+                    return match.start() + 1
+                else:
+                    return len(start_line) + 1
+        else:
+            return self._count_line_indents(self._get_last_non_empty_line(lineno)) 
+
 
     def _is_line_continued(self, line_contents):
         if line_contents.endswith('\\'):

File ropetest/codeanalyzetest.py

 import unittest
 
-from rope.codeanalyze import StatementRangeFinder
+from rope.codeanalyze import StatementRangeFinder, ArrayLinesAdapter
 
 class StatementRangeFinderTest(unittest.TestCase):
 
     def tearDown(self):
         super(StatementRangeFinderTest, self).tearDown()
 
+    def get_range_finder(self, code, line):
+        result = StatementRangeFinder(ArrayLinesAdapter(code.split('\n')), line)
+        result.analyze()
+        return result
+
     def test_simple_statement_finding(self):
-        finder = StatementRangeFinder('a = 10', 1)
-        self.assertEquals((1, 1),  finder.get_range())
+        finder = self.get_range_finder('a = 10', 1)
+        self.assertEquals(1,  finder.get_statement_start())
+
+    def test_get_start(self):
+        finder = self.get_range_finder('a = 10\nb = 12\nc = 14', 1)
+        self.assertEquals(1,  finder.get_statement_start())
+
+    def test_get_scope_end(self):
+        finder = self.get_range_finder('a = 10\nb = 12\nc = 14', 1)
+        self.assertEquals(3,  finder.get_scope_end())
+
+    def test_get_last_open_parens(self):
+        finder = self.get_range_finder('a = 10', 1)
+        self.assertTrue(finder.last_open_parens() is None)
+        
+    def test_get_last_open_parens2(self):
+        finder = self.get_range_finder('a = (10 +', 1)
+        self.assertEquals((1, 4), finder.last_open_parens())
+        
+    def test_is_line_continued(self):
+        finder = self.get_range_finder('a = 10', 1)
+        self.assertFalse(finder.is_line_continued())
+        
+    def test_is_line_continued2(self):
+        finder = self.get_range_finder('a = (10 +', 1)
+        self.assertTrue(finder.is_line_continued())
+        
 
 
 if __name__ == '__main__':

File ropetest/indentertest.py

         self.indenter.correct_indentation(3)
         self.assertEquals('def f():\n    return (2,\n            3)', self.editor.get_text())
 
-    # TODO: handle this case
-    def xxx_test_deindenting_after_implicit_continuation_after_return(self):
-        self.editor.set_text('def f():\n    return (2,\n            3)\na = 10')
-        self.indenter.correct_indentation(4)
-        self.assertEquals('def f():\n    return (2,\n            3)\na = 10',
-                          self.editor.get_text())
-
     def test_deindenting_empty_lines(self):
         self.editor.set_text('\n')
         self.indenter.deindent(2)
         self.indenter.insert_tab(self.editor.get_end())
         self.assertEquals('print "a"    ', self.editor.get_text())
 
-    # ignoring strings while correcting indentations
-    def xxx_test_ignoring_parens_in_strings(self):
+    def test_ignoring_parens_in_strings(self):
         self.editor.set_text('print "("\na = 10')
         self.indenter.correct_indentation(2)
         self.assertEquals('print "("\na = 10', self.editor.get_text())
 
+    # TODO: handle this case
+    def xxx_test_deindenting_after_implicit_continuation_after_return(self):
+        self.editor.set_text('def f():\n    return (2,\n            3)\na = 10')
+        self.indenter.correct_indentation(4)
+        self.assertEquals('def f():\n    return (2,\n            3)\na = 10',
+                          self.editor.get_text())
+
 
 if __name__ == '__main__':
     unittest.main()

File ropetest/mockeditor.py

     def get_line(self, number):
         return self.editor.get_text().split('\n')[number - 1]
 
-    def set_line(self, number, string):
+    def length(self):
+        return len(self.editor.get_text().split('\n'))
+
+    def indent_line(self, line_number, count):
         lines = self.editor.get_text().split('\n')
-        lines[number - 1] = string
+        if count > 0:
+            lines[line_number - 1] = count * ' ' + lines[line_number - 1]
+        if count < 0:
+            lines[line_number - 1] = lines[line_number - 1][-count:]
         self.editor.set_text('\n'.join(lines))
 
 

File ropetest/mockeditortest.py

         self.assertEquals('line3', line_editor.get_line(3))
         self.assertEquals('', line_editor.get_line(4))
 
-    def test_line_editor_setting(self):
+    def test_line_editor_indenting(self):
         self.editor.set_text('line1\nline2\nline3\n')
         line_editor = self.editor.line_editor()
-        line_editor.set_line(2, 'second line')
-        self.assertEquals('second line', line_editor.get_line(2))
-        self.assertEquals('line1\nsecond line\nline3\n', self.editor.get_text())
+        line_editor.indent_line(2, 2)
+        self.assertEquals('  line2', line_editor.get_line(2))
+        self.assertEquals('line1\n  line2\nline3\n', self.editor.get_text())
+
+    def test_line_editor_indenting_with_negative_indent(self):
+        self.editor.set_text('line1\nline2\nline3\n')
+        line_editor = self.editor.line_editor()
+        line_editor.indent_line(2, -2)
+        self.assertEquals('ne2', line_editor.get_line(2))
+        self.assertEquals('line1\nne2\nline3\n', self.editor.get_text())
+
+    def test_line_editor_length(self):
+        self.editor.set_text('line1')
+        line_editor = self.editor.line_editor()
+        self.assertEquals(1, line_editor.length())
+        
 
 
 def suite():