Commits

Anonymous committed 687d126

worder: move codeanalyze.WordRangeFinder to worder.Worder

Comments (0)

Files changed (13)

rope/base/codeanalyze.py

 import re
 import token
 import tokenize
-import warnings
 
-
-class WordRangeFinder(object):
-    """A class for finding boundaries of words and expressions
-
-    Note that in these methods, offset should be the index of the
-    character not the index of the character after it.
-
-    """
-
-    # XXX: some of these methods fail on badly formatted or less
-    # common code; see disabled testcases for some of them
-
-    def __init__(self, source_code):
-        import rope.base.simplify
-        self.raw = source_code
-        self.code = rope.base.simplify.real_code(source_code)
-
-    def _find_word_start(self, offset):
-        current_offset = offset
-        while current_offset >= 0 and self._is_id_char(current_offset):
-            current_offset -= 1
-        return current_offset + 1
-
-    def _find_word_end(self, offset):
-        while offset + 1 < len(self.code) and self._is_id_char(offset + 1):
-            offset += 1
-        return offset
-
-    _char_pat = re.compile(r'[\'"#]')
-    def _find_last_non_space_char(self, offset):
-        if offset <= 0:
-            return 0
-        while offset >= 0 and self.code[offset].isspace():
-            if self.code[offset] == '\n':
-                if offset > 0 and self.code[offset - 1] == '\\':
-                    offset -= 1
-                try:
-                    start = self.code.rindex('\n', 0, offset)
-                except ValueError:
-                    start = 0
-
-                match = self._char_pat.search(self.code[start:offset])
-                if match and match.group() == '#':
-                    offset = self.code.rindex('#', start, offset)
-            offset -= 1
-        return offset
-
-    def get_word_at(self, offset):
-        offset = self._get_fixed_offset(offset)
-        return self.raw[self._find_word_start(offset):
-                        self._find_word_end(offset) + 1]
-
-    def _get_fixed_offset(self, offset):
-        if offset >= len(self.code):
-            return offset - 1
-        if not self._is_id_char(offset):
-            if offset > 0 and self._is_id_char(offset - 1):
-                return offset - 1
-            if offset < len(self.code) - 1 and self._is_id_char(offset + 1):
-                return offset + 1
-        return offset
-
-    def _is_id_char(self, offset):
-        return self.code[offset].isalnum() or self.code[offset] == '_'
-
-    def _find_string_start(self, offset):
-        kind = self.code[offset]
-        offset -= 1
-        while True:
-            try:
-                offset = self.code.rindex(kind, 0, offset)
-                if offset == 0 or self.code[offset - 1] != '\\':
-                    return offset
-                offset -= 1
-            except ValueError:
-                return 0
-
-    def _find_parens_start(self, offset):
-        offset = self._find_last_non_space_char(offset - 1)
-        while offset >= 0 and self.code[offset] not in '[({':
-            if self.code[offset] not in ':,':
-                offset = self._find_primary_start(offset)
-            offset = self._find_last_non_space_char(offset - 1)
-        return offset
-
-    def _find_atom_start(self, offset):
-        old_offset = offset
-        if self.code[offset] in '\n\t ':
-            offset = self._find_last_non_space_char(offset)
-        if self.code[offset] in '\'"':
-            return self._find_string_start(offset)
-        if self.code[offset] in ')]}':
-            return self._find_parens_start(offset)
-        if self._is_id_char(offset):
-            return self._find_word_start(offset)
-        return old_offset
-
-    def _find_primary_without_dot_start(self, offset):
-        """It tries to find the undotted primary start
-
-        It is different from `self._get_atom_start()` in that it
-        follows function calls, too; such as in ``f(x)``.
-
-        """
-        last_atom = offset
-        offset = self._find_last_non_space_char(last_atom)
-        while offset > 0 and self.code[offset] in ')]':
-            last_atom = self._find_parens_start(offset)
-            offset = self._find_last_non_space_char(last_atom - 1)
-        if offset >= 0 and (self.code[offset] in '"\'})]' or
-                            self._is_id_char(offset)):
-            return self._find_atom_start(offset)
-        return last_atom
-
-    def _find_primary_start(self, offset):
-        if offset >= len(self.code):
-            offset = len(self.code) - 1
-        if self.code[offset] != '.':
-            offset = self._find_primary_without_dot_start(offset)
-        else:
-            offset = offset + 1
-        while offset > 0:
-            prev = self._find_last_non_space_char(offset - 1)
-            if offset <= 0 or self.code[prev] != '.':
-                break
-            offset = self._find_primary_without_dot_start(prev - 1)
-            if not self._is_id_char(offset):
-                break
-
-        return offset
-
-    def get_primary_at(self, offset):
-        offset = self._get_fixed_offset(offset)
-        start, end = self.get_primary_range(offset)
-        return self.raw[start:end].strip()
-
-    def get_splitted_primary_before(self, offset):
-        """returns expression, starting, starting_offset
-
-        This function is used in `rope.codeassist.assist` function.
-        """
-        if offset == 0:
-            return ('', '', 0)
-        end = offset - 1
-        word_start = self._find_atom_start(end)
-        real_start = self._find_primary_start(end)
-        if self.code[word_start:offset].strip() == '':
-            word_start = end
-        if self.code[end].isspace():
-            word_start = end
-        if self.code[real_start:word_start].strip() == '':
-            real_start = word_start
-        if real_start == word_start == end and not self._is_id_char(end):
-            return ('', '', offset)
-        if real_start == word_start:
-            return ('', self.raw[word_start:offset], word_start)
-        else:
-            if self.code[end] == '.':
-                return (self.raw[real_start:end], '', offset)
-            last_dot_position = word_start
-            if self.code[word_start] != '.':
-                last_dot_position = self._find_last_non_space_char(word_start - 1)
-            last_char_position = self._find_last_non_space_char(last_dot_position - 1)
-            if self.code[word_start].isspace():
-                word_start = offset
-            return (self.raw[real_start:last_char_position + 1],
-                    self.raw[word_start:offset], word_start)
-
-    def _get_line_start(self, offset):
-        try:
-            return self.code.rindex('\n', 0, offset + 1)
-        except ValueError:
-            return 0
-
-    def _get_line_end(self, offset):
-        try:
-            return self.code.index('\n', offset)
-        except ValueError:
-            return len(self.code)
-
-    def _is_followed_by_equals(self, offset):
-        while offset < len(self.code) and self.code[offset] in ' \\\t':
-            if self.code[offset] == '\\':
-                offset += 1
-            offset += 1
-        if offset + 1 < len(self.code) and \
-           self.code[offset] == '=' and self.code[offset + 1] != '=':
-            return True
-        return False
-
-    def _is_name_assigned_in_class_body(self, offset):
-        word_start = self._find_word_start(offset - 1)
-        word_end = self._find_word_end(offset) + 1
-        if '.' in self.code[word_start:word_end]:
-            return False
-        line_start = self._get_line_start(word_start)
-        line = self.code[line_start:word_start].strip()
-        if not line and self._is_followed_by_equals(word_end):
-            return True
-        return False
-
-    def is_a_class_or_function_name_in_header(self, offset):
-        # XXX: does not handle line breaks after def
-        word_start = self._find_word_start(offset - 1)
-        line_start = self._get_line_start(word_start)
-        prev_word = self.code[line_start:word_start].strip()
-        return prev_word in ['def', 'class']
-
-    def _find_first_non_space_char(self, offset):
-        if offset >= len(self.code):
-            return len(self.code)
-        while offset < len(self.code):
-            if offset + 1 < len(self.code) and \
-               self.code[offset] == '\\':
-                offset += 2
-            elif self.code[offset] in ' \t\n':
-                offset += 1
-            else:
-                break
-        return offset
-
-    def is_a_function_being_called(self, offset):
-        word_end = self._find_word_end(offset) + 1
-        next_char = self._find_first_non_space_char(word_end)
-        return next_char < len(self.code) and \
-               self.code[next_char] == '(' and \
-               not self.is_a_class_or_function_name_in_header(offset)
-
-    def _find_import_pair_end(self, start):
-        next_char = self._find_first_non_space_char(start)
-        if next_char >= len(self.code):
-            return len(self.code)
-        if self.code[next_char] == '(':
-            try:
-                return self.code.index(')', next_char) + 1
-            except ValueError:
-                return SyntaxError('Unmatched Parens')
-        else:
-            offset = next_char
-            while True:
-                try:
-                    offset = self.code.index('\n', offset)
-                    if offset == 0 or self.code[offset - 1] != '\\':
-                        return offset
-                    offset += 1
-                except ValueError:
-                    return len(self.code)
-
-    def is_import_statement(self, offset):
-        try:
-            last_import = self.code.rindex('import ', 0, offset)
-        except ValueError:
-            return False
-        return self._find_import_pair_end(last_import + 7) >= offset
-
-    def is_from_statement(self, offset):
-        try:
-            last_from = self.code.rindex('from ', 0, offset)
-            from_import = self.code.index(' import ', last_from)
-            from_names = from_import + 8
-        except ValueError:
-            return False
-        from_names = self._find_first_non_space_char(from_names)
-        return self._find_import_pair_end(from_names) >= offset
-
-    def is_from_statement_module(self, offset):
-        if offset >= len(self.code) - 1:
-            return False
-        stmt_start = self._find_primary_start(offset)
-        line_start = self._get_line_start(stmt_start)
-        prev_word = self.code[line_start:stmt_start].strip()
-        return prev_word == 'from'
-
-    def is_a_name_after_from_import(self, offset):
-        try:
-            # XXX: what if the char after from or around import is not
-            # space?
-            last_from = self.code.rindex('from ', 0, offset)
-            from_import = self.code.index(' import ', last_from)
-            from_names = from_import + 8
-        except ValueError:
-            return False
-        if from_names - 1 > offset:
-            return False
-        return self._find_import_pair_end(from_names) >= offset
-
-    def get_from_module(self, offset):
-        try:
-            last_from = self.code.rindex('from ', 0, offset)
-            import_offset = self.code.index(' import ', last_from)
-            end = self._find_last_non_space_char(import_offset)
-            return self.get_primary_at(end)
-        except ValueError:
-            pass
-
-    def is_from_aliased(self, offset):
-        if not self.is_a_name_after_from_import(offset):
-            return False
-        try:
-            end = self._find_word_end(offset)
-            as_end = min(self._find_word_end(end + 1), len(self.code))
-            as_start = self._find_word_start(as_end)
-            if self.code[as_start:as_end + 1] == 'as':
-                return True
-        except ValueError:
-            return False
-
-    def get_from_aliased(self, offset):
-        try:
-            end = self._find_word_end(offset)
-            as_ = self._find_word_end(end + 1)
-            alias = self._find_word_end(as_ + 1)
-            start = self._find_word_start(alias)
-            return self.raw[start:alias + 1]
-        except ValueError:
-            pass
-
-    def is_function_keyword_parameter(self, offset):
-        word_end = self._find_word_end(offset)
-        if word_end + 1 == len(self.code):
-            return False
-        next_char = self._find_first_non_space_char(word_end + 1)
-        if next_char + 2 >= len(self.code) or \
-           self.code[next_char] != '=' or \
-           self.code[next_char + 1] == '=':
-            return False
-        word_start = self._find_word_start(offset)
-        prev_char = self._find_last_non_space_char(word_start - 1)
-        if prev_char - 1 < 0 or self.code[prev_char] not in ',(':
-            return False
-        return True
-
-    def is_on_function_call_keyword(self, offset, stop_searching=0):
-        if self._is_id_char(offset):
-            offset = self._find_word_start(offset) - 1
-        offset = self._find_last_non_space_char(offset)
-        if offset <= stop_searching or \
-           self.code[offset] not in '(,':
-            return False
-        parens_start = self.find_parens_start_from_inside(offset, stop_searching)
-        if stop_searching < parens_start:
-            return True
-        return False
-
-    def find_parens_start_from_inside(self, offset, stop_searching=0):
-        opens = 1
-        while offset > stop_searching:
-            if self.code[offset] == '(':
-                break
-            if self.code[offset] != ',':
-                offset = self._find_primary_start(offset)
-            offset -= 1
-        return max(stop_searching, offset)
-
-    def is_assigned_here(self, offset):
-        operation = self.get_assignment_type(offset)
-        operations = ('=', '-=', '+=', '*=', '/=', '%=', '**=',
-                      '>>=', '<<=', '&=', '^=', '|=')
-        return operation in operations
-
-    def get_assignment_type(self, offset):
-        word_end = self._find_word_end(offset)
-        next_char = self._find_first_non_space_char(word_end + 1)
-        current_char = next_char
-        while current_char + 1 < len(self.code) and \
-              (self.code[current_char] != '=' or \
-               self.code[current_char + 1] == '=') and \
-              current_char < next_char + 3:
-            current_char += 1
-        operation = self.code[next_char:current_char + 1]
-        return operation
-
-    def get_primary_range(self, offset):
-        start = self._find_primary_start(offset)
-        end = self._find_word_end(offset) + 1
-        return (start, end)
-
-    def get_word_range(self, offset):
-        offset = max(0, offset)
-        start = self._find_word_start(offset)
-        end = self._find_word_end(offset) + 1
-        return (start, end)
-
-    def get_word_parens_range(self, offset):
-        if self.is_a_function_being_called(offset) or \
-           self.is_a_class_or_function_name_in_header(offset):
-            end = self._find_word_end(offset)
-            start_parens = self.code.index('(', end)
-            index = start_parens
-            open_count = 0
-            while index < len(self.code):
-                if self.code[index] == '(':
-                    open_count += 1
-                if self.code[index] == ')':
-                    open_count -= 1
-                if open_count == 0:
-                    return (start_parens, index + 1)
-                index += 1
-            return (start_parens, index)
-        return (None, None)
+import rope.base.worder
 
 
 def get_name_at(resource, offset):
     source_code = resource.read()
