<![CDATA[haya14busa]]> 2017-12-31T23:59:22+09:00 http://haya14busa.com/ <![CDATA[haya14busa]]> Octopress <![CDATA[2017年の振り返り]]> 2017-12-31T23:12:05+09:00 http://haya14busa.com/2017-plus-plus <![CDATA[

この記事は2017年 @haya14busa がやってきた活動,特にVimとかのざっくり振り返り記録です.

過去の振り返り記事

今年はざっくりプログラミング & Vimmer 歴 5年目でシンソツ=ニューシャイベントもあり環境も変わった年でした。

2017年 - Vim 活

VimConf 2017

今年は VimConf 2017 にて一応 キーノート=スピーカー として 60分の発表をしたのは一大イベントでした。 これほど長時間話す経験はまったくなかったので不安でいっぱいでしたが、 時間余りすぎてヤバイとかいうこともなく自分なりには頑張ったかなぁと思います。

正直準備不足は否めなくて特に通訳もあったので申し訳ない…という反省点はありました。 あと発表が自分語りによりすぎた感はあった気はします。 自分しかできない発表をしたいというのとバランスですね。

incsearch.vim is dead. Long live incsearch

incsearch.vim is dead. Long live incsearch で英語で話したのと、 VimConf でも触れましたが、incsearch.vim のインクリメンタルにハイライトする機能を 本体の Vim に入れれたのは本当によかったなぁと思います。 もっと本体のコードにも慣れていきたいですね。

2017年 - Go 活動

Go 本体への初コントリビューションとかを入れれたのはよかった。

入社とか

自分より格段に優秀なひとが周りにゴロゴロいる環境で自分とはなんなんだ…と思うのと同時にありがたい。 もっと頑張らないと。

最初はC++も全くわからないし(というかプログラミングがわかってない)やべぇ…ってなってましたが、 なんとかC++ともちょっとずつ仲良くなってきたのは2017年ではなかなかの収穫だったと思います。

はやく「C++完全に理解したい。」いいたい

スプラトゥーン2

いろいろ考えたり練習したりして、うまくなっていけると楽しいし、みんなで リーグマッチやプラベでわいわいしたりするのはとても楽しかった。 スプラトゥーンはゲーム自体も楽しいですが、スプラトゥーンによって繋がりが増えて みんなでわいわいできるのが本当に神ゲーだなぁと思います。 俺達の信じた任天堂を信じろ。

今年の総評

スプラトゥーンしすぎと、仕事したら割とプログラミング欲も満たされり疲れたりしてるせいで 今年はアウトプット少なかったなぁと反省しています…

来年はもっと趣味グラミングの時間をとりたい。あと英語を引き続き頑張らないと…

以上, @haya14busa の 2017年の振り返り記録でした.

来年もイカよろしく〜

]]>
<![CDATA[コードのエッジへ移動しろ! vim-edgemotion 作った]]> 2017-12-14T20:24:11+09:00 http://haya14busa.com/vim-edgemotion <![CDATA[

この記事は Vim2 Advent Calendar 2017 14日目の記事です。

vim-edgemotion つくった

vim-edgemotion-demo

VimConf 2017t9md さんの発表 で紹介されていた、 Atom vim-mode-plus の機能の一つ, Edge motion を Vim に移植しました。

https://github.com/haya14busa/vim-edgemotion

Edge Motion とは?

Edge Motion は上下方向へのカーソル移動を拡張するモーションで、”コードブロック”のエッジ(端)へカーソルを移動させることができます。 ブロック内にいればそのブロックの端へ、すでにブロック端にいたりブロック外で実行すると次にぶつかるブロックの端までカーソルを移動します。

VimConf2017 でのデモ(本記事の冒頭のgif) でも直感的・視覚的に移動できてよさそう感は 伝わると思うのですが、個人的に便利だなぁと思うのはキチンとインデントされているコードであれば、 関数やifブロックを効率的に、しかも言語を問わずに移動できるところです。

edgemotion indent demo

GIF では Vim script の if-elseif-else 間の移動や、function-endfunction 間の移動を行っていますが、 CでもGoでもPythonでもHaskell でも、様々な言語でこのインデントベースで次のエッジに飛ぶという カーソル移動は効果を発揮するかと思います。

vim-edgemotionにおけるコードブロックってなに?

edgemotion visualize

GIF にあるように、以下の正規表現でコードのブロックをヴィジュアライズすることができます。

1
2
" Code block regex: [^[:space:]][[:space:]]\ze[^[:space:]]\|[^[:space:]]
" :let @/ = '[^[:space:]][[:space:]]\ze[^[:space:]]\|[^[:space:]]' | set hls

要するに空白文字でないか、または非空白文字に挟まれている空白文字をコードブロックと見なしています。 これは例えば :let @/ = の空白がブロックとみなされないと、カーソルが思っても見ないところで止まったり、 予想以上のところまで移動してしまうことを防ぐためで、Atom vim-mode-plus の仕様にあわせています。

最初はそのヒューリスティックでいいのかな…という気持ちはありましたが、他にいい判定方法も思いつかなかったし仕方ない。 Edge Motion は説明なしに直感的・視覚的に使えると見せかけて仕様を理解しないと 驚いてしまうかもしれないというのがデザインの難しいところですね…

使い方サンプル

お好みのキーにマッピングしてください。僕は <C-j>/<C-k>にマッピングしてみた。

1
2
map <C-j> <Plug>(edgemotion-j)
map <C-k> <Plug>(edgemotion-k)

<Plug>(edgemotion-j) で下方向、<Plug>(edgemotion-k) で上方向にカーソルを移動します。

宣伝

vim-edgemotion は 今年僕も発表した VimConf 2017t9md さんの発表 から インスパイア… というかまるまるパクってVim plugin にポートしてできたプラグインです。

VimConf はエディタの垣根を超えるカンファレンスで奇しくも昨日の Vim2 Advent Calendar 2017 13日目の記事( vim-shiny という plugin を作った) も VimConf2017 で発表された vim-mode-plus にインスパイアされてプラグインを作っています。

宣伝2

また vim-edgemotion はもともと VimConf2017 開催中に30minくらいで作ったのですが、 あとで t9md さんに見てもらうとどうやら実装が違ったらしく、「その仕様はいいかもしれないけど それならedgemotion と名乗るな」とt9mdさんに怒られました(笑)。

結局自分でもオリジナルの仕様が良さそうということで今の仕様にその後変更しました。 この変更はつい[要出典] 先日 土善旅館 というにて開催された進捗合宿で ダメになるソファーでダメになりながら、ネコとペアプロすることで実装されました。

その様子です。

Vim進捗週末旅行@土善旅館についてあわせて読みたい

おわりに

  • Edge Motion は直感的・視覚的にカーソルの上下移動ができてなかなか可能性を感じるので使ってみてね
  • VimConf は来年の VimConf2018 もオススメ
  • 土善旅館 はネコにゃんとペアプロして積んでたタスクを崩せるので便利
]]>
<![CDATA[Go に暗黙の型変換機能を明示的に導入する]]> 2017-02-06T00:53:24+09:00 http://haya14busa.com/go-typeconv <![CDATA[

Go に暗黙の型変換はない

Go には Tour of Go でも習うように,暗黙の型変換といったものは存在せず,明示的に型変換をする必要があります.

Unlike in C, in Go assignment between items of different type requires an explicit conversion. – Type conversions https://tour.golang.org/basics/13

このデザインについては FAQ にも書いてあります. FAQ: Why does Go not provide implicit numeric conversions? https://golang.org/doc/faq#conversions

(厳密には interface への変換だけは勝手にやってくれるのでその意味では暗黙の型変換はあるといえる気もします)

このデザインは僕も好きです.The Zen of Python も言うように何事も “Explicit is better than Implicit” だと感じます. Go ではほぼ全ての機能が Explicit に表現され,Go の Readablity に繋がっています. はぁ…Go かわいいよ Go…

しかし,そうは言ってもint->int64などの安全な変換ふくめてひたすら手で型変換しなきゃいけないのはつらいこともあります. 競技プログラミングといった書捨てスクリプトで最初は int でやってたけど,int64 に変換しなきゃ…というときや, func max(x int64, ys ...int64) int64 っていうテンプレ関数を用意していても,型が int のままでは使えず毎回 int64() で囲ったり int() で戻したりする必要があります.

これは人間のやる仕事じゃない…ということでこの問題を解消するツール, go-typeconv を作りました.

go-typeconv 作った

haya14busa/go-typeconv: Bring implicit type conversion into Go in a explicit way

gotypeconv は Go のソースコードを受け取って,型まわりのエラーを見つけ,AST を自動で書き換えて修正します. gofmt と同様に,stdout に修正後のコードをプリントしたり,ファイルを直接書き換えたり,diffを表示することが出来ます.

最終的には明示的な型変換を使ったソースコードになるけど,その変換をある意味暗黙的にやってくれる.というのがコンセプトです.

インストール

1
$ go get -u github.com/haya14busa/go-typeconv/cmd/gotypeconv

使い方

./testdata/tour.input.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// https://tour.golang.org/basics/13
package main

import (
  "fmt"
  "math"
)

func main() {
  var x, y int = 3, 4
  var f float64 = math.Sqrt(x*x + y*y)
  var z uint = f
  fmt.Println(x, y, z)
}

上記のコードは以下で示すように型変換周りでエラーがでます.

1
2
3
$ go build testdata/tour.input.go
testdata/tour.input.go:11: cannot use x * x + y * y (type int) as type float64 in argument to math.Sqrt
testdata/tour.input.go:12: cannot use f (type float64) as type uint in assignment

gotypeconv を実行するとこの通り!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ gotypeconv ./testdata/tour.input.go
// https://tour.golang.org/basics/13
package main

import (
        "fmt"
        "math"
)

func main() {
        var x, y int = 3, 4
        var f float64 = math.Sqrt(float64(x*x + y*y))
        var z uint = uint(f)
        fmt.Println(x, y, z)
}

func max(x int64, ys ...int64) int64 といったテンプレートあるけど int では使えない…というときもこの通り.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

func main() {
  var (
      x int     = 1
      y int64   = 14
      z float64 = -1.4
  )

  var ans int = max(x, x+y, z)
  fmt.Println(ans)
}

func max(x int64, ys ...int64) int64 {
  for _, y := range ys {
      if y > x {
          x = y
      }
  }
  return x
}
1
2
3
4
5
6
7
8
9
10
11
12
$ gotypeconv -d testdata/max.input.go
diff testdata/max.input.go gotypeconv/testdata/max.input.go
--- /tmp/gotypeconv308367427    2017-02-06 08:53:30.460299789 +0900
+++ /tmp/gotypeconv267288262    2017-02-06 08:53:30.460299789 +0900
@@ -9,7 +9,7 @@
                z float64 = -1.4
        )

-       var ans int = max(x, x+y, z)
+       var ans int = int(max(int64(x), int64(x)+y, int64(z)))
        fmt.Println(ans)
 }

vim-gofmt

haya14busa/vim-gofmt: Formats Go source code asynchronously with multiple Go formatters.

Vim で実行したい場合は (Experimentalですが) vim-gofmt というGoの formatter を複数,非同期で実行するプラグインを作成したのでこれを使ってみてください.

設定例:

1
2
3
4
5
let g:gofmt_formatters = [
\   { 'cmd': 'gofmt', 'args': ['-s', '-w'] },
\   { 'cmd': 'goimports', 'args': ['-w'] },
\   { 'cmd': 'gotypeconv', 'args': ['-w'] },
\ ]

実行: (filetype が Go のカレントバッファで) :Fmt

正直ワンバイナリでやってほしいけど,gofmt -sgoimports が同時に実行できないので, いずれ複数フォーマッタ対応のVim プラグイン 欲しいなと思ってたので作りました. どちらも,特に gofmt はかなり高速なので複数実行してもあまり気になりません.

なお非同期にgofmt実行するといっても,バッファが書き換えられていたら上書きしたりするような挙動ではないので安心してください. (不具合あったら直します)

なお

1
:command! GoTypeConv call gofmt#fmt([{ 'cmd': 'gotypeconv', 'args': ['-w'] }])

とか書いておくと:GoTypeConvでgotypeconv だけ実行出来たりしますが,API は変わるかも知れないのでご注意ください.

