GUR.vim - Grand Unified Reference

こんなもん作ってみた。いかがでしょう。
とりあえず自分では使うつもりだけど、反響があれば vim.org にアップしてメンテナンスするつもり。

概要

統一されたインターフェイスで各種言語のリファレンスを閲覧できます。


対応言語

言語 実行コマンド
C/C++ man -a
perl perldoc -f
php w3m "http://www.php.net/ja/"
python pydoc
ruby refe
vim :help
その他 man -a

JavaScript とか対応したいけど、いいリファレンスが見つからず…

必要条件

  • Vim 7.0 以上
  • w3mPHP マニュアルを引く場合のみ)

インストール

~/.vim/plugin におくだけ。

使い方

キーワード(関数名)などの上にカーソルを置いて K を押します。


Ruby の場合、カーソル位置に応じて下記のキーワードを refe で検索します。

カーソル位置 検索するキーワード
メソッド名の上 そのメソッド
「String#split」の「split」の上 String#strip
「"hoge".split」の「split」の上 String#strip
「1234.nonzero?」の「nonzero?」の上 Integer#nonzero?
「[1,2,3].length」の「length」の上 Array#length
「1234」の上 Integer
「"hoge"」の上 String

Python もほぼ同様。
他の言語では普通の K と同様に 'iskeyword' に従いカーソル下のキーワードを抽出します。

結果ウィンドウでは , で検索履歴を前後に移動できます。

カスタマイズ

g:GUR_dict をいじることで対応言語を増やしたり、実行コマンドをカスタマイズできます。

ソース

GUR.vim

" Grand Unified Reference

if &cp || exists("g:loaded_GUR")
 finish
endif
if v:version < 700
 echoerr "GUR: this plugin requires Vim >= 7.0"
 finish
endif
let g:loaded_GUR = 1

let g:GUR_dict = {
      \ "c"            : ["r!man -a %s | col -b", 1],
      \ "cpp"          : ["r!man -a %s | col -b", 1],
      \ "perl"         : ["r!perldoc -T -t -f %s", 1],
      \ "php"          : ["r!w3m -dump http://www.php.net/ja/%s", 1],
      \ "python"       : ["r!pydoc %s", 1],
      \ "ruby"         : ["r!refe %s", 1],
      \ "default"      : ["r!man -a %s | col -b", 1],
      \ "default_count": ["r!man %d %s | col -b", 1]
      \ }

"nnoremap <silent> <unique> <Plug>GURGUReference :<C-u>call <SID>GUReference(&ft, "")<CR>
nnoremap K  :<C-u>call <SID>GUReference(&ft, "")<CR>
xnoremap K  <Esc>:<C-u>call <SID>GUReference(&ft, <SID>GetVisualRegionString())<CR>
command! -nargs=? GUR call <SID>GUReference(&ft, "<args>")

let s:separator = "==============================================="

