3 " Copyright (c) 2001, 2002 Slavik Gorbanyov <rnd@web-drive.ru>
6 " Redistribution and use, with or without modification, are permitted
7 " provided that the following conditions are met:
9 " 1. Redistributions must retain the above copyright notice, this list
10 " of conditions and the following disclaimer.
12 " 2. The name of the author may not be used to endorse or promote
13 " products derived from this script without specific prior written
16 " THIS SCRIPT IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 " OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 " WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 " DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 " INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 " (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 " SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 " HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 " STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25 " IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 " POSSIBILITY OF SUCH DAMAGE.
28 " $Id: info.vim,v 1.7 2002/11/30 21:59:05 rnd Exp $
30 let s:infoCmd = 'info --output=-'
32 let s:infoBufferName = '-Info- '
34 let s:infoBufferName = 'Info: '
36 let s:dirPattern = '^\* [^:]*: \(([^)]*)\)'
37 let s:menuPattern = '^\* \([^:]*\)::'
38 let s:notePattern = '\*[Nn]ote\%(\s\|$\)'
39 let s:indexPattern = '^\* [^:]*:\s*\([^.]*\)\.$'
40 let s:indexPatternHL = '^\* [^:]*:\s\+[^(]'
42 command! -nargs=* Info call s:Info(<f-args>)
50 let file = '('.file.')'
56 exe 'let node = node ." ". a:'.arg_idx
57 let arg_idx = arg_idx + 1
62 call s:InfoExec(file, node)
64 exe 'resize' &helpheight
68 fun! s:InfoExec(file, node, ...)
69 " a:0 != 0 means last node requested
75 if a:0 == 0 && exists('b:info_file')
76 let last_file = b:info_file
77 let last_node = b:info_node
78 let last_line = line('.')
80 let bufname = s:infoBufferName.a:file.a:node
81 if buflisted(bufname) && a:0 < 2
83 silent exe 'b!' escape(bufname, '\ ')
85 silent exe 'sb' escape(bufname, '\ ')
93 silent! exe command "+exe'setlocal''modifiable''noswapfile''buftype=nofile''bufhidden=delete'" escape(bufname, '\ ')
96 let cmd = s:infoCmd." '".a:file.a:node."' 2>/dev/null"
98 " handle shell redirection portable
100 let save_shell = &shell
103 let save_shellredir = &shellredir
106 let &shellredir = save_shellredir
108 let &shell = save_shell
111 call s:InfoBufferInit()
113 let b:info_file = a:file
114 let b:info_node = a:node
115 if exists('last_file')
116 let b:info_last_file = last_file
117 let b:info_last_node = last_node
118 let b:info_last_line = last_line
120 setlocal nomodifiable
124 echohl ErrorMsg | echo 'Info failed (node not found)' | echohl None
128 fun! s:InfoBufferInit()
130 " remove all insert mode abbreviations
133 if has("syntax") && exists("g:syntax_on")
135 syn match infoMenuTitle /^\* Menu:/hs=s+2,he=e-1
136 syn match infoTitle /^[A-Z][0-9A-Za-z `',/&]\{,43}\([a-z']\|[A-Z]\{2}\)$/
137 syn match infoTitle /^[-=*]\{,45}$/
138 syn match infoString /`[^`]*'/
139 exec 'syn match infoLink /'.s:menuPattern.'/hs=s+2'
140 exec 'syn match infoLink /'.s:dirPattern.'/hs=s+2'
141 exec 'syn match infoLink /'.s:indexPatternHL.'/hs=s+2,he=e-2'
142 syn region infoLink start=/\*[Nn]ote/ end=/\(::\|[.,]\)/
144 if !exists("g:did_info_syntax_inits")
145 let g:did_info_syntax_inits = 1
146 hi def link infoMenuTitle Title
147 hi def link infoTitle Comment
148 hi def link infoLink Directory
149 hi def link infoString String
153 " FIXME: <h> is move cursor left
154 noremap <buffer> h :call <SID>Help()<cr>
155 noremap <buffer> <CR> :call <SID>FollowLink()<cr>
156 noremap <buffer> <C-]> :call <SID>FollowLink()<cr>
157 " FIXME: <l> is move cursor right
158 " noremap <buffer> l :call <SID>LastNode()<cr>
159 noremap <buffer> ; :call <SID>LastNode()<cr>
160 noremap <buffer> <C-T> :call <SID>LastNode()<cr>
161 noremap <buffer> <C-S> /
162 " FIXME: <n> is go to next match
163 " noremap <buffer> n :call <SID>NextNode()<cr>
164 noremap <buffer> . :call <SID>NextNode()<cr>
165 noremap <buffer> p :call <SID>PrevNode()<cr>
166 noremap <buffer> > :call <SID>NextNode()<cr>
167 noremap <buffer> < :call <SID>PrevNode()<cr>
168 noremap <buffer> u :call <SID>UpNode()<cr>
169 noremap <buffer> t :call <SID>TopNode()<cr>
170 noremap <buffer> d :call <SID>DirNode()<cr>
171 noremap <buffer> s :call <SID>Search()<cr>
172 noremap <buffer> <TAB> :call <SID>NextRef()<cr>
173 nnoremap <buffer> q :q!<cr>
174 noremap <buffer> <Space> <C-F>
175 noremap <buffer> <Backspace> <C-B>
177 runtime info-local.vim
182 echo 'Info browser keys'
183 echo '-----------------'
185 echo '<Space> Scroll forward (page down).'
186 echo '<Backspace> Scroll backward (page up).'
187 echo '<Tab> Move cursor to next hyperlink within this node.'
188 echo '<Enter>,<C-]> Follow hyperlink under cursor.'
189 echo ';,<C-T> Return to last seen node.'
190 echo '.,> Move to the "next" node of this node.'
191 echo 'p,< Move to the "previous" node of this node.'
192 echo 'u Move "up" from this node.'
193 echo 'd Move to "directory" node.'
194 echo 't Move to the Top node.'
195 echo '<C-S> Search forward within current node only.'
196 echo 's Search forward through all nodes for a specified string.'
197 echo 'q Quit browser.'
199 echo 'Note: "," means "or"'
203 fun! s:InfoFirstLine()
204 let b:info_next_node = ''
205 let b:info_prev_node = ''
206 let b:info_up_node = ''
207 let line = getline(1)
208 let node_offset = matchend(line, '^File: [^, ]*')
212 let file = strpart(line, 6, node_offset-6)
216 " let file = substitute(file, '\(.*\)\.info\(\.gz\)\=', '\1', '')
217 let b:info_next_node = s:GetSubmatch( line, '\s\+Next: \([^,]*\),')
218 let b:info_prev_node = s:GetSubmatch( line, '\s\+Prev: \([^,]*\),')
219 let b:info_up_node = s:GetSubmatch( line, '\s\+Up: \(.*\)')
223 fun! s:GetSubmatch(string, pattern)
224 let matched = matchstr(a:string, a:pattern)
226 let matched = substitute(matched, a:pattern, '\1', '')
232 if exists('b:info_next_node') && b:info_next_node != ''
233 \ && match(b:info_next_node, '(.*)') == -1
234 call s:InfoExec(b:info_file, b:info_next_node)
237 echohl ErrorMsg | echo 'This is the last node' | echohl None
242 if b:info_node == 'Top'
243 if b:info_file == '(dir)'
244 echohl ErrorMsg | echo 'Already at top node' | echohl None
248 let node = b:info_node
250 let file = b:info_file
253 call s:InfoExec(file, node)
257 call s:InfoExec('(dir)', 'Top')
261 if !exists('b:info_last_node')
262 echohl ErrorMsg | echo 'No last node' | echohl None
265 call s:InfoExec(b:info_last_file, b:info_last_node, b:info_last_line)
269 let current_line = getline('.')
270 let link = matchstr(current_line, s:notePattern)
272 let link = matchstr(current_line, s:dirPattern)
274 let link = matchstr(current_line, s:menuPattern)
276 let link = matchstr(current_line, s:indexPattern)
278 echohl ErrorMsg | echo 'No link under cursor' | echohl None
281 let successPattern = s:indexPattern
283 let successPattern = s:menuPattern
285 let file = b:info_file
286 let node = substitute(link, successPattern, '\1', '')
288 let successPattern = s:dirPattern
289 let file = substitute(link, successPattern, '\1', '')
293 " we got a `*note' link.
294 let successPattern = s:notePattern
295 let current_line = current_line.' '.getline(line('.') + 1)
297 if exists('g:info_debug')
298 echo 'current_line:' current_line
301 let link_pattern = '\*[Nn]ote [^:.]\+: \([^.,]\+\)\%([,.]\|$\)'
302 let link = matchstr(current_line, link_pattern)
304 let link_pattern = '\*[Nn]ote \([^:]\+\)\%(::\)'
305 let link = matchstr(current_line, link_pattern)
307 echohl ErrorMsg | echo 'No link under cursor' | echohl None
311 let node = substitute(link, link_pattern, '\1', '')
312 let successPattern = link_pattern
314 let link_pattern = '^\(([^)]*)\)\=\s*\(.*\)'
315 let link = matchstr(node, link_pattern)
316 let file = substitute(link, link_pattern, '\1', '')
317 let node = substitute(link, link_pattern, '\2', '')
319 let file = b:info_file
325 let link_start_pos = match(current_line, successPattern)
326 let link_end_pos = matchend(current_line, successPattern)
327 let cursor_pos = col('.')
328 if cursor_pos <= link_start_pos || cursor_pos > link_end_pos
329 echohl ErrorMsg | echo 'No link under cursor' | echohl None
332 if exists('g:info_debug')
333 echo 'Link:' strpart(current_line, link_start_pos, link_end_pos - link_start_pos)
334 echo 'File:' file 'Node:' node
336 call s:InfoExec(file, node)
340 let link_pos = search('\('.s:dirPattern.'\|'.s:menuPattern.'\|'.s:notePattern.'\|'.s:indexPatternHL.'\)', 'w')
342 echohl ErrorMsg | echo 'No hyperlinks' | echohl None
349 if exists('b:info_prev_node') && b:info_prev_node != ''
350 \ && match(b:info_prev_node, '(.*)') == -1
351 call s:InfoExec(b:info_file, b:info_prev_node)
354 echohl ErrorMsg | echo 'This is the first node' | echohl None
359 if exists('b:info_up_node') && b:info_up_node != ''
360 \ && match(b:info_up_node, '(.*)') == -1
361 call s:InfoExec(b:info_file, b:info_up_node)
364 echohl ErrorMsg | echo 'This is the top node' | echohl None
368 " FIXME: there is no way to correctly abort searching.
369 " <CTRL-C> messes up the command window and stops at empty buffer.
371 if !exists('s:info_search_string')
372 let s:info_search_string = ''
374 let new_search = input('Search all nodes: ', s:info_search_string)
378 let s:info_search_string = new_search
379 let start_file = b:info_file
380 let start_node = b:info_node
381 let start_line = line('.')
382 while search(s:info_search_string, 'W') == 0
383 if !exists('b:info_next_node') || b:info_next_node == ''
384 \ || match(b:info_next_node, '(.*)') != -1
385 silent! exe 'bwipe' escape(s:infoBufferName.start_file.start_node, '\ ')
386 silent! call s:InfoExec(start_file, start_node, start_line, 'force')
387 echohl ErrorMsg | echo "\rSearch pattern not found" | echohl None
390 echo "\rSearching ... ".b:info_file b:info_next_node
391 let file = b:info_file
392 let node = b:info_next_node
394 silent call s:InfoExec(file, node, 2)