vimのtextobjで検索文字列を指定する

Vim Advent Calendar 2012の331日目の記事になります。
330日目は@LeafCageさんによる 俺が見たいのはメッセージの最後なんだ!:messagesを便利にするVimプラグイン でした。

はじめに

先日このようなvimの記事を見つけました。

feedkeys() を使用します。

function! HLsearch()
    call feedkeys(":set hlsearch\<CR>", "n")
endfunction
call HLsearch()

これでハイライトが行われます。

少々バッドノウハウっぽくはありますが、自分はこの関数内でのset hlsearchの方法を待ち望んでいました。
過去にvim-operator-userを使って検索オペレータを作ろうと思い立つも、関数内ハイライトの壁に阻まれて諦めていたのです。
でもこの方法さえ知ればもう怖いものはない、一度敗れた雪辱を果たしてやろうと思います。倍返しです。

ちなみに関数内でハイライトが効かないのは仕様だそうです(:h nohlsearch)。

実装

では早速実装してみましょう。
こんな感じ。

NeoBundle 'kana/vim-operator-user'

" 検索オペレータ
map <SPACE>/ <Plug>(operator-search)
call operator#user#define('search', 'OperatorSearch')
function! OperatorSearch(motion_wise)
    " v or V or <C-v>
    let v = operator#user#visual_command_from_wise_name(a:motion_wise)

    " textobjで選択した文字列を検索パターン用レジスタに格納
    let orig = @"
    execute 'normal! `[' . v . '`]y'
    let @/ = '\V' . substitute(escape(@", '\'), '\n', '\\n', 'g')
    let @" = orig

    " 検索履歴に追加
    call histadd('/', @/)

    " 強調表示
    if &hlsearch
        call feedkeys(":set hlsearch\<CR>", 'n')
    endif
endfunction

どやろか?

ただ上記は思いっきり自分仕様に作ってます。
自分は*の動作でも移動はせずハイライトのみするようにしているので、これも同じように作成しました。
その後n/Nで移動する感じです。

とりあえず動かしてみる。

f:id:saihoooooooo:20131026225917g:plain

うーん、いい感じっす。
あと捕捉ですがilの動作はvim-textobj-lineでやってます。

いやぁハイライトって本当にいいものですね。
私もう満足です。
一片の悔いなしっす。

が一応

さっきも言ったように上記は思いっきりオレオレ仕様であります。
普通は初回の動作でも移動+ハイライトになると思うので頑張って作ってみたいと思います。

map <SPACE>/ <Plug>(operator-search-forward)
map <SPACE>? <Plug>(operator-search-backward)

call operator#user#define('search-forward', 'OperatorSearch', 'call SetOpSearchCmdtype("/")')
call operator#user#define('search-backward', 'OperatorSearch', 'call SetOpSearchCmdtype("?")')

let s:op_search_cmdtype = ''
function! SetOpSearchCmdtype(cmdtype)
    let s:op_search_cmdtype = a:cmdtype
endfunction

function! OperatorSearch(motion_wise)
    let v = operator#user#visual_command_from_wise_name(a:motion_wise)
    let save = @"
    execute 'normal! `[' . v . '`]y'
    let @/ = '\V' . substitute(escape(@", '\'), '\n', '\\n', 'g')
    let @" = save
    call histadd('/', @/)
    if &hlsearch
        call feedkeys(":set hlsearch\<CR>", 'n')
    endif
    " キーマップから実行することで<SPACE>?でのn/Nの方向が?と同じになる
    call feedkeys(s:op_search_cmdtype . "\<CR>", 'n')
endfunction

うーん、検索周りは自分のつたないvimscript力ではこの辺が限界か。
一応動作はしますが、納得はいってません。
特に2回もfeedkeys()を使わされているのが悔しいです。
v:searchforwardもまた関数から出ると元に戻るようで。

プラグイン化も考えてたけどまた次回に・・・。
だれかキレイなものを作ってくれる方募集中です。

まとめ

@thincaさんのvim-visualstarはすっごいよく出来てる。

tablineもvim-airline

f:id:saihoooooooo:20130914014247p:plain

vimのタブラインもairline化できるようなのでヘルプから適当にみつくろって設定してみた。

" タブラインにもairlineを適用
let g:airline#extensions#tabline#enabled = 1

" (タブが一個の場合)バッファのリストをタブラインに表示する機能をオフ
let g:airline#extensions#tabline#show_buffers = 0

" 0でそのタブで開いてるウィンドウ数、1で左のタブから連番
let g:airline#extensions#tabline#tab_nr_type = 1

" タブに表示する名前(fnamemodifyの第二引数)
let g:airline#extensions#tabline#fnamemod = ':t'

ただ右上に「×マーク(マウスクリックで表示中のタブを閉じる)」だの「tabs / buffers」だのが表示されているが、ここの内容を変更する方法がどうも無さそうだった。
いつもはカレントディレクトリを表示しているからairline化しても同じようにしたかったのに。

なんで一回CUIのタブラインに戻す( ´ー`)y-~~

設定できるようになったらもっかい使ってみよう。
airlineの開発は活発そうなのでそのうち用意されるだろうと期待。

vim-airlineを入れてみた&iterm2での表示ずれ解決

f:id:saihoooooooo:20130821010915p:plain

vacの記事の vim-airline に乗り換える5つの理由|もぶろぐ に触発されて入れてみた。
予想外にいいですわこれわ。

フォントにパッチを当てないといけないとか、ちょっと面倒だったので敬遠してましたが、覚えれば簡単&基本一度当てれば終わりな訳でもっと早く入れておけばよかったと思いました。

ただwindowsだと特に気になることはなかったんですが、macのiterm2(terminal.appも)だと透過がうまく行われなかったり、三角マークがずれて表示されたりしました。

透過の問題に関してはとりあえずiterm2の透過設定をOFFに。
三角マークのずれはNon-ASCII FONTの大きさを1ポイント上げてVerticalを1.2ぐらいにすることで綺麗に表示されました(Rictyの場合)。

f:id:saihoooooooo:20130821011925p:plain

これで開発もすこぶる捗るやんけー。

hubotで自己に反応しないようにする

hubotって自分の発言にも反応してしまうのが滑稽なので、どうにかしてみました。
(かなりやっつけ)

src/listener.coffeeのTextListerクラスを改修します。
以下、hubotのアカウント名(not ロボット名)が「my_hubot」だった場合。

--- node_modules/hubot/src/listener.coffee.bak  2013-08-08 13:43:05.000000000 +0900
+++ node_modules/hubot/src/listener.coffee      2013-08-08 13:45:02.000000000 +0900
@@ -39,7 +39,7 @@
   # callback - A Function that is triggered if the incoming message matches.
   constructor: (@robot, @regex, @callback) ->
     @matcher = (message) =>
-      if message instanceof TextMessage
+      if message instanceof TextMessage and message.user.name isnt 'my_hubot'
         message.match @regex

すげーとりあえず対応ですがコレでまぁ問題なし。
ていうか調べてないけど設定でなんとかならんのかな。

定時退社hubotを書いた

日替わりメッセージで退社を促してくれる賢い奴です。
node-cronを使って平日の18:00につぶやくよう設定。

これで生産効率アップ&人間らしい生活を送れること間違いなしだろう。
できるだけ全員が見てる窓にぶっこみましょう。

# Description:
#   No overtime by node-cron.

cron = require('cron').CronJob
random = require('hubot').Response::random

module.exports = (robot) ->

  new cron '0 0 18 * * 1-5', ->
    say = '定時退社の時間になりました。' + random [
      '毎日遅くまでご苦労様です。今日ぐらいは早く帰ってもいいんじゃないですか。'
      '早く帰ってゆっくりお風呂に入れば疲れもとれますよ。'
      '今日のドラマを楽しみにしてたんじゃないですか?'
      '家であなたの帰りを待ってる人が居ますよ。'
      '役職のある人から早く帰りましょう。'
      '今日は用事があるのでお先に失礼します。'
      '貴様ら、とっととお帰りください。'
      '残業のし過ぎは生産効率の低下を招きます。'
      '残業をするのは能力が低い証拠だ。'
      'え、まだ働くんですか?社畜さんチーッス。'
      'ダラダラ仕事してんじゃねーよ亀かよ。'
      'おい、飲みに行かないか。'
      'ここは俺に任せてお前らは早く逃げろ。'
      '退社、退社、さっさと退社、しばくぞ♪'
      '本日の白州、これまで。'
      'process.exit();'
      'ああっ、もうダメッ!ぁあ…会社出るっ、会社出ますうっ!!'
    ]
    user = {room: '#hogeuser/$0123456789abcdef'}
    robot.send user, say
  , null, true

あとnode-cronは第4引数にtrueを指定するか、もしくはインスタンス生成後に

job = new cron cronTime, callback 
job.start()

しないと動かないっていうのがわからず少しハマった。
ドキュメント読まな。