basyura's blog

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

neco-tweetvim に neocomplete 用の source を追加

yomi322 さんの neco-tweetvim に neocomplete 版の source を追加しました。
@ の後で screen name の補完、# の後で tag の補完をします。

neocomplete/sources/tweetvim_name_complete.vim

let s:source = {
\   'name'      : 'tweetvim_name_complete',
\   'rank'      : 200,
\   'kind'      : 'manual',
\   'filetypes' : { 'tweetvim_say' : 1 },
\ }


function! s:source.get_complete_position(context)
  return neocomplete#tweetvim#get_complete_position(
           \ a:context, '@', g:neocomplete#auto_completion_start_length)
endfunction


function! s:source.gather_candidates(context)
  return neocomplete#tweetvim#gather_candidates(a:context, 'screen_name')
endfunction


function! neocomplete#sources#tweetvim_name_complete#define()
  return s:source
endfunction

継承をいい感じで使えるといいんですけど、案が浮かばなかったので screen_name と tag 共通のものを別にくくり出す方式で。
対象の source と、補完位置 (get_complete_position) と、候補を返す (gather_candidates) というのを実装すれば OK。

neocomplete/tweetvim.vim

screen_name と tag の共通部分。

function! neocomplete#tweetvim#get_complete_position(context, char, start)
  let col = col('.')
  let pos = 0
  while 1
    let idx = stridx(a:context.input, a:char, pos + 1)
    if idx == -1 || idx >= col
      break
    endif
    let pos = idx
  endwhile

  let pos  = matchend(a:context.input, a:char, pos)
  let text = eval("a:context.input[" . string(pos) . ":" . string(pos + a:start) . "]")
  let text = substitute(text, ' ', '', 'g')
  let pos  = len(text) < a:start ? -1 : pos

  return pos
endfunction


function! neocomplete#tweetvim#gather_candidates(context, key)
  let cache_key = 'keywords_' . a:key
  if !exists('s:' . cache_key)
    let s:[cache_key] = map(tweetvim#cache#get(a:key),
                        \ "{ 'word' : v:val, 'menu' : '[tweetvim]' }")
  endif
  "return filter(copy(get(s:, cache_key)), 'v:val.word =~ "' . a:context.complete_str . '"')
  return copy(get(s:, cache_key))
endfunction

@ or # より後ろにカーソルがあって、一定の長さ (g:neocomplete#auto_completion_start_length) 以上なら補完候補を返す (返しているつもり)。
source.kind が manual の場合は入力のたびに get_complete_position が呼ばれるって理解でいいんだろうか。このへんがよく分からんかった。get_complete_position が -1 以外を返すまでは呼ばれるって動きだろうか。
あとは、plugin 側で絞るべきなのか neocomplete に絞ってもらうのか、どちらが良いのかよく分からなかったので、ユーザ定義の matcher で絞ってもらうように、plugin 側では特に絞らずに返却。文脈にそって絞って欲しい場合は plugin 側で filter すべきなんだろうな、きっと。
まぁ、if_lua 化によって多少の無茶は許されるはず・・・と(ちがう)。

まとめ

わざわざ plugin を作らなくても、候補の dictionary があるのなら

if !exists('g:neocomplete#sources#dictionary#dictionaries')
  let g:neocomplete#sources#dictionary#dictionaries = {}
endif
let g:neocomplete#sources#dictionary#dictionaries.tweetvim_say =  $HOME . '/.tweetvim/screen_name'

とするだけで良いのであった。