また以下のようにすると保存時に自動で実行できます.が,APIは(ry

1
2
3
4
augroup vim-gofmt
  autocmd!
  autocmd BufWritePre *.go :Fmt
augroup END

機械で出来ることは機械にやらせたい

途中で IDE ちゃんになっているようですが,Go の場合 IDE ちゃんじゃなくても大丈夫!

The grammar is compact and regular, allowing for easy analysis by automatic tools such as integrated development environments. – The Go Programming Language Specification - The Go Programming Language

Go は spec にまで書かれているように,Goのためのツールを書くのはとても簡単です.

修正系ツールだけでも

  • gofmt -s はフォーマットだけでなくシンプルにかけるところを自動で修正してくれ
  • goimports は自動で import 文を追加したり削除したりしてくれ
  • gotypeconv は型変換エラーを自動で修正してくれます

また proposal: gofmt: Add option to ignore parse error if no bad node in AST · Issue #18939 · golang/go のプロポーザルにあるように,実は末尾コンマがないといったエラーも自動で修正できます. (gofmtに入るかはわからないけど個人的には入って欲しいなー)

なお https://github.com/rhysd/gofmtrlx を入れると末尾カンマは修正してくれますし,実は go-typeconv も勝手に修正してくれます.

gosimple という simple にかけるところを教えてくれる linter もあります. (自動修正機能は今の所ないけどオプションあっても良い気がするなー)

linter がたくさんあることは Go の CI で lint と カバレッジ回して非人間的なレビューは自動化しよう in 2016年 - haya14busa で紹介しました.

go/* を使えばパッケージをソースコードをパースして AST を取得したり, それを Format された形式で print したり,型情報を取得したり… などと言ったことが簡単にできます.

golang.org/x/tools/go/loader を使えば,簡単に型情報を使える形でプログラムをロードできますし, golang.org/x/tools/go/ssa なんてものもあります.

go/* パッケージ だけでなく,golang.org/x/tools/go 下のパッケージをみても面白いです.

AST ベースのツールは他言語でもたくさんあると思うので比較的わかりやすいですが, 型情報まで使えるパッケージを提供している言語はそんなになくて難しいですが,godoc の他にも公式チュートリアルが参考になります.https://golang.org/s/types-tutorial

日本語情報でも motemen さんの GoのためのGotenntenn さんの各種記事 goパッケージで簡単に静的解析して世界を広げよう #golang - Qiita が詳しいです.

ぜひ,みなさんもGoのためのGo ツール作ってみると面白いかと思います. いろんなことができるので,なかなか楽しいです.

はぁ…Go かわいいよ Go…(ため息)

]]>
<![CDATA[Go に恋した Vimmer の2016年の振り返り]]> 2016-12-29T17:47:59+09:00 http://haya14busa.com/2016-is-go-year <![CDATA[

この記事では2016年 @haya14busa がやってきた活動,特にVimとかGo 活動をざっくり振り返ります. 個人的な備忘録です.あとポエム.

過去の振り返り記事

今年はざっくりプログラミング & Vimmer 歴 4年目でした. そろそろプログラミングの基礎的なところわかってなくても,まだはじめてそんな経ってなくてまだ勉強できてないんです〜,また今度やっておきますっ! みたいな言い訳が完全に通用しない年になってきた気がします (そもそも実際に言ったことは無い). まだまだ学んでおきたい分野はたくさんあって,特にもうちょっとレイヤーが下の要素もやっていって理解しておきたさがありますね…

“Go” に恋した 2016年

AlphaGo, Pokemon GO, Amazon Go… “Go” は2016年の1つのキーワードだったように思いますが,個人的には Go 言語の “Go” に恋した1年でした.

credit: Gopher by tenntenn CC BY 3.0

Go との出会い

Go 言語との出会いは去年インターンでGoをはじめて使ったときでした.

その時一目惚れ… をしたわけではありません.

シンプルでとにかくプロジェクトで動くものは作れる感じだったので,なんとなくGoよさそうかなぁとは感じつつも, ちょっと気になっている子(Scala) との大きな違いに戸惑ってたりしてました. “Functional Programming”? なにそれおいしいの? とでも聞こえてきそうな Go の雰囲気に, 僕は一歩足を引いて評価を保留してました. ただ,さも vim で書くために生まれてきた言語では…? というほどの gofmt といった周辺のツールの充実っぷりはこの頃から大好きで,言語というよりは周囲の エコシステムがよさ…という印象でした.

また,Go と出会ったころと時を同じくして,僕はスプラトゥーンと出会ってました. お家に帰って Go やってる場合ではなく,一日中イカする毎日. イカの存在によって Go とはどこかビジネスライクな付き合いにとどまってました.イカはっょぃ.

Go との別れ

それから程なくして,インターンが終了しました. Go をいちから学ぶところからはじめたにも関わらず, 終了時にある程度のものが出来たことを Go にも感謝しつつ, 一旦 Go との付き合いもお別れになりました.

結局,まぁなんかよさそうな言語だ程度の(ふんわり)印象でここで一旦お別れしたわけですが, 後々,このときのいい環境で Go を書いてレビューしてもらったり, 教えてもらったことはとても役に立ちました. ありがたい.

Go との再会

2016年の春,忙しかった時期も一段落し,スプラトゥーンとの距離をうまく保てるようになった僕は ちょっとした小さいツールを Go で書くことにしました.

理由としては,コマンドラインツールとか Go で書くのは便利そうだったことや, 来年以降もGo書くことになりそうかなぁという打算的な思考, また同じ頃にはじめたアルバイト先のいくつかのサブシステムは Go で書かれていることもあり, Go 書けるとそのへんも触れておもしろそうかなという気持ちがありました.

数カ月の Go とのお別れ期間でいろいろ忘れていることに戸惑いつつも,書いていくうちに勘も戻ってきました. ちょっとした自分用ツールをいくつか Go で完成させて,簡単にサーバにもっていって動かすことができてよいなーと感じたり, Google App Engine で雑に動かしたりして遊んでました. アルバイト先でもちょっとずつ書いたりするようになって,Go と触れ合う時間は増えました.

2016年秋,気づけば Go に恋に落ちてた

それからも,定期的に Go で何か作ったりしてました.例えば,

この頃には Go がかなり手に馴染むようになってきました.上記に上げたツールも Go の開発を助けるツールだということからも,Go をより書くようになってきてることがうかがえます.

特にきっかけはありませんでした.でもこの頃にはもう Go に恋してたんだと思います. その結果,特に意味もなく Vim script のパーサを Go 言語で動かしたいなぁ… 既存の Python 実装とかあるけど,Go でいじりたいなぁ… と思って Go 実装を作ったりしてました.

go-vimlparser - Vim Script Parser written in Go

Just for fun ではじめましたが,結果として最速 Vim script parser 実装となって,Vimmer にも嬉しい便利なものになったと思います. Go のパフォーマンスチューニングのやり方も改めて経験できて身についたし, AST の表現や AST walker の実装をするために go/ast のコードを読んでインターフェースをパク…参考にしたりとおもしろかった. Go の標準ライブラリのデザインはとても参考になるし,それを Go 言語でザクザク読めるのは本当にいいなぁと思います.

Go と Vim との共同作業

この頃,Vim はJSONやchannel, job 機能が実装され,外部インターフェースとの通信が容易になり,もうすぐ Vim 8.0 出すよ〜という時期でした. Go に恋した Vimmer としては,もちろんここで Go 言語を使って Vim との共同作業をさせたいというのは必然です! (これでは Go と Vim がカップルになってるのではということは気にしない.でも実際2人の相性はとてもよい)

このあたりの話は Vim Advent Calendar で書きました.

vim-go-client の通信のハンドリングのデザインは Go の net/http のコードを読んで参考にしたりしました. が,もうちょっといい感じにできそうな気がする… 実際に Go で vim-stacktrace という便利プラグインを作れることを確認できて,go-vimlparser も有効活用できたりなど, Vim活にも Go が絡むようになってきてますます Go が好きになっていきました.

Go と Vim との間に生まれた子供 - reviewdog

(Go と Vim が結婚して子供を産みました.僕のGoへの恋は片想い)

reviewdog logo

reviewdog は僕の 2016年に作ったプロダクトの一番のヒット作と言えます.reviewdog の GitHub のスターは執筆時現在 268 です. reviewdog が好きなのは,実際に超便利プロダクトだということはもちろん,Vim の ‘errorformat’ という便利な機能を Go言語で port して Vim の外でも使えるようにしたという,Vim と Go の”よさ” が存分に発揮されたプロダクトだというところがとても気に入っています.

reviewdog のようなものを作るには,いろんな linter などのコマンドの結果を扱わなくてはいけません. 他のツールでは, gometalinter のように linter ごとに正規表現をアドホックに作ったり, それぞれのコマンドの runner を作ったり, checkstyle xml やJSONのある機械的な形式を吐くlinterにのみ対応するといった割り切りをしています.

Vim はそういういろんなコマンドやいろんなアウトプット形式に対応するというのは得意で, ‘errorformat’ という scanf-like な機能がこれにあたり,この形式を使うことに決めました.

ただしこの’errorformat’は Vim でしか使えません.そこを同じくマルチプラットフォームで簡単に動く Go 言語で同等の機能をポート(haya14busa/errorformat)し, Vim のよさを Go がサポートしてより広く使ってもらえるようにできました.

reviewdog は現在もちょくちょく改善していて,特にローカルでもより動かしやすいようにしようと思ってます. 年内にある程度開発してバージョン1.0にしようかと思っていましたが,もう少しかかりそうです.

reviewdog の状況

OSS で導入するのは CI サービスがうまく Secure Token を扱ってくれないせいで, 若干最初の導入が手間なのが使ってもらうには少し壁になってるのが悲しい.Travis〜CircleCI〜頼む〜改善してくれ〜. 一度導入してしまえばあとは楽だし,もしくはローカルでだけ使っても便利なのでもうちょっと使用事案増えて欲しい.

Go 活動その他

Go が好き

書いてるうちに手に馴染んできて,いつの間にか好きになってましたが,結局僕はなぜ Go に恋に落ちたのでしょうか. 1つは間違いなく Go の Simplicity に惹かれたんだと思います.

Simplicity is Complicated

Rob Pike の “Simplicity is Complicated” という発表がオススメです. 僕はこの発表をみて,胸がきゅーーんっとなりました.Go かわいいよ Go.

Go はなぜ成功したんでしょうか? コンパイル速度,実行速度,デプロイの容易さ,充実したツール,質の高いライブラリ,interfaceやconcurrencyをサポートする言語機能… そのどれもが大事で,僕も実際どれも好きですが,Rob Pike は “Simplicity” が答えだといいます. そしてこの “Simplicity” は “Complicated” だとも.

シンプルという言葉はともすれば薄っぺらくなります.特にVimmerという職業(?)柄, Vim plugin とかをいくつかみると,”simple” だとか,他にも “easy”, “minimalictic” と言った単語が並んでたりします.これは自戒を込めてですが,場合によってはちょっと薄っぺらいなぁと感じるようなものもあったりします.

Simplicity is the art of hiding complexity.

Go が simple だとか,less is more だとか,引き算の言語だ…といった表現をされるとき, 僕は全く薄っぺらいだとかは思いません. これは”Simplicity” というのは実は”Complicated” であり, “Simplicity” の背後には”complexity”を洗練された,緻密なデザインや設計,実装が隠れているからなんだと思います.

GCやgoroutine,interfaceといった実際の例がスライドで説明されているので是非見てみてください.

僕らが享受しているGo の “Simplicity” は簡単に実現されたものではありません. 僕らが Go に対して,「あぁ,こういうのでいいんだよ.こういうので.」と感じるとき, その背後には Go がたくさんの機能を削ぎ落として,直交する必要十分な機能のデザインと 複雑な実装が存在しています.

Simplicity is hard—to design.
Simplicity is complicated—to build.
But if you get it right…
Simplicity is easy—to use.
The success of Go proves it.
https://talks.golang.org/2015/simplicity-is-complicated.slide#30

“Simplicity” をデザインするのは難しく,”Simplicity”の実現はとても複雑です. でもこれらをうまくやると,”Simplicity” は使うのが簡単になります.

これが Go は, “simple” であり,”less is more” であり,”引き算の言語” であり, 「あぁ,こういうのでいいんだよ.こういうので.」ということなのでしょう.

Go is more Pythonic than Python

Go and the Zen of Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

僕が最初に学んだ言語は Python で,The Zen of Python の思想はとても好きでした.

  • Explicit is better than implicit.
  • Simple is better than complex.
  • Readability counts.
  • There should be one– and preferably only one –obvious way to do it.

Go は The Zen of Python に,ともすれば Python 以上にマッチしています. Go が Pythonic なところも間違いなく僕が Go を好きになった一因です. なんなら,Go が Python より Pythonic なので,もう Python で書くようなところは, Go で書きたい.

Go の前に学んだ言語は Scala でした. Scala が魅せてくれるプログラミング言語の可能性にはスゴイなぁ,おもしろいなぁ, と思いつつも,Scala が気になる子止まりだったのは,The Zen of Python の思想の真逆を 行くような,ないし真逆のコードが生まれやすい言語だからだと思います. Scala は今も書いているし,堅牢かつ規模の大きいコードも Scala だと比較的安心して書いていけたり, 僕に “Functional Programming” などを教えてくれた Scala は好きですが,恋に落ちるほどではなかった. 僕を含め,もう少し人類が進化するとまた変わってくる気もする.

Go とのこれから

2017年もGoと仲良くやってきたいです.個人的には特に Vim との連携とか含め. Go 本体になんかコントリビュッションしたいと思いつつできなかったので,機会があればやっていきたい.

あとは,Go のよさを再確認するため(?)に別のパラダイムの言語(rust とか)にちょい浮気しつつやっていきたいですね.

Vim との 2016年 - 本妻は Vim

2016年のVim活もザックリ振り返ります.

EasyMotion 3.0

Vimのカーソル移動はもっともっと爆速になる! Vim-EasyMotion v3.0 をリリースしました - haya14busa

結構前にやった気もしますが,今年でした. バージョン3.0の機能追加でウィンドウをまたいだEasyMotionによるカーソル移動が実現しました. 完全にライフチェンジング機能だし,待望の機能の実現で自分の Vim script 力の高まりも確認できてよかった.

vital のロード高速化

まず,revital.vim という別プラグインで vital.vim のロードを高速化できることを示した後,結局 vital.vim 本体に入れてもらいました. ついでにリファクタリングしたりインターフェースの改善も行いました(後方互換性は維持している). 特定の環境でvital.vimが遅いという問題を完全に解決し,現在では autoload 関数を呼ぶのと変わらなくなってます.

はじめてのパッチ - Vim の Contributer に!

vital のロード高速化の際に Vim のバグを踏んで,小さいですがはじめてパッチを書いて取り込まれました! このバグは本体のバグだったということもあって異常に原因究明が難しく,原因を見つけたのは lambdalisue さんでした.

今年は他にも EasyMotion で踏んだバグの修正パッチや,Vim8.0 リリース前の channelやjob 機能を叩いたパッチを書いたり, Vim 8.0 に lambda いれようぜ! ともう一回 vim-jp でちょっと騒いで,あとは vim-jp の mattn さんや k-takata さんに丸投げするなどをしてました.

そのあたりの話で1つ記事を書いてます -> Vim 8.0 released and Now, I’m a contributor of Vim !!! – Medium

今年になって小さいとは言え Vimのパッチ書いたりできたのはとても嬉しかったです. Vim のソースコードも読むことは出来るし,デバッグの仕方もわかってきたし, 場合によってはちょっと修正するくらいのことも出来るということがわかったのは収穫でした. ただ,もっと C 力は高めたほうがよさそう.

Vim 8.0 & Go関連

  • go-vimlparser
  • vim-go-client
  • vim-stacktrace
  • パッチいくつか.

詳細は Go のところで紹介したので省略.

VimConf

発表は go-vimlparser についての話ですでに書きましたが, t9md さんの t9md/atom-vim-mode-plus の発表が面白くて, Atom の vim-mode-plus のアイデアを Vim にバックポートする業などをしていました.

haya14busa/vim-metarepeat は vim-mode-plus の occurence で出来るようになる便利な機能を別の角度から実装したもので, ドットリピートをオペレータとして,テキストオブジェクト内の対象に対して一括でドットリピートを実行するものです. 記事には書いてないけどissueにちょっと考えとかを書いています -> https://github.com/vim-jp/issues/issues/977#issuecomment-259703728 vim-metarepeat はかなり便利で常用していて,記事書くかぁと思いながら今年が終了しました.

haya14busa/vim-textobj-function-syntax は関数text-objectをVimのsyntaxを使って言語ごとに用意せずに使えるようにしたものです. syntax によって使える/使えない言語がありますが,ちょっと便利. これも記事にはしてないけどissueにちょっとコメントしてます.-> https://github.com/vim-jp/issues/issues/987#issuecomment-262870187

Google Translate の衝撃と Vim

Google Japan Blog: Google 翻訳が進化しました。

今年はGoogle Translate が日英間の翻訳に Neural Machine Translation を導入して, 機械翻訳がとても流暢になりました.Google Translate さんすごい. この改善をみて,Vimから使いやすくするためにいくつかプラグインを作りました.

英語でブログ記事を書く

Google Translate の流れでいうと今年後半からは実験的にブログ記事も英語で書くようにしました. Google Translate 改善前からやっていて,改善後も勿論(?)Google Translateはあくまで補助としての使用ですが.

書いた英語 Posts

英語で書く記事かどうか決めるというよりは,基本的に英語で書いて,どうしても日本語で書いたほうがよさそうだったら日本語で書くことにしています. 直近の2つのアドベントカレンダーは普通に日本語で書いてしまって守れてないけど…正直時間が取れなかったので日本語に逃げました… まぁ日本語で発信するのもそれはそれで大事だと思うのでいいかなぁーとは思ってます.

英語で書く理由としては,今後使うし勉強のためやらなきゃ…という理由と, 発信を日本に閉じる必要性はないかなーと最近は考えてるからでした. 僕はVimとかGoについて書くことが最近は多いですが,それらの記事のターゲットは大抵 Vimmer か Gopher で, 別に想定読者を日本人に絞る必要性はないんですよね.

日本語で書いた後,英訳しようとはなかなか行動できないので,英語・日本語両方ポスト作ると言うよりは, 実質デファクトになってる英語で書けば英語読める日本人は読めるので,基本的には 英語で1つ記事を書くということにしました.

…とはいえ時間やクオリティーの兼ね合い,日本のコミュニティーの活性化, フィードバックのもらいやすさ,そして何より現状の英語力が足りてない問題など, 色々と問題はありますが.そもそもたくさんの人に読んでもらうというのが目的ではないという観点もありそうだけど.

英語で書くと特に日本では伸びづらいですが, 日本で伸びなくても今までリーチしなかった層にリーチするし, 今年書いた記事はだいたいある程度は日本人もそれ以外も読んでもらえたっぽいので, 今の所やってみてよかったです. reddit とかにあげるとそこそこ読みにきてくれて便利.(redditのCEOがコメント編集したという事件があって,若干使いたさが減りましたが…). 来年も続けていこうかなぁと思います.

英語に関してはWritingもそうだけど,どちらかといえば勉強する必要があったのはSpeaking/Listeningだった気が… という説もあり,今後がとても心配すぎる…

2016年活動情報

GitHub Contributions

GitHub Contributions (including private contributions)

GitHub Contributions (including private contributions)

GitHub Contributions (public)

GitHub Contributions (public)

Posts

Posts (English)

歩くのが下手って気付いた2016年

今年は1つ大きく躓いた出来事がありました.躓いただけなら立て直せばよかったはずだったけど, そのまま歩き続ける意味を見失い,歩みを進めようとする心はもう折れていて,しばらくその場でうずくまってしまいました. 躓いたことにも,歩き直せなかったことにも,道を変えて歩くことさえもできずに,ただただうずくまってました. 今まで通りとはいかないにしても,もうちょっとうまく歩けたはずでした. あぁ,僕は歩くのがなんて下手なんだ…

今の所,詳細はオープンインターネットには書かなくてもいいかな…と思ってます. 書く気がないのにここでふんわり書いたのは完全に自己満足です.忘年です.忘れないけど.

あとは完全にここでやるべきことではないけど,一種の懺悔みたいなものです. 僕が単に1人でうずくまったまま世界から消えても,別に世界はどうってことないけど, 無駄に不要なところにまで迷惑をおかけしてしまったり,いくつか見えた差し伸べられた手も 完全にシャットアウトしてしまったことは,単純に反省しています.ごめんなさい.

最近は人間的な生活をおくるリハビリをしていて,心に平穏は戻りつつある気はします.

最後に

2016年の振り返り記事だって言ってるのに無駄なポエムがところどころにあるせいで無駄に長くなった気がします. あとオープンにやってきたこと何でもかんでも放り込み過ぎて長くなったので,もうちょっとハイライトだけ振り返ればよかった気もする. まぁ,まだまだとはいえ,2016年結構いろいろやったのでは? と振り返って思いました. 来年の目標,なにか定量的なものを設定して振り返るのが本当は良い気がしますが, 来年はとりあえず強く生きることを目標にします.

来年がとても素晴らしい1年でありますように.

]]>
<![CDATA[Go の CI で lint と カバレッジ回して非人間的なレビューは自動化しよう in 2016年]]> 2016-12-25T23:15:50+09:00 http://haya14busa.com/ci-for-go-in-end-of-2016 <![CDATA[

この記事は Go (その3) Advent Calendar 2016 の24日目の記事です(代打). メリークリスマス!

本記事では Go 言語プロジェクトの CI で回すと便利な各種lintの紹介やカバレッジ計測の方法などなどについて紹介します.

Go 言語おすすめ linter

Go の lint 一覧といえば gometalinter じゃん? みたいな話もあると思うのですが,CIで回すには個別に linter を明示的に回すほうが良いかと思います. ということで 僕が普段使ってるオススメ linter の紹介です.

go vet

Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string. Vet uses heuristics that do not guarantee all reports are genuine problems, but it can find errors not caught by the compilers.

Go 言語標準でついているlintツールで,コンパイラが検出しないエラーを検出できます. false positive な結果も無いようにデザインされているのでエラーがリポートされた場合は安心してCIをfailにできます. go vet に検出されたエラーはほぼ100%直したほうがよいでしょう.

golint

Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes. Golint differs from govet. Govet is concerned with correctness, whereas golint is concerned with coding style.

gofmtがコードのリフォーマットを,govetがコードの正確性をチェックするのに対して,golint は Go のソースコードの “コーディングスタイル” の問題を報告します.これはエラーというよりも “suggestion” に近く, 基本的に従っていたほうがGoの慣習に沿った確実によいコードになるのですが, まれに,いやここの関数はコメント書かなくても絶対いいよね…とかいうケースもあり 若干消耗することもあるので星4です.

なお,結局はコーディングスタイルの問題をリポートするという思想からデフォルトでは問題があっても exit code は 0 になるので,落としたい場合は -set_exit_status flag を付けましょう.

errcheck

errcheck は関数のエラーの返り値をハンドリングチェックしているかどうかを静的に解析してくれるツールです. エラーを捨てた状態の場合思わぬ動作(nilになってたりだとか)するので,これをチェックしてくれるのは大変嬉しいです.

ただし,少し実際直さなくてもいいという意味でのfalse positive が多くCIで動かすには工夫が必要で星3です.エラーをチェックしてくれるという便利度は満点. なお,最近は標準ライブラリで必ずエラーがnilとして返ってくるような関数についてはリポートしないという false positive を減らす工夫もされているようです.

staticcheck

staticcheck は一言で言うとサードパーティーの go vetです.go vet でチェックされていないようなことを静的に解析してくれます. (例えば正規表現がvalidかチェックしてくれるかなどなど…たくさん項目があるのでREADMEを見てみてください.)

作者の dominikh さん は Go 言語の開発にも関わっていて,dominikh さん製Go lint ツールは個人的には 信頼できる印象です.github/go の issue でも go vet のissueなどに, staticcheck は実際こういうのチェックしてるけどと言った感じで参照されてたりするのを見かけます.

そして次の2つのツールも dominikh さん製です.

gosimple

gosimple は一言で言うとdominikh さん製のサードパーティー gofmt -s です.もっとコードをシンプルに出来るところを報告してくれます. (ただし執筆時現在,自動で修正してくれるオプションとかはない)

例えば if err != nil { return err }; return nil といった構造のコードがあれば return err で十分だよ? と報告してくれます.

報告に false positive もない印象で,あーそんなメソッドあったのか〜ということに気付いたりできてオススメです.

go-unused

使ってない identifier をチェックしてくれます. Go のコンパイラ自体が使ってない変数があるとコンパイル通らないという話もありますが, unused は グローバル変数の var や const, struct の field,export されていない関数などで 使われていないものを報告してくれます.

報告してくれないのは使われてない関数の引数くらいでしょうか? これはインターフェースを満たすための関数など, 引数もらうけど使わないんやというケースなどを考えて報告されていないのではないかという気がします.[要出典]

また個人的には使えてないので強くオススメできませんが,パッケージのリストを渡して exported なもので,渡したパッケージ内で使われていないものをチェック出来る機能もあります. https://github.com/dominikh/go-unused#whole-program-analysis “internal” packages などを使っていたりする場合は便利かもしれないですね.

unused は個人的には大変便利に使っていて,ごくまれにデバック用のexportしてない関数を報告されて, あー…ってなる以外に false positive な結果もなく便利に使っています.

この前 interface を満たすためのダミーの関数をいろんな struct に定義してたんですが, その際,追加すべきでない struct にも追加してしまい,それを unused が報告 してくれたことがありました.インターフェースを満たすかどうかといった観点での”used”もちゃんと見てくれていて大変良い子だな〜と思い感動しました. オススメです.

gofmt -s

linter ではない若干番外編その1.

gofmt -s の結果 diff があるかどうか,つまりもっとシンプルにかけた部分がないかということをチェックできます.

コマンド: (! gofmt -s -d . | grep '^')

括弧は travis などのyamlでvalidなものにするために使っています. gofmt も exit code が 1 になったりすることはないので個人的には grep '^' の結果を否定することによって,diffを表示しつつ,diffがあればfailにするという手法を使っています. (xargs -r とかはMacでは-rがないとかがある…)

基本的にGo言語開発時はみんな gofmt かけていると思いますが,-s は付けていなかったり, goimportgofmt を代用していた場合に -s オプションがなくてかけてなかったりするので CI で実行するとちょっと便利です.

ちょっと便利なんですが,-s つけるとめっちゃシンプルになって絶対いいよね…! というよりは, あー手元で実行してなくて,CIサーバでdiffでちゃったよ…直すか…とちょ〜っとだけ消耗することが あるという意味で個人的にはオススメ度星4です.(goimport -sフラグ足してくれ〜)

go test -race

  • おすすめ度: ★★★★★

linter ではない若干番外編その2. go test-race を付けるとrace condition があるかチェックしてくれます. 特に goroutine とか使ってるコードであれば,CIでのテスト実行時にはぜひ付けておくべきでしょう. 僕も何度も-raceに怒られてコード直したり,ああここlockいるなと気付かされております.

その他

あんまり僕がまだ使えてなくて,ちゃんとオススメできないけど便利な静的チェックツールはいくつかあります.

interfacer

  • https://github.com/mvdan/interfacer
  • 引数の型とかインターフェースでいいじゃん?というところを指摘してくれる. e.g. *os.File -> io.Reader
  • 個人的にはインターフェースにするといいところは最初からインターフェース使ってたりするしそこまでの恩恵は感じない

gosum

  • https://github.com/haya14busa/gosum
  • 急に拙作ツールの宣伝(?)
  • Go のインターフェースで直和型っぽいことを表現したときの,type switch に抜け漏れがないかを静的にチェックするツールです.
  • Scala でいう sealed trait のパターンマッチでコンパイラが抜け漏れがあると warning だしてくれるというやつのGoバージョン
  • 詳しくはこの記事に書きました -> Sum/Union/Variant Type in Go and Static Check Tool of switch-case handling – Medium
  • 書いてみて,使ってみて実際便利だと思ってるんですが,まだちゃんとCIで回したりはしてないので,その他枠で雑に紹介です.

ところで linter 書くときって,AST ベースで解析するツールが僕の観測範囲では多いと思うですが, Goは必要であれば go/types パッケージを使って型情報まで使って 解析できるので大変便利ですね…! 標準ライブラリでカバーされてるところも +1

“go/ast ではしゃいでるのはもう古い! 時代は go/types !” みたいな煽りタイトルの解説記事を最近は待ち望んでます. go/types 関連は標準ライブラリの中でもかなり大きいものなのでなかなか僕もまだ全貌を理解できてないです.

その他のその他

gometalinter とか Go Report Card で紹介されているツール.

https://github.com/fzipp/gocyclo とかイマイチ恩恵を受けたことがないんですが,gometalinter とか Go Report Card で使われているツールは参考になるかもしれません.

reviewdog: linter の false positive と闘う

特に golint や errcheck など,上記でオススメしたlinterの中には,false positive なリポートが結構あって,めっちゃ便利でチェックしたいんだけど CI で fail にしづらい… というものがいくつかあります.

そこで拙作ツール reviewdog の紹介です.(宣伝)

もちろん Go 製です!

(画像は実際のPull Requestのコメントへのリンクになってます)

reviewdog は Go 言語の linter に限らず,任意のコマンドの結果を’errorformat’ という形式を使うことでパースして,diff で新たに追加された部分にたいする問題だけを 表示したり,GitHub にコメントすることが出来るツールです. 詳しくは -> reviewdog を飼ってコードレビューや開発を改善しませんか - haya14busa

結果をdiffでフィルターすれば,それらの問題についてのみPull Requestのレビュー時やコミット時にチェックすることが出来るので, もし false positive な結果な場合は単に無視すれば次回以降に同じ問題は報告されません.

もちろん,例えば先に紹介した unused などは全然diffと関係ない部分で新たに問題がおきたりすることもあり, 結果を diff でフィルターするというのはfalse positiveと闘う銀の弾丸ではないのですが, 多くの場合これで十分機能を果たすでしょう. (一応 diff 外の問題もうまくまとめて報告する仕組みも足したいな〜という気持ちはあります)

Go 製ツールである reviewdog は自分自身のコードでドックフーディングしていて,この記事で紹介した いろんなGo 用 linter を実行していて,上記の画像のように実際に便利に使っています. 参考: reviewdog/reviewdog.yml (最近はyamlでも管理できるようにしていて,より簡単にローカルで実行したり,git hook で実行しやすくしたりしたいなどと改善しています)

Goのテストカバレッジをレビューでも活用する

Go は標準で go test -coverprofile=coverage.out . などと実行するとテストカバレッジを取得することができます. ただ実は CI などでカバレッジを取得する際は注意点があり,複数のパッケージをまとめてカバレッジを計測することはできません. つまり例えば go test -coverprofile=coverage.out ./... とはできません.これは go test 内部ではパッケージごとにテスト実行用バイナリを作成してそれぞれ実行してるという設計になっていることに起因します. issue は上がってますが標準では対応するのはなかなか骨が折れそうです. (ちょっと修正してコントリビューションしてみようかと格闘しましたが構造的に地味に大変そうでした…)

そこで現状で,複数パッケージのテストカバレッジに対応するために色んな所で Makefile やGoのツールを使うといった解決方法が紹介されています. しかし!紹介されていて,確かにある程度はどれも動くのですが,実は多くのスクリプトはちょっと片手落ちなものになっています. 例えば go test の -coverpkg 引数を使わないと依存先のコードカバレッジが取得できてなかったり, 結果の coverage.out に重複行が生まれるケースがあったりします.

解決策

mattn さんの mattn/goveralls では上記の問題に対応したマルチパッケージ対応テストカバレッジ機能が備わっています.というか僕がPull Request しました. Coveralls に投稿する場合は goveralls を使うと良いと思います. (goveralls -service=travis-ci でよしなにやってくれる)

ローカルでは?

goveralls にコントリビューションしたあと,あれ…これローカルでもやりたいじゃん…ということに気付き, 既存のMakefileソリューションや似たツールはいくつかあるにはあったんですが,上記の問題などの対応とか いろいろ面倒だったのでマルチパッケージカバレッジ作成用go test のラッパーツールを作りました.

haya14busa/goverage: go test -coverprofile for multiple packages

1
$ goverage -coverprofile=coverage.out ./...

とかするといい感じに coverage.out が生成されます.便利.

codecov に投稿してレビューでも活用する - “おい、coverall もいいけど codecov 使えよ”

サブタイは b4b4r07 さんリスペクトです. ref: おい、peco もいいけど fzf 使えよ - Qiita

Codecov という Coveralls と似たサービスがあるのはご存知でしょうか? 僕は以前からたまに見かけたことはあったのですが,最近はじめて使ってみて,断然 coverall よりいいじゃん…!!! と感じました.

全体的に洗練されてる…というよさもあるんですが,一番いいところは Pull Request の diff に対するカバレッジを表示できて, ブラウザの拡張をインストールすれば GitHub の Pull Request 画面上でカバーされた行をオーバーレイで確認できるところが大変気に入りました.

もちろん Codecov 上のページでも見れます: Compare ⋅ haya14busa/reviewdog

Go 言語リポジトリのカバレッジを travis で計測して codecov へ投稿する例:

1
2
3
4
5
6
7
8
9
10
# .travis.yml
install:
  - go get github.com/haya14busa/goverage

script:
  - goverage -coverprofile=coverage.txt ./...

after_success:
  - goveralls -service=travis-ci -coverprofile=coverage.txt
  - bash <(curl -s https://codecov.io/bash)

codecov 公式のGo言語用リポジトリの例 では Makefile を使ってますが, 完全に上記のマルチパッケージサポートの問題を踏んでるので goverage を使うと良いと思います.

テストカバレッジは別に100%を目指さなくてもよいと思っていて,全体のカバレッジが何%以下とか何%下がったらステータスをfail にするといった機能がcoverallやcodecov にはありますが,この辺を有効にすると結構消耗するかと思います.

ただ,カバレッジを参考にすることは有用だし,ユニットテストは基本的に書くべきです. Pull Request などではカバーすべきところをしっかりカバーしたテストを期待したいし,, レビュー時にもそのあたりが可視化されると大変便利です.

実際僕は Go のレビューするときに手元でテストまわしてcoverageみて,ここテストそもそも無いから足して欲しいだとか, この行はカバーされてないけど,カバーすべき部分なのでテストケース足して欲しいとか言ったりするんですが, codecov を使うとこのフローがやりやすいし,レビュイーもPull Request を出した時点で自分で気付いて テスト足したりできると思います.

実は codecov 使い始めたのは最近で,僕自身がチーム開発として使った経験はまだないのですが, coverall よりも codecov 使うとこの辺いい感じに可視化されて人間が指摘しなくてもよくなったり, レビュワーとして指摘しやすくなったりすると思います.

せっかく Go という言語は標準でテストが書きやすく,カバレッジ計測もしやすいので,ぜひ皆さんもカバレッジを計測して,codecov 使ってみてはいかがでしょうか?

まとめ

Go のCIでまわすと便利なlintツールを紹介したり,テストカバレッジの取得方や codecov のオススメなどをしました. Go は go/ast, go/types など go/ 下の標準パッケージを使ってコードをパースしてASTを取得したり,型情報を取得したりなど するライブラリが用意されているので,必要に応じて自前でチェックツールを作ったりもしやすく面白いです.

この記事で紹介したものは,一般的に多く使われているみなさんが知っているようなものから,あまり知られてないものまであるかと思いますが, すべて少なくとも僕が使っていて便利だなぁ〜,と思ったものを紹介してみました. 他に便利なオススメツールなどがあれば教えてください!

個人OSSプロジェクトではレビュワー最初はいない問題などがあり,最近はPull Requestを開いてlint チェックさせたり,カバレッジみたり, 改めてブラウザ画面上でセルフレビューするなどしていて,まぁちょっと面倒もあるけど良い感じです. あとは設計レビューとかもしてくれるGoのツールがあればカンペキですね〜〜〜!!!1

もちろんチーム開発でのレビューでも,消耗しがちな非人間的な指摘は機械にやってもらって, もっと大事な観点をレビューするために,この記事で各種紹介した手法は役に立つかなと思います.

来年も Go 書いていくぞ!

]]>
<![CDATA[Go で Vim プラグインを書く]]> 2016-12-21T18:14:54+09:00 http://haya14busa.com/vim-go-client <![CDATA[

この記事は Vim アドベントカレンダー 2016 の21日目の記事です.

最近は Go 言語が大好きすぎて,Vim plugin も Go で書きたい!!! という欲が出てきたので, Vim plugin を Go で書く方法について紹介します.

Go で Vim plugin を書くとは?

一口に Go で Vim plugin を書くといっても

  1. Go で書いたバイナリがメインで Vim script の autoload 関数などから呼ぶ.例: https://github.com/mattn/vim-filewatcher
  2. Go 側からも Vim script を呼ぶ,つまり Vim script で Vim の情報を取得するところなど含めて,ほぼ全部 Go で書く.

という 2 段階があると思います.本記事では2の方法も含めて紹介しますがまずは1から行きましょう.

1. Go で書いたバイナリをつかった Vim plugin の作り方

これは先程例にあげた https://github.com/mattn/vim-filewatcher がシンプルでわかりやすいです.

filewatcher/filewatcher.go で書いた Go をインストール時に cd filewatcher && go get -d && go build でビルドし, autoload/filewatcher.vim でこのバイナリを job をつかって呼んでいます.

go get -d を呼ぶことで依存するパッケージをダウンロードし,go build することで $GOBIN などを汚さずにプラグインディレクトリにバイナリを配置できます.

autoload/filewatcher.vim

1
2
3
4
5
6
7
8
let s:cmd = expand('<sfile>:h:h:gs!\\!/!') . '/filewatcher/filewatcher' . (has('win32') ? '.exe' : '')
if !filereadable(s:cmd)
  finish
endif

function! filewatcher#watch(dir, cb)
  return {'dir': a:dir, 'job': job_start([s:cmd, a:dir], { 'out_cb': a:cb, 'out_mode': 'nl' })}
endfunction

バイナリを呼んでいるVim script もとてもシンプルで, windows かどうか見ながらバイナリのパスを取得し, それを job で呼ぶだけです.簡単.プラグインの性質によっては job ではなく system() などを使ってもよいでしょう.

また,開発時には g:plugin_name#debug などを作ってそれを見て go run を呼ぶというふうに変えることもできます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function! s:separator() abort
  return fnamemodify('.', ':p')[-1 :]
endfunction

let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')

let s:base = expand('<sfile>:p:h:h')
let s:basecmd = s:base . s:separator() . fnamemodify(s:base, ':t')
let s:cmd = s:basecmd . (s:is_windows ? '.exe' : '')

if g:plugin_name#debug
  let s:cmd = ['go', 'run', s:basecmd . '.go']
elseif !filereadable(s:cmd)
  call system(printf('cd %s && go get -d && go build', s:base))
endif

僕が作ったプラグインから引っ張ってきた例で autoload/filewatcher.vim ほどシンプルではないですが,もうちょっとなんとか出来るかもしれないですね. main パッケージのファイル (s:basecmd . '.go') を1ファイルにするとgo runで呼びやすいです.

2. Go 側からも Vim script を呼ぶ必要があるようなプラグインの作り方

mattn/filewatcher ではファイルの変更を検知してstdout にJSONを吐いて,それが job の callback に渡されるという形式で単体で簡潔してましたが, 場合によっては Go 側から Vim の状態を取得したり,Vim script を呼んだりしたい場合もあります. そういうプラグインを作るには,job を JSON モードで起動し, :h channel-commands を使うことによって実現できます.

:h channel-commands

1
2
3
4
5
6
7
8
9
10
11
JSON チャンネルを使用すると、サーバープロセス側はVimへコマンドを送信できます。
そのコマンドはチャンネルのハンドラーを介さずに、Vimの内部で実行されます。

実行可能なコマンドは以下のとおりです:           *E903* *E904* *E905*
    ["redraw", {forced}]
    ["ex",     {Ex コマンド}]
    ["normal", {ノーマルモードコマンド}]
    ["eval",   {式}, {数値}]
    ["expr",   {式}]
    ["call",   {func name}, {argument list}, {number}]
    ["call",   {func name}, {argument list}]

{数値}({number}) は id で,job -> Vim に渡すさいはマイナスを指定する必要があり, その渡した id と共に評価された値が返ってきます.

例えば Go 側で stdout に ["expr","line('$')", -2] を書き込むと, Vim がline('$') を評価してその結果が stdin に [-2, "last line"] といった結果が返ってきます.

便利すぎる…

ということでidの取扱などこのあたりの処理を毎回丁寧にやるのは面倒くさいので, https://github.com/haya14busa/vim-go-client というラッパーを作りました. ドキュメント: https://godoc.org/github.com/haya14busa/vim-go-client#Client

type Client が上記の channel-commands などのに相当するメソッドを持っており, type Handler がメッセージの受け渡しを担当します.

サンプル: _example/dev/job/job.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import (
  "fmt"
  "log"
  "os"
  "time"

  vim "github.com/haya14busa/vim-go-client"
)

type myHandler struct{}

func (h *myHandler) Serve(cli *vim.Client, msg *vim.Message) {
  log.Printf("receive: %#v", msg)
  if msg.MsgID > 0 {

      if msg.Body == "hi" {
          cli.Send(&vim.Message{
              MsgID: msg.MsgID,
              Body:  "hi how are you?",
          })
      } else {
          start := time.Now()
          log.Println(cli.Expr("eval(join(range(10), '+'))"))
          log.Printf("cli.Expr: finished in %v", time.Now().Sub(start))
      }

  }
}

func main() {
  handler := &myHandler{}
  cli := vim.NewClient(vim.NewReadWriter(os.Stdin, os.Stdout), handler)
  done := make(chan error, 1)
  go func() {
      done <- cli.Start()
  }()

  cli.Ex("echom 'hi'")
  log.Println(cli.Expr("1+1"))

  select {
  case err := <-done:
      fmt.Printf("exit with error: %v\n", err)
      fmt.Println("bye;)")
  }
}

handler := &myHandler{} でハンドラを作って cli := vim.NewClient(vim.NewReadWriter(os.Stdin, os.Stdout), handler) で stdin/stdout を介してVim と通信できるclientを作成しています. あとはこいつを cli.Start() しておけば Vim から ch_sendexpr() などが呼ばれると handler に中身が渡されるし, cli.Ex("echom 'hi'") などを呼ぶと Vim 側で echom 'hi' が実行されます.

実例: vim-stacktrace

実際に vim-go-client を使ってひとつプラグインを書いてみました.

haya14busa/vim-stacktrace

stacktracefromhist.gif (1287×800)

Vim のスタックトレースをquickfix に流し込むプラグインでやっていることとしては6日目の記事の Vim scriptのエラーメッセージをパースしてquickfixに表示する - Qiita と近いです.

autoload 関数からjobに ch_evalexpr する部分([link]https://github.com/haya14busa/vim-stacktrace/blob/933f9d10c7ef99467c27609fcdd80be37c0712e8/autoload/stacktrace.vim#L12-L30()) を除いてほぼ全てがGoで実装されていて,現時点で Go の割合が 87.8 % です.

image

実装の中身としても,Vim のスタックトレースからは関数内における行番号しかとれず,ファイルの行番号が取得できない問題があるのですが, それをGoで実装したVim script parser (https://github.com/haya14busa/go-vimlparser) を使ってファイルをパースし,行番号を取得することができています. また,:CStacktraceFromhist などは Vim script の inputlist をGo側から呼んでいてインテラクティブにVimと協調して動作できることも示せました.

Go で書くよさ

実際に Vim script でやっているひともいたように,vim-stacktrace は Go が無いとかけなかったといった類のものではないですが,Goで書くといいことがたくさんありました.

  • 型がある安心感
  • テストが標準に備わっていて書きやすい (go test)
  • カバレッジも取れる! (go test -coverprofile)
  • Go のパッケージが使える (go-vimlparser, etc…)
  • etc…

カバレッジなどは現在Vim scriptのテスティングフレームワークではサポートされていないし,なかなか実装しようとしてもムズカシそうなのですが, Goでかけば標準でついてきます.とても便利.

coverall も使えます: Coverage Status

逆にPure Vim script と比較して悪いところや注意点があるとすれば

  • vim-go-client がまだ安定してない
  • channel-commands がエラーをちゃんと返してくれない(エラーがあれば “ERROR” とだけ返ってくる)
  • チャンネルの通信で少しだけオーバーヘッドがある
  • 現状vim/neovimに両対応できない

といった感じでしょうか.もうちょっとvim-go-client精錬させたいですね…頑張ります…

NeoVim のリモートプラグイン

neovim 向けには実は neovim/go-client というものが存在し,リモートプラグインをGoで書くことが出来るようです.

Vimconf 2016zchee さんが発表していた nvim-go はこれが使われています.

スライド該当部分: http://go-talks.appspot.com/github.com/zchee/talks/vimconf2016.slide#33

正直なところ neovim のリモートプラグインの先行アドバンテージ(?)は大きく,vim-go-client と比較してかなり高機能になってます. 理想としては Vim 8 でも neovim でも使えるものをかけるようにしたいのですが, neovimのリモートプラグインが高機能であることや, msgpack 依存であることからなかなか両方に対応することはムズカシイです…

うまく抽象化してロジックの部分だけ共通化して,vim8用/neovim用にメッセージのハンドラを管理してうんたん…みたいなことは出来るかも知れないので, 今後の研究課題という感じですね.あと僕がほとんどneovim使わないので nvim-go の仕様感とか知っている方はお話してくれると嬉しいです. (Vimconf で zchee さんとその話ができたのは便利だった…)

おわりに

正直まだまだGoで書かれたVim plugin は少なく発展途上ですが,実用的なプラグインを作成することもできたので,可能性を感じます. Go でかけばマルチプラットフォームに対応できるし,ライブラリがどうとか環境がどうとか気にすることなく動かせるので,Vim との親和性はかなり高いと思っています.

何よりGoはかわいい!書いていて楽しい!

まだまだ発展途上ですが,ぜひ皆さんもGoでVim プラグインを作ってみてください.

]]>
<![CDATA[Golangにおけるinterfaceをつかったテストで mock を書く技法]]> 2016-11-02T07:19:14+09:00 http://haya14busa.com/golang-how-to-write-mock-of-interface-for-testing <![CDATA[

いい記事に感化されて僕も何か書きたくなった。

Golangにおけるinterfaceをつかったテスト技法 | SOTA

リスペクト:

今週のやつではなく先週のです.今週のは特に知見がなかった…grpc-goとか使えたらクライアント勝手に生成されるしいいよねgrpc流行ると便利そう(感想) くらい

Golangにおけるinterfaceをつかったテスト技法 | SOTA めっちゃいいなーと思ったんですが,テスト用 の mock を気軽に作るテクニックはあまり詳しく紹介されてなかったのでそのあたりの1つのテクニックを書きたい.

前提

僕もテストフレームワークや外部ツールは全く使わない.標準のtestingパッケージのみを使う. testify もいらないし, mock するために gomock も基本はいらない.

とにかくGolangだけで書くのが気持ちがいい,に尽きる.

テスト用 fake client をつくる

全体の動くはずのgist: https://gist.github.com/haya14busa/27a12284ad74477a6fd6ed66d0d153ee

例えばこういう実装のテストを書くときのことを考えます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
  "context"
  "fmt"
)

type GitHub interface {
  CreateRelease(ctx context.Context, opt *Option) (string, error)
  GetRelease(ctx context.Context, tag string) (string, error)
  DeleteRelease(ctx context.Context, releaseID int) error
}

type GhRelease struct {
  c GitHub
}

func (ghr *GhRelease) CreateNewRelease(ctx context.Context) (*Release, error) {
  tag, err := ghr.c.CreateRelease(ctx, nil)
  if err != nil {
      return nil, fmt.Errorf("failed to create release: %v", err)
  }

  // check created release
  if _, err := ghr.c.GetRelease(ctx, tag); err != nil {
      return nil, fmt.Errorf("failed to get created release: %v", err)
  }

  // ...
  return &Release{}, nil
}

type Option struct{}
type Release struct{}

GitHub interface をテストでは mock したものを使いたい.そういうときには以下のように mock を作ると便利です.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type fakeGitHub struct {
  // インターフェース埋め込み
  GitHub
  FakeCreateRelease func(ctx context.Context, opt *Option) (string, error)
  FakeGetRelease    func(ctx context.Context, tag string) (string, error)
  // 埋め込みを使うので,例えば DeleteRelease はまだテストしないので mock
  // しない... いうことができる.
}

func (c *fakeGitHub) CreateRelease(ctx context.Context, opt *Option) (string, error) {
  return c.FakeCreateRelease(ctx, opt)
}

func (c *fakeGitHub) GetRelease(ctx context.Context, tag string) (string, error) {
  return c.FakeGetRelease(ctx, tag)
}

fakeGitHub という struct を作成し,インターフェースをとにかく満たすために GitHub interface を埋め込みます.

そして mock したいメソッドは新たに func (c *fakeGitHub) CreateRelease(...) (...) と 定義しなおし,実装の中身は fakeGitHub に持たせた FakeCreateRelease field に丸投げします.

このようにしてテスト用 mock を作るとそれぞれのテストで簡単に中身の実装を変えられるので大変便利です.

実際にテストしてみる例

main_test.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
  "context"
  "fmt"
  "testing"
)

type fakeGitHub struct {
  // インターフェース埋め込み
  GitHub
  FakeCreateRelease func(ctx context.Context, opt *Option) (string, error)
  FakeGetRelease    func(ctx context.Context, tag string) (string, error)
  // 埋め込みを使うので,例えば DeleteRelease はまだテストしないので mock
  // しない... いうことができる.
}

func (c *fakeGitHub) CreateRelease(ctx context.Context, opt *Option) (string, error) {
  return c.FakeCreateRelease(ctx, opt)
}

func (c *fakeGitHub) GetRelease(ctx context.Context, tag string) (string, error) {
  return c.FakeGetRelease(ctx, tag)
}

func TestGhRelease_CreateNewRelease(t *testing.T) {
  fakeclient := &fakeGitHub{
      FakeCreateRelease: func(ctx context.Context, opt *Option) (string, error) {
          return "v1.0", nil
      },
      FakeGetRelease: func(ctx context.Context, tag string) (string, error) {
          return "", fmt.Errorf("failed to get %v release!", tag)
      },
  }

  ghr := &GhRelease{c: fakeclient}

  release, err := ghr.CreateNewRelease(context.Background())
  if err != nil {
      t.Error(err)
      // => failed to get created release: failed to get v1.0 release!
  }
  _ = release
  // ...
}

以下のような感じで,簡単にテスト用mockの実装を書いて,テストすることができます.

1
2
3
4
5
6
7
8
fakeclient := &fakeGitHub{
  FakeCreateRelease: func(ctx context.Context, opt *Option) (string, error) {
      return "v1.0", nil
  },
  FakeGetRelease: func(ctx context.Context, tag string) (string, error) {
      return "", fmt.Errorf("failed to get %v release!", tag)
  },
}

上記の例では1種類の実装しかテストしてないのであまり恩恵がわかりづらいかも知れないですが, 例えば error が帰ってきたときに正しくエラーハンドリングできてるかとか, 返り値をいろいろ変えたものをいくつか作ってテストする…といったことが上記のパターンを 使うことによって簡単にできます.Table Testing することも可能.

普通にわざわざstructごと作っていると,例えばテストの関数ないでは struct の method (e.g. func (c *client) Func()) を定義することができません.

そこで FakeFunc func() というfield を持たせて実装を丸投げすることによって, 簡単にいろんな実装のテスト用 mock を作成してテストができるということの紹介でした.

まとめ

僕は最初にこのパターンを教わってなるほどなぁ…と思ったんですが,いざ世にでてみると(?) ぜんぜんこのパターンを紹介しているものが見つからなかったので紹介してみました. (一応どっかの medium の英語記事にこれに似たパターンが紹介されてたのを見た気もする…)

ぜひ使ってみてください.

あまり関係ない追記

この記事の主旨とは関係ないけど,基本的にテスト用ライブラリは使わないとはいえ, たまににヘルパー関数ほしいなーというケースがあります.

でかい struct をテストで比較するときに,比較自体は reflect.DeepEqual で出来るのだけど, もし違っていたときにどこが違うかを表示するのが面倒くさいのでヘルパー関数提供してくれるライブラリがほしい…

某社でgoのテスト書いてたときもこういう大きめのstruct比較するケースでは便利diff表示用ライブラリを 使っていた気がしたんだけど,なんかOSSで見つからない気がする… prettycmp みたいな名前だった気がするが どうだったか… そもそも記憶違いな気もする…

追記: twitter で教えてもらいましたが https://github.com/kylelemons/godebug っぽいです. 便利. https://godoc.org/github.com/kylelemons/godebug/pretty#Compare

]]>
<![CDATA[reviewdog を飼ってコードレビューや開発を改善しませんか]]> 2016-10-24T07:44:34+09:00 http://haya14busa.com/reviewdog <![CDATA[

reviewdog logo

GitHub: haya14busa/reviewdog: A code review dog who keeps your codebase healthy

英語記事: reviewdog — A code review dog who keeps your codebase healthy – Medium

reviewdog というlinter などのチェックツールの結果を自動で GitHub の Pull Request にコメントしたり, ローカルでも diff の結果から新たに導入されたエラーだけを表示するようにフィルタリングできるツールを作りました.

英語記事 を Medium に書いたし,README も書いたので 日本語記事はまぁいらないかなぁと思ったけど,柄にもなく Vim 関連以外で普通に便利ツールを書いてしまって,これは日本語でも簡単に共有しようかなぁと思いこの記事を書いています. (とはいえ機能の実現方法として Vim は関係はしてるんですけどね.)

特に和訳とかではなく,英語ではなかなか文章に落としづらかったことをだらだら書こうかと思います.英語は難しい… (だらだらと日本語を書いてるので日本語ができてるとは言ってない)

背景とかきっかけ

compiler や lint といったコードをチェックするツールはものによっては直さなくてもいいものをレポートすることがありますよね. 例えば Scala の compiler はオプションを与えたらかなりいろんな警告を出してくれますが, 別に直さなくてもいいケースがあるものがあったり, Play framework で使ってると ルーティングファイルやテンプレートファイルに対して,こちらが直せない警告を バンバンだしてきたりします.(これはバージョンを上げると直るっぽい?)

golint-set_exit_status を付けないと問題を見つけても exit status が 1 になりませんが, これはもともとあくまで コーディングスタイル の問題を見つけるものだという思想からでしょう.

Golint differs from govet. Govet is concerned with correctness, whereas golint is concerned with coding style.

Go の OSS プロジェクトでは CI で golint の問題があればビルドをFAILにすることが結構多いと思いますが, 本来直さなくてもいいところまで直さなくてはいけなくて消耗したりしていませんか?

去年 Google のインターンで Go 書いてたときは,実際 golint のエラーでテストがfail するとかではなく, bot が自動でコメントしてくれてあとは勝手に直せという感じで便利でした.

まぁ直さなくてもいい結果は少なくて大抵は直すのですが,直さなくてもいいものはゼロではないです. コメントで bot に直さなくてもいいものを指摘されたら単に無視してcloseすればいいので無駄に消耗せずにすんで便利.

また CI で落とすために指摘してくれると便利なことは多いけど設定で off にしてしまうケースもあるかなーと思います. 例えば JavaScript で関数の引数に使ってないものがあったら警告してくれると便利なケースもあるかなぁーという反面, コールバック用関数などでは渡ってくるのがわかってるからいいんだよ!というケースとか.

あとは既存のコードベースに新しく linter を導入しようとすると,既存のコードに対してエラー 出まくって導入するために直していくのが面倒だなぁ… 放置していると新しいコードに対しても lint が走らないので消耗する…ということもあるかと思います.

上記の問題を解決する1つの方法としては,とりあえずPull Request の差分に関して lint をかけたり して自動でbotがコメントしてくれる仕組みがあれば,100% 円満解決ではなくともかなりつらみが 解消されるかと思います.

世の中にはすでにそういうサービスは一応あって Hound CI とか SideCI がそうなんですが,使える言語やツールは限られています. たとえば Vim の linter を Vim プラグインのレポジトリに対して使えるようになることはないでしょう.

また,ローカルでも(差分に対して lint などを書けるという意味で)実行できないと Pull Request 出さないとチェックできないので不便です.

ということで作ったのが reviewdog になります.

あんまり技術的に面白いことがあったというよりは,本来あるべきものなのになかったので作ったという感じ. 汎用的に lint の結果をパースする手段をどのツールも提供してるものがないのが問題で, reviewdog は Vim の Quickfix 用の ‘errorformat’ という機能を Go で port することによって実現しました.

GitHub: haya14busa/errorformat: Vim’s quickfix errorformat implementation in Go

‘errorformat’ 形式を採用したのは僕が単に Vimmer だから… というのももちろんあるんですが, 仕様の全体を理解するのは結構大変とはいえ,簡単なerrorformatを書くのは簡単だし,それでいて 難しい複数行のエラーメッセージをパースできる利点があります.

emacs では compilation モードが Vim の quickfix に対応するようで,errorformat に対応するものは 正規表現とサブマッチのインデックスっぽいのですが,簡単な複数行エラーには対応してそうですが, Vim の ‘errorformat’ のほうが汎用的っぽいなーと思いました(詳しく見れてません)

‘errorformat’, 実装ポーティングして仕様を理解していくと,かなりよくできているなーという印象で Vim は時代の先を走ってると思いました.

また reviewdog 以外でも https://github.com/alecthomas/gometalinter のもっと汎用的なバージョンを言語を問わず errorformat を使って作るとかも可能なんじゃないかな〜と思います.(gometalinter 個人的にあんまり好きくないし)

インストールとか使い方書こうかと思ったけど疲れたのでREADMEとか英語のpost読んでください

あと雑だけど Google Doc の design doc な何かも一応ある reviewdog - Google Docs

CI サービス連携問題

reviewdog は Pull Request hook と実行環境さえあれば対応できてオープンなCIサービスの場合はGitHubへコメントするための API トークンを安全に環境変数などで保存する方法があれば対応できます.

travis や circle ci といったメジャーなCIサービスは両方対応していて,最初は全然このあたりは問題ではないなーと思ってたのですが, 実はOSS用のリポジトリに対して使おうとすると fork レポジトリからの pull-request では暗号化された環境変数は使えない! という問題があって,これどうすかなぁーということにかなり悩まされました. 考えてみれば当たり前で,echo $SECRET_VAR とかした悪意あるPull Request が開かれたら簡単に漏れちゃいます.

そこで,いろいろ CI サービスを探ってると https://drone.io/ の OSS バージョン https://github.com/drone/drone を見つけました.

help (Secrets · Drone) を読むと, yaml ファイルのチェックサムとセクション分けによって(完璧ではないものの)安全に 秘密の環境変数をforkからのプルリクでも使えるようになっているようです. 完全に便利なので travis とか circle ci でもこの機能ほしい…

drone.io OSS バージョンは https://drone.io/ とは別物という感じで環境変数の扱い以外も 結構便利っぽく,drone.io も reviewdog はデフォルトでサポートしました.

ただ OSS で雑にGitHubに上げたサービスに対して使うケースでも drone.io をどこかにホスティングしなくてはならないのが不便なところです… 個人的にはreviewdogのために Degital Ocean に月500円で drone.io 用サーバを立ててみて, 今の所かなり便利感ありますが,ワガママをOSS用にどっかホスティングしてほしい.

で,じゃあ結局forkからのPull Requestも受け付けるOSS用リポジトリにreviewdog導入 したい場合どうすればいいかというと,現状は Circle CI の Building Pull Requests from forks - CircleCI 機能をセキュリティリスクと引き換えに ON にするしかないかなーという感じです.

GitHub の Personal Access Token, しかも scope を適切に public_repo とかに設定しておけば, もし漏れても大したことはできないはずだし,rebokeもできるし,fork して悪意あるPull Request 作られたら流石に気付くし, まぁそもそもそんなこと GitHub の 権限すくないPersonal Access Token 得るためにやる人は 少ないのでは… という感じですね.ただ使う場合はもちろん自己責任でお願いします.

Circle CI か travis あたりが drone.io みたいに yaml のチェックサム+環境変数が使えるスコープを制限する機能がもし実装されたらそれを使っていきたい

(ところで他のCIサービスとして wrecker 試したんですが,fork からの pull-request で普通に秘密の環境変数使えてしまった気がする…問題では…あとPull Request hook によるビルドがなさそうだった)

終わりに

reviewdog 完全に便利という感じなのでぜひ使ってみてください〜

CI 用環境変数とか設定すれば Jenkins とかいろんな環境で導入できるはずだし, 一度導入したら撤退しずらいとかもないので,ギョームとかでも使えるような気がしますが リリース直後なのでいろいろ変わるかも知れないしそのへんはよしなにお願いします. GitHub Enterprise とか BitBucket 対応は気が向いたらするかもしれない. 特にGitHub Enterprise はGitHub 用の base の url 変えるだけで対応できる…?

ちょくちょく気になるところはあるのでちまちま改善していこうかなーと思います

]]>
<![CDATA[はてな・ペパボ技術大会〜インフラ技術基盤〜@京都 行ってきたメモ]]> 2016-07-02T20:06:46+09:00 http://haya14busa.com/hatena-pepabo-kyoto <![CDATA[

はてな・ペパボ技術大会〜インフラ技術基盤〜@京都 7/2

はてな・ペパボ技術大会〜インフラ技術基盤〜@京都 - connpass

行ってきました.メモってたのでせっかくなので共有しておきます. gistでいいかとおもったけどスライド埋め込みとか考えたらブログに雑に投げたほうが見なおしやすそうと思ったのでブログで. 自分で発表したわけでもないし,感想とか書いてるわけでもないけどまぁいいかってカンジ.

注意

わかってない人が書いたメモなのでいろいろわかってないメモが書かれてます. 理解度に関係なく聞き漏らしたところ雑に書いたりしてるので発表様がおっしゃってた話とちがうところもあるかもしれない. 特に座談会の内容とかは Twitterのハッシュタグみてたら @matsumotory さんと @yumu19 さんがまとめてたのでそっち見たほうがわかりやすいかも.

Togetter

http://togetter.com/li/994969

ということで雑にtogetterつくっておきました.#pepabohatena で検索したやつからスパム2-3個消しただけです. そういえばtogetterだめな人とかいるかもじゃん…!ということに気づいたんですが全員に連絡するの流石につらいのでダメなひと @haya14busa まで連絡していただければ幸いです.勝手に使って申し訳ないでした :bow:

記事書いてる間に @yumu19 さんが作ってた -> はてな・ペパボ技術大会〜インフラ技術基盤〜@京都 #pepabohatena ツイートまとめ - Togetterまとめ

タイムテーブル

time 名前 タイトル 時間
13:30 受付開始・開場 30分
14:00 y_uuki(はてな) 開会宣言・会場説明 5分
14:05 y_uuki(はてな) はてなにおけるLinuxネットワークスタックの性能改善 25分
14:30 matsumotory(ペパボ) Webサービス基盤の自律制御と動的平衡性 25分
14:55 休憩 5分
15:00 ichirin2501(はてな) 計算量と僕とWeb開発 25分
15:25 pyama(ペパボ) STNS 〜点と線を結び新しい何かを作るコト〜 25分
15:50 休憩 5分
15:55 masayoshi(はてな) 負荷分散技術を選ぶ時に考えること 25分
16:20 monochromegane(ペパボ) サービスに寄り添うログ基盤 - ログ収集のその先に - 25分
16:45 休憩 10分
16:55 masayoshi(はてな) taketo957(はてな) alotofwe(ペパボ) hanazuki(ペパボ) モデレータ stanaka(はてな) 若手インフラ座談会 40分
17:35 懇親会 軽食と飲み物を用意しています!
19:00 お開き

開会宣言・会場説明

14:00 | y_uuki(はてな) | 開会宣言・会場説明 | 5分

  • 発表6本+座談会
  • アンケート

はてなにおけるLinuxネットワークスタックの性能改善

14:05 | y_uuki(はてな) | はてなにおけるLinuxネットワークスタックの性能改善 | 25分

はてなにおけるLinuxネットワークスタックパフォーマンス改善 / Linux network performance improvement at hatena // Speaker Deck

  • はてなのウェブオペレーションエンジニア.新卒1年目の仕事
  • 今は3年目
  • 3層構成 LB <-> proxy <-> application <-> database
  • ボトルネックどこ?
    • CPU: %user/%sys/%iowait
    • Mem: used/cached/buffer
    • Disk: read/write
    • NW: tx/rx/tx
  • ソフト割り込み(パケット受信)の負荷が高いときの話 (%soft)
  • ネットワークスタック
    • ネットワークi/oを実現するために必要な要素群.低レイヤより
      • NIC/パケット送受信/etc…
  • ネットワークI/O 高速化
  • パケット受信フロー NIC -> Kernel -> Process
    • この辺の話かな? 8.3. パケット受信の概要
    • NIC: 物理デバイス
    • ネットワークカード - Wikipedia
    • NIC ->(ハード割り込み) -> パケット受信 -> プロトコル処理 -> データ受信処理 -> Process
    • 割り込み2種類ある.なぜか?
    • ハード割り込みだけだとパケット受信のたびにプロトコル処理まで実行しちゃう
    • なので一旦バッファにいれる.ソフト割り込み
  • NAPI (New API)
    • 1パケットずつでなく複数パケットごとに割り込み
    • ポーリングによってとってくる.待ち時間発生するので負荷が高い時だけやるみたいに賢くなってる
    • ソフト割り込みを減らす
  • HAProxy の台数増問題 (実際に取り組んだやつ)
  • 割り込みが多い -> 割り込み減らしたい
  • HAProxy のネットワーク負荷が高いのでHAProxyを DNS RR で並べた
  • Interrupt Coalescing
    • ハード割り込みを減らす
  • NICドライバのパラメータ検証した
  • RPS (Receive Packet Steering)
    • コア間割り込み
  • RPS でRedisチューニング
    • Redisは1スレッド
    • カーネルのネットワークI/O処理するスレッドと Redisのスレッドは分散可能
    • -> CPU% 10% 減 (コンテキストスイッチが減ったり,キャッシュが効くようになったからっぽい)
    • redisの公式ページにやりかた書いてある http://redis.io/topics/benchmarks
  • ユーザランドへゼロコピー
    • そもそもカーネルに処理しないという変態処理.
    • パケットを NIC -> ユーザランドへバイパス
    • 実装: netmap
    • まだ実用ではなさそうだけど研究されてる
    • ソケットAPIの限界
      • ソケットAPIではゼロコピーできない
  • まとめ
    • OSの内部面白い.OS内部の技術の外出し
    • ソフト割り込みにより遅延処理
    • ジョブキューのような非同期処理
    • 割り込みとポーリング
    • OSの技術をもうちょっと上のレイヤの技術に応用してやったりするのベンリ

Webサービス基盤の自律制御と動的平衡性

14:30 | matsumotory(ペパボ) | Webサービス基盤の自律制御と動的平衡性 | 25分

  • @matsumotory 松本亮介の研究・開発業績ページ by matsumoto-r
  • 技術基盤チームで研究開発してる
  • 目次
    • なめらかなシステム
    • Webサービスの現状・課題
    • 自立制御
    • 動的平衡性
  • なめらかなシステム
    • システムにとってのユーザ,システムを構成するサーバ等の要素のカテゴリや特徴を詳細に認識する
    • 人間の労苦は避けたい
  • Webサービスの現状・課題
    • bad UI / 遅い / etc… -> 快適に利用できるのが当たり前
    • サーバの運用大変
      • 高負荷・障害対応
      • 問い合わせ
      • バジョンアップ
      • 監視
      • 新規構築
  • 監視に注目してみる
    • 閾値設定はムズカシイ
    • 1時的な高負荷は無視したい.すぐもどるならok
    • 傾向が変わるならすぐ知りたい
    • 正常にみえるけど異常っぽいものを検知したい
    • 段階的な傾向の変化を検知したい.階段みたいな.でも1時的なものは無視
  • 詳細な変化と原因を知るには人の目が必要.でも自動で制御したい
    • 人は何をみてるか? 対象の振る舞いをみてる
    • 振る舞いを表す特徴が知りたい!
    • 人工知能でやれるか
  • 考え方
    • 特徴がある
    • 通常の状態を学習
    • 外れた状態を解析
    • 連速的に外れた状態が異常のはじまり
  • 各種基盤技術
    • [x] 設定をプログラム化
    • [x] リソース制御連携
    • [ ] 特徴量定義と解析 <- ここ
  • 特徴量解析
  • Webサーバの自律制御
    • 特徴量抽出による検知できる点の増加
    • 閾値以下でも傾向変化の細かい検知ができる
  • 細かい傾向変化した時点でどうするか?
    • 異常検知.誤検知もある.false positive/negative?
    • すぐ制限とかは影響でかい
    • なんかアクションはとりたい
    • システムとしてバランスを取りたい条件ある
      • マルチテナント環境とする.
      • リソースが逼迫してきたのみ制限かけたい.自動で制限したい.
      • グラフなめらかにしたい.
    • e.g.
      • どのホストが変化点スコアが高い?
      • 重み付け (優先順位つける) example.com:95, matsumoto-r.jp:93, …
      • 傾向変化ごとに重みつけリスト更新
      • 全リソースが逼迫 -> リストを元に自動制御
      • 逼迫してないなら重みつけリストをつくるところだけしとく
    • 時系列データの生成
      • 相関関係の時系列データも作れる
      • 全体の負荷とホストの負荷どっちか
  • 要素技術
    • 機能拡張 mod_mruby
    • mruby-resource
    • mruby-changefineder
    • etc…
  • 制限の実装方法
    • リクエスト単位でCPU/IOなどの割当を変更
    • cgroup/rlimit
    • e.g. 特定のcgroupにリクエストをアタッチしてCPUを20%に制限する.レスポンス返したらデタッチ
  • ミドルウェアが特徴量抽出解析・制限行いたい
  • webサービスの動的平衡性
    • 生命とは? 動的平衡にある流れ
    • 動的平衡(どうてきへいこう、英語:dynamic equilibrium)とは、物理学・化学などにおいて、互いに逆向きの過程が同じ速度で進行することにより、系全体としては時間変化せず平衡に達している状態を言う。 – 動的平衡 - Wikipedia

    • 細胞周期チェックポイント - Wikipedia
  • 今後: STNS/haconiwa, …
  • Q&A:
    • フォールトトレランスとの違い?
      • 現時点はそこまで.循環するパターン.状態を持つサーバと持たないサーバがある.この辺とかの落とし所を考えてる
    • from yuuki: Docckerとかで動的にスケールしてるけど,違うアプローチっぽい.どう違う? キューバネティスとか?
      • こちらはサーバを積極的に,自主的に落として循環していくといった部分が違う.元気なサーバでも任意の時間経過で落としたり.
    • from astj: バースト.イベントやyahoo砲とかのバズり,が怖い.これを異常検知するのはむずくてなかなか考えても答えが出ない.自律制御のスピード超えてバースト来たらどうする?
      • 全体的にはバーストに対応したい.落とす条件.3時間で落ちそうなやつが1.5時間で落ちたなら1つではく2つのVMを再構築するといった方法あるかも.落としたときに条件によって10倍再構築するとか.

netflix とかも似たようなことやってる感じらしい?

mackerel で出来たりしたら最高だな〜と思った.

計算量と僕とWeb開発

15:00 | ichirin2501(はてな) | 計算量と僕とWeb開発 | 25分

  • @ichirin2501 いちりんちゃんさん
    • アプリケーションエンジニア2年 ->(転職) web オペレーションエンジニア1年(hatena)
    • スピリチュアルな発表!
  • もくじ
    • 計算量について
    • mysql クエリ改善
  • 計算量とは?
    • 大変さの指標.
    • 係数無視 O(3n3) -> O(n3)
    • でかいやつだけ O(n3 + n2) -> O(n3)
  • 計算量といちりんちゃん
    • 競プロで向き合う.
    • 計算量と時間の基準・感覚がある
    • O(107) ~ O(109) 0 -> 1s くらいじゃん?みたいな感じ
    • データサイズからの見積もり
      • 1sec デッドラインの見積もりならこんな感じやろ的な.
      • n <= 106 -> O(n)
      • n <= 105 -> O(nlogn)
      • n <= 3000 -> O(n2)
      • n <= 100 -> O(n3)
      • n <= 20 -> O(2n)
  • MySQL 改善のはなし
    • group by / distinct の迷い.どっちでも重複除外できるので実現可.
      • follow/followerテーブル的なものを想定
      • friend_id 重複なしで 1000件ほしい
      • 結果を言うと group by だと遅いケースがあった
      • group_by で OR句のuser_idが多いと異常におそくなった
      • どう計算してるか知ればいいじゃん
    • インデックス構造
      • インデックス B+tree
      • 主キーのインデックス clustered index
      • 主キー以外のインデックス secondary index (主キーがこれでわかる)
      • B+tree はleafが連結リスト.rangeで検索とかのときにベンリ
      • 例とか.この辺発表の図をみるとわかりやすくてベンリ
    • 話をもどしてfriend_id. なぜ遅いのか?
      • オーダーが違うかも
      • 意図してないインデックスで検索してるかも -> オーダー線形なってるかも
    • 結論そうだった
      • explainするとわかる
      • オプティマイザが間違った判断してた(あるある.経験則). distinct だと解決した
      • 経験則やexplain使わなくても計算量と時間の感覚でどこがまずそうかわかることもあるよ!
        • ミドルウェアのどのへんが悪いかとか計算量の感覚やインデックスの使われ方を理解して推測したり出来るとベンリ
  • Redis の話
    • redis とか
      • kvs nosql(ほかにもグラフデータベースとかあるよねー)
      • list/set/sorted sets(rankingの実装らくとか)/etc .. データ構造いっぱいあってベンリ
      • メモリ最適化 2.2 から入ってる.勝手に圧縮してもらえる
        • -> パラメータ眺めてオーダーとか変わったりするのでは?と推測
    • 怪しいのでパラメータ変えて実際試す
      • データ数と実行時間からオーダーわかる
      • データ数10kで1 sec超えるのオカシイのではと気づく.
    • やっぱり -> データ構造違ってた
      • ドキュメントに実は書かれてたりも.
  • まとめ
    • 知識だけじゃなく感覚を身に着けてるとすぐわかったりしてベンリ.現場で検証しまくるとかできなかたったりするし.
    • まつもとりーさんも言ってたが快適にウェブサービス使えるのは当たり前だし,この辺にコミットするために計算量ベンリ
  • Q&A
    • from yuuki: このへんミスったりすると自律制御したりしても一瞬でリソース食いつぶしたりするし,このへん自律制御するのどうするかとかもおもしろそう

べんり ホーム - 鳥獣戯画制作キット

計算量の感覚大事.

STNS 〜点と線を結び新しい何かを作るコト〜

15:25 | pyama(ペパボ) | STNS 〜点と線を結び新しい何かを作るコト〜 | 25分

  • やましたさん. @pyama86
    • ムームードメインやってる
  • Linux のユーザ管理
    • いつから LDAP 主流だと錯覚してい(ry
  • 最近のユーザ管理
    • root おおい.
  • rootは問題あるよね
    • セキュリティ事故の追跡
    • sudo権限の分離
    • userの追加削除管理困難.追加でポリシーのため他のサーバにも足す…とかやるとこまる.
    • rootはアブナイ!!!
  • でもなぜやってしまうか
    • なにかあったときしか困らない
    • まぁLDAP構築たいへんだよねとかある.
  • STNS で解決!
    • STNS/STNS: Simple Toml Name Service
    • 名前解決/公開鍵取得/アカウント認証のみを提供.LDAPはいろいろできすぎて困る.
    • ls -ltr の名前は実はid. pyama -> id:1000 .これが名前解決
  • デプロイユーザの管理できる
    • capistorano とか公開鍵たしてsshでやるよね.authorized_keys 管理だるくなるよね.
    • stnsだとスッキリ!誰がdeployできるか一目瞭然でよい.
  • 組織構造を表現できる
    • group
  • シュッとインストールできるよ! 2.5min
  • 事例
    • pepaboさん事例.ユーザ管理もgithub flowで. ghe でpr -> droneでテスト & レビュー -> deploy
    • 承認してマネージャーが承認して…とかめんどくてそれ解決できてベンリ.エンジニアで管理
    • ghe で全部管理できるので入退社管理とかも楽
  • プロダクトを運用すること
    • ヒントとなった技術からアイデア得たり
    • mackerelのtomlとかgo使ってるやつ参考にしたり
    • linuxで利用可能な共通ライブラリがgo1.5から作れるようになってこれがでかい.Cを書かなくてもLinuxのシステムに手を入れられる
    • 自分が開発者だし自分がSTNSを最高にできる!!!

LDAP とかユーザ管理あんまり関わってないけど便利そうだった.良い話きけて良い.

  • Q&A:
    • from yuuki: hatena は LDAP だけどSTNSよさそう!

負荷分散技術を選ぶ時に考えること

15:55 | masayoshi(はてな) | 負荷分散技術を選ぶ時に考えること | 25分

負荷分散技術を選ぶときに考えること // Speaker Deck

  • masayoshi @yoyogidesaiz
  • 負荷分散技術の概要
    • データセンター分散したり
    • ロードバランサーでbackend web分散したり
    • サーバ内/ロールごと/サーバごと のだいたい3つの分散ある
  • サーバ内リソースの負荷分散
    • ハードウェア機能: hyper-threading, raid0
    • ハードウェア意識のOSの負荷分散: numa, rss/rfs/rps
    • OS意識のアプリケーションの作り方: fork()/clone() などのシステムコール
    • Numa NUMA - Wikipedia
      • NUMA(Non-Uniform Memory Access、ヌマ)とは、共有メモリ型マルチプロセッサコンピュータシステムのアーキテクチャのひとつで、複数プロセッサが共有するメインメモリへのアクセスコストが、メモリ領域とプロセッサに依存して均一でないアーキテクチャである – NUMA - Wikipedia

    • application
      • fork() プロセス
      • clone() スレッド
      • select() プロセス <- x (io多重化とかに影響するだけでCPUには影響しない?)
      • このへんいろいろ理解してアプリケーションコードかく! (なるほど)
  • ロールごとの分散
    • proxy/web/db などのロールの分散
    • proxyでキャッシュしたり,全部のリクエストがdbまで到達しないようにとか
    • キャッシュ
      • ユーザに近いところで返す
      • キャッシュヒットしなくて破綻とか実装に無理があったりとかもする.
      • -> 使いどころを見極める.キャッシュは麻薬
  • サーバごとの負荷分散
  • まとめ
  • Q&A
    • どこが負荷おおいか?
      • disk がおおい.dbとか.ロールごとに分けておいてキャッシュ聞かせていく戦略おおい.(計算機の仕組み的にdiskがおそい from yuuki).最近iodriveとかあって速くなってきてるのでスケールアップが多い.

サービスに寄り添うログ基盤 - ログ収集のその先に -

16:20 | monochromegane(ペパボ) | サービスに寄り添うログ基盤 - ログ収集のその先に - | 25分

サービスに寄り添うログ基盤/pepabo_log_infrastructure_bigfoot // Speaker Deck

  • monochromegane
    • minne
    • 普段 rails 書いてる.最近ログ基盤の開発してる.
  • ログはいいぞ!
    • ログはウェブサービスにおいて重要
  • 行動ログ
    • アプリケーション層で出力するログ(apacheのログとかではない)
    • いつ,だれが,なにをやったか特定できる
    • 最終結果だけでなく,どこであきらめたか.どう迷ったかが分かる
      • 特にどこであきらめたか.どう迷ったかが分かるところにサービス改善のヒントが詰まってる!!!

  • 行動ログ活用段階
    • 収集: とりあとめる
    • 分析: 視覚化したり分析
    • 活用: 分析結果を使って継続的なサービス改善
    • “ログの活用”
      • ログがたまるだけとか,グラフでてバンザイ!するだけではだめ!!
    • -> ログの活用基盤をつくる!
  • Bigfoot
    • ペパボの次世代ログ活用基盤
    • minnneのそれでもある.
    • UID 振る.アプリでログを吐いて集約基盤へ -> 分析 -> サービス改善など活用
  • Bigfoot を支える技術
    • ログを送る
      • fluentd rack-bigfoot(rails) OSS的な感じではなさそうか…?
    • 集約基盤 Treasure Data
      • 活用に力入れたかったので集約基盤はとりあえずTDに任せた.あとで自社のものにするかも
      • Hive QL. 行動ログをSQLライクに扱える
    • ワークフロー
    • 属性情報
      • 行動ログのidから性別などなど属性情報を組み合わせると分析の幅が広がる
    • 名寄せ
      • サービスのアカウントと各クライアントをマッピング
      • 未ログイン状態のアカウントも名寄せ後に過去に遡って紐付け
      • cookie syncと組み合わせてサービス間の紐付け
    • Big Cube とCube
      • BigCube: 全サービスの行動ログを集約してる
      • 切り口が確定したらCubeに切り出す(cubeに切り出されるとpostgresに入るので速く集計できたりする)
    • 視覚化と分析
    • 活用
      • 画面デザインの変更.ステップ見直し
      • A/Bテスト
      • -> 静的なフィードバック.人が判断.固定化する.
    • 動的なフィードバックがよいのでは?
      • システムが行動ログを吐く -> 変換したものをシステムが受け取る -> 挙動を変える
      • バンディットアルゴリズム
      • リコメンド
        • matrix factorization
        • 強調フィルタリングの一種
        • tdのなんかでできるらしい?
    • browse,card abondonment
  • サービスに寄り添うログ基盤
    • 単に集めるだけじゃなくて分析,活用の段階を補助する
    • 静的フィードバックから動的フィードバックへ
    • 行動ログの循環によってなめらかな世界へ.ということでログはいいぞ
  • Q&A
    • Q「送る側のサービスからログ基盤への負荷の問題はどうか」A「fluentdがよく出来ているから問題ない」 https://twitter.com/matsumotory/status/749151437850980352
    • Q「fluentd自体の負荷はどうか」A「ない、今の設計規模では大丈夫」もともとfluentd自体はつかってて大丈夫ならしい
    • td負荷? ログを送りつけるぶんには負荷は気にしなくて良い.送ったあとにインデックス使わずに集計したりするとまずい.プランやすかったりすると特に.

まじか…

活用を重要視していてよさそう.おもしろい話だった.

「ログはいいぞ」.

若手インフラ座談会

16:55 | masayoshi(はてな) taketo957(はてな) alotofwe(ペパボ) hanazuki(ペパボ) モデレータ stanaka(はてな) | 若手インフラ座談会 | 40分

  • 新卒1,2年目の人達で座談会.stanakaさん仕切ってる
  • 自己紹介
  • なんでインフラ?
    • masayoshi: ネットワーク系の研究してたり.かっこいいじゃん?
    • hanazuki: KMC入ってた.サーバーいじったりするの楽しかった.
    • taketo957: B3くらいでRailsやってたらブラックボックスおおくていろいろ調べてた.京都ならはてなでインフラできそうということでアルバイトしてisuconしたりなどなど.面白くてそういう形式で入社した.
    • alotofwe: 大学時代はそんなにインフラしてたわけじゃなかった.アプリは書いていたけど,やっていてアプリはもういいやという感じになった.インフラorセキュリティやろうとおもって,セキュリティ志望だけどセキュリティするためにはインフラ知らなきゃだめなので今インフラやってる
        1. せきゅりてぃ専門のひとpepaboにいる?
        1. ちょっといる.matsumotoryさんとかも?
      • はてなではセキュリティ専門はいない.みんなでやる.
  • アプリとインフラの境界? たとえばDBへのクエリ,スキーマあたりの責任や分担とかどう?
    • masayoshi: 監視業務のアルバイトしてその時はじめてインフラやった.そのときはコード書いてる人はいなかった.DBの設計まではしないけど性能のチェック,クエリのパフォーマンスチェックなどはしてた.はてなでは? -> だいたい一緒.
    • taketo957: 入社以前はもっとハッキリした境界あると思ってたけど,入ってみたらアプリ側が一緒にやってくれたりとか.インフラはmiddle wareより下はだいたいみてる.
    • hanazuki: パフォーマンスチューニングとかが境界.どっちからもみる.上からみるか下からみるかって感じ.境界がハッキリはしてない.
    • alotofwe: だいたいhanazukiさんと同じ.開発のひとからpull-reqが飛んできてレビューするとかまであるくらい境界は曖昧.ある程度権限はあるけどソレ以外はほとんど境界はない.
  • インフラエンジニアになって1-2年,おもしろかったことや印象的なエピソードとかある?
    • masayoshi: はてなはオンプレ環境ある.ネットワーク機器をみてエラーパケットとかみたりした.そこで傾向とかがみれて面白い
    • hanazuki: プライベートクラウド使ってる.チョット前まではオンプレ.仮想環境を使う側.awsを使うのとは違ってどう動かすかとかツールとか作っていく.この辺が楽しい
    • taketo957: いろいろわかってきて嬉しい!
    • alotofwe: 推理小説がスキ.障害の何が原因か調べるのが探偵の推理っぽくて楽しい.
  • 研修でよかったこと
    • taketo957: 今年からインフラでも新卒研修.知識編と設計編.設計編がおもしろかった.実際に動いてるサービスにたいしてこのくらいのリクエストならどう設計するか.身になる部分でよかった.
    • hanazuki: 1ヶ月間くらい研修ある.内容自体はだいたい知ってたりもしたけど試行錯誤がおもしろかった.
    • alotofwe: 今年は研修作る側.内容: railsつくって運用基盤つくってmobileつくる.最低限の生き延びるための知識を得るようなもの.インフラのものはあまり知らなかったので特に勉強になった.
    • masayoshi: 全部taketoくんに言われた! ミドルウェアの組み合わせだけじゃなくて構築するとかコードを書いて作ってみるとどこがボトルネックになるのかとかわかったりする.作ったり構築・コード書くのも研修でやるとその辺に気づけて勉強になるのでベンリ.
  • hatena は京都と東京.pepaboは福岡と東京にある.2拠点でやってることに関して感じることとか?
    • taketo957: 自分がやった話ではない.東京とのコミュニケーションコストを下げる.提示前15minのslack call雑談とかやってる.(yuukiさんが導入した)
    • masayoshi: taketoさんの続きってかんじ.今すぐ,まだまとまってないけど考えを話し合い,そういうときに音声チャットあってすぐに話せる環境があるとよい.ちょっと話したいけどいいですか?って聞いたらすぐ聞けるという環境が大事でベンリ.
    • taketo957: 補足.はてなは社内の文化的にこんなことも残すのか.というところまで残す.音声もベンリだけどチャットなどのテキストで何でも残すというところもよい.
    • hanazuki: みてるサービスが違うのでそこまでコミュニケーションをする必要はそもそもない.週1でビデオコールとかしてる.コミュニケーションとりたいけど取れないときもあるのでなんとかしたい.
    • alotofwe: slackべんり.アイコンが顔写真でよい.
    • masayoshi: アイコンもよいよね!
    • どうしても落ちがちな情報.手元で詰まってるけど大変とかいう情報とか.
  • ロールモデル.将来どうなりたいとか.社内社外問わず目標としてる人とかいるか?
    • masayoshi: 具体的な人物とかはあんまりない.エンジニアとアカデミック系では本来あんまり境界はない.インフラのパフォーマンスチューニングとかは個人の技術という側面がおおいので体系化して学術分野にも広めたりアウトプットしたい.
    • taketo957: 入社したときはyuukiさんにあこがれてといった.ネットワーク周り喋ってるひとがおおい.アプリ観点から観てるひとはあんまりすくなさそう.研究室でやってきた人力でやる仕組みとか応用したりしていきたい.(クラウドソーシング?)
    • hanazuki: あんまり決まってない.人力の認知力でやったりするのはうっかりとかが発生して困るのでつらい.半分寝てても仕事できるようにしたい
    • alotofwe: よくばりさんなので側面によって尊敬してる人が異なる.つまみ食いしていきたい.食べてる最中なのであんまり決まってない.
  • インフラエンジニアの醍醐味?.会社のいいところは?
    • taketo957: レイヤーが下がるにつれて相手にするものが多くなる.それにつれて自分の知識も増やす必要がある.いろんな知識を裏側まで見通してやっていったりするのは面白い.会社のよいところ.新しい技術・古い技術と幅がでていてよい.
    • masayoshi: 基盤をつくるのが面白い.大規模なものを支えるところが面白い.裏方だけど支えがないと動かない.このアラート放置していいんすか?とか聞くと先輩とかが話してくれて一緒に解決してくれたり.
    • hanazuki: インフラといってもハードウェアみたりチューニングしたりなどいろいろな分野・仕事がある.いろいろできるのよい.それぞれスキなレイヤで改善できるところとかよい.
    • alotofwe: アプリと比較.アプリ開発だと適当に書いても動いちゃう.インフラだと1行追加すると大きい影響があってじっくり考慮してかかなきゃだめだったりする.向上心強い人がおおい.よくしていきたいという気持ち.勉強しやすい会社.

僕の(雑)まとめ感想とか

インフラ系のことはほとんどできていなくて,知識も全然ないけど,どんな感じでやってるのかなぁと興味はあったので参加しました. アプリケーション書くときもインフラ意識して書くとよかったりするのでインフラエンジニアとかウェブオペレーションエンジニアみたいな職種につかなくても知っておくと便利. 実際座談会の話では,はてなさんでもペパボさんでもアプリケーションエンジニアとインフラエンジニアの仕事の境界とかは曖昧で,上から見るか下から見るかの違い程度しかないみたいな話をしてた気がする. 僕も,もっとインフラ意識しながら開発していきたいなーという気持ち高まった.

ネットワーク周りだとシステムコールとかいろいろ正直よくわからんくて前提知識僕が持ってないんだなーという話は多かったけど, わかんない話でもおもしろいなーと得られる部分あったり,割とまだ分かる話(相対的に)でも知らないことがたくさん学べたり,なるほどなーとかそうだよなーよいよい!という話ばかりで どの発表もよくて来てよかった.

運営・発表者の皆さんありがとうございました.

]]>
<![CDATA[6/2までKindle本50%オフセール!IT系の本で僕が欲しい本をまとめた!!!]]> 2016-05-30T22:41:43+09:00 http://haya14busa.com/amazon-kindle-sale-til-2016-06-02 <![CDATA[

2016年5月27日(金)00時00分~2016年6月2日(木)23時59分(日本時間)まで Kindle 50%OFF セール をやっているらしく,コンピュータ・IT系の本で僕が欲しい!!!よさそう!!!と思った本をまとめました.

先日 Kindle Paperwhite Wi-Fi を買った勢としてはこのKindle セールに乗るしかない!!! 積んでいた Soft skills の原著をKindle paperwhite で最近読んでますが,paperwhite かなり読みやすくて感動してます. いくつか固定レイアウト型の本がセールになってて, paperwhite で読むのはそこまでよさそうでもないけどそんなことは気にしない.

なおまとめてみると全部ポイント還元ではなく50%OFFセール本でした. 基本的にここから集めました. Amazon.co.jp: コンピュータ・IT - Kindle本: Kindleストア

かなり欲しい!!!というか多分買う本

昔,図書館で借りてたけど途中で返しちゃった.プロコンでオススメの本といえば蟻本が必ず上がるし欲しい! プロコン最近やってないけど時間とってやりたい…

(途中までしか読めてないけど)蟻本よかったしセキュリティコンテスト版もよいでしょ.という気持ち.CTFどんなもんか気になる.

去年インターンの面接の対策として物理本を借りて読んだ.コーディング面接…?なにそれ…?という状態の自分にとってめっちゃよかった.読んだことあるけど手元にKindle本としてあってもよいかも.

「世界で闘うプログラミング力を鍛える150問 トップIT企業のプログラマになるための本」がよくて,プロジェクトマネージャー版がでるとの噂を聞いて本屋でちらっと立ち読みした. 全然内容覚えてないけどよさそうな雰囲気だったので欲しい.

名著感.確かちょっと読んだことがあるのは情熱プログラマーで,闘うプログラマーの方は読んだことなかった.よさそうなので欲しい.情熱プログラマーはKindle版がそもそもなかった…洋書版ならあるけど…洋書版セールじゃないけどほしいな.

レガシーコード改善ガイド
翔泳社 (2016-01-15)
売り上げランキング: 264

聞いたことある名著感.レガシーコード改善したいし,今回のセールで一番欲しいかも知れない.(上記に挙げてる本はほとんど固定レイアウトだし…) レガシーコード改善ガイドを読んで vim-easymotion といった割とレガシーなメンテしてるプロダクトをリファクタリングするゾ!!!という気持ち.

買うか迷ってるけど欲しい!!!本

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
マイナビ出版 (2015-01-30)
売り上げランキング: 383

プロコンとかアルゴリズム本の文脈でたまに聞く.ちらっと本屋で立ち読みしたことがあった気がするけどよさそうだった.

オンラインジャッジではじめるC/C++プログラミング入門
マイナビ出版 (2014-06-28)
売り上げランキング: 6,224

C/C++わかってなくて,オンラインジャッジでやれるところがよさそう.C言語学んで Vim のコードベースにコントリビュートするゾ!!!という気持ち.

TCP/IP の基礎
TCP/IP の基礎
posted with amazlet at 16.05.30
マイナビ出版 (2011-02-23)
売り上げランキング: 18,345

TCP/IP,よくみる有名な本ではないけど,評価も高そうだし基礎として読みたい.

徳丸浩のWebセキュリティ教室(日経BP Next ICT選書)
日経BP社 (2015-11-10)
売り上げランキング: 86

自分が書いたコードが知らないうちに脆弱性持ってたりすると怖いし,セキュリティ系は知識として知っておくと便利そう.

世界でもっとも強力な9のアルゴリズム
日経BP社 (2013-10-10)
売り上げランキング: 246

聞いたことあるし読み物として面白そう?

Webエンジニアが知っておきたいインフラの基本
マイナビ出版 (2014-12-27)
売り上げランキング: 990

インフラ,わかってないので基本を知りたい!

コンピュータの構成と設計 第5版 上
日経BP社 (2015-02-25)
売り上げランキング: 484

「パタ&へネ」本.名著感.この辺の知識全然なさそうなので読むと便利そう.「ヘネパタ」本はセールじゃなかった…

コンピュータの構成と設計 第5版 下
日経BP社 (2015-02-25)
売り上げランキング: 475
OSの仕組みの絵本
OSの仕組みの絵本
posted with amazlet at 16.05.30
翔泳社 (2013-05-20)
売り上げランキング: 6,184

OSの仕組みに関する知識も全然もってないのでよさそう.

「なぜなのか」系

コンピュータはなぜ動くのかは昔読んだことあってよかった. なのでこの「なぜなのか」系統の本はよさそうというイメージ. 「なぜなのか」系は他にもセールだったけど気になるタイトルだけ抜粋.

オブジェクト指向でなぜつくるのか 第2版
日経BP社 (2014-03-05)
売り上げランキング: 114
システムはなぜダウンするのか
日経BP社 (2014-03-05)
売り上げランキング: 493

ちょっと欲しい気もする

ピープルウエア 第3版
日経BP社 (2014-02-05)
売り上げランキング: 339

聴いたことあるし名著っぽいので

テストから見えてくるグーグルのソフトウェア開発
日経BP社 (2014-02-12)
売り上げランキング: 5,657
Linuxサーバーセキュリティ徹底入門
翔泳社 (2013-11-12)
売り上げランキング: 4,856

サーバのセキュリティ,ちょっとくらい知っておいても損はなさそう.

Docker わかってないので知っておきたい.

ソーシャル・エンジニアリング
日経BP社 (2013-08-28)
売り上げランキング: 4,286

セキュリティ系は知っておいて損がなさそう.

持ってるけどオススメ!本

持ってるけどまだ読んだとは言ってない! 読みたい!!!

技術書・ビジネス書 大賞 部門

ITエンジニアに読んでほしい!技術書・ビジネス書 大賞 2016 に含まれている本.Kindle セールになってるの2つしかなかったけど.

HARD THINGS 答えがない難問と困難にきみはどう立ち向かうか
日経BP社 (2015-04-17)
売り上げランキング: 14

おすすめされた. 2015年の電子書籍で一番売れたとか評判よさそうだし読み物として買いたい.

21世紀のビジネスにデザイン思考が必要な理由
クロスメディア・パブリッシング(インプレス) (2015-07-22)
売り上げランキング: 214

55%なので6/2までのセール対象というわけではないかも.

追記.記事書いた後に見つけた本

おわりに. テンプレ的な何か

Amazonギフト券- Eメールタイプ - Amazon Kindle
Amazonギフト券 (2012-12-10)
売り上げランキング: 17

Kindle 本は欲しいものリストでプレゼントできないらしいですが,Amazon ギフト券で (hayabusa1419 at gmail.com) にプレゼントしてくれてもいいですよ???

]]>
<![CDATA[revital.vim で vital.vim を爆速にしてお前らの Vim plugin を速くする]]> 2016-03-23T04:45:43+09:00 http://haya14busa.com/revital-vim-makes-vital-vim-a-lot-faster <![CDATA[

haya14busa/revital.vim

この度, revital.vim というプラグインを作って vital.vim のモジュールのローディングを爆速にしてお前らが使ってる Vim plugin を速くしました.

めでたい.

あと気づいたんですが今日は僕の誕生日のですね.これもまためでたい.

そして本当のところは vital.vim を使ってるプラグイン開発者が, revital.vim を使って初めて速くなるので実はまだ速くなってないものが多いです. 待ちきれない方はこの記事を読んで revital.vim の使い方を覚えてプルリクしていきましょう. また爆速にはなったと思うんですが,体感には個人差・環境差があり,もともとほとんど速度が気にならない人も多いかと思うのでご注意ください. Windows だか symlink だか virtualbox だか neovim だか何かはまだよくわかってませんが,特定の 環境が原因なのか vital.vim をヘビーに使用しているプラグインにまれによく「遅いぞ?」という issue が飛んできたりしていて, そういった方には顕著に効果があると思います.(たぶん)

参考: 遅いと言ってる人たち (放置気味でスイマセン…いや workaround な修正なら前からできたんだけど手元でも再現しないものをその場対処はあんまりやりたくなくてやる気が…)

vital.vim とは?

https://github.com/vim-jp/vital.vim

Vim script の最高のライブラリです.vital.vimのいいところとダメなところを個人的に上げるとこんな感じです.

vital.vim のいいところ

  1. 組み込み式なので依存ライブラリがアップデートされても安心
  2. プラグインのユーザはvital.vimを別途インストールする必要がない
  3. めっちゃ気軽に外部ライブラリを作って使える
  4. けっこういろんなものがそろっていて種類が豊富
  5. 日本人の凄腕 Vimmer 達が開発・メンテしているので品質も高い

とにかくベンリ

vital.vim のダメなところ

  1. (ロードが) 遅い (ケースがある)(基本的に気にならないけど)
  2. ドキュメントがたぶん足りないので敷居が高い(ように見えるだけで使い方は簡単なんだけど…)
  3. インストールやアップデートでハマることが多い印象

他のところで,ファイルをコピーするのが無駄だとかキッチンシンクじゃない?みたいな 話を聞いたことがありますが,前者は全然気にする時代じゃないはずだし,後者は単なる勘違いで vital.vim は使うライブラリのみ組み込めるので問題ないはずです.

Githubのリポジトリでは

This is like a plugin which has both aspects of Bundler and jQuery at the same time.

と(たぶん昔から)書いてあって,Bundler はともかく jQuery はちょっとあんまり良い印象ではないのかなぁと思う. どちらかというと lodash とか Bundler に合わせるなら npm と言ってもいい気がする.

で,話が少々それていますが,今回 1つ目のモジュールの読み込みが遅いという部分を revital.vim で解決してみました. 2と3はドキュメントを拡充したり,:Vitalizerというコマンドをもうちょっとユーザフレンドリーにしてあげるといいのかなぁと個人的に思うのでなんとかしたいと思います. (去年くらい前から思っているのでつらい)

とにかく vital.vim 最高なので revital.vim と一緒に使っていきましょう.

速くなったというならまずはベンチマークじゃん?(雑)

スクリプト

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
command! -bar TimerStart let start_time = reltime()
command! -bar TimerEnd echo reltimestr(reltime(start_time)) | unlet start_time

function! s:_vital_of() abort
  let V = vital#of('incsearch')
  call V.load('Data.List')
  call V.unload()
endfunction

function! s:_vital_incsearch_of() abort
  let V = vital#incsearch#of()
  call V.load('Data.List')
  call V.unload()
endfunction

let s:times = 100

TimerStart
for _ in range(s:times)
  call s:_vital_of()
endfor
TimerEnd
" => 1.565324

TimerStart
for _ in range(s:times)
  call s:_vital_incsearch_of()
endfor
TimerEnd
" => 0.028437

結果: vital#of() と V.import(‘Data.List’) 相当を100回回したベンチマーク

vital.vim (x 100) revital.vim (x 100)
1.565324 sec 0.028437 sec

ref: https://github.com/haya14busa/incsearch.vim/pull/112#issue-142680963

速くなってますね!!!

(まぁ僕の環境ではもともと1回分にすると0.01秒くらいで全然遅いと感じたことなかったのですが)

上記のベンチマークは https://github.com/haya14busa/incsearch.vim のコードで回しましたが,https://github.com/easymotion/vim-easymotion や 手元でテスト用に作った全部のvital モジュールをインストールしたプラグインでも同じような結果になりました.

revital.vim の使い方

さぁこれだけ速くなってるならvital.vimを既に使ってる人なんかは特に revital.vim を使ってみたくなりましたよね? 使い方は簡単です.

わかってる人向けの簡単説明

  1. vital.vim で :Vitalize しておく
  2. revital.vim の :Revitalize コマンドを実行
  3. vital#of('{plugin-name}') の変わりに vital#{plugin-name}#of()を使う.

これだけです.インターフェースは vital.vim をそのまま使う場合とほとんど変わりません.

ちょっと丁寧な説明

a) モジュールをインストール

{pluginname} をあなたかが開発しているプラグインの名前に置き換えましょう.

  1. :cd /path/to/your/plugin プラグインのディレクトリに移動.もちろん shell で移動してから Vim を起動するとかコマンドで指定してもOK.
  2. :Vitalize --name={pluginname} . +Data.List Data.List モジュールを組み込んでみる
  3. --nameには.-が使えないので適宜vim-とか.vimとかいらない部分は削る.
  4. :Revitalize . Revitalize 実行!

b) モジュールの使い方

1
2
3
4
let s:V = vital#{pluginname}#of()
let s:List = s:V.import('Data.List')
echo s:List.uniq([1, 1, 2, 3, 1, 2])
" => [1, 2, 3]

簡単で vital.vim のみで使うケースとほとんど変わりません.

c) モジュールのアップデート

:Vitalizeでアップデートしたあとにもう一度 :Revitalize する必要があります.

  1. :cd /path/to/your/plugin プラグインのディレクトリに移動.
  2. :Vitalize . アップデートするときはこれだけ.
  3. :Revitalize . Revitalize 実行

どうして revitalize.vim は速いのか?

vital.vim はモジュールをロードする際(s:V.import('Module.Name'))に

  1. モジュール名から目的のファイルを探す
  2. そのファイルからモジュール用のオブジェクトを作る

というざっくり2段階が必要です.このうち1などはsimlinkをたどるといった関数が遅 いせいで結構な時間が環境によってはかかっていますし,2はそこまで遅くはない気もし ますがいろいろ内部でやっています.(ちゃんとprofileはしてない.) vital.vim の組み込みライブラリであるという性質上,普通にやるなら上記の段階が必要です. また歴史的経緯も含まれているかもしれないですが上記の2段階の前にはライブラリを ロードするためのローダーを同じような手順で作成する必要があります(vital#of({plugin-name})).

速くする方法はないものかなぁと考えると1つ思い当たります.

モジュール名から目的のファイルを Vim script で素早く探すのにはなかなか骨が折れますが,Vim script には autoload function という機能があり,autoload 関数をいい感じに定義しいい感じに呼 べば(path#to#module#file),対応したファイルを勝手に内部で見つけてきて関数を呼 んでくれるという仕組みが存在します.Vim script で頑張るよりも組み込み機能を 使ったほうが速いはずです!! autoload 関数使いたい!!!

しかし,autoload 関数をいい感じに定義する際にはファイルのパス情報が必要になります. ここでvital.vim は別のプラグインに組み込まれることが前提のライブラリなのでパスが定まっていません. よって autoload はそのままでは使えませんでした…

うーん.困ったな…

でもでも,よくよく考えてみると最初からパスが定まっていなくても,モジュールをプラグインに組み込んだあとのパスは決定しています. つまり,:Vitalize でモジュールをプラグインに組み込んだあとにモジュールのファイルに適切な autoload 関数を追加してしまえばいいのです.

もうわかったでしょか?

revital.vim が提供する :Revitalize コマンドは組み込んだモジュールのファイルに 適切なautoload関数を追加し, s:V.import('Module.Name') の内部では追加した autoload 関数を呼ぶようになっているのです. また,autoload関数を追加する際にモジュールのオブジェクトの雛形となるようなオブジェクトをついでに生成してやっているので, そのオブジェクトの生成するプロセス分も速くなっています.

最後にファイルを探したりといろいろと大変な vital#of({plugin-name}) の代わりに 直接 vital のローダーオブジェクトを返す vital#{plugin-name}#of() を作って revital.vim の仕事は終了です.

モジュールのファイルに autoload 関数を追加するというちょっと刺激的なハックをすることによって vital.vim のロードを爆速化することができました. ただモジュールのファイルといっても組み込んだあとのモジュールのファイルを書き換 えるだけなのでオリジナルのファイルは書き換えないし,これくらいのハックは別に問題ないかなと思います. 一応vitalモジュールのファイルの途中で :finish されると使えなくなるという問題がありますが,まずそんなケースはないでしょう.

本家に入れたいような気もするけど要相談?

確実に速くなるし,本家ではテストされてないような部分もテストしているので,少々 ランニングして安定すれば本家にオプションかなにかで入れてもいいんじゃないかなぁと思います.

しかし,一応入り口のインターフェースが変わることと,ちょっと実装にハック感があ ること,ドラスティックな変更なのでいろいろ待つよりもまずは実装して見てみるということで revital.vim を作ってみました.

後方互換性は崩さないような仕組みになっているので時が来たらvital.vim 本家に同じ機能が実装されるとよさがありますね.

おわり

haya14busa/revital.vim で vital.vim が爆速になるので vital.vim を使っている Vim プラグイン開発者各位や これから vital.vim を使ってみたいという 各位は是非お試しください!

「爆ぜろリアル! 弾けろシナプス! Rev!talize Th!s World!」

]]>
<![CDATA[Vimのカーソル移動はもっともっと爆速になる! Vim-EasyMotion v3.0 をリリースしました]]> 2016-01-19T03:14:43+09:00 http://haya14busa.com/vim-easymotion-ver-3-dot-0-overwin-motions <![CDATA[

Vim-EasyMotion でウィンドウをまたいだ移動ができるようになりました!!!

easymotion/vim-easymotion

過去の関連記事

そして今回の記事はvim-easymotionのリポジトリがorganization持ちになって久々に便利機能追加したという記事になります.

vim-easymotionとはVimのカーソル移動をブラウザでいうHit-A-Hint機能のように行うカーソル移動改善系プラグインです. よく知らないよ〜という方はREADME眺めたり, 過去記事 (Vim-EasyMotionでカーソル移動を爆速にして生産性をもっと向上させる - haya14busa ) をさらっと読むと分かるかと思います.過去記事はn-key Find Motionの節以外はだいたい現役で使えると思います.

vim-easymotion v3.0

Release EasyMotion now supports moving cursor over/across windows · easymotion/vim-easymotion

vim-easymotionではこれまでカーソルと同一ウィンドウ内にしか移動できなかったのですが,v3.0でとうとう他のウィンドウにも移動できるようになりました! めでたい. 実はv3.0 で追加するメイン機能はこれだけなんですが,個人的にかなり気に入ってしまい,「これはメジャーバージョンアップするしかない」と思い勢いだけでバージョンあげています. 他にはバグフィックスとか細かい修正で,とくに後方互換性は壊してないはずなので気軽にアップデートできると思います.

追加したマッピング

mapping description
<Plug>(easymotion-overwin-f){char} {char} にマッチする位置を対象として移動
<Plug>(easymotion-overwin-f2){char}{char} {char}{char} にマッチする位置を対象として移動
<Plug>(easymotion-overwin-line) 行を対象として移動
<Plug>(easymotion-overwin-w) 単語の先頭を対象として移動

マッピング例

ヴィジュアルモードやオペレータ待機モードで他のウィンドウに移動するというのは意味をなさないので, 他のウィンドウに移動する overwin モーションは Normal モードのマッピングのみ提供しています.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
" <Leader>f{char} to move to {char}
map  <Leader>f <Plug>(easymotion-bd-f)
nmap <Leader>f <Plug>(easymotion-overwin-f)

" s{char}{char} to move to {char}{char}
nmap s <Plug>(easymotion-overwin-f2)
vmap s <Plug>(easymotion-bd-f2)

" Move to line
map <Leader>L <Plug>(easymotion-bd-jk)
nmap <Leader>L <Plug>(easymotion-overwin-line)

" Move to word
map  <Leader>w <Plug>(easymotion-bd-w)
nmap <Leader>w <Plug>(easymotion-overwin-w)

EasyMotion触ったことなくてミニマムに始めたい場合は上記設定から気に入ったもの + 以下の設定でデフォルトマッピングをオフにするとよいかなと思います.

1
let g:EasyMotion_do_mapping = 0

もしひとつだけおすすめするならnmap s <Plug>(easymotion-overwin-f2)がオススメです. このマッピングだと画面上の見えているところへならどこへでも s{char}{char}{hint}の4 キーストロークで移動できます. <Plug>(easymotion-overwin-f)<Plug>(easymotion-overwin-w)だと候補が多すぎてヒントを2回以上打たないとダメなケースがよくあるのですが, {char}{char}と2文字打つと候補が劇的に減ってヒントは大抵のケースで1文字入力するだけですみます.

incsearch.vim との連携

vim-easymotionには<Plug>(easymotion-sn)という N 文字入力してマッチした位置 を対象として移動するモーション,言わばeasymotionの検索(/)版マッピングを提供していたのですが, 今回,それのウィンドウ間移動できるマッピングは提供していません.

というのも,<Plug>(easymotion-sn)はVimデフォルトの検索との互換性が甘いところがあり, Vimの検索はもっともっと便利になる! incsearch.vim v2.0 をリリースしました - haya14busa の記事で紹介した incsearch.vim と vim-easymotion を連携させたほうが基本的に便利になっていて, こちらを推奨したいなという訳です.

必要なもの

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
" You can use other keymappings like <C-l> instead of <CR> if you want to
" use these mappings as default search and somtimes want to move cursor with
" EasyMotion.
function! s:incsearch_config(...) abort
  return incsearch#util#deepextend(deepcopy({
  \   'modules': [incsearch#config#easymotion#module({'overwin': 1})],
  \   'keymap': {
  \     "\<CR>": '<Over>(easymotion)'
  \   },
  \   'is_expr': 0
  \ }), get(a:, 1, {}))
endfunction

noremap <silent><expr> /  incsearch#go(<SID>incsearch_config())
noremap <silent><expr> ?  incsearch#go(<SID>incsearch_config({'command': '?'}))
noremap <silent><expr> g/ incsearch#go(<SID>incsearch_config({'is_stay': 1}))

<CR><C-l>とかにすることで普段は普通の incsearch.vim, <C-l>押した時に easymotion発動といったことができたりします.僕はそういう設定にしていて,便利につかえてます.

おまけ

incsearch-migemo 連携

haya14busa/incsearch-migemo.vim

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function! s:config_migemo(...) abort
  return extend(copy({
  \   'converters': [
  \     incsearch#config#migemo#converter(),
  \   ],
  \   'modules': [incsearch#config#easymotion#module({'overwin': 1})],
  \   'keymap': {"\<C-l>": '<Over>(easymotion)'},
  \   'is_expr': 0,
  \ }), get(a:, 1, {}))
endfunction

noremap <silent><expr> m/ incsearch#go(<SID>config_migemo())
noremap <silent><expr> m? incsearch#go(<SID>config_migemo({'command': '?'}))
noremap <silent><expr> mg/ incsearch#go(<SID>config_migemo({'is_stay': 1}))

fuzzy search で easymotion

1
2
3
4
5
6
7
8
9
10
11
function! s:config_easyfuzzymotion(...) abort
  return extend(copy({
  \   'converters': [incsearch#config#fuzzyword#converter()],
  \   'modules': [incsearch#config#easymotion#module({'overwin': 1})],
  \   'keymap': {"\<CR>": '<Over>(easymotion)'},
  \   'is_expr': 0,
  \   'is_stay': 1
  \ }), get(a:, 1, {}))
endfunction

noremap <silent><expr> <Space>/ incsearch#go(<SID>config_easyfuzzymotion())

実装とか背景の話

実はこのウィンドウをまたいだ移動についてはvim-easymotionの開発を引き継いだころからずっと欲しいなぁ〜やりたいな〜と思っていた待望の機能でした. emacs版easymotionである winterTTr/ace-jump-mode や atomのsmalls (ATOM - smalls つくった - Qiita)ではラベルジャンプでウィンドウ移動ができています.

どの拡張も,もともとはEasyMotion にインスパイアされて作ったもので,他の機能はともかく少なくともウィンドウ間をまたげるという一点ではvim-easymotionを上回っていました. ではなぜ,vim-easymotionでもやりたいなぁと思っていたのに,これまで実装できていなかったかというと, これは「vim-easymotionのコードがちょっと闇でつらかった…」というわけではなく Vim の機能でやるのがきつかったことに起因します. Vimにはオーバーレイで文字を表示するといった機能がないのでvim-easymotionでは一旦バッファの文字を書き換えて戻すという実装になっていました. この実装だと同じバッファを別ウィンドウに表示している場合にラベルがバッティングしてしまいます.

では今回どうしたかというと Vim の conceal 機能, :h syn-cchar を使っています. この機能を使えば直接バッファを書き換えずにラベルを表示できるので,同じバッファが別ウィンドウにあっても違ったラベルを表示することが可能です.

Conceal の syn-cchar つらい問題

しかし,このconcealでラベルを表示するというアイデア自体は以前からあって justinmk/vim-sneak がこのスタイルで表示しています(カーソルと同一ウィンドウだけですが). 前からアイデアを知っていたのにやれなかったのは,決して「vim-easymotionのコードがちょっと闇でつらかった…」というわけではなく conceal機能はかなり制限があってやりたいことができなさそうだなぁと長らく思っていたからです. というも,syn-ccharでは1文字しか表示できないし,空白行といった無の部分に文字を表示することができません. この制限からラベルが2文字以上の時でも1文字しか表示できなかったり, Tab文字や文字幅が2以上のマルチバイトの文字をラベルに置き換えるとラベル表示時に表示にズレが生じてしまいます.

結局「vim-easymotionのコードがちょっと闇でつらかった…」

じゃあどうすればいいかというと,ここまでの話・実装を組み合わせると実は答えは出ています. ラベルを表示するさいに対象が空白行や行末,マルチバイト文字であれば一旦スペースを追加またはスペースに置き換えてしまう前処理をすればOKです. そうすれば空白行や行末を対象とできるし,2文字のラベルも表示できるし,ラベルを表示した際に表示のズレが生じません! やったぜこれで理論上実装可能じゃん!

なぜこれに気づくのに時間がかかったのかというと結局「vim-easymotionのコードがちょっと闇でつらかった…」 のでしばらくeasymotionにまともに向き合ってなかったからでした. vim-easymotionはforkする前のもともとコードがそんなに綺麗じゃなかった上に, プログラミング自体ほとんど初心者だった僕が以前に便利な機能を追加したりVimとの互換性を保ったりすることと引き換えに, コードは比較的カオス状態になっていました. 僕の実装力ではこれを互換性を保った状態でリファクタして他のウィンドウへ移動する抜本的な機能改善なんて無理や… と内心思っていましたし,実際無理感あります. こういった理由でウィンドウ間移動という待望の機能は2年間ほど待望の機能であり続けました.

1から作って互換性意識すればいいんじゃん?

互換性保ちながらリファクタしていくのがつらいなら1から作って,インターフェースを既存のeasymotionと合わせればいいんじゃないか? と気付き,今回の機能は実装されました.

「パンがなければお菓子を食べればいいじゃない」に通ずるものがあります(ない)

これに気づいてやってみると,一旦動くところまで実装する程度なら1日程度で待望の機能が実装できました. やってみるものですね.

しかし,vim-easymotionを活発に開発していた2年前の僕にはできなかったかなぁと感じていて, これまでに incsearch.vim といった他のプラグインを作った知見や,他の Vim プラグイン開発者から得た知見, そして2年間でちょっと基本的な開発力があがったおかげで開発できたなぁと思います. みなさまありがとうございました.そして自分,まだまだですがちょっと頑張ったなって思いました.

開発の実装や背景の話と2年前に熱を入れて取り組んでいたvim-easymotionに今の自分が取り組んでみたらちょっと感慨深かったなという話でした.

おわりに

vim-easymotion, また一段と便利になったので是非使ってみてください!

僕はもうこの機能なしでは生きていけなさそうです.

あぁ〜カーソルがぴょんぴょんするんじゃぁ〜

]]>
<![CDATA[2015年夏,はてなインターンでMackerelをとにかく便利にして優勝してきた]]> 2015-12-31T22:15:00+09:00 http://haya14busa.com/hatena-intern-2015 <![CDATA[

インターン終わったときに書きたかったのですが,バタバタしていたのと,イカ (Splatoon)という麻薬に犯されたので2015 年の振り返りとして書いています. 乗り遅れた感しかないけど記録として残しておきま す! 2015年全体の振り返りはこちら -> はてな,Googleインターン行ってイカやりすぎたイカVimmerの2015年の振り返り - haya14busa

今年の夏はどこかインターン行きたいなぁ〜,最高の夏を過ごしたいなぁ〜と思っていました. そんな時に はてなサマーインターン2015 情報, 特に今年は Scala や swift が書けるコースもある という話を聞いてこれは行きたい! と応募しました. その結果幸運なことに選考に受かったので8月10日から9月4日まで最高の夏を過ごしにはてなインターンに行ってきました.

はてなインターン TL;DR

  • 噂の(?)今年のインターン生の Emacs 率が0だった
  • 僕は言語がScalaのMackerelコースで, id:tyage くんさんと一緒に Mackerel を便利にして優勝してきましたっ! めでたいっ 🎉🎉🎉
  • はてなインターンで優秀なはてなの社員さんや他のインターン生の仲間と開発できたのは最高の体験でした.もっかい体験したい.

参考情報

はてなインターン応募と参加

一昨年の2013年夏にインターン探していたときも,はてなインターン卒業生のブログな どを読んで, はてなインターンよさそうだなぁ〜と思ってました.その時は能力たりな さすぎて無理だろう…というのと perl かぁ〜 という思いで応募すら出来ていま せんでした.

今年は言語が Scala (!)のコースができていたこともあったこと,一昨年よりはアルバイトでScala 書いたりとある程度経験値積んでいて,Vim でよければいくつかアピールできるようなものもあったので えいやっと応募しました.はてなインターンの選考では基本的にポートフォリオを提出するのみ だったのですが,ポートフォリオは8割 Vim でした.評価されてよかった…

ところで実際にインターンに参加してから思ったことは,もし仮に書く言語が perl だったとしても来たほうが良かったなということでした. もし来年度以降のインターン に応募しようと思っているけど perl がちょっとネック… くらいで参加を迷ってるな ら勢いで参加申し込んでもよいと思います.

結局僕は perl 1行も書きませんでしたが.Scala最高!!!(コンパイル爆速になればより…)

事前課題がScalaを基礎から勉強するのに最高

さて,晴れてはてなインターンに行けることになったらインターンまでの学習としてちょっとした事前課題が課されます.

この事前課題では perl か scala, それに JavaScript でいくつか課題が与えられ,問題を解いてテストを通していく形式でした. 事前課題は全体,課題ごとに課題のねらいが書いてあり,言語の基本を学ぶのに最高だと思いました.

Scala の課 題 は 与えられたログ用に Labeled Tab-separated Values (LTSV) パーサを書くという課題だったのですが,言語の基本やテストの仕方,テストの追加の 仕方はもちろんのこと,問題の性質上 エラーハンドリングをどうするか,Option型を 使うか,Either型を使うか,パース失敗時に エラーの行を収集するために scalaz でいう Validation 型を使えばベンリ なんじゃないか…!といろいろやりがいがありました. もっと言うと指定されたログの モデル以外にも対応できるような汎用的なLTSVパーサにしたら便利そうと思ったんですが そこまでは時間と実力が足りなくてできませんでした.残念.

LTSV パーサ,Scalaの課題としてよかったなぁと思います.

またこの事前課題は単にテストを通して提出すると終了…というわけではなくインターンの初日あたりに 講評が行われるのが最高でした.自分のコードはどう書けばよりよかったのか,他のインターン生は どう書いていてどこがよかったのかといったことも知れて便利でした.この講評はインターンの前半課題 でも行われました.

前半の講義がウェブ開発を基礎から勉強するのに最高

スケジュール

  • 1日目 Perl/Scalaによるプログラミングの基礎
  • 2日目 Perl/Scalaによるデータベースプログラミング
  • 3日目 Perl/ScalaによるWebアプリケーション開発
  • 4日目 JavaScriptによるイベント・ドリブンプログラミング
  • 5日目 自由課題
  • 6日目 インフラ
  • 7日目 SwiftでのiOSアプリ開発

前半の講義パートは言語の基礎からWebアプリケーション,インフラ,それにSwift コースや希望者はSwiftのiOSアプリ開発までを1週間で学べるようになっていました. それぞれのコースのインターン生ごとにメンターさんがついてくれます.僕の場合は id:sixeight さん でした.

毎日課題が課されてそれをメンターさんに質問したり,レビューを受けたり,講評をも らったりとヒイヒイこなして行くことで後半に向けて基礎力を向上できる仕組みになってい ました.大変でしたが楽しかった…!

課題としては自由課題が最後で,その内容はそれまでに学んできた技術と課題で作って きたブログシステムを活かして好きなWeb アプリケーションを作るというものでした.

ここで作ったサービスは社内で投票で順位もつく前半の集大成という課題で厳しくも楽 しい,燃える課題となっていました.僕はあんまり独創的なアイデアのなくこれがあれ ばベンリやろ〜というノリでちょっとしたものしか作れなかったのですが,他のイン ターン生のアプリケーションのクオリティが高くて刺激になりました.

完全に言い訳になるのですが僕は自由課題の日の前の土日で開催されたオープンイン ターネッツに公開されてない,はてなで行われたもくもく回ハッカソンで途中まで 作ってた Vim script版 power-assert に夢中になってました. 他のインターン生のつくったサービスをみてもっと自由課題に力入れればよかった…と 思ったので来年度以降はてなインターンへの参加を考えている各位は前半最後の自由 課題はやりがいあってよいよ!と言っておきます.

後半の実践パートでサービスにコミットしていく実践経験が得られて最高

実践パート - はてなサマーインターン2015 レポートサイト

今年から前半/後半の比率が代わり前半が少し短く,その分後半の実践パートが長く なったようです.開発に時間がとれてベンリ.

後半は僕は id:tyage くんさんと一緒にMackerelコースに配属され, ちょっとしたバグ修正によるインターン生最速リリース(ちょっとズルイ)から始まり4つほ ど多方面からMackerelの機能改善や新機能のリリースを行い,最終成果発表で1位を獲得できました!

だいたい id:tyage くんやメンターの id:sixeightさんをはじめとするMackerelチーム の皆さんのおかげでした.

id:tyage くんさんはインターンまでScalaほとんど書いてなかったらしいのに, 後半過程では1年くらいScala経験あった僕と同じかそれ以上のレベルでScalaかけちゃうし, JavaScriptまわりでペアプロして相談しても的確に正しい方向に導いていってくれるし最強かよ〜という感じでした. id:tyage くんさんとのペアプロ,最高に便利体験でした.

id:sixeightさんやMackerelチームの方には 方針を一緒に考えてもらったり,ダメなところをダメと指摘してもらったり,実装・仕様の相談をさせてもらったりと, 最高の体験でした.

id:tyageくんや優秀なMackerelチームのメンバーと 少しの間とは言えチームとして働く経験ができて,しかも最後には優勝というおまけまでついてきて 最高の夏を過ごせました.ありがとうございました!

はてなインターン最高でした

上述したカリキュラム上の話だけじゃなく毎日おいしいはてなのまかないをはてなの社員さんと お話しながら食べたり,他の優秀なインターン生の話を聞いたり,実力を垣間見たり, インターン途中に参加したYAPCや社内TGIF,ごはん連れてってもらったりしたときにはてなの社員さんの 話を聞いたりなどなど,最高の夏を過ごせました.

逆に慣れなかったことといえばはてなの 「シュッと」 っという言葉だけは いやいや「サッと」とかのほうが馴染むじゃん…?とあんまり慣れな かったのですが, インターン終わってから「シュッと」,いいじゃん使いやすいじゃん…? となぜかインターン終わってから馴染むようになってしまいました.不思議だ…

他のインターン生の紹介

イカしたやつらを紹介するぜ! ってやつ.終始凄い人多すぎてびびってました… 僕も頑張りたい.

最高の夏〜

]]>
<![CDATA[はてな,Googleインターン行ってイカやりすぎたイカVimmerの2015年の振り返り]]> 2015-12-31T21:15:00+09:00 http://haya14busa.com/look-back-2015 <![CDATA[

今年は4月から大きく環境が変わったり,インターンに行ったりちょくちょくVim活したりイカ(Splatoon)したりと振り返ってみる と色んな事がありました.

最初にネタバレをすると最も進捗があったのは Splatoon で最高ウデマエS+90まで行ったことでした.

マンメンミ!

去年や一昨年の

今年はプログラミング初めてからだいたい3年目でした.もう初心者とか言ってられないようになって気がしますが,まだまだ勉強不足・力不足すぎるので精進したい.

はてなサマーインターン

長くなったので記事分けた -> 2015年夏,はてなインターンでMackerelをとにかく便利にして優勝してきた - haya14busa

最高の夏〜

Google Japan Software Engineer インターン

はてなインターンが終わった後,3ヶ月ほどGoogle Japanのインターンに行っていました. Googleは内側ではオープンで全然違う国の違うチームの情報を見れたりするオープンさなのですが, 外側にはオープンではないので基本的にブログに書いたらだめっぽい,というか線引きがわ からないので参加した内容に関する感想は書けません…残念…

Googleの優秀なエンジニアさんたちとGoogleという会社で開発できたのは学びもやりが いもたくさんあって最高の体験でした.

インターン対策とか競技プログラミング

はてなとGoogle以外にもいくつか応募して受かったり落ちたりしたのですが, そのうち1回ホワイトボードコーディング面接があり,超基本的なことを聞かれているの にぜんぜん答えられない…という苦い経験がありました.

頭のどこかで僕は情報学部じゃなかったし,プログラミングも独学で好きなことやってる段階だし 基礎的も知らなくてもまだ仕方ない…と思ってた節があったりしました. しかしこれは完全に言い訳です. 基礎は土台.最低限理解して説明できる程度にはなってないと面接とか関係なく マズいな…と思いデータ構造,アルゴリズムなど勉強しなおしました.

その一環として前から興味はあったんだけどなかなか手がでなかった競技プログラミングにも今年はちょくちょく手をだしはじめました. 最近ちょっとイカしすぎておろそかになってますがやっていきたいです.

面接対策でつかった本とか

世界で闘うプログラミング力を鍛える150問

The Joel on Software 採用面接ゲリラガイド

採用面接ゲリラガイド(version 3.0) - The Joel on Software Translation Project)

Vim 活

incsearch.vim v2.0

incsearch.vimをカイゼンしてv2.0にしてました.もっと昔にやってた気がするけど今年 の7月でした.リファクタリングしてコンポーザブルにしたりと拡張性をましてincsearch.vimをより便利にできました.

vital-power-assert と VimConf | Vim script 版 powerassert!

はてなインターン中に作ってその後もちょくちょくと触ってました.またVimConfで発表したりもしました.

去年も大概だったのですが今年は個人的にVimConfに向けての準備がゼンゼン足りてなくて来年こそはちゃんとするゾ!!という気持ちです.

underscore.vim

underscore.jsのVim scriptバージョンを作ってました.実装は去年からなんだかんだやっていましたがエイプリルフールにリリースしました. 実用性がないわけではないけど,ラムダが無いとかスコープの関係上凝ったことしようとすると微妙に使いづらいのが残念ですが開発してて面白かったです.

勢い Vim プラグイン系

niconicomment.vim

niconicomment2.gif (1366×747)

思い出したように実行すると面白い.

vim-undoreplay

undoreplay_fizzbuzz2.gif (1366×747)

思い出したように実行すると面白い2.

vital.vim にPullRequest

vim-jp/vital.vim というVim scriptのライブラリにいくつか欲しいモジュールをPRで送ったりした. Vim.ScriptLocal は Vim scriptのスクリプトローカル変数を外からハックするモジュールでVim scriptのテストフレームワークである thinca/vim-themis で使ってもらったりした. 他にはPythonあたりにある便利なデータ構造をVim scriptにポートする業などしていました.

Vim Advent Calendar

前から自分が欲しかった,まとめたかった記事を書きました.今までで最高ブクマ数を頂いてありがたい.

vim-operator-flashy

年末駆け込みプラグイン.VimConfでt9mdさんが開発してる t9md/atom-vim-mode-plus でヤンク対象を フラッシュしていてわかりやすいというのを見せて頂いたのがきっかけになってます. 地味にUX向上してよい.

参加・発表したミートアップ・勉強会系

YAPC

YAPCに初めて参加しました.学生無料便利すぎました.LT発表は応募したのですが残念ながら落ちて残念.

Yokohama.vim

Yokohama.vimではなぜか基調講演という名の戦犯をしてきました. 内容はYAPCでしようとしていたVim駆動学習の話の微調整という感じでした. ちゃんと基調講演と言えるような発表をできるようになってまたYokohama.vim参加したい.

VimConf 2015

上述した.

fpinscala読書会完走

大阪で定期的に開催されていたfpinscala読書会でFunctional Programming in Scalaを(一応)読了しました. が,まだまだ理解しきれてないところ,飛ばしたところなどたくさんあるので2週目に参加してしっかり 関数型プログラミング身につけたいなと思います.

GitHub 記録

あんまり大したことできてなかった

Splatoon

Splatoon S+ 90

インターン中に買ってインターン終了までにS+になるという目標は達成したのですが, 年内にカンストするという目標は残念ながらできませんでした. しかし,今年の後半は本当にイカしかやってないというベルでやっていてイカ進捗が一番でてました. イカちゃん楽しすぎる…

よかったらみなさん一緒にイカしましょう!

その他

ちょくちょくReact触ったり, 引き続きScala書いたり, golang はじめたりしましたが, OSSっぽいこと,ここに書くようなことはほとんどできませんでした.

最後に

今年は環境が変わっただとか,インターン長期間行ってたとか,Splatoonやってたとか で特に今年後半はあんまり見える活動できてなかったです.もっと頑張りたい.

去年も似たようなこと言ってたような気がしますが来年はVimはもちろんのことVim以外のこともやっていきたいなぁと思います. あともう少し大きいレベルで目標を定めてそれに向かってやれるとよさそう(まだふわふわだけど真剣に考えなきゃ…).

それと今年最も苦労して迷惑かけたのは英語ではないかという説があるので, 来年は英語でコミュニケーションとれるように頑張って勉強していきたいですね.

以上, @haya14busa の 2015年の振り返り記録でした.

来年もイカよろしく〜

]]>
<![CDATA[Vim Mnemonic | Vim のコマンドの覚え方大全]]> 2015-12-21T02:55:02+09:00 http://haya14busa.com/vim-mnemonic <![CDATA[

この記事は Vim Advent Calendar 2015 の21日目の記事です.

もくてき

本記事では Vim のコマンドの”覚え方”を紹介します. 基本的にはトリッキーな”覚え方”ではなく由来の紹介となります. 例えば J で行連結は Join が元だとか, gfが”goto file”の略だといったことを 知っておくとなにかと憶えやすいと思います.

対象読者

主にこれから Vim を使ってみよう! でもなかなかコマンドを覚えられないっ! という Vim 初心者の方に由来を知ることで少しでも コマンドを憶えやすいようにすることが目的です. 初心者を想定しているのでコマンドの動作などもなるべく紹介していきます.

中・上級者の方には普段何気なく使ってたあのコマンドの由来を知って「フハハハハ」と ほくそ笑んでもらえるような記事になれば嬉しいです.

注意

注意点として公式のものから公式っぽいもの,独自の調査結果によるものなど信憑性はまちまちです. そしてVimのコマンドは無数にあるので覚え方大全と言っておきながらすべてを網羅できているわけではなくかなり偏っています. 抜けてるものとか間違ってるものとか俺はこう覚えてるぜ!というものがあったら教えてください!

出典は

  • ヘルプファイル
  • ソースコード
  • 出典不明だけどどこかで見聞きした話

などです.基本英語に直して理解する方針のものが多いです.

またこれからたくさんのコマンドを羅列していきますがすべてを覚える必要は一切ないこと, そして逆にここに載ってない便利な覚えるべきVimコマンドはきっとたくさんあるので覚えようと気負ったり, だいたいわかるからokと思ったりしないようにおねがいします.

そして何よりの注意点としては結局覚え方よりも実際にやってみることが大事だということです!!!

ただ単にやってみるだけでなく由来や覚え方も知ることでより憶えやすくなったらよいなというのが本記事の趣旨なので, 深く考えすぎずにそうなんだ〜へぇ〜と思いながら読むとよいと思います.

ではスタートっ

基本のカーソル移動 hjkl

vimtutor Lesson1

        ^
        k              Hint:  The h key is at the left and moves left.
  < h       l >               The l key is at the right and moves right.
        j                     The j key looks like a down arrow.
        v

出典: vimtutor

右手のホームポジションから1つ左にあるのでhは左方向に移動し, 右にあるlが右方向への移動です. jはどことなく(下矢印)に似ているので下方向に移動すると憶えましょう. kに関してはどことなく上にとんがってるので上方向に移動すると考えてもいいかも知れません.

もともとのhjklの由来

viの開発者であるビル・ジョイさんが 当時使っていたPCがADM-3A - Wikipedia, the free encyclopediaであり, そのキーボードには矢印キーはなくHJKLを使ってカーソルを移動していたのが由来とのこと.

931px-KB_Terminal_ADM3A.svg.png (931×301)

この由来は知っても憶えやすくならない単なる豆知識でした.

VIM の保存,終了と! (<bang>)

コマンド 説明と由来 help
:q :q[uit] カレントウィンドウを閉じる.quit から :h :q
:qa :qa[ll] quit all から. :h :qa
:q!, :qa! バッファに変更点があっても閉じる. quit + !
:w :w[rite] バッファ全体をカレントファイルに書き込む.writeから :h :w
:wq :wqall :w:qの組み合わせ :h :wq

コマンド末尾の!

:h :command-bang

Vim のコマンドは!修飾子を取ることができ,!の有無によって動作が変わる場合があります. 基本的には強制的に実行するという意味合いが多いので憶えておくと未知のコマンド + !に出会った時に びっくりしないですみますね.

挿入コマンド

コマンド 説明と由来 help
i カーソルの前にテキストを[count]回挿入する.insertから :h i
I 行の先頭の非空白文字の前にテキストを[count]回挿入する.insertの大文字バージョン.大文字は行指向になるパターンが多い印象 :h I
a カーソルの後ろにテキストを[count]回追加する.appendから :h a
A 行末にテキストを[count]回追加する. appendの大文字バージョン :h A
o カーソルのある行の下に新しい行を作り、そこにテキストを[count]回繰り返し挿入する.open line から. :h o
O カーソルのある行の上に新しい行を作り、そこにテキストを[count]回繰り返し挿入する.open line の大文字バージョン. :h O
gi 最後に入力がされた場所にテキストを入力. goto last insert position and start insert と思ってたがgは単なるprefixかも.後述するgvgiのヴィジュアル版っぽい :h gi

モーション,オペレータ, テキストオブジェクト, ヴィジュアルモードについて

個々のモーション(hjlk, w, etc…)やオペレータ(d,c, y…)について それぞれ説明する前に全体的な動作について理解しておくと覚えることが減って, しかもとても便利なのでぜひ理解しましょう.

モーション {motion}

  [count] {motion}

:h motion.txt

モーションとはカーソル移動コマンドです.[count]を前置すると[count] x {motion}分だけ移動します. すでに見たhjklももちろんモーションなので4hなどすると左に4文字移動するという意味になります.

オペレータ {operator} とモーション {motion}

:h operator

d2w.gif (661×157)

  {operator} {motion}

厳密にはカウントを{operator},{motion}のそれぞれに前置することができるので以下のようになります. ([count]を両者に前置させると掛け算になります. 2d3w -> 6つの単語を削除. 普通に6dwd6wとしたほうが基本的にわかりやすそうですね)

  [count] {operator} [count] {motion}

またオペレータによってはレジスタを前置できます.:h registers

  ["x] [count] {operator} [count] {motion}

Vimは変更する,削除するといった操作を表すオペレータ{operator}と, その操作の適用範囲であるモーション{motion}を組み合わせることでテキストを編集できます. 先ほどみた{motion}に操作を加えている形になっています.

オペレータ {operator} とテキストオブジェクト {text-objects}

:h text-objects

di'.gif (657×128)

  {operator} {text-objects}

また,移動コマンドとしては使えないけれど{operator}と組み合わせた時の操作範囲 となるテキストオブジェクトを先ほどの{motion}の代わりに使うことができます. 例えば文字列の中身を削除(オペレータの1つ)したい場合の”文字列の中身”は移動するような概念 ではないですが,オペレータの操作対象として妥当です.

ヴィジュアルモード とモーション,テキストオブジェクト

そして厳密にはオペレータではないですがヴィジュアルモードでもモーションやテキストオブジェクトと 組み合わせることができ,対象の範囲を選択できます.

vi'.gif (657×177)

  {visual-operation} {motion|text-objects}

ヴィジュアルモードを”選択する”という”操作”とみれば自然に理解できるかと思います.

オペレータの対象範囲としてのヴィジュアル選択範囲 {Visual}

そしてヴィジュアルモードで選択した範囲{Visual}{motion}{text-objects} と同様にオペレータの対象範囲として使えます.

  {Visual} {operator}

{operator}{operator} は行指向オペレーション!

gqq.gif (1131×312)

  [count] {operator} {operator}

そして上記の組み合わせの特殊なケースとしてオペレータ (2コマンド以上のオペレータの場合は最後の文字のみでも可), を繰り返して入力すると操作範囲が行指向になります.

例えばddyy といったオペレータを繰り返すと行を削除したりヤンクできるといった具合です.

僕が観測している範囲ではこの挙動はddyyといった基本的な編集オペレータだけでなく, 全てのオペレータに当てはまっているので憶えておくと便利です.(ソースコードは読んでないので確証がない.)

例えば2文字のオペレータであるgq(:h gq)は gqqgqgqと打つことでカーソル下の行を整形できたりします.

1つの変更単位としての {operator} {motion|text-objects}

Vim のおすすめコマンド10選!!! として “diw でカーソル下の単語を消す” といったここで述べた オペレータとテキストオブジェクトをいっしょにまとめたものを1つのコマンドとして 紹介するような記事がたくさんあったりしますが,これらは1つのコマンドでもなんでもないです. オペレータやモーション,テキストオブジェクトの概念を理解すれば無限大の組み合わせをそれぞれ 覚える必要はなく,便利なオペレータとモーションを別々に覚えてそれらを組み合わせればよいです.

これは覚え方とはまた別ですが上述したオペレータとモーション,テキストオブジェクトの組み合わせは 1つの”変更単位”となっておりドット.コマンドで同じ操作を繰り返すことも可能です (:h .).

説明が長くなりましたが,ここからそれぞれのオペレータやモーションの覚え方について見ていきましょう.

オペレータ {operator}

コマンド 説明と由来 help
c 変更する.changeから. :h c
d 削除する.deleteから. :h d
y コピーする.yankから. :h y
gU 大文字にする.おそらくprefixとしてg + Uppercaseから. 大文字にマッチする正規表現の \U :h gU
gu 小文字にする.おそらくgUの逆で小文字にするから.小文字にマッチする正規表現の \u :h gu
> 右にインデントをシフトする.見た目から?. :h <
< 左にインデントをシフトする.見た目から?. :h >
zf 折畳を作成する.zは後述するが”z” は紙片を折った様子を横からみた姿に見えるから.fは折りたたみを作成するのが一番基本コマンドと考えてfoldから? :h zf

他にも由来わからないけど便利なオペレータはあるので使って憶えましょう.個人的に憶えておくと便利そうなものをリストアップ.

コマンド 説明と由来 help
= フィルタ処理. インデント整形 :h =
gq 整形する.80文字で折り返すよう整形とかできる :h gq

先ほどオペレータを連続させると対象が行指向になると説明したように, ccなら行を変更,==gqqで行を整形できます.個別に覚える必要はないですね.

また ヴィジュアルモードでの<>を連続で操作するために下記のようなマッピング をしている人もいますが,他の選択範囲に対するオペレータによる操作となんら変わらないので 代わりにドットリピート.を使って繰り返すことができます.

1
2
vnoremap > >gv
vnoremap < <gv

ちょっと脱線してその他の変更コマンド系

c, d, y, gU…の他にもオペレータではない変更系コマンドがたくさんあります.:h change

コマンド 説明と由来 help
C 行末まで変更する.changeの大文字バージョン. :h C
D 行末まで削除する.deleteの大文字バージョン. :h D
Y 行をコピーする.yankの大文字バージョン.viの互換性から行末までではなく行. :h Y
p テキストをレジスタから貼付ける.put から.paste と憶えてもよさそう :h p
P カーソルの前にテキストを貼り付ける. pの逆 :h P
x カーソル下の文字を削除. バツマークに似てるから? :h x
X カーソルの前の文字を削除. xの逆 :h x
s カーソル下の文字を削除して挿入を始める. substituteから. :h s
S 行を削除して挿入を始める. sの大文字バージョン. :h S
r カーソル下の文字を置き換える. replaceから. :h r
R 置換モードに入る. replaceから. :h R
gr gR r, Rの仮想文字バージョン. g prefix :h gr :h gR
J, gJ 行を連結する. Join から :h J
:s/// 置換コマンド. 省略しないとsubstitute :h :substitute

由来ナゾ変更コマンド

コマンド 説明と由来 help
<C-a> カーソルの下または後の数またはアルファベットに [count] を加える. addから? :h CTRL-A
<C-x> カーソルの下または後の数またはアルファベットに [count] を減じる. 英語ではsubtractなので何故xかは不明.CTRL-Sが端末によっては使えない.XAに近いから? :h CTRL-A

モーション {motion}

:h motion.txt

左右の移動

コマンド 説明と由来 help
0 その行の最初の文字に移動. 0文字目??? :h 0
^ その行の最初の文字に移動. 正規表現の^ :h ^
$ その行の最後の文字に移動. 正規表現の$ :h $
g0 g^ g$ それぞれのスクリーン行バージョン(折り返し考慮)
gm スクリーンの幅の真ん中に移動. midleから.出典: ソースコード. middle of “g0” and “g$”. :h gm
f{char} 右に向かって [count] 番目に現れる {char} に移動.findから…と思いきやヘルプ/ソースコード的にはfindとは一切書いてない.確かにfindに移動するニュアンスはないか…? :h f
F{char} fの左に向かうバージョン. :h F
t{char} 右に向かって [count] 番目に現れる {char} まで移動.till(〜まで) から :h f
T{char} tの左バージョン :h f

由来ナゾ

コマンド 説明と由来 help
; [count] 回最後の f, t, F, T を繰り返す. 余った記号で;を順方向,,を逆方向にした? :h ;
, [count] 回最後の f, t, F, T を反対方向に繰り返す. :h ,

上下の移動

コマンド 説明と由来 help
G [count] 行目の最初の非空白文字に移動.カウントがなければ最後の行. Goto から :h G
gg [count] 行目の最初の非空白文字に移動.カウントがなければ最初の行. goto から :h gg
{count}% ファイルの {count} パーセントの位置に移動.そのままパーセントで直感的! :h %

単語単位の移動

コマンド 説明と由来 help
w [count] words前方に移動. word から :h w :h word
W [count] WORDS(非空白文字の連続)前方に移動. word から :h G :h WORD
e [count] word 前方の単語の終わりに移動. end of word から :h e
E [count] WORD 前方の単語の終わりに移動. End of WORD から :h E
b [count] words後方に移動. backward から :h b
B [count] WORDS後方に移動. backward から :h B
ge [count] word 後方の単語の終わりに移動. g prefix + end of word から :h ge
gE [count] WORD 後方の単語の終わりに移動. g prefix + End of WORD から :h gE

オブジェクト単位で移動

3つのオブジェクト, sentence(文), paragraph(段落), section(セクション) が存在し,それぞれ(), {}, []が対応する. セクションのみ少し例外で1桁目の’{‘ か ’}‘への移動を2コマンド目で表現する.

他に特に覚え方はわからないので表は省略.

:h object-motions

ジャンプ

コマンド 説明と由来 help
CTRL-O ジャンプリストの中の [count] だけ古いカーソル位置に移動({motion}ではない). Older cursor position から :h CTRL-O
CTRL-I ジャンプリストの中の [count] だけ新しいカーソル位置に移動({motion}ではない). キーボードでOの左にIがある :h CTRL-I
g; 変更リスト中の [count] 個前の位置に移動. ;が順方向への移動っぽい :h g;
g, 変更リスト中の [count] 個後の位置に移動. ,が逆方向への移動っぽい :h g,

様々な移動

コマンド 説明と由来 help
% 対応するアイテム([{}])にジャンプ. percent と parentheses(丸括弧) をかけている? %の文字が/を挟んで対応したがあると憶えてもよいかも :h %
H スクリーンの最上行から [count] 行目(デフォルト: スクリーンの最上行)に移動, Home(top) of window とhelpに書いてあるがhomeってそんなニュアンスあるんだろうか… High と覚えてもよいかもしれない. :h H
M スクリーンの中央に移動.Middle line of windowから :h M
L スクリーンの最下行から [count] 行目(デフォルト: スクリーンの最下行)に移動.Last line on the windowから.HHighと覚えた場合はLLowと覚えてもよいかも :h L

角括弧コマンド: [ + “?”, ] + “?” 系

:h [

ブロックやメソッド,コメントといった何かしら決められたものの始まり,終わりに移動する. “?”には({mM#*/などが入る.

コマンド 説明と由来
[{, [(, ]} or ]) マッチしない ‘{’, ‘(’, ‘}’ ‘)’ に移動
[#, ]# マッチしない #if..#endif の最初か最後に移動.
[/, [*, ]/, ]* Cスタイルコメントの最初か最後に移動.
[m or ]m Javaスタイルメソッドの最初に移動.
[M or ]M Javaスタイルメソッドの最後に移動.

テキストオブジェクト {text-objects}

基本的なテキストオブジェクトはi, aの2種類がそれぞれprefixとしてつくものが多い. iinner(内部)を表し,aa(1つの)まとまり(“a”n object)を表している. iinside, aのをオブジェクトのaround(まわり)まで含むと覚えてたりしてもよいと思いますが公式は”inner”と”a”です.

コマンド 説明と由来 help
aw, iw wordを選択. “a word”, “inner word”. aは周りのホワイトスペースを含む :h aw :h iw
aW, iW WORDを選択. “a WORD”, “inner WORD” :h aW :h iW
as, is “sentence”(文)を選択 :h as :h is
ap, ip “paragraph”(段落)を選択 :h ap :h ip
ab, a(, a), ib i(, i) (,)ブロック,またはその内部を選択. braces block から :h ab
aB, a{, a}, iB i{, i} {,}ブロック,またはその内部を選択. Brackets block から :h aB
a[, a], i[, i] [,]ブロック,またはその内部を選択 :h a[
a<, a>, i<, i> <,>ブロック,またはその内部を選択 :h at :h it
a", a', a`, i", i', i` 前の引用符から次の引用符まで, またはその内部を選択 :h aquote
gn 最後に使われた検索パターンを前方/後方検索してマッチを選択. g prefix + n, Nから :h gn :h gN

テキストオブジェクトの部分をハイライトした例 textobjects-example.gif (1366×721)

削除オペレータとの組み合わせ例

コマンド 動作
“dl” 1文字削除 (“x” と同じです)
“diw” inner word を削除
“daw” a word を削除
“diW” inner WORD を削除 (参照: |WORD|)
“daW” a WORD を削除 (参照: |WORD|)
“dgn” 次に検索パターンにマッチするものを削除
“dd” 1行削除
“dis” inner sentence を削除
“das” a sentence を削除
“dib” inner ‘(’ ‘)’ block を削除
“dab” a ‘(’ ‘)’ block を削除
“dip” inner paragraph を削除
“dap” a paragraph を削除
“diB” inner ‘{’ ‘}’ Block を削除
“daB” a ‘{’ ‘}’ Block を削除

検索コマンド

コマンド 説明と由来 help
/ 前方検索.由来はナゾ :h /
? 後方検索.SHIFT + //の逆から :h ?
n 最後の “/” か “?” を [count] 回繰り返す.next から :h n
N 最後の “/” か “?” を逆方向に [count] 回繰り返す.nの大文字でnの逆 :h N
* カーソルに最も近い単語で前方検索.USキーボードで4段目右手の中指 :h *
# カーソルに最も近い単語で後方検索.USキーボードで4段目左手の中指 :h #
g* g# *, # の”\<“ と ”>“(単語区切り)を加えないバージョン :h g*
gd カーソルからローカル宣言を検索.goto + declaration から. :h gd
gD カーソルからグローバル宣言を検索.gdの大文字バージョン :h gD

スクロールコマンド

上/下方スクロール

                                 +----------------+
                                 | some text      |
                                 | some text      |
                                 | some text      |
  +---------------+              | some text      |
  | some text     |  CTRL-U  --> |                |
  |               |              | 123456         |
  | 123456        |              +----------------+
  | 7890          |
  |               |              +----------------+
  | example       |  CTRL-D -->  | 7890           |
  +---------------+              |                |
                                 | example        |
                                 | example        |
                                 | example        |
                                 | example        |
                                 +----------------+
コマンド 説明と由来 help
CTRL-E 下へ[count]行ウィンドウをスクロール.Extra lines から(vimdocでは割増との訳注あり. 追加の行?) :h CTRL-E
CTRL-Y 上へ[count]行ウィンドウをスクロール.由来ナゾ… :h CTRL-Y
CTRL-D ウィンドウをバッファ内で下にスクリーンの半分スクロールする.Scroll Downwards から :h CTRL-D
CTRL-U ウィンドウをバッファ内で上にスクリーンの半分スクロールする.Scroll Upwards から :h CTRL-U
CTRL-F ページ前方(下方)にスクロール.Scroll Forwards から :h CTRL-F
CTRL-B ページ後方(上方)にスクロール.Scroll Backwards から :h CTRL-B

カーソル相関スクロール

  +------------------+            +------------------+
  | some text        |            | some text        |
  | some text        |            | some text        |
  | some text        |            | some text        |
  | some text        |  zz  -->   | line with cursor |
  | some text        |            | some text        |
  | some text        |            | some text        |
  | line with cursor |            | some text        |
  +------------------+            +------------------+
コマンド 説明と由来 help
zt [count]行(省略時はカーソルのある行)をウィンドウの最上行にして再描画. z prefix + top of window から :h zt
zz [count]行(省略時はカーソルのある行)をウィンドウの中央にして再描画. 由来はナゾ… :h zz
zb [count]行(省略時はカーソルのある行)をウィンドウの最下行にして再描画. z prefix + bottom of window から :h zz

折畳コマンド

全ての折畳コマンドは z で始まっている.z は紙片を折った様子を横からみた姿に見える.

コマンド 説明と由来 help
zf 折畳を作成する.(再掲) fは折りたたみを作成するのが一番基本コマンドと考えてfoldから? :h zf
zd 折畳を1つ削除する. delete から :h zd
zD 折畳を再帰的に削除する. 折りたたみコマンドにおける大文字は”再帰的”, “すべて”といった意味合いのものが多い :h zD
zE 折畳をすべて削除する. Eliminate から :h zE
zo 折畳を1つ開く. open から :h zo
zO 折畳を再帰的に開く. zoの大文字版 :h zO
zc 折畳を1つ閉じる. close から :h zc
zC 折畳を再帰的に閉じる. zcの大文字版 :h zC
za 折畳をトグル(開いていたら閉じ,閉じていたら開く). 由来はナゾ… :h za
zA 折畳を再帰的にトグル. zaの大文字版 :h za
zv カーソルのある行がちょうど表示されるレベルまで折畳を開く. View cursor から :h zv
zx, zX 折畳を更新する. 由来はナゾ…というかそもそもhelp読んでも動作がいまいちわからない… :h zx
zm 折畳をより閉じる(‘foldlevel’ を1減少させる) Fold More から :h zm
zM 全ての折畳を閉じる(‘foldlevel’ に0を設定する) zm の大文字版 :h zM
zr 折畳をより開く(‘foldlevel’ を1増加させる) Reduce folding から :h zr
zR 全ての折畳を開く(‘foldlevel’ に最大の折畳レベルを設定する) zr の大文字版 :h zR
zn 折畳しない(‘foldenable’ をリセットする。全ての折畳が開かれる) Fold none から :h zn
zN 折畳する(‘foldenable’ をセットする。全ての折畳が ‘foldenable’がリセットされる以前と同様になる) Fold normalから.しかしnが2つあって正直意味ない… :h zN
zi ‘foldenable’ を反転する. Invert から :h zi
[z, ]z 現在の開いている折畳の先頭/末尾へ移動する.角括弧コマンド(:h [)は似た動作をする :h z[
zj カーソルより下方の折畳へ移動. 折りたたみにおけるhjklj移動 :h zj
zk カーソルより上方の折畳へ移動. 折りたたみにおけるhjklk移動 :h zk

(zxとかzn,zNなどは覚えなくてもたいていの動作に支障ないのではという気がする...)

undo と redo のコマンド

コマンド 説明と由来 help
u [count] 個の変更を元に戻す.undo から :h u
CTRL-R undo された変更を [count] 個やり直す.redo から :h CTRL-R

ビジュアルモード

コマンド 説明と由来 help
v 文字単位のビジュアルモードを開始する.visualから :h v
V 行単位のビジュアルモードを開始する.vの大文字版 :h V
CTRL-V 矩形ビジュアルモードを開始する.vのctrl版 :h CTRL-V
gv 最後に使用したのと同じ範囲のビジュアルモードを開始する. gotoか単なるprefix. 最後に入力がされた場所にテキストを入力するgiのヴィジュアル版と言える :h gv
v_o 選択されたテキストのもう一方の端へ移動する.Go to other end of highlighted text から :h v_o
v_O 選択されたテキストのもう一方の端へ移動する.矩形選択では行内のもう一方のコーナーに移動する.Go to other end of highlighted text から :h v_O

その他

すべてのコマンドを体型的に網羅するとを諦めてその他で残りの言及すべきっぽいコマンドをいっしょくたにまとめる男の姿がそこにはあった…

コマンド 説明と由来 help
CTRL-W + “?” ウィンドウコマンド. Window commandsから. Windowの操作や”?“の結果を新規ウィンドウで開いたりする :h CTRL-W
i_CTRL-R c_CTRL-R レジスタの内容を挿入する. Registerから :h i_CTRL-R
gf カーソルの下か後ろの名前のファイルを編集する. goto file から :h gf
K カーソル位置のキーワードを調べるためのプログラムを実行.Keyword から :h K
q タイプした文字をレジスタ{0-9a-zA-Z”}にレコーディングする.特に覚え方は見つからなかったが,macro -> maqro みたいな覚え方もあり…? :h q

おわりに,そして覚えるのもよいけれど…

以上で本記事のコマンドの覚え方大全は終了です. ここまで読んだ方お疲れ様です.そして「アレ…?まだまだあるはずじゃない?」と思ったそこのあなた,正解です. 挿入モード,コマンドモードのコマンドはほとんどかけてないですし,単純に抜けてるものもあるかと思います. チカラ付きました… ただこう理解するとわかりやすいよ〜というコマンドはなるべく網羅したような気がします. もっとこれ追加しろとかあったらコメントかtwitterか何かで言っていただけると嬉しいです. そしてVimを始めたころの初々しい気持ちを忘れていて,いやもっとここ丁寧に書かなきゃわからんぜ!ってとこも遠慮無くおねがいします.

そして覚えるのも大事ですが,それと同じくらいわからないことをドキュメントから調べる力を つけるのも重要かなぁと思います.最初は覚えられなくても調べることができればかなり便利です. たとえば :helpだけでなく:helpgrepを使いこなしたり, :h help-context を読んで引きたい項目の指定ができるようになるとはかどります. (他にも(を末尾につけることで関数のヘルプを引くといった書いてないプチハックとかもあります)

種類 修飾子
ノーマルモードコマンド (無し) :help x
ビジュアルモードコマンド v_ :help v_u
挿入モードコマンド i_ :help i_<Esc>
コマンドラインコマンド : :help :quit
コマンドライン編集 c_ :help c_<Del>
Vim の起動引数 - :help -r
オプション ' :help 'textwidth'
正規表現 / :help /[

僕が思うhelp周辺の便利ツールとかは Vimのhelpを快適に引こう - haya14busa にまとめているのでよかった読んでみてください.

おわり.

Vim のコマンドを覚えて思考のスピードで編集しましょう!!!

]]>
<![CDATA[ブログを書くまでが #yapcasia なのでブログでLT発表して感想かく ― Vim駆動学習]]> 2015-08-23T10:24:12+09:00 http://haya14busa.com/yapcasia-2015 <![CDATA[

YAPC::Asia 2015 に1日目・2日目と参加してきました.

ブログを書くまでがYAPCで,LTしてみたかったならやぷしー終了するまでにブログで発表すればいいじゃない

YAPC 2015 初めての参加でしたが一言で言うと最高でした!!! 刺激になるトーク多すぎました.

ただひとつ悔やまれるのは1日目の前日くらいに勢いでLT応募したけど,そのまま勢いで落ちたのでチョット発表してみたかった…倍率高すぎる〜

(本当に悔やまれるのはトラック被って見れなかった裏トラックを見れてないということで,資料とかまとめとかYouTube見ていくゾ!!!)

…ところで,YAPCはブログを書くまで終わらないんだからブログでLTしたかった内容発表すれば, それはもうYAPCでLTしたことになるのでは…!?!?!?(混乱)

と考えたのでブログで発表したかった資料をあげる最高にカッコワルイことを今からします. その後参加記みたいな何かを書いています.

いろいろと,読む前でも読んだ上でも思うことはあると思いますが自分でも思うことはあるので 察してそっとしておいてください!! いろいろと反省はしている.

Vim 駆動学習

※ 1箇所gifが表現できないのでクリックして見てと言っているけどSpeaker Deckだとクリックできないのでスルーしてください(PDFをダウンロードすれば一応クリックできるけど)

LT説明や資料の反省とか.あと参考記事

エモイ話だ…

基本的にはこれまでにどこかでアウトプットした内容で,なるべくそうならないように意識はしてたのだけど, 説明文とか内容とか何か聴衆に有益情報話すというより,単なる自分語り感が強すぎるのがウーンっていう感じだと思っています.

まぁ技術的な説明とかは他の人のほうが断然できるはずだし, 僕ができるのは僕の経験を話すこと.それが一番僕にしかできない発表で, そこで何か聴衆に響かせることができたらいいなって考えたのでこういった内容になってます.

内容がだいたいどこかで以前アウトプットしたものになった原因は,これまで基本的には記事書くようにしていたからっていうことや, 聴衆がどうよりも経験としてYAPCみたいな舞台で発表したいというエゴによって自分のなかでGoサインが勢いで出てしまった感じになってます.

資料できてここまで書いて思ったけどたぶん落ちてよかったんじゃないかなぁみたいな思いもある. 普通に5minオーバーすると思うし.

これからまた頑張っていくゾイ!!!

参考記事

だいたい僕のブログ読んでたらLT資料読まなくてもだいたいわかる感じになっている. もし興味出たっていう優しいかたは気になった記事を読んでいただけると少し詳しく書かれています.

自分の LT のくだり終了.

YAPC::Asia 2015 Day1 感想

聴いたTalkと感想

1日目は朝5時半くらいから起きて東京に向かったのでなかなか大変でしたが,早起きチャレンジ成功してよかった.

1) メリークリスマス! - YAPC::Asia Tokyo 2015

perl を書いたことは無かったけど歌って踊るラリーウォールみれてよかった. perl6 面白そうなようなので(又聞き),クリスマス期待.

2) 世界展開する大規模ウェブサービスのデプロイを支える技術 - YAPC::Asia Tokyo 2015

Miiverse のデプロイを支える技術の話だった.海を超えたgit pullとかその不可を抑えるためのランダムスリープみたいな つらい話からはじまって,それを解決していった話が聞けました. 大規模ウェブサービスのデプロイ,やったこと一切なくてあまり個人の実感がわかないのでムズカシイけどこういう話も勉強していきたい.

3) TBD - YAPC::Asia Tokyo 2015

Rubyのパパ Matz が Rubyの話しを封印したと思ったら10分で封印を解いたり, Ruby のディスと見せかけて間接的にperlディスってたりしておもしろかった. 内容も面白くて,主流のアーキテクチャが変わると言語デザインも変わってきて,それに対応してデザインされているのが streem ならしい.

4) Perlの上にも三年 〜 ずっとイケてるサービスを作り続ける技術 〜 - YAPC::Asia Tokyo 2015

おもしろトークだった上にめっちゃ開発の参考になる話が多かった.紹介されていた本読みたい -> YAPCの発表で紹介した本 - hitode909の日記. DDDとかユビキタス言語の導入の話とかがとてもいい話で,僕もやっていきたい. 最近自分でもプログラムやドキュメントに書いてる単語がブレていたりするのを感じていて, ユビキタス言語.md (ファイル名違う気がする) とかをリポジトリに置いて管理するのとかもよさそうと思った.

事前条件/事後条件とかあってそこで 責任外のことをチェックせずお互いを信頼するとスッキリするという話もなんとなくスッキリして面白かった.

5) うっかりをなくす技術 - YAPC::Asia Tokyo 2015

うっかりすることよくあるので,うっかりをなくす技術を聞きに行った. assertion ライブラリ的なものを最近作っていたのでassertionの話とかもっと聞きたかった.契約プログラミングとか???(わかってない) defer/guard object とかスゴイよさそうだった.golang に defer とかあるようだし,goやっていきたい.

追記: あとせっかくうっかりをなくす技術聴いたのに2日目に会場に荷物忘れるうっかりをやらかした. そしてこの文をブログに書くのを忘れるといううっかりもして,うっかりしないようにするのはムズカシイ…

6) Lightning Talks Day 1 - YAPC::Asia Tokyo 2015

Slack + Hubot でお前の一番好きな二次元嫁キャラと一緒に仕事をする 話とかが思考がぶっ飛んでいて面白かった. PHPでregister_globalsの話をしてた方が最初にgongo/emacs-ikkuとか作っていて 「Vimでやらなきゃ…負けてられない…」とか考えていたけどまだできていない.

YAPC::Asia 2015 Day2 感想

聴いたTalkと感想

Mackerel開発におけるScalaとGo、そしてPerl - YAPC::Asia Tokyo 2015

  • 複数の言語,技術を用途によって適切に使い分けてるのがすごい.
  • Scalaのimplicit conversion の解決遅すぎ問題のつらさわかる〜みたいな気持ちになったりした.
  • ちょっと最初の10minくらい遅れていってしまってツライ…ちゃんと見直したい.

我々はどのように冗長化を失敗したのか - YAPC::Asia Tokyo 2015

  • 式年遷宮面白かった.
  • 発表前から資料がアップロードされていてよさ.
  • 冗長化ムズカシイ.

MySQLで2億件のシリアルデータと格闘したチューニングの話 - YAPC::Asia Tokyo 2015

  • 自分にとっては難しかったのとあまり集中して聞けてなかったのであまり記憶がない…(LT採択どうかな〜みたいな気持ちで気が気じゃなかったのもある)
  • あとから資料見てみたい.

Adventures in Refactoring - YAPC::Asia Tokyo 2015

  • GitHubの中の人のリファクタリングの話でめっちゃよかった!!! リファクタリングしたい気持ちが高まりまくった.
  • リファクタリングは挙動を変えては行けなくて,それはバグも同じで一緒に修正したら問題の切り分けができなくてダメだよという話は, わかるという思いと共に,そうは言ってもムズカシイので意識してデキルようにしていきたい.
  • リファクタリングをナゼするのかとか,成果を計測しなくてはならないというのは全然意識したことがなかった.資料とか見直しながらやっていきたい.
  • (変数・)メソッド名のアンダースコアはリファクタリングチャンス.なるほどという感じだった.
  • とにかくリファクタリングやっていくゾ!!!

Posture for Engineers - YAPC::Asia Tokyo 2015

  • 肩こりがひどいので姿勢の話を聞きに行った.
  • いくつかヨガのポーズみたいなものを教えてもらったので実践できればいいのだけどデキルだろうか…
  • バランスボールのって開発してたら逆に肩こりと首こりがヤバくなった経験があってすぐにやめちゃったのでいい座り方・使い方がしりたい.(質問しそこねた)

Run containerized workloads with Lattice - YAPC::Asia Tokyo 2015

  • 正直わからんかったマン(ムズカシイ…)

Profiling & Optimizing in Go - YAPC::Asia Tokyo 2015

  • とにかくライブコーディングがカッコヨサで Go の標準で提供されている tool すごい強いし,コンパイル速くて書きやすそうだし, まだ Go はかけてないけど,Go 書いていくゾ!という気持ちになった. Go 書き始めたらまた参照して学んで行きたい.

Lightning Talks Day 2 - YAPC::Asia Tokyo 2015

  • どのLT もこれぞLTってカンジで良かった…あんなLTできるようになりたい.
  • @orga_chem さんの Vim script 静的解析の闇がトリで Vim script の闇を暴露していていい話だった. Vim script はああいった感じでカワイイのでぜひ興味を持った皆さんには vint のチカラを借りながら Vim script を書いてみて欲しい. 基本静的解析のときツライという話で,v:count, 関数と変数のネームスペース, map() の引数とかが文字列って以外は普通に書いてるときの 闇としては避けられそう.(十分多い…???)

個人的な悔やまれることとか

  • 最近Vim版power-assert作って補足して頂いたりしたので @t_wada さんに会ってご挨拶くらいしておきたかった. Vim版 power-assert もっと便利にしておきたい.
  • 世界の @miyagawa さんを見かけて Rebuild.fm 大好きでめっちゃ聴いてますって言ってみたかったけど,誰かと話されていて, Rebuild.fm 聴いてますとか耳たこかもしれないし,その後何か話せるか考えたけど訳分からんくておそらく Rebuild.fm で Vim とかエディタの話をしてほしいとかは言わないまでも,Vimmerのゲスト増やしてくださいみたいな困らせるようなことを喋りそうで そうこう言ってるうちに機会が過ぎてしまった…フベン…
  • Twitter のタイムラインを見る限り,オープンインターネッツ上で見たことあるひとがたくさんいたけど,現実の顔と一致しなくて結局 会わずじまいな人が多かったのでまた何かの機会にお会いしたい.会えたひとも何人かいてそれはよかった.
  • 発表しようと思うならもっと早めに用意しないと,僕のレベルだと時間をいっぱいかけてやっと聴くに耐えるものになるかどうかギリギリみたいな 感じだと思うし,ちゃんと準備したい.

全体的に

YAPC::Asia 2015,YAPC初めての参加でしたが本当に最高でした!!! 同時通訳の方の同時通訳がすごくて圧巻されたり,会場のWifiはあの人数だったのに個人的にはほとんど繋がっていて CONBU さん凄かった. 無限に湧き出てくるコーヒーも美味しかったし,懇親会も豪華だしスポンサーの皆様ありがとうございました.

そして発表者の皆様,@lestrratさんをはじめとした運営の皆様,他の参加者の方々, (漏れていたとしたらその方々!), 最高のYAPC本当にありがとうございました.楽しかったです!!!

絶対来年も参加したい!!!!

]]>
<![CDATA[Vim script版 power-assert! テスト書いてないとかお前それ Vim script の前でも同じこと言えんの?]]> 2015-08-19T22:13:51+09:00 http://haya14busa.com/vim-vital-power-assert <![CDATA[

