mirror of
https://github.com/gryf/pythonhelper.git
synced 2026-04-25 07:01:25 +02:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 468b229276 | |||
| 154c343257 | |||
| 81bdd2f51c | |||
| 839012de28 | |||
| c41a450990 | |||
| 81a638762b | |||
| 3164a9e568 | |||
| 4f1dbc74bd | |||
| b2ca2a2fca | |||
| c011e488ce | |||
| d8a44e8439 | |||
| 03f3c9ef1e | |||
| c9e210a331 | |||
| 327dce4b7a | |||
| 9809859c73 | |||
| d40dbfe22b | |||
| e37a64afc4 | |||
| c6b824f536 | |||
| 8f88e4bc7f | |||
| 476707b687 | |||
| e1d3306adc | |||
| 1e1d1cc6f1 | |||
| 4a9a963dd6 | |||
| dcf5507d0f | |||
| 1b61929c9a | |||
| 0cccd82b2a | |||
| 702b07e390 | |||
| a361304e73 | |||
| aee6f328aa | |||
| ba115f64cc | |||
| d6046886a3 | |||
| f323d3cc74 | |||
| fe86a19e35 | |||
| c9dc1f3ec0 | |||
| e70cca0a11 | |||
| 005a35dcf6 | |||
| 42d72d9e6b | |||
| 6494b9de1c | |||
| 864d36fd02 | |||
| 333db2d78e | |||
| 888970d931 | |||
| 2e27597939 | |||
| 0ce33afc6c | |||
| 49d018fdc6 | |||
| 2caa97d75f | - | ||
| b0841be7ee | - | ||
| ab9d0e5e69 | - |
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) 2016, Roman Dobosz at al.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the organization nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ROMAN DOBOSZ BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
This is a mirror of http://www.vim.org/scripts/script.php?script_id=435
|
|
||||||
|
|
||||||
Vim script to help moving around in larger Python source files. It displays current class, method or function the cursor is placed in in the status line for every python file. It's more clever than Yegappan Lakshmanan's taglist.vim because it takes into account indetation and comments to determine what tag the cursor is placed in and from version 0.80 doesn't need exuberant ctags utility.
|
|
||||||
|
|
||||||
Note: The script displays current tag on the status line only in NORMAL mode. This is because CursorHold event in VIM is fired up only in this mode. However if you'd like to know what tag you are in even in INSERT or VISUAL mode, contact me (email specified in the script) and I'll send you a patch that enables firing up CursorHold event in those modes as well.
|
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
Pythonhelper
|
||||||
|
============
|
||||||
|
|
||||||
|
This was a mirror of `http://www.vim.org/scripts/script.php?script_id=435`_.
|
||||||
|
|
||||||
|
This Vim plugin helps in Python development by placing a name of current class,
|
||||||
|
method or function under the cursor on the status line in normal and insert
|
||||||
|
mode.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
To use this plugin either ``+python`` or ``+python3`` feature compiled in vim is
|
||||||
|
required. To check it, issue ``:version`` in vim instance and look for python
|
||||||
|
entries.
|
||||||
|
|
||||||
|
To install it, any kind of Vim package manager can be used, like NeoBundle_,
|
||||||
|
Pathogen_, Vundle_ or vim-plug_.
|
||||||
|
|
||||||
|
For manual installation, copy subdirectories from this repository to your
|
||||||
|
``~/.vim`` directory.
|
||||||
|
|
||||||
|
Next, place one of the available functions either on your ``.vimrc``:
|
||||||
|
|
||||||
|
.. code:: vim
|
||||||
|
|
||||||
|
set statusline=[....]\ %{TagInStatusLine()}\ [.....]
|
||||||
|
|
||||||
|
or under ``~/.vim/ftplugin/python/your_file.vim``:
|
||||||
|
|
||||||
|
.. code:: vim
|
||||||
|
|
||||||
|
setlocal statusline=[....]\ %{TagInStatusLine()}\ [.....]
|
||||||
|
|
||||||
|
Functions, which may be placed on the status line are as follows:
|
||||||
|
|
||||||
|
* ``TagInStatusLine`` - shows name and type of the tag, i.e.:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
ClassName.some_method_name (method)
|
||||||
|
|
||||||
|
* ``TagInStatusLineTag`` - shows only the name:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
ClassName.some_method_name
|
||||||
|
|
||||||
|
* ``TagInStatusLineType`` - shows only the tag type:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
method
|
||||||
|
|
||||||
|
Restart vim, and you all set.
|
||||||
|
|
||||||
|
Changelog
|
||||||
|
---------
|
||||||
|
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| Version | Date | Author | Notes |
|
||||||
|
+=========+============+================+===========================================================================================================================================================================================+
|
||||||
|
| 1.1 | 2016-12-10 | Roman Dobosz | Fixed python3 enabled vim (without python2) |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 1.0 | 2016-05-30 | Roman Dobosz | Rewrite python part (simplifying the code, clean it up, separate from vimscript, add some tests), make it Python3 compatible, lots of other changes |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| | 2012-02-12 | cheater | `Several bug fixes`_, code cleanup, docs update |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| | 2010-02-13 | Oluf Lorenzen | `Updated the way how to display information on status line`_ |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 0.83 | 2010-01-04 | Michal Vitecek | Added support for the CursorHoldI event so that the class/method/function is recognized also in Insert mode |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 0.82 | 2009-07-10 | Michal Vitecek | fixed a bug when nested functions/classes were not properly detected |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 0.81 | 2003-03-13 | Michal Vitecek | fixed a small bug in indent level recognition |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 0.80 | 2002-10-18 | Michal Vitecek | removed the dependency on exuberant ctags which parsed the python source code wrongly anyways. From now on only VIM with python support is needed. This might greatly help windoze users. |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 0.72 | 2002-10-03 | Michal Vitecek | fixed problem with parsing ctags output on python files that use tabs |
|
||||||
|
| | | | when there is a syntax error in the file and ctags parses it incorrectly a warning is displayed in the command line |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 0.71 | 2002-10-02 | Michal Vitecek | fixed problem with undefined window-bound variable w:PHStatusLine when a window has been split into two. |
|
||||||
|
| | | | unbound event BufWinEnter because it's not needed because of the above change now |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| 0.70 | 2002-10-02 | Michal Vitecek | Initial upload |
|
||||||
|
+---------+------------+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
Originally, there was no licence whatsoever, so I've put it under under 3-clause
|
||||||
|
BSD license. See LICENSE file for details.
|
||||||
|
|
||||||
|
.. _Pathogen: https://github.com/tpope/vim-pathogen
|
||||||
|
.. _Vundle: https://github.com/gmarik/Vundle.vim
|
||||||
|
.. _NeoBundle: https://github.com/Shougo/neobundle.vim
|
||||||
|
.. _vim-plug: https://github.com/junegunn/vim-plug
|
||||||
|
.. _http://www.vim.org/scripts/script.php?script_id=435: http://www.vim.org/scripts/script.php?script_id=435
|
||||||
|
.. _Updated the way how to display information on status line: https://github.com/Finkregh/pythonhelper/commit/49d018fdc638f759a4d3d89f97ba5d26baddb1cd
|
||||||
|
.. _Several bug fixes: https://github.com/Finkregh/pythonhelper/pull/2
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
"""
|
||||||
|
Simple analyzer for python source files. Collect and give info about file
|
||||||
|
structure: classes, its methods and functions.
|
||||||
|
|
||||||
|
Note, it'll probably not be behaving well with mixed spaces and tabs
|
||||||
|
indentation.
|
||||||
|
|
||||||
|
version: 1.0
|
||||||
|
date: 2016-05-30
|
||||||
|
author: Roman Dobosz <gryf@vimja.com>
|
||||||
|
"""
|
||||||
|
from collections import OrderedDict
|
||||||
|
import re
|
||||||
|
|
||||||
|
import vim
|
||||||
|
|
||||||
|
|
||||||
|
RE_TAG_TYPE = re.compile(r'\s*(def|class)[ \t]+([^(:]+).*')
|
||||||
|
RE_INDENT = re.compile(r'([ \t]*).*')
|
||||||
|
|
||||||
|
|
||||||
|
class PythonTag(object):
|
||||||
|
"""A simple storage class representing a python tag."""
|
||||||
|
|
||||||
|
def __init__(self, tag_type='', full_name='', line_number=0,
|
||||||
|
indent_level=0):
|
||||||
|
"""Initializes instances of Python tags.
|
||||||
|
|
||||||
|
:param tag_type: Tag type as string
|
||||||
|
:param full_name: Full tag name (in dotted notation)
|
||||||
|
:param line_number: line number on which the tag starts
|
||||||
|
:param indent_level: indentation level of the tag (number)
|
||||||
|
"""
|
||||||
|
self.tag_type = tag_type
|
||||||
|
self.name = full_name.split(".")[-1]
|
||||||
|
self.full_name = full_name
|
||||||
|
self.line_number = line_number
|
||||||
|
self.indent_level = indent_level
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Representation for the PythonTag objects"""
|
||||||
|
return ("<PythonTag object at %s: %0.2d [%d] %s %s>" %
|
||||||
|
(hex(id(self)),
|
||||||
|
self.line_number,
|
||||||
|
self.indent_level,
|
||||||
|
self.tag_type,
|
||||||
|
self.full_name))
|
||||||
|
|
||||||
|
|
||||||
|
class EvenSimplerPythonTagsParser(object):
|
||||||
|
"""Simplified version for Python source code tag parser."""
|
||||||
|
|
||||||
|
def get_tags(self):
|
||||||
|
"""
|
||||||
|
Find tag in current buffer. Store them in OrderedDict and return.
|
||||||
|
|
||||||
|
:returns: OrderedDict with tags for current buffer or empty
|
||||||
|
OrderedDict in case of no tags found.
|
||||||
|
"""
|
||||||
|
tags_stack = []
|
||||||
|
tags = OrderedDict()
|
||||||
|
|
||||||
|
for line_no, line in enumerate(vim.current.buffer):
|
||||||
|
|
||||||
|
tag_match = RE_TAG_TYPE.match(line)
|
||||||
|
|
||||||
|
if not tag_match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
indent_level = self._get_indent_level(line)
|
||||||
|
|
||||||
|
for _ in range(len(tags_stack)):
|
||||||
|
if tags_stack and tags_stack[-1].indent_level >= indent_level:
|
||||||
|
tags_stack.pop()
|
||||||
|
|
||||||
|
if not tags_stack:
|
||||||
|
break
|
||||||
|
|
||||||
|
tag = PythonTag(tag_match.group(1),
|
||||||
|
self._get_full_name(tags_stack,
|
||||||
|
tag_match.group(2)),
|
||||||
|
line_no,
|
||||||
|
indent_level)
|
||||||
|
tag.tag_type = self._get_tag_type(tag, tags_stack)
|
||||||
|
|
||||||
|
tags[line_no] = tag
|
||||||
|
tags_stack.append(tag)
|
||||||
|
|
||||||
|
return tags
|
||||||
|
|
||||||
|
def _get_tag_type(self, tag, tags_stack):
|
||||||
|
"""
|
||||||
|
Calculate name for provided tag.
|
||||||
|
|
||||||
|
:param tag: PythonTag object
|
||||||
|
:param tags_stack: list of PythonTag objects ordered from root to leaf
|
||||||
|
|
||||||
|
:returns: 'class', 'method' or 'function' as a tag type
|
||||||
|
"""
|
||||||
|
if tag.tag_type == 'class':
|
||||||
|
return 'class'
|
||||||
|
|
||||||
|
if tags_stack and tags_stack[-1].tag_type == 'class':
|
||||||
|
return 'method'
|
||||||
|
|
||||||
|
return 'function'
|
||||||
|
|
||||||
|
def _get_full_name(self, tags_stack, name):
|
||||||
|
"""
|
||||||
|
Return full logical name dot separated starting from upper entity
|
||||||
|
|
||||||
|
:param tags_stack: list of PythonTag objects ordered from root to leaf
|
||||||
|
:param name: class, method or function name
|
||||||
|
|
||||||
|
:returns: full name starting from root, separated by dot, like:
|
||||||
|
function_name
|
||||||
|
ClassName
|
||||||
|
ClassName.method_name
|
||||||
|
ClassName.method_name.inner_function_name
|
||||||
|
"""
|
||||||
|
if tags_stack:
|
||||||
|
return tags_stack[-1].full_name + "." + name
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
def _get_indent_level(self, line):
|
||||||
|
"""
|
||||||
|
Calculate and get the indentation level for provided line
|
||||||
|
|
||||||
|
:param line: a string, against which indentation should be calculated
|
||||||
|
|
||||||
|
:returns: counted number of whitespaces
|
||||||
|
|
||||||
|
"""
|
||||||
|
return len(RE_INDENT.match(line).group(1))
|
||||||
|
|
||||||
|
|
||||||
|
class PythonHelper(object):
|
||||||
|
TAGS = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find_tag(cls, buffer_number, changed_tick):
|
||||||
|
"""
|
||||||
|
Tries to find the best tag for the current cursor position.
|
||||||
|
|
||||||
|
:param buffer_number: buffer number in vim
|
||||||
|
:param changed_tick: always-increasing number used to indicate that
|
||||||
|
the buffer has been modified since the last time
|
||||||
|
"""
|
||||||
|
tag = PythonHelper._get_tag(buffer_number, changed_tick)
|
||||||
|
|
||||||
|
s_line = '%s (%s)' % (tag.full_name, tag.tag_type) if tag else ''
|
||||||
|
s_line_tag = tag.full_name if tag else ''
|
||||||
|
s_line_type = tag.tag_type if tag else ''
|
||||||
|
|
||||||
|
vim.command('let w:PHStatusLine="%s"' % s_line)
|
||||||
|
vim.command('let w:PHStatusLineTag="%s"' % s_line_tag)
|
||||||
|
vim.command('let w:PHStatusLineType="%s"' % s_line_type)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_tag(cls, buffer_number, changed_tick):
|
||||||
|
"""
|
||||||
|
Get the nearest tag object or None.
|
||||||
|
|
||||||
|
:param buffer_number: buffer number in vim
|
||||||
|
:param changed_tick: always-increasing number used to indicate that
|
||||||
|
the buffer has been modified since the last time
|
||||||
|
|
||||||
|
:returns: PythonTag tag object or None
|
||||||
|
"""
|
||||||
|
|
||||||
|
if PythonHelper.TAGS.get(buffer_number) and \
|
||||||
|
PythonHelper.TAGS[buffer_number]['changed_tick'] == changed_tick:
|
||||||
|
tags = PythonHelper.TAGS[buffer_number]['tags']
|
||||||
|
else:
|
||||||
|
parser = EvenSimplerPythonTagsParser()
|
||||||
|
tags = parser.get_tags()
|
||||||
|
PythonHelper.TAGS['buffer_number'] = {'changed_tick': changed_tick,
|
||||||
|
'tags': tags}
|
||||||
|
|
||||||
|
# get line number of current cursor position from Vim's internal data.
|
||||||
|
# It is always a positive number, starts from 1. Let's decrease it by
|
||||||
|
# one, so that it will not confuse us while operating vim interface by
|
||||||
|
# python, where everything starts from 0.
|
||||||
|
line_number = vim.current.window.cursor[0] - 1
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
line = vim.current.buffer[line_number]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
line_indent = len(RE_INDENT.match(line).group(1))
|
||||||
|
if line.strip():
|
||||||
|
break
|
||||||
|
# line contains nothing but white characters, looking up to grab
|
||||||
|
# some more context
|
||||||
|
line_number -= 1
|
||||||
|
|
||||||
|
tag = tags.get(line_number)
|
||||||
|
|
||||||
|
# if we have something at the beginning of the line, just return it;
|
||||||
|
# it doesn't matter if it is the tag found there or not
|
||||||
|
if line_indent == 0 or tag:
|
||||||
|
return tag
|
||||||
|
|
||||||
|
# get nearest tag
|
||||||
|
for line_no in range(line_number - 1, 0, -1):
|
||||||
|
tag = tags.get(line_no)
|
||||||
|
line = vim.current.buffer[line_no]
|
||||||
|
upper_line_indent = len(RE_INDENT.match(line).group(1))
|
||||||
|
|
||||||
|
if tag and upper_line_indent < line_indent:
|
||||||
|
return tag
|
||||||
|
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
|
||||||
|
if upper_line_indent == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if upper_line_indent >= line_indent:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if tag and tag.indent_level >= line_indent:
|
||||||
|
tag = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
return tag
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def delete_tags(cls, buffer_number):
|
||||||
|
"""Removes tag data for the specified buffer number.
|
||||||
|
|
||||||
|
:param buffer_number: buffer number in vim
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
del PythonHelper.TAGS[buffer_number]
|
||||||
|
except KeyError:
|
||||||
|
# If we don't have tags for specified buffer, just pass
|
||||||
|
pass
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
" File: pythonhelper.vim
|
||||||
|
" Author: Michal Vitecek <fuf-at-mageo-dot-cz>
|
||||||
|
" Author: Roman Dobosz <gryf@vimja.com>
|
||||||
|
" Version: 1.1
|
||||||
|
" License: 3-clause BSD license
|
||||||
|
" Last Modified: 2016-12-10
|
||||||
|
|
||||||
|
" VIM functions {{{
|
||||||
|
let s:plugin_path = expand('<sfile>:p:h', 1)
|
||||||
|
|
||||||
|
|
||||||
|
function! s:SetPython(msg)
|
||||||
|
if !exists('g:_python')
|
||||||
|
if has('python')
|
||||||
|
let g:_python = {'exec': 'python', 'file': 'pyfile'}
|
||||||
|
elseif has('python3')
|
||||||
|
let g:_python = {'exec': 'python3', 'file': 'py3file'}
|
||||||
|
else
|
||||||
|
echohl WarningMsg|echomsg a:msg|echohl None
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:PHLoader()
|
||||||
|
if !exists('g:pythonhelper_py_loaded')
|
||||||
|
call s:SetPython("PythonHelper unavailable: "
|
||||||
|
\ "requires Vim with Python support")
|
||||||
|
execute g:_python['file'] . ' ' . s:plugin_path . '/pythonhelper.py'
|
||||||
|
let g:pythonhelper_py_loaded = 1
|
||||||
|
else
|
||||||
|
echohl "already loaded"
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHCursorHold()
|
||||||
|
" only Python is supported {{{
|
||||||
|
if (!exists('b:current_syntax') || (b:current_syntax != 'python'))
|
||||||
|
let w:PHStatusLine = ''
|
||||||
|
let w:PHStatusLineTag = ''
|
||||||
|
let w:PHStatusLineType = ''
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" call Python function findTag() with the current buffer number and change
|
||||||
|
" status indicator
|
||||||
|
execute g:_python['exec'] . ' PythonHelper.find_tag(' . expand("<abuf>") .
|
||||||
|
\ ', ' . b:changedtick . ')'
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHBufferDelete()
|
||||||
|
" set the PHStatusLine etc for this window to an empty string
|
||||||
|
let w:PHStatusLine = ""
|
||||||
|
let w:PHStatusLineTag = ''
|
||||||
|
let w:PHStatusLineType = ''
|
||||||
|
|
||||||
|
" call Python function deleteTags() with the current buffer number and
|
||||||
|
" change status indicator
|
||||||
|
execute g:_python['exec'] . ' PythonHelper.delete_tags(' . expand("<abuf>") . ')'
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! TagInStatusLine()
|
||||||
|
" return value of w:PHStatusLine in case it's set
|
||||||
|
if (exists("w:PHStatusLine"))
|
||||||
|
return w:PHStatusLine
|
||||||
|
" otherwise just return an empty string
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! TagInStatusLineTag()
|
||||||
|
" return value of w:PHStatusLineTag in case it's set
|
||||||
|
if (exists("w:PHStatusLineTag"))
|
||||||
|
return w:PHStatusLineTag
|
||||||
|
" otherwise just return an empty string
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! TagInStatusLineType()
|
||||||
|
" return value of w:PHStatusLineType in case it's set
|
||||||
|
if (exists("w:PHStatusLineType"))
|
||||||
|
return w:PHStatusLineType
|
||||||
|
" otherwise just return an empty string
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHPreviousClassMethod()
|
||||||
|
call search('^[ \t]*\(class\|def\)\>', 'bw')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHNextClassMethod()
|
||||||
|
call search('^[ \t]*\(class\|def\)\>', 'w')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHPreviousClass()
|
||||||
|
call search('^[ \t]*class\>', 'bw')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHNextClass()
|
||||||
|
call search('^[ \t]*class\>', 'w')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHPreviousMethod()
|
||||||
|
call search('^[ \t]*def\>', 'bw')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! PHNextMethod()
|
||||||
|
call search('^[ \t]*def\>', 'w')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" event binding, Vim customization {{{
|
||||||
|
|
||||||
|
" load Python code
|
||||||
|
call s:PHLoader()
|
||||||
|
|
||||||
|
" autocommands
|
||||||
|
autocmd CursorHold * call PHCursorHold()
|
||||||
|
autocmd CursorHoldI * call PHCursorHold()
|
||||||
|
autocmd BufDelete * silent call PHBufferDelete()
|
||||||
|
|
||||||
|
" period of no activity after which the CursorHold event is triggered
|
||||||
|
if (exists("g:pythonhelper_updatetime"))
|
||||||
|
let &updatetime = g:pythonhelper_updatetime
|
||||||
|
endif
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" vim:foldmethod=marker
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class Foo(object):
|
||||||
|
"""Some doc"""
|
||||||
|
CLASS_ATTR = {"dict": 1,
|
||||||
|
"bla": "foobar"}
|
||||||
|
|
||||||
|
def __init__(self, arg):
|
||||||
|
"""initializaion"""
|
||||||
|
self.arg = arg
|
||||||
|
|
||||||
|
def method(self, x, y):
|
||||||
|
"""very important method"""
|
||||||
|
|
||||||
|
def inner_funtion(x, y):
|
||||||
|
for i in y:
|
||||||
|
x = x + i
|
||||||
|
|
||||||
|
result = y[:]
|
||||||
|
result.append(x)
|
||||||
|
return result
|
||||||
|
|
||||||
|
result = None
|
||||||
|
result2 = """\
|
||||||
|
multiline
|
||||||
|
string
|
||||||
|
the
|
||||||
|
annoying
|
||||||
|
bastard"""
|
||||||
|
|
||||||
|
if self.arg < 100:
|
||||||
|
result = inner_funtion(x, y)
|
||||||
|
|
||||||
|
return result if result else result2
|
||||||
|
|
||||||
|
def main():
|
||||||
|
instance = Foo(10)
|
||||||
|
print(os.path.curdir, instance.method(2, [1, 2, 3]))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Executable
+146
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class Command(object):
|
||||||
|
def __call__(self, args):
|
||||||
|
print(args)
|
||||||
|
|
||||||
|
|
||||||
|
class Window(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.cursor = (1, 1)
|
||||||
|
|
||||||
|
|
||||||
|
class Current(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.buffer = []
|
||||||
|
self.window = Window()
|
||||||
|
|
||||||
|
|
||||||
|
class MockVim(object):
|
||||||
|
current = Current()
|
||||||
|
command = Command()
|
||||||
|
|
||||||
|
with open('test_py_example.py') as fobj:
|
||||||
|
BUFFER = fobj.read().split('\n')
|
||||||
|
|
||||||
|
|
||||||
|
sys.modules['vim'] = vim = MockVim
|
||||||
|
|
||||||
|
|
||||||
|
import pythonhelper
|
||||||
|
|
||||||
|
|
||||||
|
class TestTagsHelperWithEmptyBuffer(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
vim.current.buffer = []
|
||||||
|
|
||||||
|
def test_get_tag(self):
|
||||||
|
vim.current.window.cursor = (1, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 2)
|
||||||
|
self.assertIsNone(tag)
|
||||||
|
|
||||||
|
|
||||||
|
class TestTagsHelper(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
vim.current.buffer = BUFFER
|
||||||
|
|
||||||
|
def test_import(self):
|
||||||
|
vim.current.window.cursor = (1, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 2)
|
||||||
|
self.assertIsNone(tag)
|
||||||
|
|
||||||
|
def test_class(self):
|
||||||
|
vim.current.window.cursor = (4, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'class')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (6, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'class')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (7, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'class')
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
vim.current.window.cursor = (9, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'method')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (11, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'method')
|
||||||
|
|
||||||
|
def test_method(self):
|
||||||
|
vim.current.window.cursor = (13, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'method')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (15, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'method')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (24, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'method')
|
||||||
|
|
||||||
|
# those tests should run if we have some kind of lexer analiser, not a
|
||||||
|
# simple py source parser.
|
||||||
|
#
|
||||||
|
# vim.current.window.cursor = (32, 1)
|
||||||
|
# tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
# self.assertEqual(tag.tag_type, 'method')
|
||||||
|
|
||||||
|
# vim.current.window.cursor = (34, 1)
|
||||||
|
# tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
# self.assertEqual(tag.tag_type, 'method')
|
||||||
|
|
||||||
|
def test_inner_function(self):
|
||||||
|
vim.current.window.cursor = (16, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'function')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (18, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'function')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (22, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'function')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (23, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'function')
|
||||||
|
|
||||||
|
def test_main(self):
|
||||||
|
vim.current.window.cursor = (37, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'function')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (38, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'function')
|
||||||
|
|
||||||
|
vim.current.window.cursor = (40, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertEqual(tag.tag_type, 'function')
|
||||||
|
|
||||||
|
def test_ifmain(self):
|
||||||
|
vim.current.window.cursor = (41, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertIsNone(tag)
|
||||||
|
|
||||||
|
vim.current.window.cursor = (42, 1)
|
||||||
|
tag = pythonhelper.PythonHelper._get_tag(1, 3)
|
||||||
|
self.assertIsNone(tag)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -1,625 +0,0 @@
|
|||||||
" File: pythonhelper.vim
|
|
||||||
" Author: Michal Vitecek <fuf-at-mageo-dot-cz>
|
|
||||||
" Version: 0.80
|
|
||||||
" Last Modified: Oct 19, 2002
|
|
||||||
"
|
|
||||||
" Overview
|
|
||||||
" --------
|
|
||||||
" Vim script to help moving around in larger Python source files. It displays
|
|
||||||
" current class, method or function the cursor is placed in on the status
|
|
||||||
" line for every python file. It's more clever than Yegappan Lakshmanan's
|
|
||||||
" taglist.vim because it takes into account indetation and comments to
|
|
||||||
" determine what tag the cursor is placed in.
|
|
||||||
"
|
|
||||||
" Requirements
|
|
||||||
" ------------
|
|
||||||
" This script needs only VIM compiled with Python interpreter. It doesn't rely
|
|
||||||
" on exuberant ctags utility. You can determine whether your VIM has Python
|
|
||||||
" support by issuing command :ver and looking for +python in the list of
|
|
||||||
" features.
|
|
||||||
"
|
|
||||||
" Note: The script displays current tag on the status line only in NORMAL
|
|
||||||
" mode. This is because CursorHold event is fired up only in this mode.
|
|
||||||
" However if you badly need to know what tag you are in even in INSERT or
|
|
||||||
" VISUAL mode, contact me on the above specified email address and I'll send
|
|
||||||
" you a patch that enables firing up CursorHold event in those modes as well.
|
|
||||||
"
|
|
||||||
" Installation
|
|
||||||
" ------------
|
|
||||||
" 1. Make sure your Vim has python feature on (+python). If not, you will need
|
|
||||||
" to recompile it with --with-pythoninterp option to the configure script
|
|
||||||
" 2. Copy script pythonhelper.vim to the $HOME/.vim/plugin directory
|
|
||||||
" 3. Run Vim and open any python file.
|
|
||||||
"
|
|
||||||
python << EOS
|
|
||||||
|
|
||||||
# import of required modules {{{
|
|
||||||
import vim
|
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# global dictionaries of tags and their line numbers, keys are buffer numbers {{{
|
|
||||||
TAGS = {}
|
|
||||||
TAGLINENUMBERS = {}
|
|
||||||
BUFFERTICKS = {}
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# class PythonTag() {{{
|
|
||||||
class PythonTag(object):
|
|
||||||
# DOC {{{
|
|
||||||
"""A simple storage class representing a python tag.
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# STATIC VARIABLES {{{
|
|
||||||
|
|
||||||
# tag type IDs {{{
|
|
||||||
TAGTYPE_CLASS = 0
|
|
||||||
TAGTYPE_METHOD = 1
|
|
||||||
TAGTYPE_FUNCTION = 2
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# tag type names {{{
|
|
||||||
typeName = {
|
|
||||||
TAGTYPE_CLASS : "class",
|
|
||||||
TAGTYPE_METHOD : "method",
|
|
||||||
TAGTYPE_FUNCTION : "function",
|
|
||||||
}
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# METHODS {{{
|
|
||||||
|
|
||||||
def __init__(self, type, name, fullName, lineNumber, indentLevel):
|
|
||||||
# DOC {{{
|
|
||||||
"""Initializes instances of class PythonTag().
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
type -- tag type
|
|
||||||
|
|
||||||
name -- short tag name
|
|
||||||
|
|
||||||
fullName -- full tag name (in dotted notation)
|
|
||||||
|
|
||||||
lineNumber -- line number on which the tag starts
|
|
||||||
|
|
||||||
indentLevel -- indentation level of the tag
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
self.type = type
|
|
||||||
self.name = name
|
|
||||||
self.fullName = fullName
|
|
||||||
self.lineNumber = lineNumber
|
|
||||||
self.indentLevel = indentLevel
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
# DOC {{{
|
|
||||||
"""Returns a string representation of the tag.
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
return "%s (%s) [%s, %u, %u]" % (self.name, PythonTag.typeName[self.type],
|
|
||||||
self.fullName, self.lineNumber, self.indentLevel,)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
__repr__ = __str__
|
|
||||||
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# SimplePythonTagsParser() {{{
|
|
||||||
class SimplePythonTagsParser(object):
|
|
||||||
# DOC {{{
|
|
||||||
"""Provides a simple python tag parser. Returns list of PythonTag()
|
|
||||||
instances.
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# STATIC VARIABLES {{{
|
|
||||||
# how many chars a single tab represents (visually)
|
|
||||||
TABSIZE = 8
|
|
||||||
|
|
||||||
# regexp used to get indentation and strip comments
|
|
||||||
commentsIndentStripRE = re.compile('([ \t]*)([^\n#]*).*')
|
|
||||||
# regexp used to get class name
|
|
||||||
classRE = re.compile('class[ \t]+([^(:]+).*')
|
|
||||||
# regexp used to get method or function name
|
|
||||||
methodRE = re.compile('def[ \t]+([^(]+).*')
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# METHODS {{{
|
|
||||||
|
|
||||||
def __init__(self, source):
|
|
||||||
# DOC {{{
|
|
||||||
"""Initializes the instance of class SimplePythonTagsParser().
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
source -- source for which the tags will be generated. It must
|
|
||||||
provide callable method readline (i.e. as file objects do).
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
# make sure source has readline() method {{{
|
|
||||||
if (not(hasattr(source, 'readline') and
|
|
||||||
callable(source.readline))):
|
|
||||||
raise AttributeError("Source must have callable readline method.")
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# remember what the source is
|
|
||||||
self.source = source
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def getTags(self):
|
|
||||||
# DOC {{{
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
tagLineNumbers = []
|
|
||||||
tags = {}
|
|
||||||
|
|
||||||
# list (stack) of all currently active tags
|
|
||||||
tagsStack = []
|
|
||||||
|
|
||||||
lineNumber = 0
|
|
||||||
while 1:
|
|
||||||
# get next line
|
|
||||||
line = self.source.readline()
|
|
||||||
|
|
||||||
# finish if this is the end of the source {{{
|
|
||||||
if (line == ''):
|
|
||||||
break
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
lineNumber += 1
|
|
||||||
lineMatch = self.commentsIndentStripRE.match(line)
|
|
||||||
lineContents = lineMatch.group(2)
|
|
||||||
# class tag {{{
|
|
||||||
tagMatch = self.classRE.match(lineContents)
|
|
||||||
if (tagMatch):
|
|
||||||
currentTag = self.getPythonTag(tagsStack, lineNumber, lineMatch.group(1),
|
|
||||||
tagMatch.group(1), self.tagClassTypeDecidingMethod)
|
|
||||||
tagLineNumbers.append(lineNumber)
|
|
||||||
tags[lineNumber] = currentTag
|
|
||||||
# }}}
|
|
||||||
# function/method/none tag {{{
|
|
||||||
else:
|
|
||||||
tagMatch = self.methodRE.match(lineContents)
|
|
||||||
if (tagMatch):
|
|
||||||
currentTag = self.getPythonTag(tagsStack, lineNumber, lineMatch.group(1),
|
|
||||||
tagMatch.group(1), self.tagFunctionTypeDecidingMethod)
|
|
||||||
tagLineNumbers.append(lineNumber)
|
|
||||||
tags[lineNumber] = currentTag
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# return the tags data for the source
|
|
||||||
return (tagLineNumbers, tags,)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def getPreviousTag(self, tagsStack):
|
|
||||||
# DOC {{{
|
|
||||||
"""Returns the previous tag (instance of PythonTag()) from the
|
|
||||||
specified tag list if possible. If not, returns None.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
tagsStack -- list (stack) of currently active PythonTag() instances
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
if (len(tagsStack)):
|
|
||||||
previousTag = tagsStack[-1]
|
|
||||||
else:
|
|
||||||
previousTag = None
|
|
||||||
|
|
||||||
# return the tag
|
|
||||||
return previousTag
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def computeIndentLevel(self, indentChars):
|
|
||||||
# DOC {{{
|
|
||||||
"""Computes indent level from the specified string.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
indentChars -- white space before any other character on line
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
indentLevel = 0
|
|
||||||
for char in indentChars:
|
|
||||||
if (char == '\t'):
|
|
||||||
indentLevel += self.TABSIZE
|
|
||||||
else:
|
|
||||||
indentLevel += 1
|
|
||||||
|
|
||||||
return indentLevel
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def getPythonTag(self, tagsStack, lineNumber, indentChars, tagName, tagTypeDecidingMethod):
|
|
||||||
# DOC {{{
|
|
||||||
"""Returns instance of PythonTag() based on the specified data.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
tagsStack -- list (stack) of tags currently active. Note: Modified
|
|
||||||
in this method!
|
|
||||||
|
|
||||||
lineNumber -- current line number
|
|
||||||
|
|
||||||
indentChars -- characters making up the indentation level of the
|
|
||||||
current tag
|
|
||||||
|
|
||||||
tagName -- short name of the current tag
|
|
||||||
|
|
||||||
tagTypeDecidingMethod -- reference to method that is called to
|
|
||||||
determine type of the current tag
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
indentLevel = self.computeIndentLevel(indentChars)
|
|
||||||
previousTag = self.getPreviousTag(tagsStack)
|
|
||||||
# code for enclosed tag {{{
|
|
||||||
while (previousTag):
|
|
||||||
if (previousTag.indentLevel >= indentLevel):
|
|
||||||
del tagsStack[-1]
|
|
||||||
else:
|
|
||||||
tagType = tagTypeDecidingMethod(previousTag.type)
|
|
||||||
tag = PythonTag(tagType, tagName, "%s.%s" % (previousTag.fullName, tagName,), lineNumber, indentLevel)
|
|
||||||
tagsStack.append(tag)
|
|
||||||
return tag
|
|
||||||
previousTag = self.getPreviousTag(tagsStack)
|
|
||||||
# }}}
|
|
||||||
# code for tag in top indent level {{{
|
|
||||||
else:
|
|
||||||
tagType = tagTypeDecidingMethod(None)
|
|
||||||
tag = PythonTag(tagType, tagName, tagName, lineNumber, indentLevel)
|
|
||||||
tagsStack.append(tag)
|
|
||||||
return tag
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def tagClassTypeDecidingMethod(self, previousTagsType):
|
|
||||||
# DOC {{{
|
|
||||||
"""Returns tag type of the current tag based on its previous tag (super
|
|
||||||
tag) for classes.
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
return PythonTag.TAGTYPE_CLASS
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def tagFunctionTypeDecidingMethod(self, previousTagsType):
|
|
||||||
# DOC {{{
|
|
||||||
"""Returns tag type of the current tag based on its previous tag (super
|
|
||||||
tag) for functions/methods.
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
if (previousTagsType == PythonTag.TAGTYPE_CLASS):
|
|
||||||
return PythonTag.TAGTYPE_METHOD
|
|
||||||
else:
|
|
||||||
return PythonTag.TAGTYPE_FUNCTION
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# class VimReadlineBuffer() {{{
|
|
||||||
class VimReadlineBuffer(object):
|
|
||||||
# DOC {{{
|
|
||||||
"""A simple wrapper class around vim's buffer that provides readline
|
|
||||||
method.
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
# METHODS {{{
|
|
||||||
|
|
||||||
def __init__(self, vimBuffer):
|
|
||||||
# DOC {{{
|
|
||||||
"""Initializes the instance of class VimReadlineBuffer().
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
vimBuffer -- VIM's buffer
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
self.vimBuffer = vimBuffer
|
|
||||||
self.currentLine = -1
|
|
||||||
self.bufferLines = len(vimBuffer)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def readline(self):
|
|
||||||
# DOC {{{
|
|
||||||
"""Returns next line from the buffer. If all the buffer has been read,
|
|
||||||
returns empty string.
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
self.currentLine += 1
|
|
||||||
|
|
||||||
# notify end of file if we reached beyond the last line {{{
|
|
||||||
if (self.currentLine == self.bufferLines):
|
|
||||||
return ''
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# return the line with added newline (vim stores the lines without newline)
|
|
||||||
return "%s\n" % (self.vimBuffer[self.currentLine],)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def getNearestLineIndex(row, tagLineNumbers):
|
|
||||||
# DOC {{{
|
|
||||||
"""Returns index of line in tagLineNumbers list that is nearest to the
|
|
||||||
current cursor row.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
row -- current cursor row
|
|
||||||
|
|
||||||
tagLineNumbers -- list of tags' line numbers (ie. their position)
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
nearestLineNumber = -1
|
|
||||||
nearestLineIndex = -1
|
|
||||||
i = 0
|
|
||||||
for lineNumber in tagLineNumbers:
|
|
||||||
# if the current line is nearer the current cursor position, take it {{{
|
|
||||||
if (nearestLineNumber < lineNumber <= row):
|
|
||||||
nearestLineNumber = lineNumber
|
|
||||||
nearestLineIndex = i
|
|
||||||
# }}}
|
|
||||||
# if we've got past the current cursor position, let's end the search {{{
|
|
||||||
if (lineNumber >= row):
|
|
||||||
break
|
|
||||||
# }}}
|
|
||||||
i += 1
|
|
||||||
return nearestLineIndex
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def getTags(bufferNumber, changedTick):
|
|
||||||
# DOC {{{
|
|
||||||
"""Reads the tags for the specified buffer number. Returns tuple
|
|
||||||
(taglinenumber[buffer], tags[buffer],).
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
bufferNumber -- number of the current buffer
|
|
||||||
|
|
||||||
changedTick -- ever increasing number used to tell if the buffer has
|
|
||||||
been modified since the last time
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
global TAGLINENUMBERS, TAGS, BUFFERTICKS
|
|
||||||
|
|
||||||
# return immediately if there's no need to update the tags {{{
|
|
||||||
if ((BUFFERTICKS.has_key(bufferNumber)) and (BUFFERTICKS[bufferNumber] == changedTick)):
|
|
||||||
return (TAGLINENUMBERS[bufferNumber], TAGS[bufferNumber],)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# get the tags {{{
|
|
||||||
simpleTagsParser = SimplePythonTagsParser(VimReadlineBuffer(vim.current.buffer))
|
|
||||||
tagLineNumbers, tags = simpleTagsParser.getTags()
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# update the global variables {{{
|
|
||||||
TAGS[bufferNumber] = tags
|
|
||||||
TAGLINENUMBERS[bufferNumber] = tagLineNumbers
|
|
||||||
BUFFERTICKS[bufferNumber] = changedTick
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# return the tags data
|
|
||||||
return (tagLineNumbers, tags,)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def findTag(bufferNumber, changedTick):
|
|
||||||
# DOC {{{
|
|
||||||
"""Tries to find the best tag for the current cursor position.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
bufferNumber -- number of the current buffer
|
|
||||||
|
|
||||||
changedTick -- ever increasing number used to tell if the buffer has
|
|
||||||
been modified since the last time
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
try:
|
|
||||||
# get the tags data for the current buffer {{{
|
|
||||||
tagLineNumbers, tags = getTags(bufferNumber, changedTick)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# link to vim internal data {{{
|
|
||||||
currentBuffer = vim.current.buffer
|
|
||||||
currentWindow = vim.current.window
|
|
||||||
row, col = currentWindow.cursor
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# get the index of the nearest line
|
|
||||||
nearestLineIndex = getNearestLineIndex(row, tagLineNumbers)
|
|
||||||
# if any line was found, try to find if the tag is appropriate {{{
|
|
||||||
# (ie. the cursor can be below the last tag but on a code that has nothing
|
|
||||||
# to do with the tag, because it's indented differently, in such case no
|
|
||||||
# appropriate tag has been found.)
|
|
||||||
if (nearestLineIndex > -1):
|
|
||||||
nearestLineNumber = tagLineNumbers[nearestLineIndex]
|
|
||||||
# walk through all the lines in range (nearestTagLine, cursorRow) {{{
|
|
||||||
for i in xrange(nearestLineNumber + 1, row):
|
|
||||||
line = currentBuffer[i]
|
|
||||||
# count the indentation of the line, if it's lower that the tag's, the found tag is wrong {{{
|
|
||||||
if (len(line)):
|
|
||||||
# compute the indentation of the line {{{
|
|
||||||
lineStart = 0
|
|
||||||
j = 0
|
|
||||||
while ((j < len(line)) and (line[j].isspace())):
|
|
||||||
if (line[j] == '\t'):
|
|
||||||
lineStart += SimplePythonTagsParser.TABSIZE
|
|
||||||
else:
|
|
||||||
lineStart += 1
|
|
||||||
j += 1
|
|
||||||
# if the line contains only spaces, it doesn't count {{{
|
|
||||||
if (j == len(line)):
|
|
||||||
continue
|
|
||||||
# }}}
|
|
||||||
# if the next character is # (python comment), this line doesn't count {{{
|
|
||||||
if (line[j] == '#'):
|
|
||||||
continue
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
# if the line's indentation starts before the nearest tag's one, the tag is wrong {{{
|
|
||||||
if (lineStart < tags[nearestLineNumber].indentLevel):
|
|
||||||
nearestLineNumber = -1
|
|
||||||
break
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
else:
|
|
||||||
nearestLineNumber = -1
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# describe the cursor position (what tag it's in) {{{
|
|
||||||
tagDescription = ""
|
|
||||||
if (nearestLineNumber > -1):
|
|
||||||
tagInfo = tags[nearestLineNumber]
|
|
||||||
tagDescription = "[in %s (%s)]" % (tagInfo.fullName, PythonTag.typeName[tagInfo.type],)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# update the variable for the status line so it will be updated next time
|
|
||||||
vim.command("let w:PHStatusLine=\"%s\"" % (tagDescription,))
|
|
||||||
except:
|
|
||||||
# spit out debugging information {{{
|
|
||||||
ec, ei, tb = sys.exc_info()
|
|
||||||
while (tb != None):
|
|
||||||
if (tb.tb_next == None):
|
|
||||||
break
|
|
||||||
tb = tb.tb_next
|
|
||||||
print "ERROR: %s %s %s:%u" % (ec.__name__, ei, tb.tb_frame.f_code.co_filename, tb.tb_lineno,)
|
|
||||||
time.sleep(0.5)
|
|
||||||
# }}}
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
def deleteTags(bufferNumber):
|
|
||||||
# DOC {{{
|
|
||||||
"""Removes tags data for the specified buffer number.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
|
|
||||||
bufferNumber -- number of the buffer
|
|
||||||
"""
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# CODE {{{
|
|
||||||
global TAGS, TAGLINENUMBERS, BUFFERTICKS
|
|
||||||
|
|
||||||
try:
|
|
||||||
del TAGS[bufferNumber]
|
|
||||||
del TAGLINENUMBERS[bufferNumber]
|
|
||||||
del BUFFERTICKS[bufferNumber]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
EOS
|
|
||||||
|
|
||||||
|
|
||||||
function! PHCursorHold()
|
|
||||||
" only python is supported {{{
|
|
||||||
if (!exists('b:current_syntax') || (b:current_syntax != 'python'))
|
|
||||||
let w:PHStatusLine = ''
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
" }}}
|
|
||||||
|
|
||||||
" call python function findTag() with the current buffer number and changed ticks
|
|
||||||
execute 'python findTag(' . expand("<abuf>") . ', ' . b:changedtick . ')'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! PHBufferDelete()
|
|
||||||
" set PHStatusLine for this window to empty string
|
|
||||||
let w:PHStatusLine = ""
|
|
||||||
|
|
||||||
" call python function deleteTags() with the cur
|
|
||||||
execute 'python deleteTags(' . expand("<abuf>") . ')'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! TagInStatusLine()
|
|
||||||
" return value of w:PHStatusLine in case it's set
|
|
||||||
if (exists("w:PHStatusLine"))
|
|
||||||
return w:PHStatusLine
|
|
||||||
" otherwise just return empty string
|
|
||||||
else
|
|
||||||
return ""
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
" autocommands binding
|
|
||||||
autocmd CursorHold * call PHCursorHold()
|
|
||||||
autocmd BufDelete * silent call PHBufferDelete()
|
|
||||||
|
|
||||||
" time that determines after how long time of no activity the CursorHold event
|
|
||||||
" is fired up
|
|
||||||
set updatetime=1000
|
|
||||||
|
|
||||||
" color of the current tag in the status line (bold cyan on black)
|
|
||||||
highlight User1 gui=bold guifg=cyan guibg=black
|
|
||||||
" color of the modified flag in the status line (bold black on red)
|
|
||||||
highlight User2 gui=bold guifg=black guibg=red
|
|
||||||
" the status line will be displayed for every window
|
|
||||||
set laststatus=2
|
|
||||||
" set the status line to display some useful information
|
|
||||||
set stl=%-f%r\ %2*%m%*\ \ \ \ %1*%{TagInStatusLine()}%*%=[%l:%c]\ \ \ \ [buf\ %n]
|
|
||||||
|
|
||||||
|
|
||||||
" vim:foldmethod=marker
|
|
||||||
Reference in New Issue
Block a user