" Where Am I for Python VIM Plugin. " " Copyright 2008 Shane Harper (http://shaneharper.net) " " This program is free software: you can redistribute it and/or modify " it under the terms of the GNU General Public License as published by " the Free Software Foundation, either version 3 of the License, or " (at your option) any later version. " " This program is distributed in the hope that it will be useful, " but WITHOUT ANY WARRANTY; without even the implied warranty of " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " GNU General Public License for more details. " " You should have received a copy of the GNU General Public License " along with this program. If not, see . " THIS FILE WAS GENERATED BY A SCRIPT. " Time & Date: Mon Jul 14 21:54:26 2008 " Mercurial Revision ID: 77dfff44b224 if exists("*WhereAmIPython") finish endif command -nargs=0 WhereAmIPython call WhereAmIPython() function WhereAmIPython() python << END import vim #!/usr/bin/env python # where_am_i.py # Copyright 2008 Shane Harper (http://shaneharper.net) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import re import sys indent_regexp = re.compile('(\s*?\f)*(\s*)') def indent_column(s): """Return the indent column number. Indent can be made up of spaces, tabs and form-feed characters. """ return len(indent_regexp.match(s).group(2).expandtabs()) def logical_lines(file): """Generator returning the next logical line as a list of statements. No characters are filtered out: Line indentation, blank lines and comments are returned. """ logical_line = [] statement = "" in_string = "" def in_triple_quote_string(): return len(in_string) == 3 unmatched_brackets = "" for l in file: in_comment = False i = 0 while i < len(l): if in_string: if l[i] == '\\': i += 1 elif l[i:i+len(in_string)] == in_string: i += len(in_string) - 1 in_string = "" else: if l[i:i+3] == '"""' or l[i:i+3] == "'''": in_string = l[i:i+3] i += 2 elif l[i] == '"' or l[i] == "'": in_string = l[i] elif l[i] == '#': in_comment = True break elif l[i] in '([{': unmatched_brackets += l[i] elif l[i] in ')]}': # XXX What if l[i] doesn't match unmatched_brackets[:-1] ? unmatched_brackets = unmatched_brackets[:-1] elif re.match('(:|;)\s*[^#\s]', l[i:]) and not unmatched_brackets: logical_line += [statement + l[:i+1]] statement = "" l = l[i+1:] i = -1 i += 1 statement += l if len(unmatched_brackets) == 0 and not in_triple_quote_string() and \ (in_comment or not l.endswith('\\\n')): yield logical_line + [statement] logical_line = [] statement = "" if logical_line or statement: yield logical_line + [statement] class Statement: def __init__(self, text, end_indent): self.text = text self.end_indent = end_indent def where_am_i(file, stop_line_number): """Return the lines defining the class/es and function/s that a specified line belongs to as well as the conditions that must be satisfied for program execution to reach the specified line. stop_line_number is 1-based. (1, not 0, refers to the first line.) """ conditions = [] blank_line_or_only_a_comment_regexp = re.compile('^\s*(#.*)?\n?$') line_number = 0 for line in logical_lines(file): logical_line = ''.join(line) # --------------------------------------------------------------------- # Remove conditions that no longer apply. # --------------------------------------------------------------------- if not blank_line_or_only_a_comment_regexp.match(line[0]): indent = indent_column(line[0]) while conditions and indent <= conditions[-1].end_indent and ( # Lines indented the same as the current line can still be # relevant (e.g. the "if" corresponding to an "else"). not (indent == conditions[-1].end_indent and re.match('\s*(else|elif|except)\W', line[0]) and not re.match('\s*except\W', conditions[-1].text))): conditions.pop() # --------------------------------------------------------------------- # Stop if finished. # --------------------------------------------------------------------- line_number += logical_line.count('\n') if line_number >= stop_line_number: break # --------------------------------------------------------------------- # Add new condition, if there is one. # --------------------------------------------------------------------- if re.match('\s*(if|for|while|else|elif|return|raise|break|continue|' 'try|except)\W', logical_line) or \ (len(line) == 1 and re.match('\s*(class|def)\W', logical_line)): end_indent = indent_column(logical_line) for statement in line: if re.match('\s*(return|raise)\W', statement): for c in reversed(conditions): if re.match('\s*def\W', c.text): end_indent = c.end_indent break else: end_indent = -1 if re.match('\s*(break|continue)\W', statement): for c in reversed(conditions): end_indent = min(end_indent, c.end_indent) if re.match('\s*(for|while)\W', c.text): break conditions.append(Statement(logical_line, end_indent)) return ''.join([c.text for c in conditions]) def _xmap(fn, iterator): for i in iterator: yield fn(i) print where_am_i(file=_xmap(lambda line:line+'\n', vim.current.buffer), stop_line_number=vim.current.window.cursor[0]), END endfunction