はわわーっ

はわわわわっ

vimshellからtweetしてみよう

あけましておめでとうございます。
この記事はVim Advent Calendar 2012 : ATND 44日目の記事になります。
43日目はフォルダ間の移動を高速に「どこでもどあゔぃむー」 - honbin logでした。

この記事ではvimshellからツイットするtweetコマンドを作ってみます。
完成版はhttps://github.com/yomi322/vimshell-tweetにあります。

vimshellのコマンドを作るには autoload/vimshell/commands/ というディレクトリの中に
コマンド名.vim というファイルをつくります。
中身はとりあえずこんな感じです。

" autoload/vimshell/commands/tweet.vim

let s:save_cpo = &cpo
set cpo&vim


let s:command = { 'name': 'tweet',
\                 'kind': 'internal',
\                 'description': 'tweet {message}...' }

function! vimshell#commands#tweet#define()
  return s:command
endfunction


let &cpo = s:save_cpo
unlet s:save_cpo

vimshell#commands#tweet#define() で必要なキーをもつ辞書を返します。
キーは

  • name: コマンド名の文字列(たぶん必須)
  • kind: コマンドの種類(internal にすればよさそう)
  • description: コマンドの使い方(たぶんオプション)
  • execute: コマンドを実行する関数(たぶん必須)
  • complete: コマンドの補完関数(オプション)

という感じだと思います。
この辺はドキュメントに記載されていない気がするので()の中は嘘かもしれません。

それではコマンドの中身を作ります。こんな感じです。

function! s:command.execute(args, context)
  if exists(':TweetVimCommandSay')
    execute 'TweetVimCommandSay' join(a:args)
    return
  else
    let message = 'TweetVim is not available'
    call vimshell#error_line(a:context.fd, message)
  endif
endfunction

さっきの辞書にexecuteというキーの関数をつくります。
引数は2つあって最初の方はコマンドの引数がリストになって渡されます。
例えば

vimshell% tweet foo bar baz

のように実行されたときには args は ['foo', 'bar', 'baz'] となります。
2つ目の引数はvimshellのコンテクストの辞書です。こっちはよくわかってないです。

関数の中身はそのままですね。
TweetVimCommandSay というコマンドがあればそれを実行し、なければエラーを表示します。
vimshellに出力したいときは vimshell#print(), vimshell#print_line(), vimshell#error_line()あたりの関数を使えばよいでしょう。

あとは補完関数を書きましょう。
ちなみに補完を使うにはneocomplcacheが必要です(自動補完を有効にする必要はない)。
では、completeというキーの関数をつくります。
引数は args でこれはexecuteのものと一緒です。

function! s:command.complete(args)
  let arglead = a:args[-1]
  if arglead =~# '^@'
    let comp = map(tweetvim#cache#get('screen_name'),
    \              "{ 'word': '@' . v:val, 'menu': 'tweet' }")
    return vimshell#complete#helper#keyword_filter(comp, arglead)
  elseif arglead =~# '^#'
    let comp = map(tweetvim#cache#get('hash_tag'),
    \              "{ 'word': '#' . v:val, 'menu': 'tweet' }")
    return vimshell#complete#helper#keyword_filter(comp, arglead)
  else
    return []
  endif
endfunction

カーソル位置の単語は a:args[-1] で取得できます。
補完候補のリストを vimshell#complete#helper#keyword_filter() でフィルターしたものを返り値とすればよいです。
補完候補の形式はneocomplcacheの形式と同じ辞書です。

これで完成です。
最後にソース全体を載せておきます。

let s:save_cpo = &cpo
set cpo&vim


let s:command = { 'name': 'tweet',
\                 'kind': 'internal',
\                 'description': 'tweet {message}...' }

function! vimshell#commands#tweet#define()
  return s:command
endfunction


function! s:command.execute(args, context)
  if exists(':TweetVimCommandSay')
    execute 'TweetVimCommandSay' join(a:args)
    return
  else
    let message = 'TweetVim is not available'
    call vimshell#error_line(a:context.fd, message)
  endif
endfunction


function! s:command.complete(args)
  let arglead = a:args[-1]
  if arglead =~# '^@'
    let comp = map(tweetvim#cache#get('screen_name'),
    \              "{ 'word': '@' . v:val, 'menu': 'tweet' }")
    return vimshell#complete#helper#keyword_filter(comp, arglead)
  elseif arglead =~# '^#'
    let comp = map(tweetvim#cache#get('hash_tag'),
    \              "{ 'word': '#' . v:val, 'menu': 'tweet' }")
    return vimshell#complete#helper#keyword_filter(comp, arglead)
  else
    return []
  endif
endfunction


let &cpo = s:save_cpo
unlet s:save_cpo

こんな感じでvimshellは簡単にコマンドをつくれるので、便利なものをどんどん作りましょう。

明日は@さんです。