from rope.base import ast from rope.base.utils import pycompat def find_visible(node, lines): """Return the line which is visible from all `lines`""" root = ast_suite_tree(node) return find_visible_for_suite(root, lines) def find_visible_for_suite(root, lines): if len(lines) == 1: return lines[0] line1 = lines[0] line2 = find_visible_for_suite(root, lines[1:]) suite1 = root.find_suite(line1) suite2 = root.find_suite(line2) def valid(suite): return suite is not None and not suite.ignored if valid(suite1) and not valid(suite2): return line1 if not valid(suite1) and valid(suite2): return line2 if not valid(suite1) and not valid(suite2): return None while suite1 != suite2 and suite1.parent != suite2.parent: if suite1._get_level() < suite2._get_level(): line2 = suite2.get_start() suite2 = suite2.parent elif suite1._get_level() > suite2._get_level(): line1 = suite1.get_start() suite1 = suite1.parent else: line1 = suite1.get_start() line2 = suite2.get_start() suite1 = suite1.parent suite2 = suite2.parent if suite1 == suite2: return min(line1, line2) return min(suite1.get_start(), suite2.get_start()) def ast_suite_tree(node): if hasattr(node, "lineno"): lineno = node.lineno else: lineno = 1 return Suite(node.body, lineno) class Suite(object): def __init__(self, child_nodes, lineno, parent=None, ignored=False): self.parent = parent self.lineno = lineno self.child_nodes = child_nodes self._children = None self.ignored = ignored def get_start(self): if self.parent is None: if self.child_nodes: return self.local_start() else: return 1 return self.lineno def get_children(self): if self._children is None: walker = _SuiteWalker(self) for child in self.child_nodes: ast.walk(child, walker) self._children = walker.suites return self._children def local_start(self): return self.child_nodes[0].lineno def local_end(self): end = self.child_nodes[-1].lineno if self.get_children(): end = max(end, self.get_children()[-1].local_end()) return end def find_suite(self, line): if line is None: return None for child in self.get_children(): if child.local_start() <= line <= child.local_end(): return child.find_suite(line) return self def _get_level(self): if self.parent is None: return 0 return self.parent._get_level() + 1 class _SuiteWalker(object): def __init__(self, suite): self.suite = suite self.suites = [] def _If(self, node): self._add_if_like_node(node) def _For(self, node): self._add_if_like_node(node) def _While(self, node): self._add_if_like_node(node) def _With(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) def _AsyncWith(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) def _TryFinally(self, node): proceed_to_except_handler = False if len(node.finalbody) == 1: if pycompat.PY2: proceed_to_except_handler = isinstance(node.body[0], ast.TryExcept) elif pycompat.PY3: try: proceed_to_except_handler = isinstance( node.handlers[0], ast.ExceptHandler ) except IndexError: pass if proceed_to_except_handler: self._TryExcept(node if pycompat.PY3 else node.body[0]) else: self.suites.append(Suite(node.body, node.lineno, self.suite)) self.suites.append(Suite(node.finalbody, node.lineno, self.suite)) def _Try(self, node): if len(node.finalbody) == 1: self._TryFinally(node) else: self._TryExcept(node) def _TryExcept(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) for handler in node.handlers: self.suites.append(Suite(handler.body, node.lineno, self.suite)) if node.orelse: self.suites.append(Suite(node.orelse, node.lineno, self.suite)) def _add_if_like_node(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite)) if node.orelse: self.suites.append(Suite(node.orelse, node.lineno, self.suite)) def _FunctionDef(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite, ignored=True)) def _AsyncFunctionDef(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite, ignored=True)) def _ClassDef(self, node): self.suites.append(Suite(node.body, node.lineno, self.suite, ignored=True))