NeoBundle管理下で定義されたコマンドを抽出してメニューに表示する

![スクリーンショット 2013-10-27 15.00.49.png](https://qiita-image-store.s3.amazonaws.com/0/29365/aeafcbec-7454-e100-89f9-5bd6affa0e6f.png)


NeoBundleとかで管理されたコマンドをメニューに表示させたらチートシートみたいで便利なんじゃないかと思いましたがそんなことはなかったです。

```vim
let s:DEFAULT_GROUP = '* default *'
let s:TOP_MENU_LABEL = 'AutoMenu'



function! s:isIgnoreVerbosePattern(verbose)
  " TODO: depending on user locale
  let verboseIgnorePatterns = [
    \ '^$',
    \ '^\s*名前',
  \ ]

  for pattern in verboseIgnorePatterns
    if a:verbose =~ pattern
      return 1
    endif
  endfor

  return 0
endfunction



function! s:verboseOf(command)
  let v = ''
  redir => v
  execute join(['silent', 'verbose', a:command], ' ')
  redir END

  let filteredTokens = []
  for line in split(v, '\n')
    if !s:isIgnoreVerbosePattern(line)
      call add(filteredTokens, line)
    endif
  endfor

  let verboses = []

  let len = len(filteredTokens)
  for i in range(0, len - 1 - 1)
    let current  = filteredTokens[i + 0]
    let next = filteredTokens[i + 1]

    " TODO: is this depending on our language?
    let setAtToken = matchlist(next, '^\s\+Last set from\s\+\(.\+\)$')
    if (len(setAtToken)) <= 0 || (setAtToken[1] == '')
      continue
    end

    let setAt = setAtToken[1]

    let work = {'st': current, 'setAt': setAt}
    call add(verboses, work)
  endfor

  return verboses
endfunction



function! s:groupFromPath(path, filter)
  let matched = matchlist(a:path, a:filter)
  if len(matched) >= 1
    if matched[1] == ''
      return s:DEFAULT_GROUP
    else
      return substitute(matched[1], '\.', '\\\.', 'g')
    endif
  else
    return ''
  endif
endfunction


function! s:parseCmdVerbose(verbose)
  let token = matchlist(a:verbose, '^.\{4}\(\S\+\)\s\+\(\S\)')
  if len(token) <= 0
    return {}
  else
    if token[2] == 'b'
      echo a:verbose
    endif
    return {'cmd': token[1], 'nargs': token[2]}
  endif
endfunction



function! s:parseMapVerbose(verbose)
  let token = matchlist(a:verbose, '^\(\S\?\)\s\+\(\S\+\)')
  if len(token) <= 0
    return {}
  else
    return {'mode': token[1], 'map': token[2]}
  endif
endfunction



function! Feedkeys(keybinding)
  let keybinding = a:keybinding
  let pattern = '<.\{-}>'

  " subtitute <C-x> into \<C-x>
  " TODO: not work if nested blanket were appeared
  let i = 0
  while i < 10
    let i += 1
    let start = match(keybinding, pattern)
    let end = matchend(keybinding, pattern)
    if start == -1
      break
    endif
    let keybinding = substitute(keybinding, keybinding[start : end], eval('"' . '\' . keybinding[start : end] . '"'), 'g')
  endwhile
endfunction



function! s:filterByPath(verboses, filterPath)
  let res = []
  for verbose in a:verboses
    let group = s:groupFromPath(verbose['setAt'], a:filterPath)
    if group == ''
      continue
    endif
    let work = verbose
    let work['group'] = group
    call add(res, work)
  endfor

  return res
endfunction



function! AutoMenu_CreateFromPath(filterPath)
  let mapVerboses = s:filterByPath(s:verboseOf('map'), a:filterPath)
  let cmdVerboses = s:filterByPath(s:verboseOf('command'), a:filterPath)

  " clear menu
  execute join(['silent!', 'unmenu', s:TOP_MENU_LABEL], ' ')

  for cmdVerbose in cmdVerboses
    let moreCmdVerbose = s:parseCmdVerbose(cmdVerbose['st'])
    if moreCmdVerbose == {}
      continue
    endif
    let cmdVerbose['cmd'] = moreCmdVerbose['cmd']
    let cmdVerbose['nargs'] = moreCmdVerbose['nargs']

    let menuExp = [s:TOP_MENU_LABEL, cmdVerbose['group'], cmdVerbose['cmd']]
    execute join(['menu', join(menuExp, '.'), ":" . cmdVerbose['cmd'] . '<CR>'], ' ')
  endfor
endfunc
```
上記を適当に保存、soureして。


```vim
AutoMenu_CreateFromPath('^\~\/dotfiles\/\.vim\/bundle\/\([^\/]\+\)/')
```


こんな感じで実行すると引数渡ししたディレクトリ配下からコマンドを収集します。
メニューのグループ名は正規表現でカッコで囲ったグループ1番目から拾います。