VimConf 2024に行ってきた
前回はTinyでしたが、今回はNormal Buildに戻りました。
実は前々からぼんやりと思考のVimコマンド化みたいなことを話してみたいと思っていました。
また、2020年から3年ほど英語を勉強してきた*1こともあり、いつか英語で登壇したいという想いもありました。
正直、考えがまとまりきっていなかった上に英語力にもそこまで自信はありませんでしたが、迷っていても仕方ないので、思い切って英語での登壇に挑戦することにしました。
ただ、あまり長く喋る自信はなかったため、発表時間は15分を選択。
しかしやはり短かったようで、余裕がありそうなら話そうと思っていたページを入れるどころか、いくつか削らないといけませんでした。
削ったり入れられなかったのは以下のような内容です。
- quickfixを自動で開くautocmd
- quickfix関連のbuiltin関数の紹介とその応用例
- vim-lspの紹介とquickfixとの組み合わせ
- caddfile/caddbuffer/caddexprの紹介
- quickfixをソートする自作コマンドの紹介
- 自作した外部コマンドとの連携
- 各種デモ
- など
正確な時間は記録していませんが、体感的には例年の2倍ほど準備に時間をかけたと思います。
その甲斐あって自分としては十分かなというトークができたと思うし、今はやり切った感に溢れています。
そういえば、過去の登壇では練習だと発表時間ギリギリか少し超えてしまっていたのに、本番はいつも少し早く終わっていました。
昨夜に最後の練習していて気付いたんですが、その原因は以下のような点だったかもしれません。
- 普段の練習の時は基本的に何も見ないようにしていた
- スライドを見ながら喋ることでトークが途切れにくくなる
- スピーカーノートを使うとほとんど詰まらなくなる
- 結局当日は使えなかったけど...
- 開始時間と終了時間しか見ていなかった
- 本番では経過時間を意識していたため、遅れないよう話す速度を調整していた
ちなみに、今回はトークを覚えるためにスピーカーノートの内容を.txtにし、macのsayコマンドで.m4aにしたものをずっと聴いたり一緒に喋るということをやっていました。
音声は何種類か試してみましたが最終的にSiri(英語)の1、3、4の3つに落ち着きました。*2
話すスピードは未設定だと速すぎたので-rate=120と少し遅めに設定していました。
そして今日も今まで通りGoogleスライドを使いましたが、なんと会場のスクリーンにプレゼンを映しながらスピーカーノートを表示することができませんでした。
接続チェックの段階でそれに気付いたときはかなり焦りましたが*3、スピーカーノートが完成したところで満足せず、何も見ないで話せるようになるまで練習しておいたおかげで事なきを得ました。
さて、他の方々の素晴らしい発表や懇親会・二次会についても書きたい気持ちはあるのですが、そろそろ体力の限界を迎えてしまいそうなので、一旦このあたりで締めます。。。
それでは今年も素晴らしいカンファレンスを企画・運営してくださったスタッフの皆様、熱心に聞いてくださった参加者の皆様に心より感謝申し上げます。
特に、発表の後に色々な方から感想や質問をいただけて非常に嬉しかったです。
本当にありがとうございました。
VimConf 2023 Tiny に行ってきた
昨日(11/18)はVimConfに行ってきました。
前回が2019年だったので実に4年ぶりの開催でした。
2017年から2019年は3回ともスピーカーでしたが、今回は久しぶりの一聴衆でした。
なので当日は全く緊張する必要がなく、とても穏やかな気持ちで参加することができました。
また、休憩時間や懇親会などで久しぶりに会えた人と話をすることができ、とても懐かしい気持ちになりました。
そして古巣の皆さんのVim愛は変わらないどころか、さらに強まっていて感慨深かったです。
ただ後になってみると、新しい所属でのVim活なんかをLTしても良かったかなと思ったりもしました。
この感覚はVimConf 2016に参加した時に似ていて、あの時も「自分も登壇したい!」という気持ちになったことを覚えています。
最後に、発表者とスタッフの方に改めて大きな感謝を伝えたいです。
特にスタッフの方は限られたリソースの中でとても大変だったと思いますが、今までのVimConfと変わらない体験を得ることができました。
来年も楽しみにしています。
gnosticでOpenAPIをProtocol Buffersに変換する
gnosticとgnostic-grpcを使えばOpenAPIの.yaml(.json)を.protoに変換できる。
1. gnosticをインストールする
go install github.com/google/gnostic@latest
2. gnostic-grpcをインストールする
git clone https://github.com/google/gnostic-grpc
cd gnostic-grpc
./COMPILE-PROTOS.sh
./plugin-creation.sh
※ protocコマンドがないとエラーになるので Protocol Buffer Compiler Installation | gRPC あたりを参考にインストールしておく必要がある。
3. 変換する
gnostic --grpc-out=proto openapi.yaml
- openapi.yaml
openapi: 3.0.3 info: description: Foo API Document. title: Foo API version: "1.0" paths: /foo: post: operationId: PostFoo requestBody: content: application/json: schema: $ref: '#/components/schemas/PostFooRequest' responses: "201": content: application/json: schema: $ref: '#/components/schemas/Foo' description: Created "400": content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' description: Bad Request /foo/{id}: get: operationId: GetFoo parameters: - in: path name: id required: true schema: type: string responses: "200": content: application/json: schema: $ref: '#/components/schemas/Foo' description: OK "404": content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' description: Not Found components: schemas: ErrorResponse: properties: code: type: string message: type: string type: object Foo: properties: data: type: string id: type: string type: object PostFooRequest: properties: data: type: string type: object
次のようなprotoになる。
- proto/openapi.proto
syntax = "proto3"; package openapi; import "google/api/annotations.proto"; import "google/protobuf/descriptor.proto"; import "google/protobuf/empty.proto"; option go_package = ".;openapi"; message PostFooRequest { string data = 1; } //PostFooParameters holds parameters to PostFoo message PostFooRequest { PostFooRequest post_foo_request = 1; } //GetFooParameters holds parameters to GetFoo message GetFooRequest { string id = 1; } service Openapi { rpc PostFoo ( PostFooRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { post:"/foo" body:"post_foo_request" }; } rpc GetFoo ( GetFooRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/foo/{id}" }; } }
※ OpenAPIの構成によっては変換できない場合がある。
また、OpenAPI 3.1には対応していない。
github.com/swaggest/openapi-goを使ってOpenAPIを生成する
github.com/swaggest/openapi-goを使うとGoのコードからOpenAPIの定義(json/yaml)を生成できる。
例:
package main import ( "encoding/json" "fmt" "log" openapi "github.com/swaggest/openapi-go" "github.com/swaggest/openapi-go/openapi3" ) type endpoint struct { id string method string path string request any responses []response } type response struct { status int body any } func main() { r := openapi3.Reflector{ Spec: &openapi3.Spec{ Openapi: "3.0.3", Components: &openapi3.Components{}, }, } r.Spec.Info. WithTitle("Foo API"). WithDescription("Foo API Document."). WithVersion("1.0") list := []endpoint{ { id: "PostFoo", method: "POST", path: "/foo", request: new(PostFooRequest), responses: []response{ {status: 201, body: new(Foo)}, {status: 400, body: new(ErrorResponse)}, }, }, { id: "GetFoo", method: "GET", path: "/foo/{id}", request: new(GetFooRequest), responses: []response{ {status: 200, body: new(Foo)}, {status: 404, body: new(ErrorResponse)}, }, }, } for _, v := range list { oc, err := r.NewOperationContext(v.method, v.path) if err != nil { log.Println(err) continue } oc.SetID(v.id) oc.AddReqStructure(v.request) for _, resp := range v.responses { oc.AddRespStructure(resp.body, openapi.WithHTTPStatus(resp.status)) } if err := r.AddOperation(oc); err != nil { log.Println(err) continue } } // b, err := r.Spec.MarshalYAML() b, err := json.MarshalIndent(r.Spec, "", " ") if err != nil { log.Println(err) return } fmt.Println(string(b)) } type PostFooRequest struct { Data string `json:"data"` } type GetFooRequest struct { ID string `json:"id" path:"id"` } type Foo struct { ID string `json:"id"` Data string `json:"data"` } type ErrorResponse struct { Code string `json:"code"` Message string `json:"message"` }
list の中身を既存コードから生成すれば途中からでも比較的簡単にOpenAPIを始められる。
macvimをビルドする(2023/08)
だいぶ前にも書いていた。
ここ最近は毎朝UpdateMacVimを実行している。*1
UpdateMacVim() { cd $HOME/go/src/github.com/macvim-dev/macvim git fetch origin master if [ -z "`git diff FETCH_HEAD --shortstat`" ]; then cd - return 0 fi git merge FETCH_HEAD make distclean && ConfigureMacVim && make rm -rf $HOME/.local/MacVim.app cp -R src/MacVim/build/Release/MacVim.app $HOME/.local/ cd - }
ConfigureMacVim() { ./configure \ --enable-fail-if-missing \ --with-features=huge \ --enable-terminal \ --enable-multibyte \ --enable-python3interp \ --enable-luainterp \ --with-lua-prefix="$(brew --prefix lua)" \ --enable-cscope \ --with-tlib=ncurses \ --with-compiledby="daisuzu <daisuzu@gmail.com>" \ CFLAGS="-I$(brew --prefix)/include" \ LDFLAGS="-L$(brew --prefix)/lib" \ --prefix=$HOME/.local "$*" }
あとは$HOME/.local/MacVim.app/Contents/binにパスを通しておけばOK。
Goの外部パッケージに独自の変更を加える
外部パッケージを使っていて、ちょっとした修正を試したい時は以下のような方法があります。
- 1. 外部パッケージをForkしてgo.modで置き換える
- 2. 外部パッケージのコピーをリポジトリに追加してgo.modで置き換える
- 3. 変更したファイルをリポジトリに追加してoverlayで書き換える
1. 外部パッケージをForkしてgo.modで置き換える
最も基本的なやり方なので特に理由がなければこちらの方法にするのが良いでしょう。
対象のパッケージのForkに変更を加え、以下のようにしてgo.modで置き換えます。
go mod edit -replace github.com/EXTERNAL_/PACKAGE@v1.0.0=github.com/daisuzu/PACKAGE@development go mod tidy
なお、Forkにコミットを追加する場合はその度にgo mod tidyで擬似バージョンを更新していく必要があります。
2. 外部パッケージのコピーをリポジトリに追加してgo.modで置き換える
Forkを作りたくなかったり、試行錯誤したい場合などはリポジトリの中に対象の外部パッケージをコピーする方がやりやすいかもしれません。
その場合も同様にgo.modで置き換えます。
go mod edit -replace github.com/EXTERNAL_/PACKAGE@v1.0.0=./PATH_TO_COPY
3. 変更したファイルをリポジトリに追加してoverlayで書き換える
変更が数行程度だと上記の方法が面倒だと感じることがあるかもしれません。
その場合は変更したファイルのみをリポジトリに追加し、goコマンドのoverlayフラグで書き換えることも可能です。
以下のようなoverlay.jsonを用意してgo build -overlay=overlay.jsonのように指定します。
{ "Replace": { "/GO_MOD_CACHE/PATH_TO_EXTERNAL_PACKAGE/TARGET.go": "/PATH_TO_COPY/TARGET.go" } }
いずれの方法もうまくいったら本家に還元しましょう。
Vimの極意
この記事はVim Advent Calendar 2022の1日目の記事です。
今年でVimをメインエディタにして15年になります。
最近どうすれば思考する速度でテキストを編集できるようになる*1のか考えたりすることがあったので、この機会に軽くまとめてみます。
簡単な操作であれば「○○をしたい」と思った瞬間にそうなっていることもありますが、実際はそうならないことがの方が多いです。
それが何故なのかというと、複雑な編集をする際には自分のやりたいことをVimの操作に変換する必要があり、そこに時間がかかっているからだと考えました。
そこで思いついたのが、やろうとしていること自体をVimのコマンド群として捉えられるようになればさらに高速にテキストを編集できるのではないか、ということです。
具体例をあげてみると、以下のようなGoのコードでカーソルがfuncのfにある時に、戻り値の型をClientInterfaceから*Clientに変更したいと思ったら、
func NewClient() ClientInterface { return newDefaultClient() }
戻り値の位置にカーソルを移動して、カーソル下の単語を*Clientに書き換えよう、と考えてVimを操作するのではなく、
最初から$b、cw*Clientと考えてVimを操作します。
つまり、「あらゆる編集操作をVimのコマンドで表現できるようになる」ことがVimの極意ということになります。
実際に文章に書いて読んでみるとかなり難しそうな気がしてきましたが、
これを会得する方法としては以下の7つが考えられます。
- 極意のことを意識しながらVimを使い続ける
- 定期的にVimのヘルプを読み返してコマンドとしての語彙を増やす
- 指に負担を感じたら少し立ち止まってより良い方法がないか調べる
- VimGolfをやる
- 覚えにくい処理をユーザー定義コマンドやマッピングにする
- vimrcを育てる
- 4で汎用性が高いものをプラグインにする
- 既に似たようなプラグインがあればそれを使っても良い
- 5で有用なものをVimの本体に組み込む
- Vim本体の実装に対する知識が必要
ある程度身に付いてくれば先のサンプルコードで戻り値の型がわからなくてもすぐに
/newD<CR><C-]>Wyiw<C-T>j%bcw<C-R>0<ESC>が出てくるようになることでしょう。
ぜひ試してみてください。