" GNU Info browser " " Copyright (c) 2001, 2002 Slavik Gorbanyov " All rights reserved. " " Redistribution and use, with or without modification, are permitted " provided that the following conditions are met: " " 1. Redistributions must retain the above copyright notice, this list " of conditions and the following disclaimer. " " 2. The name of the author may not be used to endorse or promote " products derived from this script without specific prior written " permission. " " THIS SCRIPT IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. " " $Id: info.vim,v 1.7 2002/11/30 21:59:05 rnd Exp $ let s:infoCmd = 'info --output=-' if has('win32') let s:infoBufferName = '-Info- ' else let s:infoBufferName = 'Info: ' endif let s:dirPattern = '^\* [^:]*: \(([^)]*)\)' let s:menuPattern = '^\* \([^:]*\)::' let s:notePattern = '\*[Nn]ote\%(\s\|$\)' let s:indexPattern = '^\* [^:]*:\s*\([^.]*\)\.$' let s:indexPatternHL = '^\* [^:]*:\s\+[^(]' command! -nargs=* Info call s:Info() fun! s:Info(...) let file = "(dir)" let node = "Top" if a:0 let file = a:1 if file[0] != '(' let file = '('.file.')' endif if a:0 > 1 let node = a:2 let arg_idx = 3 while arg_idx <= a:0 exe 'let node = node ." ". a:'.arg_idx let arg_idx = arg_idx + 1 endwhile endif endif call s:InfoExec(file, node) if winheight(2) != -1 exe 'resize' &helpheight endif endfun fun! s:InfoExec(file, node, ...) " a:0 != 0 means last node requested if a:0 let line = a:1 else let line = 1 endif if a:0 == 0 && exists('b:info_file') let last_file = b:info_file let last_node = b:info_node let last_line = line('.') endif let bufname = s:infoBufferName.a:file.a:node if buflisted(bufname) && a:0 < 2 if &ft == 'info' silent exe 'b!' escape(bufname, '\ ') else silent exe 'sb' escape(bufname, '\ ') endif else if &ft == 'info' let command = 'e!' else let command = 'new' endif silent! exe command "+exe'setlocal''modifiable''noswapfile''buftype=nofile''bufhidden=delete'" escape(bufname, '\ ') setf info let cmd = s:infoCmd." '".a:file.a:node."' 2>/dev/null" " handle shell redirection portable if $OS !~? 'Windows' let save_shell = &shell set shell=/bin/sh endif let save_shellredir = &shellredir set shellredir=> exe "silent 0r!".cmd let &shellredir = save_shellredir if $OS !~? 'Windows' let &shell = save_shell endif call s:InfoBufferInit() endif let b:info_file = a:file let b:info_node = a:node if exists('last_file') let b:info_last_file = last_file let b:info_last_node = last_node let b:info_last_line = last_line endif setlocal nomodifiable if s:InfoFirstLine() exe line else echohl ErrorMsg | echo 'Info failed (node not found)' | echohl None endif endfun fun! s:InfoBufferInit() " remove all insert mode abbreviations iabc if has("syntax") && exists("g:syntax_on") syn case match syn match infoMenuTitle /^\* Menu:/hs=s+2,he=e-1 syn match infoTitle /^[A-Z][0-9A-Za-z `',/&]\{,43}\([a-z']\|[A-Z]\{2}\)$/ syn match infoTitle /^[-=*]\{,45}$/ syn match infoString /`[^`]*'/ exec 'syn match infoLink /'.s:menuPattern.'/hs=s+2' exec 'syn match infoLink /'.s:dirPattern.'/hs=s+2' exec 'syn match infoLink /'.s:indexPatternHL.'/hs=s+2,he=e-2' syn region infoLink start=/\*[Nn]ote/ end=/\(::\|[.,]\)/ if !exists("g:did_info_syntax_inits") let g:did_info_syntax_inits = 1 hi def link infoMenuTitle Title hi def link infoTitle Comment hi def link infoLink Directory hi def link infoString String endif endif " FIXME: is move cursor left noremap h :call Help() noremap :call FollowLink() noremap :call FollowLink() " FIXME: is move cursor right " noremap l :call LastNode() noremap ; :call LastNode() noremap :call LastNode() noremap / " FIXME: is go to next match " noremap n :call NextNode() noremap . :call NextNode() noremap p :call PrevNode() noremap > :call NextNode() noremap < :call PrevNode() noremap u :call UpNode() noremap t :call TopNode() noremap d :call DirNode() noremap s :call Search() noremap :call NextRef() nnoremap q :q! noremap noremap runtime info-local.vim endfun fun! s:Help() echohl Title echo 'Info browser keys' echo '-----------------' echohl None echo ' Scroll forward (page down).' echo ' Scroll backward (page up).' echo ' Move cursor to next hyperlink within this node.' echo ', Follow hyperlink under cursor.' echo ';, Return to last seen node.' echo '.,> Move to the "next" node of this node.' echo 'p,< Move to the "previous" node of this node.' echo 'u Move "up" from this node.' echo 'd Move to "directory" node.' echo 't Move to the Top node.' echo ' Search forward within current node only.' echo 's Search forward through all nodes for a specified string.' echo 'q Quit browser.' echohl SpecialKey echo 'Note: "," means "or"' echohl None endfun fun! s:InfoFirstLine() let b:info_next_node = '' let b:info_prev_node = '' let b:info_up_node = '' let line = getline(1) let node_offset = matchend(line, '^File: [^, ]*') if node_offset == -1 return 0 endif let file = strpart(line, 6, node_offset-6) if file == 'dir' return 1 endif " let file = substitute(file, '\(.*\)\.info\(\.gz\)\=', '\1', '') let b:info_next_node = s:GetSubmatch( line, '\s\+Next: \([^,]*\),') let b:info_prev_node = s:GetSubmatch( line, '\s\+Prev: \([^,]*\),') let b:info_up_node = s:GetSubmatch( line, '\s\+Up: \(.*\)') return 1 endfun fun! s:GetSubmatch(string, pattern) let matched = matchstr(a:string, a:pattern) if matched != '' let matched = substitute(matched, a:pattern, '\1', '') endif return matched endfun fun! s:NextNode() if exists('b:info_next_node') && b:info_next_node != '' \ && match(b:info_next_node, '(.*)') == -1 call s:InfoExec(b:info_file, b:info_next_node) return 1 else echohl ErrorMsg | echo 'This is the last node' | echohl None endif endfun fun! s:TopNode() if b:info_node == 'Top' if b:info_file == '(dir)' echohl ErrorMsg | echo 'Already at top node' | echohl None return endif let file = '(dir)' let node = b:info_node else let file = b:info_file let node = 'Top' endif call s:InfoExec(file, node) endfun fun! s:DirNode() call s:InfoExec('(dir)', 'Top') endfun fun! s:LastNode() if !exists('b:info_last_node') echohl ErrorMsg | echo 'No last node' | echohl None return endif call s:InfoExec(b:info_last_file, b:info_last_node, b:info_last_line) endfun fun! s:FollowLink() let current_line = getline('.') let link = matchstr(current_line, s:notePattern) if link == '' let link = matchstr(current_line, s:dirPattern) if link == '' let link = matchstr(current_line, s:menuPattern) if link == '' let link = matchstr(current_line, s:indexPattern) if link == '' echohl ErrorMsg | echo 'No link under cursor' | echohl None return endif let successPattern = s:indexPattern else let successPattern = s:menuPattern endif let file = b:info_file let node = substitute(link, successPattern, '\1', '') else let successPattern = s:dirPattern let file = substitute(link, successPattern, '\1', '') let node = 'Top' endif else " we got a `*note' link. let successPattern = s:notePattern let current_line = current_line.' '.getline(line('.') + 1) if exists('g:info_debug') echo 'current_line:' current_line endif let link_pattern = '\*[Nn]ote [^:.]\+: \([^.,]\+\)\%([,.]\|$\)' let link = matchstr(current_line, link_pattern) if link == '' let link_pattern = '\*[Nn]ote \([^:]\+\)\%(::\)' let link = matchstr(current_line, link_pattern) if link == '' echohl ErrorMsg | echo 'No link under cursor' | echohl None return endif endif let node = substitute(link, link_pattern, '\1', '') let successPattern = link_pattern let link_pattern = '^\(([^)]*)\)\=\s*\(.*\)' let link = matchstr(node, link_pattern) let file = substitute(link, link_pattern, '\1', '') let node = substitute(link, link_pattern, '\2', '') if file == '' let file = b:info_file endif if node == '' let node = 'Top' endif endif let link_start_pos = match(current_line, successPattern) let link_end_pos = matchend(current_line, successPattern) let cursor_pos = col('.') if cursor_pos <= link_start_pos || cursor_pos > link_end_pos echohl ErrorMsg | echo 'No link under cursor' | echohl None return endif if exists('g:info_debug') echo 'Link:' strpart(current_line, link_start_pos, link_end_pos - link_start_pos) echo 'File:' file 'Node:' node endif call s:InfoExec(file, node) endfun fun! s:NextRef() let link_pos = search('\('.s:dirPattern.'\|'.s:menuPattern.'\|'.s:notePattern.'\|'.s:indexPatternHL.'\)', 'w') if link_pos == 0 echohl ErrorMsg | echo 'No hyperlinks' | echohl None else echo endif endfun fun! s:PrevNode() if exists('b:info_prev_node') && b:info_prev_node != '' \ && match(b:info_prev_node, '(.*)') == -1 call s:InfoExec(b:info_file, b:info_prev_node) return 1 else echohl ErrorMsg | echo 'This is the first node' | echohl None endif endfun fun! s:UpNode() if exists('b:info_up_node') && b:info_up_node != '' \ && match(b:info_up_node, '(.*)') == -1 call s:InfoExec(b:info_file, b:info_up_node) return 1 else echohl ErrorMsg | echo 'This is the top node' | echohl None endif endfun " FIXME: there is no way to correctly abort searching. " messes up the command window and stops at empty buffer. fun! s:Search() if !exists('s:info_search_string') let s:info_search_string = '' endif let new_search = input('Search all nodes: ', s:info_search_string) if new_search == '' return endif let s:info_search_string = new_search let start_file = b:info_file let start_node = b:info_node let start_line = line('.') while search(s:info_search_string, 'W') == 0 if !exists('b:info_next_node') || b:info_next_node == '' \ || match(b:info_next_node, '(.*)') != -1 silent! exe 'bwipe' escape(s:infoBufferName.start_file.start_node, '\ ') silent! call s:InfoExec(start_file, start_node, start_line, 'force') echohl ErrorMsg | echo "\rSearch pattern not found" | echohl None return endif echo "\rSearching ... ".b:info_file b:info_next_node let file = b:info_file let node = b:info_next_node silent bwipe silent call s:InfoExec(file, node, 2) endwhile endfun