Vim script で最高の assertion 体験,vital-power-assert を作りました

Gyazo

haya14busa/vital-power-assert

テスト書いてないとかお前それ Vim script の前でも同じこと言えんの?

ということで Vim script 版 power-assert, vital-power-assert を作りました.

Vim script でも power-assert できてテストをバリバリ書けるんだから Vim で書いてる他の言語でテスト書いてないとか Vim が泣いちゃいますね…(煽り,そしてブーメラン)

使い方とか 力こそパワー!! 百聞よりパワー!!

使っている様子です

1
2
3
4
" in your vimrc
NeoBundle 'vim-jp/vital.vim'
NeoBundle 'haya14busa/vital-vimlcompiler'
NeoBundle 'haya14busa/vital-power-assert'
1
2
3
4
5
6
7
8
9
10
11
12
13
let s:V = vital#of('vital')
let s:PowerAssert = s:V.import('Vim.PowerAssert')
let s:assert = s:PowerAssert.assert
execute s:PowerAssert.define('PowerAssert')
function! s:power_assert() abort
  let x = { 'ary': [1, 2, 3], 'power': 'assert' }
  let l:zero = 0
  let s:two = 2
  PowerAssert index(x.ary, l:zero) is# s:two
  " or
  execute s:assert('index(x.ary, l:zero) is# s:two')
