Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(437)

Unified Diff: tools/telemetry/third_party/rope/rope/base/worder.py

Issue 1132103009: Example of refactoring using rope library. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/third_party/rope/rope/base/worder.py
diff --git a/tools/telemetry/third_party/rope/rope/base/worder.py b/tools/telemetry/third_party/rope/rope/base/worder.py
new file mode 100644
index 0000000000000000000000000000000000000000..c85c6b3662833e3b138573596ef1b0563f9284a0
--- /dev/null
+++ b/tools/telemetry/third_party/rope/rope/base/worder.py
@@ -0,0 +1,525 @@
+import bisect
+import keyword
+
+import rope.base.simplify
+
+
+def get_name_at(resource, offset):
+ source_code = resource.read()
+ word_finder = Worder(source_code)
+ return word_finder.get_word_at(offset)
+
+
+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.
+ """
+
+ def __init__(self, code, handle_ignores=False):
+ simplified = rope.base.simplify.real_code(code)
+ self.code_finder = _RealFinder(simplified, code)
+ self.handle_ignores = handle_ignores
+ self.code = code
+
+ def _init_ignores(self):
+ ignores = rope.base.simplify.ignored_regions(self.code)
+ self.dumb_finder = _RealFinder(self.code, self.code)
+ self.starts = [ignored[0] for ignored in ignores]
+ self.ends = [ignored[1] for ignored in ignores]
+
+ def _context_call(self, name, offset):
+ if self.handle_ignores:
+ if not hasattr(self, 'starts'):
+ self._init_ignores()
+ start = bisect.bisect(self.starts, offset)
+ if start > 0 and offset < self.ends[start - 1]:
+ return getattr(self.dumb_finder, name)(offset)
+ return getattr(self.code_finder, name)(offset)
+
+ def get_primary_at(self, offset):
+ return self._context_call('get_primary_at', offset)
+
+ def get_word_at(self, offset):
+ return self._context_call('get_word_at', offset)
+
+ def get_primary_range(self, offset):
+ return self._context_call('get_primary_range', offset)
+
+ def get_splitted_primary_before(self, offset):
+ return self._context_call('get_splitted_primary_before', offset)
+
+ def get_word_range(self, offset):
+ return self._context_call('get_word_range', offset)
+
+ def is_function_keyword_parameter(self, offset):
+ return self.code_finder.is_function_keyword_parameter(offset)
+
+ def is_a_class_or_function_name_in_header(self, offset):
+ return self.code_finder.is_a_class_or_function_name_in_header(offset)
+
+ def is_from_statement_module(self, offset):
+ return self.code_finder.is_from_statement_module(offset)
+
+ def is_from_aliased(self, offset):
+ return self.code_finder.is_from_aliased(offset)
+
+ def find_parens_start_from_inside(self, offset):
+ return self.code_finder.find_parens_start_from_inside(offset)
+
+ def is_a_name_after_from_import(self, offset):
+ return self.code_finder.is_a_name_after_from_import(offset)
+
+ def is_from_statement(self, offset):
+ return self.code_finder.is_from_statement(offset)
+
+ def get_from_aliased(self, offset):
+ return self.code_finder.get_from_aliased(offset)
+
+ def is_import_statement(self, offset):
+ return self.code_finder.is_import_statement(offset)
+
+ def is_assigned_here(self, offset):
+ return self.code_finder.is_assigned_here(offset)
+
+ def is_a_function_being_called(self, offset):
+ return self.code_finder.is_a_function_being_called(offset)
+
+ def get_word_parens_range(self, offset):
+ return self.code_finder.get_word_parens_range(offset)
+
+ def is_name_assigned_in_class_body(self, offset):
+ return self.code_finder.is_name_assigned_in_class_body(offset)
+
+ def is_on_function_call_keyword(self, offset):
+ return self.code_finder.is_on_function_call_keyword(offset)
+
+ def _find_parens_start(self, offset):
+ return self.code_finder._find_parens_start(offset)
+
+ def get_parameters(self, first, last):
+ return self.code_finder.get_parameters(first, last)
+
+ def get_from_module(self, offset):
+ return self.code_finder.get_from_module(offset)
+
+ def is_assigned_in_a_tuple_assignment(self, offset):
+ return self.code_finder.is_assigned_in_a_tuple_assignment(offset)
+
+ def get_assignment_type(self, offset):
+ return self.code_finder.get_assignment_type(offset)
+
+ def get_function_and_args_in_header(self, offset):
+ return self.code_finder.get_function_and_args_in_header(offset)
+
+ def get_lambda_and_args(self, offset):
+ return self.code_finder.get_lambda_and_args(offset)
+
+ def find_function_offset(self, offset):
+ return self.code_finder.find_function_offset(offset)
+
+
+class _RealFinder(object):
+
+ def __init__(self, code, raw):
+ self.code = code
+ self.raw = raw
+
+ 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
+
+ def _find_last_non_space_char(self, offset):
+ while offset >= 0 and self.code[offset].isspace():
+ if self.code[offset] == '\n':
+ return offset
+ offset -= 1
+ return max(-1, 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]
+ try:
+ return self.code.rindex(kind, 0, offset)
+ 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] == '\n':
+ return offset + 1
+ if self.code[offset].isspace():
+ 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)):
+ atom_start = self._find_atom_start(offset)
+ if not keyword.iskeyword(self.code[atom_start:offset + 1]):
+ return atom_start
+ 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_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()
+ return not line and self.get_assignment_type(offset) == '='
+
+ def is_a_class_or_function_name_in_header(self, offset):
+ 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) and self.code[offset].isspace():
+ if self.code[offset] == '\n':
+ return offset
+ offset += 1
+ 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_end(self, start):
+ return self._get_line_end(start)
+
+ def is_import_statement(self, offset):
+ try:
+ last_import = self.code.rindex('import ', 0, offset)
+ except ValueError:
+ return False
+ return self._find_import_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_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:
+ if len(self.code) > offset and self.code[offset] == '\n':
+ line_start = self._get_line_start(offset - 1)
+ else:
+ line_start = self._get_line_start(offset)
+ last_from = self.code.rindex('from ', line_start, 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_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)
+ equals = self.code[next_char:next_char + 2]
+ if equals == '==' or not equals.startswith('='):
+ return False
+ word_start = self._find_word_start(offset)
+ prev_char = self._find_last_non_space_char(word_start - 1)
+ return prev_char - 1 >= 0 and self.code[prev_char] in ',('
+
+ def is_on_function_call_keyword(self, offset):
+ stop = self._get_line_start(offset)
+ if self._is_id_char(offset):
+ offset = self._find_word_start(offset) - 1
+ offset = self._find_last_non_space_char(offset)
+ if offset <= stop or self.code[offset] not in '(,':
+ return False
+ parens_start = self.find_parens_start_from_inside(offset)
+ return stop < parens_start
+
+ def find_parens_start_from_inside(self, offset):
+ stop = self._get_line_start(offset)
+ while offset > stop:
+ if self.code[offset] == '(':
+ break
+ if self.code[offset] != ',':
+ offset = self._find_primary_start(offset)
+ offset -= 1
+ return max(stop, offset)
+
+ def is_assigned_here(self, offset):
+ return self.get_assignment_type(offset) is not None
+
+ def get_assignment_type(self, offset):
+ # XXX: does not handle tuple assignments
+ word_end = self._find_word_end(offset)
+ next_char = self._find_first_non_space_char(word_end + 1)
+ single = self.code[next_char:next_char + 1]
+ double = self.code[next_char:next_char + 2]
+ triple = self.code[next_char:next_char + 3]
+ if double not in ('==', '<=', '>=', '!='):
+ for op in [single, double, triple]:
+ if op.endswith('='):
+ return op
+
+ 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, opening='(', closing=')'):
+ end = self._find_word_end(offset)
+ start_parens = self.code.index(opening, end)
+ index = start_parens
+ open_count = 0
+ while index < len(self.code):
+ if self.code[index] == opening:
+ open_count += 1
+ if self.code[index] == closing:
+ open_count -= 1
+ if open_count == 0:
+ return (start_parens, index + 1)
+ index += 1
+ return (start_parens, index)
+
+ def get_parameters(self, first, last):
+ keywords = []
+ args = []
+ current = self._find_last_non_space_char(last - 1)
+ while current > first:
+ primary_start = current
+ current = self._find_primary_start(current)
+ while current != first and self.code[current] not in '=,':
+ current = self._find_last_non_space_char(current - 1)
+ primary = self.raw[current + 1:primary_start + 1].strip()
+ if self.code[current] == '=':
+ primary_start = current - 1
+ current -= 1
+ while current != first and self.code[current] not in ',':
+ current = self._find_last_non_space_char(current - 1)
+ param_name = self.raw[current + 1:primary_start + 1].strip()
+ keywords.append((param_name, primary))
+ else:
+ args.append(primary)
+ current = self._find_last_non_space_char(current - 1)
+ args.reverse()
+ keywords.reverse()
+ return args, keywords
+
+ def is_assigned_in_a_tuple_assignment(self, offset):
+ start = self._get_line_start(offset)
+ end = self._get_line_end(offset)
+ primary_start = self._find_primary_start(offset)
+ primary_end = self._find_word_end(offset)
+
+ prev_char_offset = self._find_last_non_space_char(primary_start - 1)
+ next_char_offset = self._find_first_non_space_char(primary_end + 1)
+ next_char = prev_char = ''
+ if prev_char_offset >= start:
+ prev_char = self.code[prev_char_offset]
+ if next_char_offset < end:
+ next_char = self.code[next_char_offset]
+ try:
+ equals_offset = self.code.index('=', start, end)
+ except ValueError:
+ return False
+ if prev_char not in '(,' and next_char not in ',)':
+ return False
+ parens_start = self.find_parens_start_from_inside(offset)
+ # XXX: only handling (x, y) = value
+ return offset < equals_offset and \
+ self.code[start:parens_start].strip() == ''
+
+ def get_function_and_args_in_header(self, offset):
+ offset = self.find_function_offset(offset)
+ lparens, rparens = self.get_word_parens_range(offset)
+ return self.raw[offset:rparens + 1]
+
+ def find_function_offset(self, offset, definition='def '):
+ while True:
+ offset = self.code.index(definition, offset)
+ if offset == 0 or not self._is_id_char(offset - 1):
+ break
+ offset += 1
+ def_ = offset + 4
+ return self._find_first_non_space_char(def_)
+
+ def get_lambda_and_args(self, offset):
+ offset = self.find_function_offset(offset, definition='lambda ')
+ lparens, rparens = self.get_word_parens_range(offset, opening=' ',
+ closing=':')
+ return self.raw[offset:rparens + 1]
« no previous file with comments | « tools/telemetry/third_party/rope/rope/base/utils.py ('k') | tools/telemetry/third_party/rope/rope/contrib/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698