-    word_finder = WordRangeFinder(source_code)
+    word_finder = rope.base.worder.Worder(source_code)
     return word_finder.get_word_at(offset)
 
 

rope/base/evaluate.py

 import rope.base.builtins
 import rope.base.pynames
 import rope.base.pyobjects
-from rope.base import ast, astutils, exceptions, pyobjects, arguments
-from rope.base.codeanalyze import WordRangeFinder
+from rope.base import ast, astutils, exceptions, pyobjects, arguments, worder
 
 
 BadIdentifierError = exceptions.BadIdentifierError
         self.source_code = pymodule.source_code
         self.module_scope = pymodule.get_scope()
         self.lines = pymodule.lines
-        self.word_finder = WordRangeFinder(self.source_code)
+        self.word_finder = worder.Worder(self.source_code)
 
     def _is_defined_in_class_body(self, holding_scope, offset, lineno):
         if lineno == holding_scope.get_start() and \

rope/base/worder.py

+import re
+
+
+class Worder(object):
+    """A class for finding boundaries of words and expressions
+
+    Note that in these methods, offset should be the index of the
+    character not the index of the character after it.
+
+    """
+
+    # XXX: some of these methods fail on badly formatted or less
+    # common code; see disabled testcases for some of them
+
+    def __init__(self, source_code):
+        import rope.base.simplify
+        self.raw = source_code
+        self.code = rope.base.simplify.real_code(source_code)
+
+    def _find_word_start(self, offset):
+        current_offset = offset
+        while current_offset >= 0 and self._is_id_char(current_offset):
+            current_offset -= 1
+        return current_offset + 1
+
+    def _find_word_end(self, offset):
+        while offset + 1 < len(self.code) and self._is_id_char(offset + 1):
+            offset += 1
+        return offset
+
+    _char_pat = re.compile(r'[\'"#]')
+    def _find_last_non_space_char(self, offset):
+        if offset <= 0:
+            return 0
+        while offset >= 0 and self.code[offset].isspace():
+            if self.code[offset] == '\n':
+                if offset > 0 and self.code[offset - 1] == '\\':
+                    offset -= 1
+                try:
+                    start = self.code.rindex('\n', 0, offset)
+                except ValueError:
+                    start = 0
+
+                match = self._char_pat.search(self.code[start:offset])
+                if match and match.group() == '#':
+                    offset = self.code.rindex('#', start, offset)
+            offset -= 1
+        return offset
+
+    def get_word_at(self, offset):
+        offset = self._get_fixed_offset(offset)
+        return self.raw[self._find_word_start(offset):
+                        self._find_word_end(offset) + 1]
+
+    def _get_fixed_offset(self, offset):
+        if offset >= len(self.code):
+            return offset - 1
+        if not self._is_id_char(offset):
+            if offset > 0 and self._is_id_char(offset - 1):
+                return offset - 1
+            if offset < len(self.code) - 1 and self._is_id_char(offset + 1):
+                return offset + 1
+        return offset
+
+    def _is_id_char(self, offset):
+        return self.code[offset].isalnum() or self.code[offset] == '_'
+
+    def _find_string_start(self, offset):
+        kind = self.code[offset]
+        offset -= 1
+        while True:
+            try:
+                offset = self.code.rindex(kind, 0, offset)
+                if offset == 0 or self.code[offset - 1] != '\\':
+                    return offset
+                offset -= 1
+            except ValueError:
+                return 0
+
+    def _find_parens_start(self, offset):
+        offset = self._find_last_non_space_char(offset - 1)
+        while offset >= 0 and self.code[offset] not in '[({':
+            if self.code[offset] not in ':,':
+                offset = self._find_primary_start(offset)
+            offset = self._find_last_non_space_char(offset - 1)
+        return offset
+
+    def _find_atom_start(self, offset):
+        old_offset = offset
+        if self.code[offset] in '\n\t ':
+            offset = self._find_last_non_space_char(offset)
+        if self.code[offset] in '\'"':
+            return self._find_string_start(offset)
+        if self.code[offset] in ')]}':
+            return self._find_parens_start(offset)
+        if self._is_id_char(offset):
+            return self._find_word_start(offset)
+        return old_offset
+
+    def _find_primary_without_dot_start(self, offset):
+        """It tries to find the undotted primary start
+
+        It is different from `self._get_atom_start()` in that it
+        follows function calls, too; such as in ``f(x)``.
+
+        """
+        last_atom = offset
+        offset = self._find_last_non_space_char(last_atom)
+        while offset > 0 and self.code[offset] in ')]':
+            last_atom = self._find_parens_start(offset)
+            offset = self._find_last_non_space_char(last_atom - 1)
+        if offset >= 0 and (self.code[offset] in '"\'})]' or
+                            self._is_id_char(offset)):
+            return self._find_atom_start(offset)
+        return last_atom
+
+    def _find_primary_start(self, offset):
+        if offset >= len(self.code):
+            offset = len(self.code) - 1
+        if self.code[offset] != '.':
+            offset = self._find_primary_without_dot_start(offset)
+        else:
+            offset = offset + 1
+        while offset > 0:
+            prev = self._find_last_non_space_char(offset - 1)
+            if offset <= 0 or self.code[prev] != '.':
+                break
+            offset = self._find_primary_without_dot_start(prev - 1)
+            if not self._is_id_char(offset):
+                break
+
+        return offset
+
+    def get_primary_at(self, offset):
+        offset = self._get_fixed_offset(offset)
+        start, end = self.get_primary_range(offset)
+        return self.raw[start:end].strip()
+
+    def get_splitted_primary_before(self, offset):
+        """returns expression, starting, starting_offset
+
+        This function is used in `rope.codeassist.assist` function.
+        """
+        if offset == 0:
+            return ('', '', 0)
+        end = offset - 1
+        word_start = self._find_atom_start(end)
+        real_start = self._find_primary_start(end)
+        if self.code[word_start:offset].strip() == '':
+            word_start = end
+        if self.code[end].isspace():
+            word_start = end
+        if self.code[real_start:word_start].strip() == '':
+            real_start = word_start
+        if real_start == word_start == end and not self._is_id_char(end):
+            return ('', '', offset)
+        if real_start == word_start:
+            return ('', self.raw[word_start:offset], word_start)
+        else:
+            if self.code[end] == '.':
+                return (self.raw[real_start:end], '', offset)
+            last_dot_position = word_start
+            if self.code[word_start] != '.':
+                last_dot_position = self._find_last_non_space_char(word_start - 1)
+            last_char_position = self._find_last_non_space_char(last_dot_position - 1)
+            if self.code[word_start].isspace():
+                word_start = offset
+            return (self.raw[real_start:last_char_position + 1],
+                    self.raw[word_start:offset], word_start)
+
+    def _get_line_start(self, offset):
+        try:
+            return self.code.rindex('\n', 0, offset + 1)
+        except ValueError:
+            return 0
+
+    def _get_line_end(self, offset):
+        try:
+            return self.code.index('\n', offset)
+        except ValueError:
+            return len(self.code)
+
+    def _is_followed_by_equals(self, offset):
+        while offset < len(self.code) and self.code[offset] in ' \\\t':
+            if self.code[offset] == '\\':
+                offset += 1
+            offset += 1
+        if offset + 1 < len(self.code) and \
+           self.code[offset] == '=' and self.code[offset + 1] != '=':
+            return True
+        return False
+
+    def _is_name_assigned_in_class_body(self, offset):
+        word_start = self._find_word_start(offset - 1)
+        word_end = self._find_word_end(offset) + 1
+        if '.' in self.code[word_start:word_end]:
+            return False
+        line_start = self._get_line_start(word_start)
+        line = self.code[line_start:word_start].strip()
+        if not line and self._is_followed_by_equals(word_end):
+            return True
+        return False
+
+    def is_a_class_or_function_name_in_header(self, offset):
+        # XXX: does not handle line breaks after def
+        word_start = self._find_word_start(offset - 1)
+        line_start = self._get_line_start(word_start)
+        prev_word = self.code[line_start:word_start].strip()
+        return prev_word in ['def', 'class']
+
+    def _find_first_non_space_char(self, offset):
+        if offset >= len(self.code):
+            return len(self.code)
+        while offset < len(self.code):
+            if offset + 1 < len(self.code) and \
+               self.code[offset] == '\\':
+                offset += 2
+            elif self.code[offset] in ' \t\n':
+                offset += 1
+            else:
+                break
+        return offset
+
+    def is_a_function_being_called(self, offset):
+        word_end = self._find_word_end(offset) + 1
+        next_char = self._find_first_non_space_char(word_end)
+        return next_char < len(self.code) and \
+               self.code[next_char] == '(' and \
+               not self.is_a_class_or_function_name_in_header(offset)
+
+    def _find_import_pair_end(self, start):
+        next_char = self._find_first_non_space_char(start)
+        if next_char >= len(self.code):
+            return len(self.code)
+        if self.code[next_char] == '(':
+            try:
+                return self.code.index(')', next_char) + 1
+            except ValueError:
+                return SyntaxError('Unmatched Parens')
+        else:
+            offset = next_char
+            while True:
+                try:
+                    offset = self.code.index('\n', offset)
+                    if offset == 0 or self.code[offset - 1] != '\\':
+                        return offset
+                    offset += 1
+                except ValueError:
+                    return len(self.code)
+
+    def is_import_statement(self, offset):
+        try:
+            last_import = self.code.rindex('import ', 0, offset)
+        except ValueError:
+            return False
+        return self._find_import_pair_end(last_import + 7) >= offset
+
+    def is_from_statement(self, offset):
+        try:
+            last_from = self.code.rindex('from ', 0, offset)
+            from_import = self.code.index(' import ', last_from)
+            from_names = from_import + 8
+        except ValueError:
+            return False
+        from_names = self._find_first_non_space_char(from_names)
+        return self._find_import_pair_end(from_names) >= offset
+
+    def is_from_statement_module(self, offset):
+        if offset >= len(self.code) - 1:
+            return False
+        stmt_start = self._find_primary_start(offset)
+        line_start = self._get_line_start(stmt_start)
+        prev_word = self.code[line_start:stmt_start].strip()
+        return prev_word == 'from'
+
+    def is_a_name_after_from_import(self, offset):
+        try:
+            # XXX: what if the char after from or around import is not
+            # space?
+            last_from = self.code.rindex('from ', 0, offset)
+            from_import = self.code.index(' import ', last_from)
+            from_names = from_import + 8
+        except ValueError:
+            return False
+        if from_names - 1 > offset:
+            return False
+        return self._find_import_pair_end(from_names) >= offset
+
+    def get_from_module(self, offset):
+        try:
+            last_from = self.code.rindex('from ', 0, offset)
+            import_offset = self.code.index(' import ', last_from)
+            end = self._find_last_non_space_char(import_offset)
+            return self.get_primary_at(end)
+        except ValueError:
+            pass
+
+    def is_from_aliased(self, offset):
+        if not self.is_a_name_after_from_import(offset):
+            return False
+        try:
+            end = self._find_word_end(offset)
+            as_end = min(self._find_word_end(end + 1), len(self.code))
+            as_start = self._find_word_start(as_end)
+            if self.code[as_start:as_end + 1] == 'as':
+                return True
+        except ValueError:
+            return False
+
+    def get_from_aliased(self, offset):
+        try:
+            end = self._find_word_end(offset)
+            as_ = self._find_word_end(end + 1)
+            alias = self._find_word_end(as_ + 1)
+            start = self._find_word_start(alias)
+            return self.raw[start:alias + 1]
+        except ValueError:
+            pass
+
+    def is_function_keyword_parameter(self, offset):
+        word_end = self._find_word_end(offset)
+        if word_end + 1 == len(self.code):
+            return False
+        next_char = self._find_first_non_space_char(word_end + 1)
+        if next_char + 2 >= len(self.code) or \
+           self.code[next_char] != '=' or \
+           self.code[next_char + 1] == '=':
+            return False
+        word_start = self._find_word_start(offset)
+        prev_char = self._find_last_non_space_char(word_start - 1)
+        if prev_char - 1 < 0 or self.code[prev_char] not in ',(':
+            return False
+        return True
+
+    def is_on_function_call_keyword(self, offset, stop_searching=0):
+        if self._is_id_char(offset):
+            offset = self._find_word_start(offset) - 1
+        offset = self._find_last_non_space_char(offset)
+        if offset <= stop_searching or \
+           self.code[offset] not in '(,':
+            return False
+        parens_start = self.find_parens_start_from_inside(offset, stop_searching)
+        if stop_searching < parens_start:
+            return True
+        return False
+
+    def find_parens_start_from_inside(self, offset, stop_searching=0):
+        opens = 1
+        while offset > stop_searching:
+            if self.code[offset] == '(':
+                break
+            if self.code[offset] != ',':
+                offset = self._find_primary_start(offset)
+            offset -= 1
+        return max(stop_searching, offset)
+
+    def is_assigned_here(self, offset):
+        operation = self.get_assignment_type(offset)
+        operations = ('=', '-=', '+=', '*=', '/=', '%=', '**=',
+                      '>>=', '<<=', '&=', '^=', '|=')
+        return operation in operations
+
+    def get_assignment_type(self, offset):
+        word_end = self._find_word_end(offset)
+        next_char = self._find_first_non_space_char(word_end + 1)
+        current_char = next_char
+        while current_char + 1 < len(self.code) and \
+              (self.code[current_char] != '=' or \
+               self.code[current_char + 1] == '=') and \
+              current_char < next_char + 3:
+            current_char += 1
+        operation = self.code[next_char:current_char + 1]
+        return operation
+
+    def get_primary_range(self, offset):
+        start = self._find_primary_start(offset)
+        end = self._find_word_end(offset) + 1
+        return (start, end)
+
+    def get_word_range(self, offset):
+        offset = max(0, offset)
+        start = self._find_word_start(offset)
+        end = self._find_word_end(offset) + 1
+        return (start, end)
+
+    def get_word_parens_range(self, offset):
+        if self.is_a_function_being_called(offset) or \
+           self.is_a_class_or_function_name_in_header(offset):
+            end = self._find_word_end(offset)
+            start_parens = self.code.index('(', end)
+            index = start_parens
+            open_count = 0
+            while index < len(self.code):
+                if self.code[index] == '(':
+                    open_count += 1
+                if self.code[index] == ')':
+                    open_count -= 1
+                if open_count == 0:
+                    return (start_parens, index + 1)
+                index += 1
+            return (start_parens, index)
+        return (None, None)

