" GNU Info browser " Copyright (c) 2001 rnd " $Id: info.vim,v 1.2 2001/06/17 00:01:40 rnd Exp $ let s:infoCmd = 'info --output=-' let s:dirPattern = '^\* [^:]*: \(([^)]*)\)' let s:menuPattern = '^\* \([^:]*\)::' let s:notePattern = '\*[Nn]ote \([^():]*\)\(::\|$\)' 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 strpart(file, 0, 1) != '(' let file = '('.file.')' endif if a:0 > 1 let node = a:2 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 = 'Info: '.a:file.a:node if bufexists(bufname) && a:0 < 2 if &ft == 'info' silent exe 'b!' escape(bufname, '\ ') else silent exe 'sb' escape(bufname, '\ ') endif else if &ft == 'info' silent exe 'e!' escape(bufname, '\ ') else silent exe 'new' escape(bufname, '\ ') endif setlocal modifiable noswapfile buftype=nofile bufhidden=hide setf info let cmd = s:infoCmd." '".a:file.a:node."' 2>/dev/null" exe "0r!".cmd 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 call s:InfoFirstLine() setlocal nomodifiable exe line 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:notePattern.'/' 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' 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 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() " 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 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 '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 echohl ErrorMsg | echo 'Info failed' | echohl None return endif let file = strpart(line, 6, node_offset-6) if file == 'dir' return 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: \(.*\)') 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 let successPattern = s:notePattern let file = b:info_file let node = substitute(link, successPattern, '\1', '') 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('Info: '.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