├── README └── plugin └── info.vim /README: -------------------------------------------------------------------------------- 1 | This is a mirror of http://www.vim.org/scripts/script.php?script_id=21 2 | 3 | requires vim 6.x and GNU info. adds new command `Info': without arguments it displays "directory" node, with single argument displays this file (eg `:Info libc'). with two arguments, first specifies file and second node to display (eg `:Info libc POSIX\ Threads' -- note space escaping). within browser, press h key to see available key shortcuts. 4 | -------------------------------------------------------------------------------- /plugin/info.vim: -------------------------------------------------------------------------------- 1 | " GNU Info browser 2 | " 3 | " Copyright (c) 2001, 2002 Slavik Gorbanyov 4 | " All rights reserved. 5 | " 6 | " Redistribution and use, with or without modification, are permitted 7 | " provided that the following conditions are met: 8 | " 9 | " 1. Redistributions must retain the above copyright notice, this list 10 | " of conditions and the following disclaimer. 11 | " 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 14 | " permission. 15 | " 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. 27 | " 28 | " $Id: info.vim,v 1.7 2002/11/30 21:59:05 rnd Exp $ 29 | 30 | let s:infoCmd = 'info --output=-' 31 | if has('win32') 32 | let s:infoBufferName = '-Info- ' 33 | else 34 | let s:infoBufferName = 'Info: ' 35 | endif 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\+[^(]' 41 | 42 | command! -nargs=* -complete=shellcmd Info call s:Info() 43 | 44 | fun! s:Info(...) 45 | let file = "(dir)" 46 | let node = "Top" 47 | if a:0 48 | let file = a:1 49 | if file[0] != '(' 50 | let file = '('.file.')' 51 | endif 52 | if a:0 > 1 53 | let node = a:2 54 | let arg_idx = 3 55 | while arg_idx <= a:0 56 | exe 'let node = node ." ". a:'.arg_idx 57 | let arg_idx = arg_idx + 1 58 | endwhile 59 | endif 60 | endif 61 | 62 | call s:InfoExec(file, node) 63 | if winheight(2) != -1 64 | exe 'resize' &helpheight 65 | endif 66 | endfun 67 | 68 | fun! s:InfoExec(file, node, ...) 69 | " a:0 != 0 means last node requested 70 | if a:0 71 | let line = a:1 72 | else 73 | let line = 1 74 | endif 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('.') 79 | endif 80 | let bufname = s:infoBufferName.a:file.a:node 81 | if buflisted(bufname) && a:0 < 2 82 | if &ft == 'info' 83 | silent exe 'b!' escape(bufname, '\ ') 84 | else 85 | silent exe 'sb' escape(bufname, '\ ') 86 | endif 87 | else 88 | if &ft == 'info' || bufname('%') == '' 89 | let command = 'e!' 90 | else 91 | let command = 'tabnew' 92 | endif 93 | silent! exe command escape(bufname, '\ ') 94 | setf info 95 | setlocal modifiable noswapfile buftype=nofile bufhidden=delete 96 | 97 | " Info buffers shouldn't be part of saved buffer list (in case 'shada' contains %) 98 | autocmd bufwinleave setlocal nobuflisted 99 | 100 | let cmd = s:infoCmd." '".a:file.a:node."' 2>/dev/null" 101 | 102 | " handle shell redirection portable 103 | if $OS !~? 'Windows' 104 | let save_shell = &shell 105 | set shell=/bin/sh 106 | endif 107 | let save_shellredir = &shellredir 108 | set shellredir=> 109 | exe "silent 0r!".cmd 110 | let &shellredir = save_shellredir 111 | if $OS !~? 'Windows' 112 | let &shell = save_shell 113 | endif 114 | 115 | call s:InfoBufferInit() 116 | endif 117 | let b:info_file = a:file 118 | let b:info_node = a:node 119 | if exists('last_file') 120 | let b:info_last_file = last_file 121 | let b:info_last_node = last_node 122 | let b:info_last_line = last_line 123 | endif 124 | setlocal nomodifiable 125 | if s:InfoFirstLine() 126 | exe line 127 | else 128 | echohl ErrorMsg | echo 'Info failed (node not found)' | echohl None 129 | endif 130 | endfun 131 | 132 | fun! s:InfoBufferInit() 133 | 134 | " remove all insert mode abbreviations 135 | iabc 136 | 137 | if has("syntax") && exists("g:syntax_on") 138 | syn case match 139 | syn match infoMenuTitle /^\* Menu:/hs=s+2,he=e-1 140 | syn match infoTitle /^[A-Z][0-9A-Za-z `',/&]\{,43}\([a-z']\|[A-Z]\{2}\)$/ 141 | syn match infoTitle /^[-=*]\{,45}$/ 142 | syn match infoString /`[^`]*'/ 143 | exec 'syn match infoLink /'.s:menuPattern.'/hs=s+2' 144 | exec 'syn match infoLink /'.s:dirPattern.'/hs=s+2' 145 | exec 'syn match infoLink /'.s:indexPatternHL.'/hs=s+2,he=e-2' 146 | syn region infoLink start=/\*[Nn]ote/ end=/\(::\|[.,]\)/ 147 | 148 | if !exists("g:did_info_syntax_inits") 149 | let g:did_info_syntax_inits = 1 150 | hi def link infoMenuTitle Title 151 | hi def link infoTitle Comment 152 | hi def link infoLink Directory 153 | hi def link infoString String 154 | endif 155 | endif 156 | 157 | " FIXME: is move cursor left 158 | noremap h :call Help() 159 | noremap :call FollowLink() 160 | noremap :call FollowLink() 161 | " FIXME: is move cursor right 162 | " noremap l :call LastNode() 163 | noremap ; :call LastNode() 164 | noremap :call LastNode() 165 | noremap / 166 | " FIXME: is go to next match 167 | " noremap n :call NextNode() 168 | noremap . :call NextNode() 169 | noremap p :call PrevNode() 170 | noremap > :call NextNode() 171 | noremap < :call PrevNode() 172 | noremap u :call UpNode() 173 | noremap t :call TopNode() 174 | noremap d :call DirNode() 175 | noremap s :call Search() 176 | noremap :call NextRef() 177 | nnoremap q :q! 178 | noremap 179 | noremap 180 | 181 | runtime info-local.vim 182 | endfun 183 | 184 | fun! s:Help() 185 | echohl Title 186 | echo 'Info browser keys' 187 | echo '-----------------' 188 | echohl None 189 | echo ' Scroll forward (page down).' 190 | echo ' Scroll backward (page up).' 191 | echo ' Move cursor to next hyperlink within this node.' 192 | echo ', Follow hyperlink under cursor.' 193 | echo ';, Return to last seen node.' 194 | echo '.,> Move to the "next" node of this node.' 195 | echo 'p,< Move to the "previous" node of this node.' 196 | echo 'u Move "up" from this node.' 197 | echo 'd Move to "directory" node.' 198 | echo 't Move to the Top node.' 199 | echo ' Search forward within current node only.' 200 | echo 's Search forward through all nodes for a specified string.' 201 | echo 'q Quit browser.' 202 | echohl SpecialKey 203 | echo 'Note: "," means "or"' 204 | echohl None 205 | endfun 206 | 207 | fun! s:InfoFirstLine() 208 | let b:info_next_node = '' 209 | let b:info_prev_node = '' 210 | let b:info_up_node = '' 211 | let line = getline(1) 212 | let node_offset = matchend(line, '^File: [^, ]*') 213 | if node_offset == -1 214 | return 0 215 | endif 216 | let file = strpart(line, 6, node_offset-6) 217 | if file == 'dir' 218 | return 1 219 | endif 220 | " let file = substitute(file, '\(.*\)\.info\(\.gz\)\=', '\1', '') 221 | let b:info_next_node = s:GetSubmatch( line, '\s\+Next: \([^,]*\),') 222 | let b:info_prev_node = s:GetSubmatch( line, '\s\+Prev: \([^,]*\),') 223 | let b:info_up_node = s:GetSubmatch( line, '\s\+Up: \(.*\)') 224 | return 1 225 | endfun 226 | 227 | fun! s:GetSubmatch(string, pattern) 228 | let matched = matchstr(a:string, a:pattern) 229 | if matched != '' 230 | let matched = substitute(matched, a:pattern, '\1', '') 231 | endif 232 | return matched 233 | endfun 234 | 235 | fun! s:NextNode() 236 | if exists('b:info_next_node') && b:info_next_node != '' 237 | \ && match(b:info_next_node, '(.*)') == -1 238 | call s:InfoExec(b:info_file, b:info_next_node) 239 | return 1 240 | else 241 | echohl ErrorMsg | echo 'This is the last node' | echohl None 242 | endif 243 | endfun 244 | 245 | fun! s:TopNode() 246 | if b:info_node == 'Top' 247 | if b:info_file == '(dir)' 248 | echohl ErrorMsg | echo 'Already at top node' | echohl None 249 | return 250 | endif 251 | let file = '(dir)' 252 | let node = b:info_node 253 | else 254 | let file = b:info_file 255 | let node = 'Top' 256 | endif 257 | call s:InfoExec(file, node) 258 | endfun 259 | 260 | fun! s:DirNode() 261 | call s:InfoExec('(dir)', 'Top') 262 | endfun 263 | 264 | fun! s:LastNode() 265 | if !exists('b:info_last_node') 266 | echohl ErrorMsg | echo 'No last node' | echohl None 267 | return 268 | endif 269 | call s:InfoExec(b:info_last_file, b:info_last_node, b:info_last_line) 270 | endfun 271 | 272 | fun! s:FollowLink() 273 | let current_line = getline('.') 274 | let link = matchstr(current_line, s:notePattern) 275 | if link == '' 276 | let link = matchstr(current_line, s:dirPattern) 277 | if link == '' 278 | let link = matchstr(current_line, s:menuPattern) 279 | if link == '' 280 | let link = matchstr(current_line, s:indexPattern) 281 | if link == '' 282 | echohl ErrorMsg | echo 'No link under cursor' | echohl None 283 | return 284 | endif 285 | let successPattern = s:indexPattern 286 | else 287 | let successPattern = s:menuPattern 288 | endif 289 | let file = b:info_file 290 | let node = substitute(link, successPattern, '\1', '') 291 | else 292 | let successPattern = s:dirPattern 293 | let file = substitute(link, successPattern, '\1', '') 294 | let node = 'Top' 295 | endif 296 | else 297 | " we got a `*note' link. 298 | let successPattern = s:notePattern 299 | let current_line = current_line.' '.getline(line('.') + 1) 300 | 301 | if exists('g:info_debug') 302 | echo 'current_line:' current_line 303 | endif 304 | 305 | let link_pattern = '\*[Nn]ote [^:.]\+: \([^.,]\+\)\%([,.]\|$\)' 306 | let link = matchstr(current_line, link_pattern) 307 | if link == '' 308 | let link_pattern = '\*[Nn]ote \([^:]\+\)\%(::\)' 309 | let link = matchstr(current_line, link_pattern) 310 | if link == '' 311 | echohl ErrorMsg | echo 'No link under cursor' | echohl None 312 | return 313 | endif 314 | endif 315 | let node = substitute(link, link_pattern, '\1', '') 316 | let successPattern = link_pattern 317 | 318 | let link_pattern = '^\(([^)]*)\)\=\s*\(.*\)' 319 | let link = matchstr(node, link_pattern) 320 | let file = substitute(link, link_pattern, '\1', '') 321 | let node = substitute(link, link_pattern, '\2', '') 322 | if file == '' 323 | let file = b:info_file 324 | endif 325 | if node == '' 326 | let node = 'Top' 327 | endif 328 | endif 329 | let link_start_pos = match(current_line, successPattern) 330 | let link_end_pos = matchend(current_line, successPattern) 331 | let cursor_pos = col('.') 332 | if cursor_pos <= link_start_pos || cursor_pos > link_end_pos 333 | echohl ErrorMsg | echo 'No link under cursor' | echohl None 334 | return 335 | endif 336 | if exists('g:info_debug') 337 | echo 'Link:' strpart(current_line, link_start_pos, link_end_pos - link_start_pos) 338 | echo 'File:' file 'Node:' node 339 | endif 340 | call s:InfoExec(file, node) 341 | endfun 342 | 343 | fun! s:NextRef() 344 | let link_pos = search('\('.s:dirPattern.'\|'.s:menuPattern.'\|'.s:notePattern.'\|'.s:indexPatternHL.'\)', 'w') 345 | if link_pos == 0 346 | echohl ErrorMsg | echo 'No hyperlinks' | echohl None 347 | else 348 | echo 349 | endif 350 | endfun 351 | 352 | fun! s:PrevNode() 353 | if exists('b:info_prev_node') && b:info_prev_node != '' 354 | \ && match(b:info_prev_node, '(.*)') == -1 355 | call s:InfoExec(b:info_file, b:info_prev_node) 356 | return 1 357 | else 358 | echohl ErrorMsg | echo 'This is the first node' | echohl None 359 | endif 360 | endfun 361 | 362 | fun! s:UpNode() 363 | if exists('b:info_up_node') && b:info_up_node != '' 364 | \ && match(b:info_up_node, '(.*)') == -1 365 | call s:InfoExec(b:info_file, b:info_up_node) 366 | return 1 367 | else 368 | echohl ErrorMsg | echo 'This is the top node' | echohl None 369 | endif 370 | endfun 371 | 372 | " FIXME: there is no way to correctly abort searching. 373 | " messes up the command window and stops at empty buffer. 374 | fun! s:Search() 375 | if !exists('s:info_search_string') 376 | let s:info_search_string = '' 377 | endif 378 | let new_search = input('Search all nodes: ', s:info_search_string) 379 | if new_search == '' 380 | return 381 | endif 382 | let s:info_search_string = new_search 383 | let start_file = b:info_file 384 | let start_node = b:info_node 385 | let start_line = line('.') 386 | while search(s:info_search_string, 'W') == 0 387 | if !exists('b:info_next_node') || b:info_next_node == '' 388 | \ || match(b:info_next_node, '(.*)') != -1 389 | silent! exe 'bwipe' escape(s:infoBufferName.start_file.start_node, '\ ') 390 | silent! call s:InfoExec(start_file, start_node, start_line, 'force') 391 | echohl ErrorMsg | echo "\rSearch pattern not found" | echohl None 392 | return 393 | endif 394 | echo "\rSearching ... ".b:info_file b:info_next_node 395 | let file = b:info_file 396 | let node = b:info_next_node 397 | silent bwipe 398 | silent call s:InfoExec(file, node, 2) 399 | endwhile 400 | endfun 401 | --------------------------------------------------------------------------------