1
0
mirror of https://github.com/gryf/pythonhelper.git synced 2026-04-25 07:01:25 +02:00

48 Commits

Author SHA1 Message Date
gryf 154c343257 Updated README, added license file 2016-05-30 19:42:17 +02:00
gryf 81bdd2f51c Docstring cleanup, small refactoring, removed outdated docs 2016-05-30 19:35:41 +02:00
gryf 839012de28 Fixed bug regarding deleting not existed tags cache. 2016-05-25 08:32:54 +02:00
gryf c41a450990 Bugfix and move plugin to python specific ftplugin
Move to python specific plugin (ftplugin),
Fixed bug regarding empty buffer.
2016-05-25 08:27:36 +02:00
gryf 81a638762b Moved variable with script path out of function
Otherwise it will point to the current path, instead of script path.
Changing it to script variables will change that behaviour to desirable one.
2016-05-24 14:56:40 +02:00
gryf 3164a9e568 Make vim mock Python3 compatible 2016-05-23 21:18:17 +02:00
gryf 4f1dbc74bd Simplify implementation for tag search
Provide alternative implementation of searching for the right tag.
Also, make functionality testable.
2016-05-23 20:59:39 +02:00
gryf b2ca2a2fca Added tests for pythonhelper 2016-05-23 20:58:06 +02:00
gryf c011e488ce WIP: Rewrite parser 2016-05-21 19:48:12 +02:00
gryf d8a44e8439 Get rid of catch-everything exception mechanism.
It seams, that entire block for looking for tags has been closed between
try:except keywords, catching all the exceptions, which is not necessary just
for iterating all over the file looking for particular pattern. Remove it
until real problem appear on wild - for sure I will extensively test that ;).
2016-05-19 21:24:22 +02:00
gryf 03f3c9ef1e Unifying regexp for detecting class/function
Removing unnecessary functions for "detecting" context of the
class/function. Unifying regexp for detecting class/method/function,
get rid of excess iterator as a facade for vim buffer. Simplifying function
for gathering tags.
2016-05-19 21:14:07 +02:00
gryf c9e210a331 Get rid of globals 2016-05-19 06:48:31 +02:00
gryf 327dce4b7a Refactor PythonTag class 2016-05-18 21:22:21 +02:00
gryf 9809859c73 Enforce PEP8 on the Python code 2016-05-18 21:16:23 +02:00
gryf d40dbfe22b Separate vim and pthon scripts 2016-05-18 20:19:10 +02:00
gryf e37a64afc4 Removing clutter from python section 2016-05-17 18:38:49 +02:00
Oluf Lorenzen c6b824f536 Merge pull request #2 from cheater/master
Some updates to pythonhelper