endfunction
call s:power_assert()

上記コードを実行するとこうなります.

1
2
3
4
5
6
7
8
9
vital: PowerAssert:
index(x.ary, l:zero) is# s:two
     |||     |       |   |
     |||     |       |   2
     |||     |       0
     |||     0
     ||[1, 2, 3]
     |{'ary': [1, 2, 3], 'power': 'assert'}
     -1

インストールや詳しい使い方は GitHub/help を参照してください.

-> haya14busa/vital-power-assert

基本的に関数とコマンドのassert方法を用意しているのですが,プラグインのコードに残しておいたりするものは 関数の .assert(), Vimのテスティングフレームワークで使う際などはコマンドでやることを推奨してます.

1
2
3
4
" 関数で assert. 引数は文字列として渡す必要がある
execute s:assert('index(x.ary, l:zero) is# s:two')
" コマンドで assert. 文字列で囲う必要がないのでシンタックスハイライトも効く
PowerAssert index(x.ary, l:zero) is# s:two

両者ともデバッグ用変数をONにしないとassertは実行されないので,プラグインに埋め込んでおいても プラグインのユーザが使ってる時は発動しないし,評価なにもされないのでコードに残しておいても 問題ないようになっています.(コマンドの方はユーザのVimにコマンドが新たに定義されてしまうので推奨しません)

