basyura's blog

あしたになったらほんきだす。

勉強がてら unite の source を作ってみた

諸事情によりプロジェクトの開発サーバが起動するまで時間がかかりそうだったので、unite の source 作りに取り組んでみた。
自分のプロジェクトは資源を CVS で管理し、各ユーザごとにタグ付けをしている。フォルダ構成としては、

root_directory
  ├ user A
  │ ├ ContextRoot
  │ │ ├ scripts
  │ │ └ css
  │ └ src  
  │   ・・・
  │    ├ libA
  │    └ libC
  ├ user B
  │ ├ ContextRoot
  │ │ ├ script
  │ │ └ css
  │ └ src  
  │   ・・・
  │    ├ libA
  │    └ libC
  ├ user C
 ・・・
  ├ user D

といった感じで、root_directory に各ユーザごとの資源をチェックアウトして同じ構成で配置している。
このユーザ選択と、各下位フォルダへの移動を楽にできないかとやってみた。

if !exists('g:unite_project_root')
  let g:unite_project_root = '/'
endif
if !exists('g:unite_project_sub_dirs')
  let g:unite_project_sub_dirs = []
endif

let s:unite_source = {}
let s:unite_source.name = 'project'
let s:unite_source.default_action = {'cdable' : 'cd'}
let s:unite_source.action_table = {'src' : 'cd'}

function! s:unite_source.gather_candidates(args, context)
  let list = map(split(globpath(g:unite_project_root ,'*'),'\n') ,
        \ '[fnamemodify(v:val , ":t") , v:val]')
  return map(list, '{
        \ "abbr"   : v:val[0],
        \ "word"   : v:val[1],
        \ "source" : "project",
        \ "kind"   : "cdable",
        \ "action__directory": v:val[1],
        \ }')
endfunction

" action table
let s:action_table = {}
" cd src
let s:action_table.src = {'description' : 'cd src'}
function! s:action_table.src.func(candidate)
  execute 'cd ' . a:candidate.word . '/src'
endfunction
" cd ContextRoot
let s:action_table.context_root = {'description' : 'cd ContextRoot'}
function! s:action_table.context_root.func(candidate)
  execute 'cd ' . a:candidate.word . '/ContextRoot'
endfunction
" cd scripts
let s:action_table.scripts = {'description' : 'cd scripts'}
function! s:action_table.scripts.func(candidate)
  execute 'cd ' . a:candidate.word . '/ContextRoot/scripts'
endfunction
" cd jsp
let s:action_table.jsp = {'description' : 'cd jsp'}
function! s:action_table.jsp.func(candidate)
  execute 'cd ' . a:candidate.word . '/ContextRoot/jsp'
endfunction

" sub dir
for s:v in g:unite_project_sub_dirs
  let s:action_table[s:v['action']] = {'description' : s:v['description']}
  function! s:action_table[s:v['action']].func(candidate, ...) dict
    if a:0 != 0
      let self.path = a:1
      return self.path
    else
      execute 'cd '. a:candidate.word . '/' . self.path
    endif
  endfunction
  call s:action_table[s:v['action']].func(0, s:v['path'])
endfor

let s:unite_source.action_table.cdable = s:action_table

" source
function! unite#sources#project#define()
  return s:unite_source
endfunction

source 名は project なんだけど、一般的に想像されるような機能は全くない・・・。

Unite project

で、user A、user B・・・が候補として上がり、選択(Enter)するとそこへ cd する。選択(Enter)せずに Tab すると、各下位フォルダの候補(src,ContextRoot,scripts,jsp)が上がり、選択(Enter)するとそこへ cd する。それだけ。
外部設定(g:unite_project_sub_dirs)で、cd 先を設定できるようにもしてみた("sub dir のあたり)。本当はクロージャ(?)でカッコよく書きたかったけど、どうもうまくいかない(理解出来ない)ので無理やり dict を使って実装してみた。
頑張ってみたけど、unite の bookmark や、file で substitute 定義とか、vimshell で alias 切ったりした方が何倍もいい・・・な・・・orz
これとは別に

nnoremap <Leader><Leader> :Unite "Unite の後ろは半角空白がある

この定義が最近のお気に入り。leader は m なので、 mm の後にソース名を入力ってのがなかなかイイ。