rope/contrib/codeassist.py

 import rope.base.codeanalyze
 import rope.base.evaluate
 import rope.contrib.findit
-from rope.base import pyobjects, pynames, builtins, exceptions
-from rope.base.codeanalyze import (ArrayLinesAdapter, LogicalLineFinder,
-                                   SourceLinesAdapter, WordRangeFinder)
+from rope.base import pyobjects, pynames, builtins, exceptions, worder
+from rope.base.codeanalyze import ArrayLinesAdapter, LogicalLineFinder, SourceLinesAdapter
 from rope.refactor import functionutils, importutils
 
 
     Where starting_offset is the offset returned by this function.
 
     """
-    word_finder = WordRangeFinder(source_code)
+    word_finder = worder.Worder(source_code)
     expression, starting, starting_offset = \
         word_finder.get_splitted_primary_before(offset)
     return starting_offset
 
 def starting_expression(source_code, offset):
     """Return the expression to complete"""
-    word_finder = WordRangeFinder(source_code)
+    word_finder = worder.Worder(source_code)
     expression, starting, starting_offset = \
         word_finder.get_splitted_primary_before(offset)
     if expression:
         self.resource = resource
         self.maxfixes = maxfixes
         self.later_locals = later_locals
-        self.word_finder = WordRangeFinder(source_code)
+        self.word_finder = worder.Worder(source_code)
         self.expression, self.starting, self.offset = \
             self.word_finder.get_splitted_primary_before(offset)
 
         offset = self.offset
         if offset == 0:
             return {}
-        word_finder = WordRangeFinder(self.code)
+        word_finder = worder.Worder(self.code)
         lines = SourceLinesAdapter(self.code)
         lineno = lines.get_line_number(offset)
         stop_line = LogicalLineFinder(lines).logical_line_in(lineno)[0]
 
 
 def _find_pyname_at(project, source_code, offset, resource, maxfixes):
-    word_finder = WordRangeFinder(source_code)
+    word_finder = worder.Worder(source_code)
     lineno = source_code[:offset].count('\n')
     expression = word_finder.get_primary_at(offset)
     expression = expression.replace('\\\n', ' ').replace('\n', ' ')

rope/contrib/generate.py

 import rope.base.evaluate
-from rope.base import change, codeanalyze, pyobjects, exceptions, pynames
+from rope.base import change, codeanalyze, pyobjects, exceptions, pynames, worder
 from rope.refactor import sourceutils, importutils, functionutils, suites
 
 
     def get_passed_args(self):
         result = []
         source = self.source_pymodule.source_code
-        finder = codeanalyze.WordRangeFinder(source)
+        finder = worder.Worder(source)
         if finder.is_a_function_being_called(self.offset):
             start, end = finder.get_primary_range(self.offset)
             parens_start, parens_end = finder.get_word_parens_range(end - 1)

rope/refactor/change_signature.py

 import copy
 
 import rope.base.exceptions
-from rope.base import pyobjects, codeanalyze, taskhandle, evaluate
+from rope.base import pyobjects, codeanalyze, taskhandle, evaluate, worder
 from rope.base.change import ChangeContents, ChangeSet
 from rope.refactor import occurrences, sourceutils, functionutils, rename
 
         self._source = None
 
     def get_changed_module(self):
-        word_finder = codeanalyze.WordRangeFinder(self.source)
+        word_finder = worder.Worder(self.source)
         change_collector = sourceutils.ChangeCollector(self.source)
         for occurrence in self.occurrence_finder.find_occurrences(self.resource):
             if not occurrence.is_called() and not occurrence.is_defined():

rope/refactor/encapsulate_field.py

-from rope.base import pynames, taskhandle, codeanalyze, evaluate, exceptions
+from rope.base import pynames, taskhandle, codeanalyze, evaluate, exceptions, worder
 from rope.base.change import ChangeSet, ChangeContents
 from rope.refactor import sourceutils, occurrences
 
 
     def get_changed_module(self):
         result = []
-        word_finder = codeanalyze.WordRangeFinder(self.source)
+        word_finder = worder.Worder(self.source)
         for occurrence in self.finder.find_occurrences(self.resource,
                                                        self.pymodule):
             start, end = occurrence.get_word_range()
         start_offset = self.lines.get_line_start(start_line)
 
         line = self.source[start_offset:self.lines.get_line_end(end_line)]
-        word_finder = codeanalyze.WordRangeFinder(line)
+        word_finder = worder.Worder(line)
 
         rel_word_start = offset - start_offset
         rel_start = occurance.get_primary_range()[0] - start_offset

rope/refactor/functionutils.py

 import rope.base.exceptions
 import rope.base.pyobjects
-from rope.base import codeanalyze
+from rope.base import codeanalyze, worder
 
 
 class DefinitionInfo(object):
     def __init__(self, call, implicit_arg):
         self.call = call
         self.implicit_arg = implicit_arg
-        self.word_finder = codeanalyze.WordRangeFinder(self.call)
+        self.word_finder = worder.Worder(self.call)
         self.last_parens = self.call.rindex(')')
         self.first_parens = self.word_finder._find_parens_start(self.last_parens)
 

rope/refactor/inline.py

 
 import rope.base.exceptions
 import rope.refactor.functionutils
-from rope.base import pynames, pyobjects, codeanalyze, taskhandle, evaluate
+from rope.base import pynames, pyobjects, codeanalyze, taskhandle, evaluate, worder
 from rope.base.change import ChangeSet, ChangeContents
 from rope.refactor import occurrences, rename, sourceutils, importutils, move
 
         self.pycore = project.pycore
         this_pymodule = self.pycore.resource_to_pyobject(resource)
         self.pyname = evaluate.get_pyname_at(this_pymodule, offset)
-        range_finder = codeanalyze.WordRangeFinder(resource.read())
+        range_finder = worder.Worder(resource.read())
         self.region = range_finder.get_primary_range(offset)
         self.name = range_finder.get_word_at(offset)
         self.offset = offset
                 self.source[end_parens:end])
 
     def _find_end_parens(self, source, offset):
-        finder = codeanalyze.WordRangeFinder(source)
+        finder = worder.Worder(source)
         return finder.get_word_parens_range(offset)[1]
 
     def _get_pymodule(self):

rope/refactor/introduce_parameter.py

 import rope.base.change
-from rope.base import codeanalyze, exceptions, evaluate
+from rope.base import codeanalyze, exceptions, evaluate, worder
 from rope.refactor import functionutils, sourceutils, occurrences
 
 
                 'Cannot find the definition of <%s>' % self.name)
 
     def _get_primary(self):
-        word_finder = codeanalyze.WordRangeFinder(self.resource.read())
+        word_finder = worder.Worder(self.resource.read())
         return word_finder.get_primary_at(self.offset)
 
     def _get_name_and_pyname(self):

rope/refactor/occurrences.py

 import re
 
 import rope.base.pynames
-from rope.base import (pynames, pyobjects, codeanalyze,
-                       evaluate, exceptions, utils)
+from rope.base import pynames, pyobjects, codeanalyze, evaluate, exceptions, utils, worder
 
 
 class Finder(object):
     @property
     @utils.cacheit
     def word_finder(self):
-        return codeanalyze.WordRangeFinder(self.source_code)
+        return worder.Worder(self.source_code)

rope/refactor/rename.py

 import warnings
 
-from rope.base import exceptions, codeanalyze, pyobjects, pynames, taskhandle, evaluate
+from rope.base import (exceptions, codeanalyze, pyobjects,
+                       pynames, taskhandle, evaluate, worder)
 from rope.base.change import ChangeSet, ChangeContents, MoveResource
 from rope.refactor import occurrences, sourceutils
 
         self.old_pyname = evaluate.get_pyname_at(self.pymodule, offset)
 
     def get_old_name(self):
-        word_finder = codeanalyze.WordRangeFinder(self.resource.read())
+        word_finder = worder.Worder(self.resource.read())
         return word_finder.get_primary_at(self.offset)
 
     def _get_scope_offset(self):

ropetest/codeanalyzetest.py

 import unittest
 
 import rope.base.evaluate
-from rope.base import exceptions, ast
-from rope.base.codeanalyze import \
-    (TokenizerLogicalLineFinder, SourceLinesAdapter, WordRangeFinder,
-     LogicalLineFinder, get_block_start, CustomLogicalLineFinder)
+from rope.base import exceptions, ast, worder
+from rope.base.codeanalyze import TokenizerLogicalLineFinder, SourceLinesAdapter, LogicalLineFinder, get_block_start, CustomLogicalLineFinder
 from ropetest import testutils
 
 
         super(WordRangeFinderTest, self).tearDown()
 
     def _find_primary(self, code, offset):
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         result = word_finder.get_primary_at(offset)
         return result
 
 
     def test_word_finder_on_word_beginning(self):
         code = 'print a_var\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         result = word_finder.get_word_at(code.index('a_var'))
         self.assertEquals('a_var', result)
 
 
     def test_word_finder_on_word_ending(self):
         code = 'print a_var\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         result = word_finder.get_word_at(code.index('a_var') + 5)
         self.assertEquals('a_var', result)
 
         self.assertEquals('A()', self._find_primary(code, 2))
 
     def test_splitted_statement(self):
-        word_finder = WordRangeFinder('an_object.an_attr')
+        word_finder = worder.Worder('an_object.an_attr')
         self.assertEquals(('an_object', 'an_at', 10),
                           word_finder.get_splitted_primary_before(15))
 
     def test_empty_splitted_statement(self):
-        word_finder = WordRangeFinder('an_attr')
+        word_finder = worder.Worder('an_attr')
         self.assertEquals(('', 'an_at', 0),
                           word_finder.get_splitted_primary_before(5))
 
     def test_empty_splitted_statement2(self):
-        word_finder = WordRangeFinder('an_object.')
+        word_finder = worder.Worder('an_object.')
         self.assertEquals(('an_object', '', 10),
                           word_finder.get_splitted_primary_before(10))
 
     def test_empty_splitted_statement3(self):
-        word_finder = WordRangeFinder('')
+        word_finder = worder.Worder('')
         self.assertEquals(('', '', 0),
                           word_finder.get_splitted_primary_before(0))
 
     def test_empty_splitted_statement4(self):
-        word_finder = WordRangeFinder('a_var = ')
+        word_finder = worder.Worder('a_var = ')
         self.assertEquals(('', '', 8),
                           word_finder.get_splitted_primary_before(8))
 
     def test_empty_splitted_statement5(self):
-        word_finder = WordRangeFinder('a.')
+        word_finder = worder.Worder('a.')
         self.assertEquals(('a', '', 2),
                           word_finder.get_splitted_primary_before(2))
 
 
     def test_import_statement_finding(self):
         code = 'import mod\na_var = 10\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         self.assertTrue(word_finder.is_import_statement(code.index('mod') + 1))
         self.assertFalse(word_finder.is_import_statement(code.index('a_var') + 1))
 
     def test_import_statement_finding2(self):
         code = 'import a.b.c.d\nresult = a.b.c.d.f()\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         self.assertFalse(word_finder.is_import_statement(code.rindex('d') + 1))
 
     def test_word_parens_range(self):
         code = 's = str()\ns.title()\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         result = word_finder.get_word_parens_range(code.rindex('()') - 1)
         self.assertEquals((len(code) - 3, len(code) - 1), result)
 
     # XXX: not crossing new lines
     def xxx_test_is_a_function_being_called_with_parens_on_next_line(self):
         code = 'func\n(1, 2)\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         self.assertFalse(word_finder.is_a_function_being_called(1))
 
     # XXX: handling triple quotes
     # XXX: get_word_parens_range should ignore string literals
     def xxx_test_get_word_parens_range_and_string_literals(self):
         code = 'f(1, ")", 2)'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         result = word_finder.get_word_parens_range(0)
         self.assertEquals((1, len(code) - 1), result)
 
     def test_is_assigned_here_for_equality_test(self):
         code = 'a == 1\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         self.assertFalse(word_finder.is_assigned_here(0))
 
     # XXX: is_assigned_here should work for tuple assignments
     def xxx_test_is_assigned_here_for_tuple_assignment(self):
         code = 'a, b == (1, 2)\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         self.assertTrue(word_finder.is_assigned_here(0))
 
     def test_is_from_with_from_import_and_multiline_parens(self):
         code = 'from mod import \\\n  (f,\n  g, h)\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         self.assertTrue(word_finder.is_from_statement(code.rindex('g')))
 
     def test_is_from_with_from_import_and_line_breaks_in_the_middle(self):
         code = 'from mod import f,\\\n g\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         self.assertTrue(word_finder.is_from_statement(code.rindex('g')))
 
     def test_one_letter_function_keyword_arguments(self):
         code = 'f(p=1)\n'
-        word_finder = WordRangeFinder(code)
+        word_finder = worder.Worder(code)
         index = code.rindex('p')
         self.assertTrue(word_finder.is_function_keyword_parameter(index))
 
     def test_find_parens_start(self):
         code = 'f(p)\n'
-        finder = WordRangeFinder(code)
+        finder = worder.Worder(code)
         self.assertEquals(1, finder.find_parens_start_from_inside(2))
 
     def test_find_parens_start_with_multiple_entries(self):
         code = 'myfunc(p1, p2, p3\n'
-        finder = WordRangeFinder(code)
+        finder = worder.Worder(code)
         self.assertEquals(code.index('('),
                           finder.find_parens_start_from_inside(len(code) - 1))
 
     def test_find_parens_start_with_nested_parens(self):
         code = 'myfunc(p1, (p2, p3), p4\n'
-        finder = WordRangeFinder(code)
+        finder = worder.Worder(code)
         self.assertEquals(code.index('('),
                           finder.find_parens_start_from_inside(len(code) - 1))
 
     def test_find_parens_start_with_parens_in_strs(self):
         code = 'myfunc(p1, "(", p4\n'
-        finder = WordRangeFinder(code)
+        finder = worder.Worder(code)
         self.assertEquals(code.index('('),
                           finder.find_parens_start_from_inside(len(code) - 1))
 
     def test_find_parens_start_with_parens_in_strs_in_multiple_lines(self):
         code = 'myfunc  (\np1\n , \n "(" \n, \np4\n'
-        finder = WordRangeFinder(code)
+        finder = worder.Worder(code)
         self.assertEquals(code.index('('),
                           finder.find_parens_start_from_inside(len(code) - 1))