power-assert 最高! 一番好きな assertion ライブラリです!

vital-power-assert はもちろん JavaScript の assertion ライブラリである https://github.com/power-assert-js/power-assert にインスパイアされて作っています.

何がベンリなのかとかは @t_wadaさんが本家のpower-assertの紹介とかで 各所で説明なされているので説明は不要だとおもいますが,

個人的にはやはり

  1. assert 失敗時の式がどうなってるか一目瞭然のグラフィカルな見た目
  2. たくさんの matcher を一つ一つ憶えたりドキュメントを見なくても assert 一つだけ知ってれば使える優しさ

あたりのよさが使ってみて,開発してみて本当によいなと思います.

マッチャーは自然言語的な書き方ができたり, テストのコード自体が間違えにくいみたいなところがよいとチラッと聞いたことがありますが 僕は断然power-assertのほうが好きという思いが強まりました (間違ってたり他にもある場合は教えてください)

Vim script 版 vital-power-assert のよさ

(このあたりは特に Vim script 書いてる人/興味あるひと向けです)

power-assert としてのよさはもちろんのところ, Vim script の assertion ライブラリとしての vital-power-assert のいいところがあります.

それはassertionを実行する際のスコープが assert する行と 同じなので, スクリプトローカル変数やローカル関数など何でもassert する 式の中で使えるということです!!! (わかりづらい)

