Skip to content

Commit a37a24f

Browse files
committed
Added :PyRef command, converted to autoload script
The plug-in now defines the :PyRef command in Python buffers. Also most of the code was moved to an auto-load that won't be loaded until the Python file type is used. Finally all sanity checking is now done at runtime.
1 parent b059112 commit a37a24f

File tree

3 files changed

+151
-151
lines changed

3 files changed

+151
-151
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# Context-sensitive documentation <br> for Python source code in Vim
22

3-
The `pyref.vim` script is a plug-in for the [Vim text editor](http://www.vim.org/) that maps the `<F1>` key in [Python](http://python.org/) buffers to search through the [Python language reference](http://docs.python.org/reference/index.html) and [library reference](http://docs.python.org/library/index.html) documentation for the keyword or identifier at the current cursor position and open the first match in your web browser. When no GUI is available a command-line web browser like `lynx` or `w3m` will be used, otherwise the plug-in prefers a graphical web browser like Mozilla Firefox or Google Chrome.
3+
The `pyref.vim` script is a plug-in for the [Vim text editor](http://www.vim.org/) that looks up keywords and identifiers in the [Python language reference](http://docs.python.org/reference/index.html) and [library reference](http://docs.python.org/library/index.html) documentation using your web browser. The `:PyRef` command looks up the identifier given as an argument while the `<F1>` mapping looks up the item at the text cursor. Both are only made available inside Python buffers.
44

55
## How does it work?
66

7-
The search works by scanning through a special index file with keyword, URL pairs separated by tabs and delimited by newlines. The index file is included in the ZIP archive linked to below but you can also create it yourself using the Python script [spider.py](http://github.com/xolox/vim-pyref/blob/master/spider.py).
7+
The search works by scanning through a special index file with keyword, URL pairs separated by tabs and delimited by newlines. The index file is included in the ZIP archive linked below but you can also create it yourself using the Python script [spider.py](http://github.com/xolox/vim-pyref/blob/master/spider.py).
88

99
## Install & usage
1010

11-
Unzip the most recent [ZIP archive](http://peterodding.com/code/vim/downloads/pyref) file inside your Vim profile directory (usually this is `~/.vim` on UNIX and `%USERPROFILE%\vimfiles` on Windows), restart Vim and execute the command `:helptags ~/.vim/doc` (use `:helptags ~\vimfiles\doc` instead on Windows). Now try it out: Open a Python script and press the `<F1>` key.
11+
Unzip the most recent [ZIP archive](http://peterodding.com/code/vim/downloads/pyref) file inside your Vim profile directory (usually this is `~/.vim` on UNIX and `%USERPROFILE%\vimfiles` on Windows), restart Vim and execute the command `:helptags ~/.vim/doc` (use `:helptags ~\vimfiles\doc` instead on Windows). Now try it out: Open a Python script and press the `<F1>` key on something interesting.
1212

1313
The following paragraphs explain the available options:
1414

autoload.vim

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
" Vim auto-load script
2+
" Author: Peter Odding <peter@peterodding.com>
3+
" Last Change: September 18, 2010
4+
" URL: http://peterodding.com/code/vim/pyref/
5+
6+
let s:script = expand('<sfile>:p:~')
7+
8+
function! xolox#pyref#enable() " {{{1
9+
command! -buffer -nargs=? PyRef call xolox#pyref#lookup(<q-args>)
10+
let command = '%s <silent> <buffer> %s %s:call xolox#pyref#at_cursor()<CR>'
11+
let mapping = exists('g:pyref_mapping') ? g:pyref_mapping : '<F1>'
12+
execute printf(command, 'nmap', mapping, '')
13+
if mapping =~ '^<[^>]\+>'
14+
execute printf(command, 'imap', mapping, '<C-O>')
15+
endif
16+
endfunction
17+
18+
function! xolox#pyref#at_cursor() " {{{1
19+
try
20+
let isk_save = &isk
21+
let &isk = '@,48-57,_,192-255,.'
22+
let ident = expand('<cword>')
23+
finally
24+
let &isk = isk_save
25+
endtry
26+
call xolox#pyref#lookup(ident)
27+
endfunction
28+
29+
function! xolox#pyref#lookup(identifier) " {{{1
30+
31+
let mirror = s:find_mirror()
32+
let ident = xolox#trim(a:identifier)
33+
34+
" Do something useful when there's nothing at the current position.
35+
if ident == ''
36+
call xolox#open#url(mirror . '/contents.html')
37+
return
38+
endif
39+
40+
" Escape any dots in the expression so it can be used as a pattern.
41+
let pattern = substitute(ident, '\.', '\\.', 'g')
42+
43+
" Search for an exact match of a module name or identifier in the index.
44+
let indexfile = s:find_index()
45+
try
46+
let lines = readfile(indexfile)
47+
catch
48+
let lines = []
49+
call xolox#warning("%s: Failed to read index file! (%s)", s:script, indexfile)
50+
endtry
51+
if s:try_lookup(lines, mirror, '^\C\(module-\|exceptions\.\)\?' . pattern . '\t')
52+
return
53+
endif
54+
55+
" Search for a substring match on word boundaries.
56+
if s:try_lookup(lines, mirror, '\C\<' . pattern . '\>.*\t')
57+
return
58+
endif
59+
60+
" Try to match a method name of one of the standard Python types: strings,
61+
" lists, dictionaries and files (not exactly ideal but better than nothing).
62+
for [url, method_pattern] in s:object_methods
63+
let method = matchstr(ident, method_pattern)
64+
if method != ''
65+
if url =~ '%s'
66+
let url = printf(url, method)
67+
endif
68+
call xolox#open#url(mirror . '/' . url)
69+
return
70+
endif
71+
endfor
72+
73+
" Search for a substring match in the index.
74+
if s:try_lookup(lines, mirror, '\C' . pattern . '.*\t')
75+
return
76+
endif
77+
78+
" Split the expression on all dots and search for a progressively smaller
79+
" suffix to resolve object attributes like "self.parser.add_option" to
80+
" global identifiers like "optparse.OptionParser.add_option". This relies
81+
" on the uniqueness of the method names in the standard library.
82+
let parts = split(ident, '\.')
83+
while len(parts) > 1
84+
call remove(parts, 0)
85+
let pattern = '\C\<' . join(parts, '\.') . '$'
86+
if s:try_lookup(lines, mirror, pattern)
87+
return
88+
endif
89+
endwhile
90+
91+
" As a last resort, search all of http://docs.python.org/ using Google.
92+
call xolox#open#url('http://google.com/search?btnI&q=inurl:docs.python.org/+' . ident)
93+
94+
endfunction
95+
96+
" This list of lists contains [url_format, method_pattern] pairs that are used
97+
" to recognize calls to methods of objects that are one of Python's standard
98+
" types: strings, lists, dictionaries and file handles.
99+
let s:object_methods = [
100+
\ ['library/stdtypes.html#str.%s', '\C\.\@<=\(capitalize\|center\|count\|decode\|encode\|endswith\|expandtabs\|find\|format\|index\|isalnum\|isalpha\|isdigit\|islower\|isspace\|istitle\|isupper\|join\|ljust\|lower\|lstrip\|partition\|replace\|rfind\|rindex\|rjust\|rpartition\|rsplit\|rstrip\|split\|splitlines\|startswith\|strip\|swapcase\|title\|translate\|upper\|zfill\)$'],
101+
\ ['tutorial/datastructures.html#more-on-lists', '\C\.\@<=\(append\|count\|extend\|index\|insert\|pop\|remove\|reverse\|sort\)$'],
102+
\ ['library/stdtypes.html#dict.%s', '\C\.\@<=\(clear\|copy\|fromkeys\|get\|has_key\|items\|iteritems\|iterkeys\|itervalues\|keys\|pop\|popitem\|setdefault\|update\|values\)$'],
103+
\ ['library/stdtypes.html#file.%s', '\C\.\@<=\(close\|closed\|encoding\|errors\|fileno\|flush\|isatty\|mode\|name\|newlines\|next\|read\|readinto\|readline\|readlines\|seek\|softspace\|tell\|truncate\|write\|writelines\|xreadlines\)$']]
104+
105+
function! s:try_lookup(lines, mirror, pattern) " {{{1
106+
call xolox#debug("%s: Trying to match pattern %s", s:script, a:pattern)
107+
let index = match(a:lines, a:pattern)
108+
if index >= 0
109+
let url = split(a:lines[index], '\t')[1]
110+
call xolox#open#url(a:mirror . '/' . url)
111+
return 1
112+
endif
113+
endfunction
114+
115+
function! s:find_mirror() " {{{1
116+
if exists('g:pyref_mirror')
117+
return g:pyref_mirror
118+
else
119+
let local_mirror = '/usr/share/doc/python2.6/html'
120+
if isdirectory(local_mirror)
121+
return 'file://' . local_mirror
122+
else
123+
return 'http://docs.python.org'
124+
endif
125+
endif
126+
endfunction
127+
128+
function! s:find_index() " {{{1
129+
if exists('g:pyref_index')
130+
let index = g:pyref_index
131+
elseif xolox#is_windows()
132+
let index = '~/vimfiles/misc/pyref_index'
133+
else
134+
let index = '~/.vim/misc/pyref_index'
135+
endif
136+
let abspath = fnamemodify(index, ':p')
137+
if !filereadable(abspath)
138+
let msg = "%s: The index file doesn't exist or isn't readable! (%s)"
139+
call xolox#warning(msg, s:script, index)
140+
return
141+
endif
142+
return abspath
143+
endfunction
144+
145+
" vim: ts=2 sw=2 et nowrap

pyref.vim

Lines changed: 3 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -11,159 +11,14 @@
1111
" Don't source the plug-in when its already been loaded or &compatible is set.
1212
if &cp || exists('g:loaded_pyref')
1313
finish
14-
endif
15-
16-
" Configuration defaults. {{{1
17-
18-
" Use a script-local function to define the configuration defaults so that we
19-
" don't pollute Vim's global scope with temporary variables.
20-
21-
function! s:CheckOptions()
22-
if !exists('g:pyref_mapping')
23-
let g:pyref_mapping = '<F1>'
24-
endif
25-
if !exists('g:pyref_mirror')
26-
let local_mirror = '/usr/share/doc/python2.6/html'
27-
if isdirectory(local_mirror)
28-
let g:pyref_mirror = 'file://' . local_mirror
29-
else
30-
let g:pyref_mirror = 'http://docs.python.org'
31-
endif
32-
endif
33-
if !exists('g:pyref_index')
34-
if has('win32') || has('win64')
35-
let g:pyref_index = '~/vimfiles/misc/pyref_index'
36-
else
37-
let g:pyref_index = '~/.vim/misc/pyref_index'
38-
endif
39-
endif
40-
if !filereadable(fnamemodify(g:pyref_index, ':p'))
41-
let msg = "pyref.vim: The index file doesn't exist or isn't readable! (%s)"
42-
echoerr printf(msg, g:pyref_index)
43-
return 0 " Initialization failed.
44-
endif
45-
return 1 " Initialization successful.
46-
endfunction
47-
48-
if s:CheckOptions()
49-
" Don't reload the plug-in once its been successfully initialized.
50-
let g:loaded_pyref = 1
5114
else
52-
" Don't finish sourcing the script when there's no point.
53-
finish
15+
let g:loaded_pyref = 1
5416
endif
5517

56-
" Automatic command to define key-mapping. {{{1
18+
" Automatic command to enable plug-in for Python buffers only.
5719

5820
augroup PluginPyRef
59-
autocmd! FileType python call s:DefineMappings()
21+
autocmd! FileType python call xolox#pyref#enable()
6022
augroup END
6123

62-
function! s:DefineMappings() " {{{1
63-
let command = '%s <silent> <buffer> %s %s:call <Sid>PyRef()<CR>'
64-
" Always define the normal mode mapping.
65-
execute printf(command, 'nmap', g:pyref_mapping, '')
66-
" Don't create the insert mode mapping when "g:pyref_mapping" has been
67-
" changed to something like K because it'll conflict with regular input.
68-
if g:pyref_mapping =~ '^<[^>]\+>'
69-
execute printf(command, 'imap', g:pyref_mapping, '<C-O>')
70-
endif
71-
endfunction
72-
73-
function! s:PyRef() " {{{1
74-
75-
" Get the identifier under the cursor including any dots to match
76-
" identifiers like `os.path.join' instead of single words like `join'.
77-
try
78-
let isk_save = &isk
79-
let &isk = '@,48-57,_,192-255,.'
80-
let ident = expand('<cword>')
81-
finally
82-
let &isk = isk_save
83-
endtry
84-
85-
" Do something useful when there's nothing at the current position.
86-
if ident == ''
87-
call xolox#open#url(g:pyref_mirror . '/contents.html')
88-
return
89-
endif
90-
91-
" Escape any dots in the expression so it can be used as a pattern.
92-
let pattern = substitute(ident, '\.', '\\.', 'g')
93-
94-
" Search for an exact match of a module name or identifier in the index.
95-
let indexfile = fnamemodify(g:pyref_index, ':p')
96-
try
97-
let lines = readfile(indexfile)
98-
catch
99-
let lines = []
100-
echoerr "pyref.vim: Failed to read index file! (" . indexfile . ")"
101-
endtry
102-
if s:JumpToEntry(lines, '^\C\(module-\|exceptions\.\)\?' . pattern . '\t')
103-
return
104-
endif
105-
106-
" Search for a substring match on word boundaries.
107-
if s:JumpToEntry(lines, '\C\<' . pattern . '\>.*\t')
108-
return
109-
endif
110-
111-
" Try to match a method name of one of the standard Python types: strings,
112-
" lists, dictionaries and files (not exactly ideal but better than nothing).
113-
for [url, method_pattern] in s:object_methods
114-
let method = matchstr(ident, method_pattern)
115-
if method != ''
116-
if url =~ '%s'
117-
let url = printf(url, method)
118-
endif
119-
call xolox#open#url(g:pyref_mirror . '/' . url)
120-
return
121-
endif
122-
endfor
123-
124-
" Search for a substring match in the index.
125-
if s:JumpToEntry(lines, '\C' . pattern . '.*\t')
126-
return
127-
endif
128-
129-
" Split the expression on all dots and search for a progressively smaller
130-
" suffix to resolve object attributes like "self.parser.add_option" to
131-
" global identifiers like "optparse.OptionParser.add_option". This relies
132-
" on the uniqueness of the method names in the standard library.
133-
let parts = split(ident, '\.')
134-
while len(parts) > 1
135-
call remove(parts, 0)
136-
let pattern = '\C\<' . join(parts, '\.') . '$'
137-
if s:JumpToEntry(lines, pattern)
138-
return
139-
endif
140-
endwhile
141-
142-
" As a last resort, search all of http://docs.python.org/ using Google.
143-
call xolox#open#url('http://google.com/search?btnI&q=inurl:docs.python.org/+' . ident)
144-
145-
endfunction
146-
147-
" This list of lists contains [url_format, method_pattern] pairs that are used
148-
" to recognize calls to methods of objects that are one of Python's standard
149-
" types: strings, lists, dictionaries and file handles.
150-
let s:object_methods = [
151-
\ ['library/stdtypes.html#str.%s', '\C\.\@<=\(capitalize\|center\|count\|decode\|encode\|endswith\|expandtabs\|find\|format\|index\|isalnum\|isalpha\|isdigit\|islower\|isspace\|istitle\|isupper\|join\|ljust\|lower\|lstrip\|partition\|replace\|rfind\|rindex\|rjust\|rpartition\|rsplit\|rstrip\|split\|splitlines\|startswith\|strip\|swapcase\|title\|translate\|upper\|zfill\)$'],
152-
\ ['tutorial/datastructures.html#more-on-lists', '\C\.\@<=\(append\|count\|extend\|index\|insert\|pop\|remove\|reverse\|sort\)$'],
153-
\ ['library/stdtypes.html#dict.%s', '\C\.\@<=\(clear\|copy\|fromkeys\|get\|has_key\|items\|iteritems\|iterkeys\|itervalues\|keys\|pop\|popitem\|setdefault\|update\|values\)$'],
154-
\ ['library/stdtypes.html#file.%s', '\C\.\@<=\(close\|closed\|encoding\|errors\|fileno\|flush\|isatty\|mode\|name\|newlines\|next\|read\|readinto\|readline\|readlines\|seek\|softspace\|tell\|truncate\|write\|writelines\|xreadlines\)$']]
155-
156-
function! s:JumpToEntry(lines, pattern) " {{{1
157-
if &verbose
158-
echomsg "pyref.vim: Trying to match" string(a:pattern)
159-
endif
160-
let index = match(a:lines, a:pattern)
161-
if index >= 0
162-
let url = split(a:lines[index], '\t')[1]
163-
call xolox#open#url(g:pyref_mirror . '/' . url)
164-
return 1
165-
endif
166-
return 0
167-
endfunction
168-
16924
" vim: ts=2 sw=2 et nowrap

0 commit comments

Comments
 (0)