1. revamped all the comments
2. made it possible to get just the tag name or just the tag type
3. fixed a lot of stupid code (but not all)
4. made it stop clobbering updatetime (still optional, though)
2012-02-13 05:57:37 -08:00
cheater 8f88e4bc7f Only set updatetime if g:pythonhelper_updatetime is set. 2012-02-12 14:27:54 +01:00
cheater 476707b687 Deleted stray comment. 2012-02-12 14:27:53 +01:00
cheater e1d3306adc You can now set the updatetime used by pythonhelper with g:pythonhelper_updatetime. 2012-02-12 14:27:51 +01:00
cheater 1e1d1cc6f1 Doc bug. 2012-02-12 14:27:50 +01:00
cheater 4a9a963dd6 Added the ability to separately access the tag name and tag type, via TagInStatusLineTag or TagInStatusLineType respectively. 2012-02-12 14:27:48 +01:00
cheater dcf5507d0f Doc bug. 2012-02-12 14:27:47 +01:00
cheater 1b61929c9a Doc bug. 2012-02-12 14:27:44 +01:00
cheater 0cccd82b2a Use a zipper instead of counting by hand. 2012-02-12 14:27:41 +01:00
cheater 702b07e390 Code style. 2012-02-12 14:27:39 +01:00
cheater a361304e73 Deleted non-iterator code. Refactoring complete. 2012-02-12 14:27:36 +01:00
cheater aee6f328aa Continue refactoring towards an iterator. 2012-02-12 14:27:34 +01:00
cheater ba115f64cc Initial iterator-based implementation. 2012-02-12 14:27:32 +01:00
cheater d6046886a3 Exceptions can tell you the same thing. 2012-02-12 14:27:31 +01:00
cheater f323d3cc74 Starting refactor towards an iterator. 2012-02-12 14:27:29 +01:00
cheater fe86a19e35 Added a FIXME for the 'except Exception'. 2012-02-12 14:27:27 +01:00
cheater c9dc1f3ec0 Lame use of exceptions. Never ever use an except block without qualifying the exception type, never ever catch exceptions from multiple sources at once. 2012-02-12 14:27:25 +01:00
cheater e70cca0a11 Corrected documentation. 2012-02-12 14:27:22 +01:00
cheater 005a35dcf6 More bad English in the doc. 2012-02-12 14:27:20 +01:00
cheater 42d72d9e6b More bad English in the doc. 2012-02-12 14:27:18 +01:00
cheater 6494b9de1c Tweaked installation instructions. 2012-02-12 14:27:11 +01:00
cheater 864d36fd02 Bad English in the installation instructions. 2012-02-12 14:26:58 +01:00
cheater 333db2d78e Yet more typos in the installation instructions 2012-02-12 14:26:55 +01:00
cheater 888970d931 More typos in the installation instructions 2012-02-12 14:26:52 +01:00
cheater 2e27597939 Code style. Tabs AND spaces? WTF. http://i.imgur.com/Lo5Vv.png 2012-02-12 14:26:47 +01:00
cheater 0ce33afc6c Typo in installation instructions. 2012-02-12 14:26:41 +01:00
Oluf Lorenzen 49d018fdc6 let user decide where/how to add the StatusLine
Signed-off-by: Oluf Lorenzen <ol@axxeo.de>
2010-12-17 14:57:15 +01:00
Michal Vitecek 2caa97d75f Version 0.83
Added support for the CursorHoldI event so that the class/method/function is recognized also in Insert mode.
-
Michal Vitecek b0841be7ee Version 0.82: - fixed a bug when nested functions/classes were not properly detected -
Michal Vitecek ab9d0e5e69 Version 0.81: - fixed a small bug in indent level recognition -
Michal Vitecek f5b70c33bb Version 0.80
- 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.
-
Michal Vitecek 91b230cb51 Version 0.72
- 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
-
8 changed files with 694 additions and 369 deletions
+24
View File
@@ -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.
-5
View File
@@ -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.
+98
View File
@@ -0,0 +1,98 @@
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.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
+241
View File
@@ -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
+143
View File
@@ -0,0 +1,143 @@
" File: pythonhelper.vim
" Author: Michal Vitecek <fuf-at-mageo-dot-cz>
" Author: Roman Dobosz <gryf@vimja.com>
" Version: 1.0
" License: 3-clause BSD license
" Last Modified: 2016-05-24
" VIM functions {{{
let g:pythonhelper_python = 'python'
let s:plugin_path = expand('<sfile>:p:h', 1)
function! s:PHLoader()
if !exists('g:pythonhelper_py_loaded')
if has('python')
exe 'pyfile ' . s:plugin_path . '/pythonhelper.py'
elseif has('python3')
let g:pythonhelper_python = 'python3'
exe 'py3file ' . s:plugin_path . '/pythonhelper.py'
else
echohl WarningMsg|echomsg
\ "PythonHelper unavailable: "
\ "requires Vim with Python support"|echohl None
finish
endif
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:pythonhelper_python . ' 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:pythonhelper_python . ' 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
+42
View File
@@ -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()
+146
View File
@@ -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()
-364
View File
@@ -1,364 +0,0 @@
" File: pythonhelper.vim
" Author: Michal Vitecek <fuf-at-mageo-dot-cz>
" Version: 0.7
" Last Modified: Oct 2, 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 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.
"
" Requirements
" ------------
" This script needs VIM compiled with Python interpreter and relies on
" exuberant ctags utility to generate the tag listing. You can determine
" whether your VIM has Python support by issuing command :ver and looking for
" +python in the list of features.
"
" The exuberant ctags can be downloaded from http://ctags.sourceforge.net/ and
" should be reasonably new version (tested with 5.3).
"
" Note: The script doesn't display 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 on even in INSERT or
" VISUAL mode, contact me on the above specified email address and I'll send
" you patch that enables it.
"
" 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. Edit the script and modify the location of your exuberant tags utility
" (variable CTAGS_PROGRAM).
" 4. Run Vim and open any python file.
"
python << EOS
# import of required modules {{{
import vim
import os
import popen2
import time
import sys
# }}}
# CTAGS program and parameters {{{
CTAGS_PROGRAM = "/usr/local/bin/ctags"
CTAGS_PARAMETERS = "--language-force=python --format=2 --sort=0 --fields=+nK -L - -f - "
# }}}
# global dictionaries of tags and their line numbers, keys are buffer numbers {{{
TAGS = {}
TAGLINENUMBERS = {}
BUFFERTICKS = {}
# }}}
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. It does so by executing
the CTAGS program and parsing its output. 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 CTAGS_PROGRAM, CTAGS_PARAMETERS
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],)
# }}}
# read the tags and fill the global variables {{{
currentBuffer = vim.current.buffer
currentWindow = vim.current.window
row, col = currentWindow.cursor
# create a temporary file with the current content of the buffer {{{
fileName = "/tmp/.%s.%u.ph" % (os.path.basename(currentBuffer.name), os.getpid(),)
f = open(fileName, "w")
for line in currentBuffer:
f.write(line)
f.write('\n')
f.close()
# }}}
# run ctags on it {{{
try:
ctagsOutPut, ctagsInPut = popen2.popen4("%s %s" % (CTAGS_PROGRAM, CTAGS_PARAMETERS,))
ctagsInPut.write(fileName + "\n")
ctagsInPut.close()
except:
os.unlink(fileName)
return
# }}}
# parse the ctags' output {{{
tagLineNumbers = []
tags = {}
while 1:
line = ctagsOutPut.readline()
# if empty line has been read, it's the end of the file {{{
if (line == ''):
break
# }}}
# if the line starts with !, then it's a comment line {{{
if (line[0] == '!'):
continue
# }}}
# split the line into parts and parse the data {{{
# the format is: [0]tagName [1]fileName [2]tagLine [3]tagType [4]tagLineNumber [[5]tagOwner]
tagData = line.split('\t')
name = tagData[0]
# get the tag's indentation {{{
start = 2
j = 2
while ((j < len(tagData[2])) and (tagData[2][j].isspace())):
if (tagData[2][j] == '\t'):
start += 8
else:
start += 1
j += 1
# }}}
type = tagData[3]
line = int(tagData[4][5:])
if (len(tagData) == 6):
owner = tagData[5].strip()
else:
owner = None
# }}}
tagLineNumbers.append(line)
tags[line] = (name, type, owner, start)
ctagsOutPut.close()
# }}}
# clean up the now unnecessary stuff {{{
os.unlink(fileName)
# }}}
# update the global variables {{{
TAGS[bufferNumber] = tags
TAGLINENUMBERS[bufferNumber] = tagLineNumbers
BUFFERTICKS[bufferNumber] = changedTick
# }}}
# }}}
return (TAGLINENUMBERS[bufferNumber], TAGS[bufferNumber],)
# }}}
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 += 8
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][3]):
nearestLineNumber = -1
break
# }}}
# }}}
# }}}
else:
nearestLineNumber = -1
# }}}
# describe the cursor position (what tag it's in) {{{
tagDescription = ""
if (nearestLineNumber > -1):
tagInfo = tags[nearestLineNumber]
# use the owner if any exists {{{
if (tagInfo[2] != None):
fullTagName = "%s.%s()" % (tagInfo[2].split(':')[1], tagInfo[0],)
# }}}
# otherwise use just the tag name {{{
else:
fullTagName = tagInfo[0]
# }}}
tagDescription = "[in %s (%s)]" % (fullTagName, tagInfo[1],)
# }}}
# 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()
" call python function deleteTags() with the cur
execute 'python deleteTags(' . expand("<abuf>") . ')'
endfunction
function! TagInStatusLine()
if (exists("w:PHStatusLine"))
return w:PHStatusLine
else
return ""
endif
endfunction
" autocommands binding
autocmd CursorHold * silent 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]