1
2
3
4
5
6
7
8
9
:let s:x = 2
:PowerAssert s:x == 1
" or
:execute s:assert('s:x == 1')
" => ちゃんとs:xも使える
"   s:x == 1
"   |   |
"   |   0
"   2

例えば Vimのテスティングフレームワークの一つの thinca/vim-themis の assert コマンドではスクリプトローカル変数が使えなくて不便…ということがあったりするのですが vital-power-assert を使えばそのあたりを気にせず使うことができます.ベンリ.

どうやって実装しているか

Vim script のパース & コンパイル

power-assert のようにassert が失敗したときに式中のそれぞれの変数や関数, 演算子の評価結果を得るためにはまず与えられた式をパースする必要があります. そこでは Vim script で Vim script をパースできる ynkdir/vim-vimlparser を使用させていただいています. 使っていて改めて ynkdir さんすごすぎる…

vitalのライブラリとして使うためにhaya14busa/vital-vimlcompilerにバンドルしちゃっています.(ライセンス的に問題なさそうだったのでynkdirさんに相談するまえに衝動的にVim版power-assertを作ってしまいました…スイマセン)

とにかく,vimlparser のおかげでVim scriptをパースしてASTを得ることができたので, あとはASTをトラバースして評価したいノードを集めることができました.