function! s:GUReference(language, word)
  try
    let bufnr_save = bufnr("%")
    if exists("b:GUR_language")
      let language = b:GUR_language
    else
      let language = a:language
    endif
    let isk_save = &l:isk
    let pos_save = getpos(".")
    let re_number = '[0-9]\+$'

    " Extract the word.
    if a:word != ""
      let word = a:word
    else
      if language ==? "vim"
        normal! K
        return
      else
        let line = strpart(getline('.'), 0, col('.'))
        let re = '\(\S\+\)\.\k*$'
        let match = matchstr(line, re)
        let receiver = substitute(match, re, '\1', '')

        let syntax = synIDattr(synID(line("."),col("."),1),"name")
        if 0
          " dummy
        elseif language ==? "c" || language ==? "cpp"
          let word = expand("<cword>")
        elseif language ==? "html"
          let word = expand("<cword>")
        elseif language ==? "javascript"
          let word = expand("<cword>")
        elseif language ==? "perl"
          let word = expand("<cword>")
        elseif language ==? "php"
          let word = expand("<cword>")
        elseif language ==? "python"
          if syntax =~ 'String'
            let word = "__builtin__.str"
          elseif expand("<cword>") =~? re_number
            let word = "__builtin__.int"
          else
            if receiver =~ "[\"']$"
              let word = "__builtin__.str." . expand("<cword>")
            elseif receiver =~? re_number
              let word = "__builtin__.int." . expand("<cword>")
            elseif receiver =~ '\]$'
              let word = "__builtin__.list." . expand("<cword>")
            else
              let word = expand("<cword>")
            endif
          endif
        elseif language ==? "ruby"
          setlocal isk+=#,!,?
          if syntax =~ 'String'
            let word = "String"
          elseif expand("<cword>") =~? re_number
            let word = "Integer"
          else
            let sp = search('^==== \k\+ ====$', 'bWn', line(".") - 200 >= 0 ? line(".") - 200 : 0)
            if sp
              " in _GUR_ruby buffer
              let module_name = matchstr(getline(sp), '\k\+')
              let word = module_name . "#" . expand("<cword>")
            else
              if receiver =~ "[\"']$"
                let word = "String#" . expand("<cword>")
              elseif receiver =~? re_number
                let word = "Integer#" . expand("<cword>")
              elseif receiver =~ '\]$'
                let word = "Array#" . expand("<cword>")
              else
                let word = expand("<cword>")
              endif
            endif
          endif
        else
          let word = expand("<cword>")
        endif
      endif
    endif

    " Setup the result buffer.
    if !(has_key(g:GUR_dict, language) && g:GUR_dict[language][1] == 0)
      call s:SingletonBuffer("_GUR_" . language, 1)
      let b:GUR_language = language
      setlocal bt=nofile
      nnoremap <buffer> q :close<CR>
      nnoremap <silent> <C-p> :call <SID>GUR_Back()<CR>
      nnoremap <silent> <C-n> :call <SID>GUR_Forward()<CR>
      normal! G
      call append(line("$"), s:separator)
      normal! G
    endif

    let escaped_word = shellescape(word, '%#')
    let dont_move = 0

    " Do look-up command.
    if has_key(g:GUR_dict, language)
      let cmd = "sil " . printf(g:GUR_dict[language][0], escaped_word)
    else
      if v:count == 0
        let cmd = "sil " . printf(g:GUR_dict["default"][0], escaped_word)
      else
        let cmd = "sil " . printf(g:GUR_dict["default_count"][0], v:count, escaped_word)
      endif
    endif
    echomsg cmd
    exe cmd
  finally
    let &l:isk = isk_save
  endtry

  if !dont_move
    exe "normal! `[kz\<CR>"
  endif

endfunction

function! s:GUR_Back()
  call search('^'.s:separator, 'bW')
  exe "normal! z\<CR>"
endfunction

function! s:GUR_Forward()
  call search('^'.s:separator, 'W')
  exe "normal! z\<CR>"
endfunction

" If a buffer named 'name' exists, move the cursor on it.
" Else create one.
function! s:SingletonBuffer(name, split)
  let name = a:name
  if bufexists(name)
    let bufnr = s:GetBufferNumberByName(name, 0)
    let winlist = filter(range(1, winnr("$")), 'winbufnr(v:val)==' . bufnr)
    if empty(winlist)
      if a:split 
        split 
      endif
      exe "b " . bufnr
    else
      exe winlist[0] . "wincmd w"
    endif
  else
    if a:split
      exe "sil new " . escape(name, ' |')
    else
      exe "sil e " . escape(name, ' |')
    endif
  endif
endfunction

function! s:GetVisualRegionString()
  " save the register
  let old_reg=getreg('a')
  let old_regmode=getregtype('a')

  silent normal! gv"ay
  let selected=@a

  " restore it
  call setreg('a', old_reg, old_regmode)
  return selected
endfunction

function! s:GetBufferNumberByName(name, ignorecase)
  let buflist = filter(range(1, bufnr("$")), 'bufexists(v:val)')
  for bufnr in buflist
    if (a:ignorecase && bufname(bufnr) ==? a:name) || bufname(bufnr) ==# a:name
      return bufnr
    end
  endfor
  return -1
endfunction