そして再コンパイル

あとは集めたASTのノードの式中の位置を記録, そしてASTをVim scriptに戻してスコープに気を付けながら評価すれば必要なものが揃います.

vimlparser に付属している Compiler オブジェクトはS式的なものにコンパイルするものだったので, Vim scriptにコンパイルする haya14busa/vital-vimlcompiler というライブラリを作って使用しています.

注意点としてまだexprのコンパイルしか実装してないので関数とかはコンパイルできないです. ドキュメントもないし今のところ完全に vital-power-assert 用になっていますが,何かできたら面白そう.

Vim script スコープハック

Vim のコマンドの引数はStringとして渡されてその場で評価しているわけではないので 普通にやるとスクリプトローカル変数が無いと怒られます. もちろん文字列で受け取って eval() してもスコープは変わってるので対応できません.

これを完全に解消するためにはassertする行と同じ位置で評価する必要がありますが, そこで execute を使うことによって実現しています.

どういうことかというと,{rhs} である s:assert('...') が評価され返り値が帰ってくるのですが, その返り値に実行したいコマンドを文字列として返すと :executeによってそのコマンドが同じスコープで 実行できて… という感じで実装しております.

1
2
3
4
5
6
7
8
9
10
11
:execute s:assert('x == 1')
" ->
:execute 'execute' "s:assert2(x == 1, 'x == 1')"
" -> {expr} と 文字列の '{expr}' を別の関数に渡し直す
:execute s:assert2(x == 1, 'x == 1')
" -> 評価したいノードリストを引数にとる関数を返す
:execute 'execute' "s:assert3('x == 1', [{pos: `xの位置`, value: x}])"
" -> { "value": x } で xが評価される
:execute s:assert3('x == 1', [{"pos": `xの位置`, "value": x}])
" -> 実はこのあとここで `:throw` コマンドを返してthrowすることによってエラー位置をこの行にしたり...
:throw ...

コマンドも基本は同じでコマンドの{rhs}executeになってるのですが, どうしてもスクリプトローカル変数だけはコマンドを定義したファイルの方で評価されてしまうので ファイルごとにコマンドは定義する必要があるのはこれが理由です.

つまりスクリプトローカル変数諦めるなら一回定義すればあとは同じように使えますが そもそもコマンドは雑な開発用スクリプトとかテスティングフレームワークで使うことを想定しているので そんな感じで察してください.

グラフィカルな描画

ところで見た目とユーザの驚き的には power-assert のあのグラフィカルな表示を作るところが華と見ることもできそうですが, 今のところとりあえず線が被らない最低限のアルゴリズムで作っているのでもっと改善した表示ができると思います.

もしいい感じのグラフ描画のアルゴリズムわかる人は僕に教えてくださると大変ウレシイです

最後に

勢いでワーッと作っていてまだまだ改善点はあるのですが,一通り開発してテストしてる限りではめっちゃよい感じに動くので Vim script 書いてる方や興味あるかたは是非使って見ていただけると嬉しいです.

あと themis との連携を書いてますが,別に themis の作者である thinca さんに使い方とか確認をとったわけでもないので もしかしたらもうちょっと良く出来たりするかもしれません.

フィードバックとか使い方の質問とか github: https://github.com/haya14busa/vital-power-assert , twitter: https://twitter.com/haya14busa , Lingr: http://lingr.com/room/vim あたりにいただけると嬉しさあります.

Vim script でも power-assert して最高の assertion 書いていくゾ!!!

]]>
<![CDATA[Vimの検索はもっともっと便利になる! incsearch.vim v2.0 をリリースしました]]> 2015-07-06T01:34:01+09:00 http://haya14busa.com/incsearch-dot-vim-ver-2 <![CDATA[

haya14busa/incsearch.vim

incsearch.vim について知らないかたはこちらの記事を参照してください. 簡単に言えばVimのインクリメンタル検索をカイゼンするプラグインです. -> incsearch.vimでVimの検索体験をリッチにする - haya14busa

incsearch.vim v2.0 をリリースした!

v0.9, v1.0, v1.1, v1.2, … とこれまで incsearch.vim をインクリメンタルにカイゼンにカイゼンを重ねてきました… そして本日, incsearch.vim は晴れて一段階進化し, バージョン2.0 となりました!

この進化を一言で言えば,incsearch.vim はもっともっと Vim の検索を便利にすべく 進化・拡張可能 になりました.

2.0で何ができるようになったか?

百聞は一見に如かず.以下のgifとともに拡張プラグイン達をご覧ください!

曖昧検索 | fuzzy search

incsearch-fuzzy.gif

1
2
3
4
5
6
7
NeoBundle 'haya14busa/incsearch-fuzzy.vim'
" マッピング例
map z/ <Plug>(incsearch-fuzzy-/)
map z? <Plug>(incsearch-fuzzy-?)
map zg/ <Plug>(incsearch-fuzzy-stay)
" サクッとためす
" :call incsearch#call(incsearch#config#fuzzy#make())

曖昧スペル検索 | fuzzy spell search

incsearch-fuzzyspell.gif

1
2
3
4
5
6
7
NeoBundle 'haya14busa/incsearch-fuzzy.vim'
" マッピング例
map z/ <Plug>(incsearch-fuzzyspell-/)
map z? <Plug>(incsearch-fuzzyspell-?)
map zg/ <Plug>(incsearch-fuzzyspell-stay)
" サクッとためす
" :call incsearch#call(incsearch#config#fuzzyspell#make())

高速でインクリメンタルなmigemo検索 | migemo search

incsearch-migemo.gif

1
2
3
4
5
6
7
NeoBundle 'haya14busa/incsearch-migemo.vim'
" マッピング例
map m/ <Plug>(incsearch-migemo-/)
map m? <Plug>(incsearch-migemo-?)
map mg/ <Plug>(incsearch-migemo-stay)
" サクッとためす
" :call incsearch#call(incsearch#config#migemo#make())

EasyMotion との連携 | integration with vim-easymotion

incsearch-easymotion.gif

1
2
3
4
5
6
7
NeoBundle 'haya14busa/incsearch-easymotion.vim'
" マッピング例
map z/ <Plug>(incsearch-easymotion-/)
map z? <Plug>(incsearch-easymotion-?)
map zg/ <Plug>(incsearch-easymotion-stay)
" サクッとためす
" :call incsearch#call(incsearch#config#easymotion#make())

拡張はcomposable!

incsearch-fuzzy-easymotion.gif incsearch.vim x fuzzy x easymotion

1
2
3
4
5
6
7
8
9
10
11
12
13
" incsearch.vim x fuzzy x vim-easymotion

function! s:config_easyfuzzymotion(...) abort
  return extend(copy({
  \   'converters': [incsearch#config#fuzzy#converter()],
  \   'modules': [incsearch#config#easymotion#module()],
  \   'keymap': {"\<CR>": '<Over>(easymotion)'},
  \   'is_expr': 0,
  \   'is_stay': 1
  \ }), get(a:, 1, {}))
endfunction

noremap <silent><expr> <Space>/ incsearch#go(<SID>config_easyfuzzymotion())

仕組み

converter 機能

  • :h incsearch-config-converters

バージョン2.0でのメイン追加機能,パターンコンバート機能です. 入力したパターンを変換して正規表現を返すコンバーター(複数可)を指定することによって, 入力した正規表現だけでなく,変換された正規表現も追加で検索してくれます.

fuzzy, fuzzyspell, migemo の拡張はどれもこの新たに入った仕組み,converter機能を使っています.

他にも スネークケースとキャメルケースを相互変換してどちらも検索できるようにするとか, {a,b}\(a\|b\)に変換するようなオレオレ簡易正規表現シンタックス(?)シュガーを作るとか, inputを受け取って正規表現を返すというシンプルな機能ながら,活用可能性はたくさんあります! (※ これらはそのうち実装していくと思います)

module 機能

vital-over という incsearch.vim が使用しているライブラリにおけるモジュールを追加できる機能です. これがどんなことができて,どうやって作ればいいかといったことはそれだけで別の一つの記事どころか数十記事書ける内容なので割愛しますが, かなりいろんなことができるようになるはずです.(が,その分何でもできすぎるので注意は必要そうですが).

haya14busa/incsearch-easymotion.vim がこの機能を使って検索後に easymotion/vim-easymotion の機能を呼び出しています.

自分で拡張を作ることも可能!※ ただしAPIが固まってるとは言ってない

もちろんもっと便利な拡張を自分で作っていくことも可能です. ここでは例としてちょっとした converter を作ってみましょう

参照

  • :h incsearch#go()
  • :h incsearch-config()

仕様

  • 正規表現をエスケープして単なる文字列として返し,検索する.
  • つまりは\Vの機能.ただし,普通の正規表現に加えて検索できるところが違う.

実装

pattern を受け取って正規表現を返す関数 (s:noregexp()) を実装して, incsearch#go()に渡すconfigのconvertersとして渡してあげるだけ. ※ converterオブジェクトを作って渡してあげる方法もありますが,現時点では関数を渡してできること大差ない.

1
2
3
4
5
6
7
8
9
10
function! s:noregexp(pattern) abort
  return '\V' . escape(a:pattern, '\')
endfunction

function! s:config() abort
  return {'converters': [function('s:noregexp')]}
endfunction

noremap <silent><expr> z/ incsearch#go(<SID>config())
" 例: /\vpattern と検索したら \vpattern がマッチする.普通の検索なら pattern だけ

めっちゃ簡単!!!

Special Thanks

おしょーさん(github@osyo-manga)のライブラリには 毎度お世話になっており,incsearch.vim v2.0 はコマンドラインライブラリである osyo-manga/vital-over の拡張性があってこそ リリースできたことだったり,cmigemoコマンドでも高速に migemo 検索できるのはそのまま osyo-manga/vital-migemo のおかげだったりします.

また Lingrのvim部屋 でもincsearch.vimの開発に関わる質問をしたりして参考にしたりフィードバックをもらったりしました. 皆さんありがとうございますっ!

終わりに.そして次のカイゼンへ…

incsearch.vim はシンプルかつVimデフォルトの検索と同じように快適に使用できるオススメプラグインです. それがバージョン2.0 になり,望めばもっともっと便利に,拡張可能なものになりました. 気になった方は是非つかってみてください.

そして incsearch.vim にはまだまだ改善点があるかと思います(ジッサイあります). なにか見つけた方や「おまえーっ…APIがなーっ…ふわふわでなぁーっ…使いにくくてなーっ…ゆるさーん(バシーン)」 という方は Issuestwitter:@haya14busa, はたまた Lingrのvim部屋 あたりなどでフィードバックいただければカイゼンしていきたいと思うのでよろしくお願いしますっ

haya14busa/incsearch.vim

]]>
<![CDATA[2014年の振り返り. またはプログラミング2年目をVimに捧げるとこうなるという話]]> 2014-12-30T00:00:00+09:00 http://haya14busa.com/haya14busa-s-happy-vimming-year-2014 <![CDATA[

この記事について

この記事は Vim Advent Calendar 2014 の30日目かもしれません(または来年, Vim記事集約系のイベントやりたいという気持ちの1つです).

今年の2月に 1年間『Vim駆動学習』してきて最高に楽しかったのでオススメしたい - haya14busa というVim歴換算で1年を振り返ったエントリーを書いたのですが, 今回は2014年を振り返ってみたら, やっぱりVim活たのしかったなぁという日記的エントリーです.

また思い返してみると僕がプログラミング始めたのは2年と少し前だったので, 2014年はプログラミング2年目の年でもありました. この文脈でみると「プログラミング2年目をVimに捧げるとどうなるか?」 というエントリーとも言えそうです. (※1年目からかなりの割合をVimに捧げていました)

2014年, 今年のVim活を振り返る

※Vim活が9割です

カーソル移動系人気プラグインの vim-easymotion の開発を乗っ取った!

20141230_easymotion_contribution_graph easymotion_on_speed_ex

これは2013年の後半からforkして活動していて, 今年の2月くらいに起こった出来事なのですでに下記の記事に少し感想を書いています.

-> 1年間『Vim駆動学習』してきて最高に楽しかったのでオススメしたい - haya14busa

記事執筆現在, GitHub のStar数 1556 で, 開発を引き継いだ時点での Star数は 1000 にちょっと届かない程度でした. もともとほっといた場合の伸び具合がどれくらいかはわからないですが, 自分が引き継いだ時点で Star数 約1000 のリポジトリが, さらに500強増えたという事実は本当に嬉しいです. GitHub Star 500+ めでたい.

これだけ 「たくさんのスター数がついている≒たくさんの人に使っていただけている」 プロジェクトを開発 & メンテするというのはなかなかに学ぶことがおおくてとても楽しかったです. 今後はリファクタリング含めもっと便利にしたいなぁと思っているんですが, そう思ってから半年くらい経過しております(すでに十分に便利&安定してきているのでモチベが低い).

最高の検索体験を提供したい! incsearch.vim つくった

incremental_regex_building

今年の前半がvim-easymotionだとしたら, 後半はほとんど incsearch.vim でした. 実はこのプラグインは最初はサクッと気晴らしにつくったぜーというレベルのものだったのですが, なんだかんだちゃんとしたものにしようという流れになり目一杯頑張りました.

また使用しているライブラリの osyo-manga/vital-over にもコントリビューションという名のリクエスト丸投げや, 設計についてコメント, ダメダメな Pull Request を交えながらいろいろ関わらせていただきました. おしょーさんありがとうございます, 迷惑いっぱいかけてスイマセンッ, これからもよろしくお願いします..!

便利さの9割は vital-over のおかげなのですが, 自分が1から開発してるリポジトリーでスター数3桁を達成してこれもまた非常に嬉しいです. これもめでたい.

GitHubの VimL trending で Repositories & Developers の両方で1位も獲得できました…! 嬉しさ.

github-trending-viml-today-incsearch github-developer-today-vimL-2014-10-22 22:51:58

VimConf 2014 で発表した

VimConf2014 という感極まるVimのカンファレンスが今年も開催されたのですが, そこでLT枠で発表させていただきました. Vimの便利な検索機能と上述した incsearch.vim の紹介をしました. 発表グダったりもしましたが, よかったという反応をいただけたり, スライドを英語で書いたので英語圏の人にも見てもらって便利さを伝えられたのは良かったです. 英語というより gif をふんだんに活用したので伝わったという説もある. (usevim, reddit )

ちなみに動画はテンパッて間違ったことも多々言ってるので鵜呑みにしないでください… この辺慣れるには場数を踏む必要がありそう

最高の検索体験を提供したい + α!. vim-asterisk 勢いでつくった

asterisk_z_star

Vim の * ってとても便利なのですが, 自分のユースケースとしては次のマッチにジャンプして欲しくないという不満があったので, そこを解消するために作りました. * からの gn テキストオブジェクト活用でスムーズに編集できるようになってとても気に入っています.

結構小さいプラグインではあるのですが, 今まで得たVim scriptの知識や経験をたくさん使っていたり, ビジュアルモードにおける * 機能は thinca/vim-visualstar の部分をお借りしたり, マルチバイトの扱いをプルリク貰って直したりとなかなかよさ気なものになりました.

実は似たような機能を提供してるプラグインはいくつかあるのですが, どれも不満がまだありました(Vimの検索機能周りの拡張は結構落とし穴が多い). incsearch.vim の経験などが活きて落とし穴はうまく回避できました. 気になった方は是非使ってみてください.

vimrc 読書会にいっぱい参加 & 管理のデータ化・自動化 & 統計ページ をつくった

確か管理者権限貰ったのは恐らく今年(すこしあやしい…厳密には去年だったかも…)で, 参加するだけでなくちょっとずつ運営面でも関わらせていただきました. Jekyll を覚えて 読書会のデータを yaml でデータ化したり, そのデータを使って Angularとd3.jsで 読書会の統計ページをつくったり, データ化によって扱いやすくなったので python や ruby で簡単なスクリプトを作成して更新作業を自動化したりしました. 読書会は毎週やるので更新が一瞬でできるようになって非常に便利になりました. 統計ページのJS, よくない部分多々あるのでそのうち書き直したい.

毎週土曜日23:00からやっているので皆さん参加しましょう! -> http://lingr.com/signup?letmein=vim

Vim プラグイン読書会やってた

こちらも1ヶ月周期でやっていました. Vim プラグイン開発, 興味ある. という方にオススメです! 一緒に読みましょう. またvimrc読書会とVim プラグイン読書会での経験が, Vim以外の普段の開発のレビューにも活かされたような気も…しま…す(自信なし).

執筆時現在次の日付が更新されてないという少々のグダりを見せていますが, 落ち着いたら必ず更新します!

Osaka.vim やで

connpass上では運営になっていますが一切の運営をしていません…!

関西のVim勉強会, 近年ではほとんどなかったので便利. 関西の皆さん参加しましょう.

Vimで緊急地震速報が見れるの便利では…? eew.vim つくった

EmacsやSublimeに移植されたりとちょっとだけ話題になった(すこし).

その他ちっちゃい Vim plugin 達

  • haya14busa/unite-ghq
    • uniteでghqできるの便利では…?と思ってつくって公開したら, そのわずか数時間前に sorah さんが sorah/unite-ghq を公開しており, しかもこちらのほうが基本的によくできていて「アアアッ…」とリアルで声がでた少し苦い思い出のあるプラグイン. 自分のバージョン使ってるの自分だけだとおもう. でも便利. 既存プラグインの検索は直前まで入念にやりましょうという教訓を得ました. (数日前にやった時点ではなかったので大丈夫だと思ってた)
  • haya14busa/unite-reading-vimrc
    • vimrc読書会のログやvimrcのリンクをuniteで絞り込んで開ける. たまに vimrc読書会 の管理とか過去の記録を見るときに便利.
  • haya14busa/endtagcomment.vim
    • よくvimrcで見かけるhtmlタグにコメントをつけるスクリプトをforkしてプラグイン化しただけ. もともと便利
  • haya14busa/unite-action-vimfiler_lcd
    • vimfilerでlcdしてから開くというuniteアクションが欲しかった. 結局つかってないのであまり便利じゃない.
  • haya14busa/vim-textobj-number
    • 数字のテキストオブジェクト. たまに便利. テキストオブジェクト学ぶかぁーという意図も含まれてた気がするけど結局それ以降テキストオブジェクト系プラグインは作成してない. なにかアイデア湧いたら作りたい.

Vim活以外の進捗(残りの1割)

  • プログラミングのバイトを4月からはじめました(たぶんVim活アピール補正はかかりました. ある意味 Vim script もペイする言語…?).
  • それに伴って Scala をやりはじめたんですが視野が広がってとてもよかったし, もっと学んでいきたいです.
  • JavaScript もそこそこ書いて, Angular.js に触れたりなどしました. underscore.js も便利.
  • しかし Scala その他 Vim 以外のOSS活動の進捗ダメです
  • heroku にPythonで小さいウェッブアプリをつくったのですが, 乗っかってるサービスが終了したので一緒にお亡くなりになってつらい.

勉強会にちょっとずつ参加するようになった

Vim 以外の勉強会にも今年はちょっとずつ参加するようになりました(Vimも基本的には今年からですが). しかしこれは進捗でゎなぃな…

その他

GitHub current streak 365 日達成した

dotfile 1行変更といったものがたくさんあるので全く大したことはしてないのですが, 日によってはそのついでにちゃんとプログラミングするぞっ! というきっかけになるので自分には合ってる継続的進捗管理術でした. 1年継続めでたい.

2014年 GitHub の Pull Requests / Issues 一覧

追記:

PullRequest 8 opens / 83 closed, Issues 53 open / 95 closed ※99% Vimである

Podcast 聴くようになった

聴いてるPodcastは

という感じです. 例に漏れず(?), Rebuild.fm から聴き始めてめっちゃおもしろいっ! となり, そこから バイリンガルニュース や The Changelog を聴くようになりました. 最近はずっと気になってはいたけど聞いていなかった mozaic.fm を聴くようになって, こちらも Rebuild とは別のベクトルのよさがあって全部聴きました. 技術系(?)英語 podcast の binpress podcast というポッドキャストは, 全部無料でスクリプトがついているというエイゴ・ゼンゼン・キキトレナイ勢としては歓喜のサービスがついていてお気に入りです. おもしろさは回によって違う感じですが, 英語もついでに学ぶかーという方にはオススメです.

リスニング能力高めてもっと英語のポッドキャストを楽しめるようになりたい…

英語の記事をコンスタントに読む & 検索するようになった

1年目も別に避けたりはしてなかったのですが, 最近は能動的に英語の情報に触れようとする確率が高くなったかなと思います. 検索や話題になっている記事を除けば, 見ているのはだいたい上記のリンクです. ただ単に読んでるだけではなかなか得るものも少ないので, 自分から英語で発信する能力をつけたいなーというのが最近の悩みです. 現状 GitHub の issue のやりとりやプラグインのドキュメントは基本英語を使ってるのですが, もっともっと使っていかないとなかなか身につかないですねー.

来年の抱負とか

  • Vim もっと頑張る.
  • Vim のソースコード側も C言語ちょっとぐらい読み書きできるようになって簡単なバグくらいなら報告だけでなく修正案まで出せるようにしたい.
  • Vim 以外も頑張る. なにか小さなウェッブサービスとか適当につくったりしたい.
  • Scala力をより深めるのに加えて C, Go, Haskell あたりの言語を触って学んでいきたいという思いがある.
  • データベース周りとか, 今触ってるレイヤーと別の部分も触っていきたい. 最低限基礎力が欲しい.
  • 英語力をつけていきたい. Vim について英語で適当に英語用Twitterアカウントつくってつぶやくとかなら継続してできるのでは…?という考えがある. 欲を言えば Lingr の Vim 部屋のノリで英語でチャットとかできれば一番いいんじゃないかという思いがある. IRC に #vim チャンネルあるらしいけど, IRCよくわからない…ログ読みたい…
  • 人生は厳しいので人生についてちゃんと考える必要がある. 人生キビシイ.

「プログラミング2年目をVimに捧げるとどうなるか?」

少々Vim以外の進捗が少なすぎる気がするのは反省ですが, Vim活によってVimというコンテキストに留まらず, たくさんの貴重な経験ができました. git や GitHub, テスト, CI, オブジェクト指向やらクロージャ, 変数や関数の名前の付け方, 英語での技術系コミュニケーションなどなど, たくさんのことを Vim駆動で理解したり, 理解を深めた1年間だったと思います.

Vim駆動学習, よさがあるのでオススメです. ※ただし用法用量を守って正しく Vim 活しないと Vim 以外の進捗が遅れます. 反省…

また Vim だといろいろな言語を使ってる人がいて, しかも雲の上のような存在の人もたくさんいたり, オンラインでもオフラインの勉強会でも活動が活発なのでVim以外にもいろんなことが知れるし, プログラミング初学者でも最初から Vim をガンガン使ってプログラミングしていくのはおもしろいかなぁと思います.

振り返ってみるとたくさんのVimmerにお世話になったなぁと思います. ※もちろんVimmerでないひとにもお世話になりました.

みなさん今年はお世話になりました. 来年の2015年もぜひ仲良くしてください. よろしくお願いします.

以上, @haya14busa の 2年目のプログラミング Vim活記録でした.

]]>
<![CDATA[incsearch.vimでVimの検索体験をリッチにする]]> 2014-12-11T22:38:41+09:00 http://haya14busa.com/enrich-your-search-experience-with-incsearch-vim <![CDATA[

この記事はVim Advent Calendar 2014の14日目の記事です。 13日目は deris さんによるスマートフォンでVimを操作するためにやっておいたほうがいいこと - derisの日記 でした。

1. incsearch.vim つくった

Vimの検索体験をリッチにする, incsearch.vim を作りました

haya14busa/incsearch.vim

incremental_regex_building


あなたとincsearch.vim 今すぐインストール

1
2
3
4
5
6
NeoBundle 'haya14busa/incsearch.vim'
Plugin 'haya14busa/incsearch.vim'
Plug 'haya14busa/incsearch.vim'
map /  <Plug>(incsearch-forward)
map ?  <Plug>(incsearch-backward)
map g/ <Plug>(incsearch-stay)

TL;DR

  1. Vim デフォルトの検索だとインクリメンタルハイライトは1つのマッチしかみてくれない
  2. incsearch.vim はマッチしたものすべてをインクリメンタルにハイライトする
  3. デフォルトのコマンドラインと高い互換性を持っているのでインストールしてデフォルトの/を置き換えてもスムーズかつ手軽に使える
  4. 本日バージョン1.0としてリリースしました
  5. ぜひ使ってみてください…!

※ ホントにToo long になった感があるので記事は長くて嫌いになってもincsearch.vimはシンプルに使えるので嫌いにならないでくださいっ…!

2. Introduction

Vimの検索を便利にする. incsearch.vim を バージョン 1.0 としてリリースしました!

「百聞は一見に如かず」ということで, 冒頭のgifなどを見ていただくだけで大事なことはすべて伝えつくしてしまった感があります. 「もう便利さはわかった!」 という方は記事なんてすっ飛ばして是非ブラウザバックして使ってみてください!

しかし今まで日本語でまともに解説したことがなかったこともあるので, ちょっとした便利機能やカスタマイズの仕方, 開発についてなど話していきたいと思います. もうすでに使っていたり, 聞いたりしたことあるよーという方も, 本日バージョン1.0としてリリースし, 以前から比べてインクリメンタルに改善してきたので少しは新しい情報もあるかなーと思います

3. incsearch.vim の機能を解説していくっ!

3.1 シンプルにすべてをハイライトするっ

  1. デフォルトの :h ‘incsearch’ とは違い, マッチしたパターンのすべてをハイライトする
  2. 別ウィンドウのハイライトも対応できる(オプションで変更可, version 1.0 で追加されました)

一番シンプルかつメインの機能としてマッチしたパターンをすべてハイライトします. 便利さ, 自明っぽいので説明を省きます!

incsearch_window

3.2 正規表現をインテラクティブに作って確認する

  1. デフォルトの検索だとエンターを押して:set hlsearch 状態になるまで, 現在入力している正規表現がどこにマッチしているかわからない
  2. incsearch.vim はもちろん正規表現に対応しており, スムーズに正規表現を作っていける
  3. <Plug>(incsearch-stay) というマッピングを提供しており, これはカーソルが動かないので途中でウィンドウ外に飛ぶといったこともない

マップ例:

1
map g/ <Plug>(incsearch-stay)

incremental_regex_building

(冒頭のgifと同じ)

これは vimrc によく書かれているプラグインマネージャが提供してるインストールコマンドから, インストールされているプラグインの部分とマッチする正規表現を作ってます(簡易版ですが). 普段の検索時にも勿論便利なのですが, 正規表現作る際の便利さは1つしかマッチを確認できないデフォルトの挙動と比べると段違いに捗ります. もしも incsearch.vim でデフォルトの検索を置き換えるまでもないかなーという人でも, 正規表現による検索の際のためにg/など好みのマッピングに定義しておくとこういうケースでは便利に使えると思われます.

3.3 検索中のインクリメンタルカーソル移動とスクロールで快適ファイル内検索

  1. Emacsは検索中にカーソルを前後に動かせるけどVimにはない…
    • ※ Vim には n/N があるので別になくてもよい
  2. incsearch.vim はデフォルトでは <Tab>/<S-Tab>で前後のマッチに移動できる
  3. Emacsや他のエディタでは見ない機能としてスクロール機能を提供しており, 画面内に目的地がないと判断すれば一気にスキップして画面外の次のマッチに飛べる(デフォルトでは<C-j>/<C-k>)

incremental_move_and_scroll

なにが便利か?

a) オペレータ待機モード時のモーションとドットリピート

ノーマルモードでは問題でもないですが, d/{pattern} といった オペレータ待機モード で使う場合, 決定したあとに n/N を使うことはできません. しかし, 最初に目測でマッチを確認してからカウントをつけて 3d/{pattern} とするのはとてもしんどい上に間違う可能性もあり, 生産的ではありません…

また1回だけの場合は ビジュアルモード を使えば上記の問題は回避できますが, これだとドットリピート が効きません.

そこで, incsearch.vim の <Tab>/<S-Tab> (:h <Over>(incsearch-next)) を使って 検索中にカーソルを移動させれば一目で目的地まであとどれくらいかもわかるし, オペレータと組み合わせるモーションとしての使用も, その後のドットリピートの問題も解消できます.

b) :h jumplist の更新が1回で済む

ノーマルモードでは問題でもないといったな? あれは嘘だ!

Vimには :h jump-motions というモーションの種類があり, これに属するモーションを 行うとジャンプ前のカーソル位置が記憶され, <C-o>/<C-i> でそれらのカーソル位置を 行ったり来たりできる超便利機能が存在します. 検索系のモーション(/,?,n,N, etc..) はこの jump-motions に属しており incsearch.vim でも勿論対応しているのでその機能を バリバリ使うことができます.

ここで問題なのは nN も jump-motions ということです. 検索後に n/Nで移動したあとやっぱり検索した元の位置に戻りたいな〜という時に n/N を押した回数分<C-o>を押す(またはカウントを前置する)必要があって地味に不便です.

incsearch.vim で検索中に<Tab>を押して移動してから検索を決定すれば勿論 jumplist の更新は1回で済むのでjumplistを汚すことなく十二分にそのジャンプ機能の便利さを享受することができます. 地味なよさがありますね.

c) スクロール機能で n 連打せずファイル内をサクっと検索

a), b) は1つ1つ前後に移動する機能の紹介でしたが, incsearch.vim は スクロール(デフォルトでは <C-j>がスクロールダウン, <C-k>がスクロールアップ)という機能を搭載しており, これは人に よってはライフチェンジングになりうるなかなかオススメ機能の1つです.

先ほどのgifを見てもらうとわかりやすいかと思うのですが,

デフォルトの場合

  1. 「あーファイル内の{pattern}って部分に用があるな〜」
  2. 「よーし/{pattern}<CR>で検索してn, n, n, …」
  3. 「まだ見つからない…(ファイル内に{pattern}がたくさんあって辿り着かない)」
  4. => 不便…

incsearch.vimのスクロール機能を使った場合

  1. 「あーファイル内の{pattern}って部分に用があるな〜」
  2. 「よーし/{pattern}で incsearch.vim を起動しよう」
  3. 「あー画面内にいっぱい{pattern}がある…よし<C-j> で次の画面へ」
  4. <C-j>を数回押して目的地発見. 任意で<Tab>/<S-Tab>で前後に移動してから<CR>!」
  5. => 幸せ便利

勿論, そもそもファイル内にたくさん存在しないようなキーワードを使って検索したり, タグが存在するなら ctagsなどを使用してタグジャンプしたほうが断然よいですが, いつでもユニークなキーワードが思い浮かんだり, タグが存在するわけではないので万能ではありません. 検索だとかなり汎用性が高いので個人的にはとても多用してる機能の1つになってます.

また他にもファイルの横断検索を補助するような機能を提供しているVimの機能や プラグインなどなどはあるとは思いますが, 以下のようなメリットがあります

  1. “検索” として使える
    • gn:substituteと連携したりなど”検索”は他のVimの機能と一緒に使うことによって, 相乗効果でより手に馴染む快適なキーストロークでエディットすることができます.
  2. 周囲のコンテキスト, 前後の行がみやすい
    • :vimgrepunite-line といった機能はだいたい前後の行が見れなかったりして ユースケースによっては困ることもあります. ただしgrep などは複数のファイルを扱える大きなメリットがあるので使い分けれるようになるのが一番よさそうです.

3.4 オート:nohlsearch

  1. :set hlsearchって便利でもあるけどだいたいウザイ
    • nnoremap <Esc><Esc> :<C-u>nohlsearch<CR> と言ったマッピングで検索後に消す人が多いと思います.
  2. incsearch.vim の オート:nohlsearch 機能を使えば検索後カーソル移動したらハイライトが消えるようになります.
  3. 地味に便利

incsearch_auto_nohlsearch

設定

1
2
3
4
5
6
7
8
set hlsearch
let g:incsearch#auto_nohlsearch = 1
map n  <Plug>(incsearch-nohl-n)
map N  <Plug>(incsearch-nohl-N)
map *  <Plug>(incsearch-nohl-*)
map #  <Plug>(incsearch-nohl-#)
map g* <Plug>(incsearch-nohl-g*)
map g# <Plug>(incsearch-nohl-g#)

<Plug>(incsearch-nohl-n) などは単なる <Plug>(incsearch-nohl)n のエイリアス なので独自の n*の機能を提供しているわけではありません

3.5 他のプラグインと組み合わせて使う

(3.4のつづき)

incsearch.vim/ とそれにまつわる検索の便利機能を提供するようにシンプルにしようとデザインしてるつもりです(そこっ!検索にまつわる〜とか言ってる時点でシンプルではないとか言わない!). なので n* を拡張したい場合に備え別の拡張プラグインと同時に扱える用に設計しています.

普通に一緒に使う分には何も考えなくとも併用できますが, incsearch.vim の オート :nohlsearch 機能 を使いたい場合はマッピングをちょっといじる必要があるので自分が使ってる例を出してみます

n 拡張プラグイン vim-auzu と一緒に使う

1
2
3
4
map   n <Plug>(incsearch-nohl-n)
map   N <Plug>(incsearch-nohl-N)
nmap  n <Plug>(incsearch-nohl)<Plug>(anzu-n-with-echo)
nmap  N <Plug>(incsearch-nohl)<Plug>(anzu-N-with-echo)

vim-anzu は n/N を押すとファイル内の 現在位置までの数/全マッチ数 を表示してくれる拡張機能です.

* 拡張プラグイン vim-asterisk と一緒に使う

1
2
3
4
map *  <Plug>(incsearch-nohl)<Plug>(asterisk-*)
map g* <Plug>(incsearch-nohl)<Plug>(asterisk-g*)
map #  <Plug>(incsearch-nohl)<Plug>(asterisk-#)
map g# <Plug>(incsearch-nohl)<Plug>(asterisk-g#)

または vim-asterisk のz*機能(カーソルが動かない*) をメインに使う場合

1
2
3
4
map *  <Plug>(incsearch-nohl0)<Plug>(asterisk-z*)
map g* <Plug>(incsearch-nohl0)<Plug>(asterisk-gz*)
map #  <Plug>(incsearch-nohl0)<Plug>(asterisk-z#)
map g# <Plug>(incsearch-nohl0)<Plug>(asterisk-gz#)

vim-asterisk は僕が最近作った * をカイゼンするプラグインです. 機能としては

  1. カーソルを動かさない * 機能の提供(マッピングにzのprefixがついてる)
    • 動かさずに *g* でカーソル位置の単語を検索レジスタ(@/)に入れたあとに gn などを組み合わせて編集したいというケースでは次のマッチに飛ぶ必要がないので カーソル動かないバージョンの * が欲しかった. どうせ n/N ですぐ動かせる
    • noremap * *N という解決法はダサいしウィンドウが一時的に動くので不便
  2. ビジュアルモードで選択したテキストを検索するvisual-star 機能
    • サクッと勢いで作ったので thinca/vim-visualstar のマルチバイトや keyword の扱いの部分のコードをお借りしています. ありがとうございます
    • visual-star は CursorMoved イベントが2回発生してしまうという問題があり, incsearch.vim の オート:nohlsearch 機能と併用できなかった. なので visual-star機能と同時に使いたい場合はvim-asteriskのvim-asterisk機能を使うと便利
  3. ignorecase だけでなく smartcase の値も一緒にみてくれる
    • デフォルトはなぜか ignorecase の値しかみてくれず, smartcase を設定していてもignorecase状態で検索される
    • 非直感的すぎるので vim-asterisk は :set ignorecaseの値も:set smartcaseをみるようになっています

3.6 Vim のデフォルトからちょっとカイゼン

a) magic オプションカイゼン

Vimには 'magic' という正規表現のエスケープする文字を変えるオプションがありますが \m, \M しか設定できません(:h /magic). またこれは/だけでなくすべての正規表現の挙動を変えてしまい設定すると, 対応できていないプラグインが動かなくなったりする問題があります (:h 'magic')

incsearch.vim ではこれをカイゼンして \v, \V, \m, \M の, どの magic でも設定できる. また勿論他のプラグインには一切影響しません.

1
let g:incsearch#magic = '\v'

b) カーソル下単語補完やCtrl-Wによる単語削除のカイゼン

Vimはset incsearch状態で検索中に<C-r><C-w>を押すとコマンドラインのカーソル前の単語とのマッチをみて, バッファのカーソル下の単語を補完してくれる機能を持っています. この機能自体はとてもべんりなのですが, コマンドラインが /\vwo の状態, カーソル下の単語が word の時に <C-r><C-w> を押しても補完が発動せず単にカーソル下の単語が挿入され /\vwowordになってしまいます.

これはvery magicオプションを設定する\vv部分とwoとの区別がなくvwor がコマンドラインのカーソル前の単語と認識されているのが原因なので, カーソル前の単語 の範囲をかしこく決めてくれる機能を提供しています(オプションでoffにすることは可能です. :h g:incsearch#smart_backward_word)

<C-w> によるカーソル前の単語の削除も同様の問題がありこれもカイゼンして, /\vword状態で<C-w>を押すとデフォルトだと/\となるところを\vとなるようにしています

incsearch_smart_backward_word

見た目はとっても地味で聞いてもピンとこないかもしれないですが, 挙動が直感的になるとても気に入ってる機能のひとつです. ジッサイベンリ

3.6 マッピングについて

  1. :cnoremap:cmap に設定されているマッピングは自動で適用される
  2. incsearch.vim が提供する機能へのマッピングや, :cnoremap の設定の上書き, incsearch.vim でのみマッピングしたいという場合は :h IncSearchNoreMap コマンド を使います

設定例

1
2
3
4
5
6
7
8
9
10
augroup incsearch-keymap
    autocmd!
    autocmd VimEnter * call s:incsearch_keymap()
augroup END
function! s:incsearch_keymap()
    IncSearchNoreMap <Right> <Over>(incsearch-next)
    IncSearchNoreMap <Left>  <Over>(incsearch-prev)
    IncSearchNoreMap <Down>  <Over>(incsearch-scroll-f)
    IncSearchNoreMap <Up>    <Over>(incsearch-scroll-b)
endfunction

3.7 autocmd で拡張できる

参照: :h incsearch-autocmd

incsearch.vim は Vim デフォルトのコマンドラインのと違い独自の autocmd イベントを発行しているので, それをつかってincsearch.vimを拡張することができます. 以下は inside/vim-search-pulse というプラグインの機能を簡単に組み込んでみた例です. 個人的には要らないけどなんかカッコイイ…!?

incsearch_autocmd_flash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function! s:flash()
  for _ in range(1, 3)
    set cursorline!
    redraw
    sleep 50m
  endfor
  set cursorline!
endfunction

augroup incsearch-pulse
    autocmd!
    autocmd User IncSearchExecute call s:flash()
augroup END

noremap <silent> <Plug>(my-flash) :<C-u>call <SID>flash()<CR>
map n <Plug>(incsearch-nohl-n)<Plug>(my-flash)
map N <Plug>(incsearch-nohl-N)<Plug>(my-flash)
" デフォルトの場合
" map n n<Plug>(my-flash)
" map N N<Plug>(my-flash)

4. Development

開発の話とか.

4.1 Design

ここまでいろいろと紹介しておいてどうかという話なのですが, incsearch.vim は シンプルに機能を提供し , デフォルトのコマンドラインと高い互換性を保ちつつ, それでいて拡張性の高いものにしようというコンセプトでつくっています(実際そうなっているとは言ってない)

特に一番苦心したのはデフォルトのコマンドラインとの互換性を保つように開発するところでした. 現在はVimのデフォルトの/でできるほとんどのことはできますが,

ビジュアルモードでのハイライトだったり, vim-repeat といった別のプラグインに依存せず ドットリピートを可能にしたり, 逆にそれを可能にした <expr> マッピングによる textlock という Vimの制限を解消するために, 必要でない部分は<expr> マッピングを使わないようにするといった回り道を しながら, 現在はほぼデフォルトと互換性のある状態にできたと思います.

<expr> についてちょっと解説したりする

  1. :h :map-<expr>
  2. ドットリピート対応できる
  3. :h function-search-undo とかの制限をかいくぐれる
  4. いろいろいじった上で最終的にデフォルトのマッピングを返して実行! ということができるのでデフォルトのモーションを拡張する際にとても便利
  5. しかし問題点もある

<expr> とは expression mapping の略で, {rhs}(right-hand-side) を評価した値を返してくれます.

1
noremap <expr> g/ printf("/%s\<CR>", input('>'))

上記のマッピング例では, {rhs}input('>') が評価され, そこで入力した値を{pattern}とすると それがprintf()によって最終的には /{pattern}<CR> になります.

何がいいのかというと, 実際に dg/pattern などと使った際に, {rhs} が評価されて d/pattern になります. これはそもそも最初から d/pattern と打った時と同様の挙動になるので, あたかもデフォルトの/を使ったような挙動を簡単に実装できるのです! これによって ドットリピート や :function-search-undo に対応することが可能となります.

とは行ってもイマイチわかりにくいと思うので <expr> を使わないマッピングを見てみましょう.

1
2
3
4
function! s:non_expr_search()
    execute 'normal!' printf("/%s\<CR>", input('>'))
endfunction
noremap z/ :<C-u>call <SID>non_expr_search()<CR>

単に検索という意味では動いているように見えるのですが, function-search-undoの制限により ハイライトされず, また dz/patternd:<C-u>call <SID>non_expr_search()<CR> となるので, ドットリピート時にも s:non_expr_search 関数が呼ばれてしまします. これによってその中の input()がドットリピートでも呼ばれてしまい入力待ちになってしまいます. これでは以前に入力した値をつかって検索してくれるデフォルトのドットリピートの挙動が再現出来ていません…不便…

普通のマッピングだとこういう問題があるのでそれを解消ができる <expr> はデフォルトのモーションの拡張にとてもあっているのですが, <expr><expr>で上述した副作用として :normal が使えないといった問題もあるので これから<expr>を使ってプラグイン作りたいっという方は注意して使ってみてください.

incsearch.vim や先ほど紹介した vim-asterisk も<expr>を活用して便利にしたりしています. 他にも rhysd/clever-f.vimderis/vim-shot-f といった f を拡張するプラグインでも <expr> が使用されており, もともとこの<expr>でドットリピートに対応するという方法は 僕は clever-f で初めてみました. スゴイ.

4.2 vital-over, または incsearch.vim のカスタムコマンドラインインターフェースについて

incsearch.vim は vital-over というカスタムコマンドラインインターフェースを提供するライブラリを使わせていただいてます. incsearch.vim のデザインとしてVimデフォルトとの互換性を目指していると先ほど書いたのですが, 実はincsearch.vimがやっている部分は検索だけで, コマンドラインインターフェースがVimのデフォルトと同じように使えるのはこの vital-over というライブラリのおかげです. 本当にアツイプラグインなので興味あるVim プラグイン開発者は是非使ってみてください!

モジュール性が高いのでガッツリとしたコマンドラインとしてではなく, ちょっと便利な input()getchar() として使えるかなーと思います. そのうち何かまた作ってみたい…!

4.3 テストとかLintとか

  1. themis.vim でテスト書いた
  2. vim-lint / vint (期待)
  3. テストって便利って改めて気づいた…!
  4. themis のテスト書き方自体は vim-jp/vital.vim など他のものを参考にするとよいと思う

incsearch_dot_reporter_test.png incsearch_badges.png

今回incsearch.vim は(現在が開発&メンテしてるvim-easymotionでもちょっとずつやってはいましたが)割と初めてスクラッチでテストやlintをしながら 書いたものだったのですが Vim script の開発周りの環境はかなり便利です. 特に Vim を使えるところが開発しやすくてよいです.

CI も travis で 最新に近い Vim と travis.org の Vim のバージョンの両方でテスト (Test on the latest Vim by crazymaster · Pull Request #241 · vim-jp/vital.vim) したり, Vim プラグインを Windows 環境でテストする - 永遠に未完成 を参考に Windows 環境 でもテストがしてみたりしてなかなかよかったです. (Windows環境だけで違う挙動になるのヤメローッ!)

Lint に関しては Kuniwak/vint という python 製ツールも気になっているので期待ですね.

4.4 Vim script 楽しいっ

テストやLintツールもあるし, vim-jp/vital.vimosyo-manga/vital-over といったライブラリ, Vim から引ける膨大な:help, 困ったら vim-jp – LingrIssues · vim-jp/issues に相談, バグ報告できる環境, Vim を使って開発できる Vim script を書くのは楽しいです…!


……..

….ほんとうですよっ!? (言語仕様は寛容な心でカバー)

ぜひぜひみなさんVim プラグイン開発やっていって開発効率を上げたり快適でリッチなエディタ環境を作ってみましょう…!

(※ 用法, 用量を守ってただしくVimしましょう)

5. 最後に

話が逸れてる感をひしひしと感じますが, あくまで incsearch.vim を紹介・解説する記事だったはずなので話を戻しましょう.

incsearch.vim, 今でもとても便利だと思っているのですが, まだまだ開発していく余地があります. (後方互換性を壊すことはしないと思います)

autocmd の拡張例を上述しましたが, まだコマンドラインの中身をいじれるようなインターフェースは実は提供できていません. どこまでオープンにしていけばいいのか, オープンにしたら本当に便利になるのか, などなど僕にとっては難しくゆっくり考えながら決めて行きたいです. 意見頂けたりとかコントリビューションお待ちしています…!

Vim Advent Calendar でテンションあがってちょっと長くなってしまいました. ここまで読んでくださった方ありがとうございます.

incsearch.vim 是非1度使ってみてください. 気に入って頂けるとそれはとっても嬉しいです!

それではVim Advent Calendar 2014の14日目の記事でした.

Happy Vimming!

]]>