vimproc に Windows の DLL ダウンロード機能を入れた

tl;dr

以下を vimrc の最初の方に書く。

let g:vimproc#download_windows_dll = 1

本文

vimproc は外部プロセスを非同期実行するのに必要な Vim のライブラリ。これを利用しているプラグインも結構ある。
これはとても便利なのだけど、動的ライブラリを別途ビルドする必要がある。
LinuxMac OS X なんかの環境では、標準でビルド環境が整備されているのもあり、ここで苦労することはあまりないと思う。しかし、Windows だと標準ではビルド環境がないため、ビルドすること自体が難しい。
kaoriya 版の Vim に同梱されている vimproc には DLL も含まれているので、これを利用している人もいるのではないだろうか。
ただ、最新版が使いたかったり、複数 OS で同じ設定を使いたいという理由でプラグインマネージャで vimproc を管理したい人もいるだろう。私がそうだ。そういった場合、kaoriya 版のプラグインはちょっと使いづらい。
とりあえずビルドだけでもなんとかならないか、と思って以前やったのがこれ。

Add appveyor.yml to build DLL for MS Windows by thinca · Pull Request #217 · Shougo/vimproc.vim · GitHub

これによって、vimproc のバージョンが上がり、タグが打たれると、自動で DLL がビルドされて誰でもダウンロードできるようになった。ダウンロードは以下のページからできる。

https://github.com/Shougo/vimproc.vim/releases

というわけで自分でビルドしなくてもダウンロードして配置するだけ。便利。だったのだけど、正直ダウンロードしてくるのも面倒になった。プラグインマネージャで vimproc をインストールした直後は必ず「DLL がない」とエラーが出る。とてもつらい。
すでにビルド済みバイナリはネット上にあるわけなので、ダウンロードも自動でやってくれるとラクチン。というわけでそういう機能を作った。

Download windows dll automatically by thinca · Pull Request #242 · Shougo/vimproc.vim · GitHub

最初は全自動だったのだけど、デフォルトでダウンロードされるのは困る、という意見もあり、確かにアグレッシブすぎる感じがしたので、設定を足した。以下の設定を vimrc に書いておけば、DLL がない場合は vimproc がロードされるタイミングで自動でダウンロードされる*1。また、DLL が古かった場合は自動で更新してくれる(この場合は Vim の再起動が必要)。

let g:vimproc#download_windows_dll = 1

この設定は vimproc が使用されるより前に書く必要があり、プラグインマネージャが vimproc を使う場合もあるため、できるだけ vimrc の先頭に書いた方が良い。
これでようやく vimproc の DLL との格闘に終止符を打てた気がする。
Vim には job という外部プロセスを非同期実行するための機能が入りつつあり、きちんと入って各プラグインがこちらを利用し始めたら vimproc 自体がオワコンになる可能性も 0 ではないが*2、今は気にしないでおこう…。

*1:ダウンロードも最初は curl 必須だったのだけど、raa0121 さんの提案で powershell でもダウンロードするようにしたので大抵の環境でダウンロード可能になった。raa0121++

*2:vimproc には非同期実行以外にもいくつか機能があるのでそっちの用途で使われる可能性はありそう

全角スペースを可視化するプラグイン zenspace.vim を作った

プログラミングをしていると、混ざっていると困る全角スペース。
可視化するように設定を書いている人も多いんじゃないでしょうか。
実際ググると、全角スペースを表示するための設定がたくさん出てきます。私も vimrc に設定書いてました。
でもこれ…みんなやってるなら、もうプラグインでよくない?
ってことでプラグインにしました。

zenspace.vim - Show Ideographic Space (a.k.a. Zenkaku Space).
https://github.com/thinca/vim-zenspace

全角スペースを表示するためだけのプラグインです。(画像内のタブの表示は 'listchars' によるもの)

デフォルトでは 'list' オプションの値に連動して、可視/不可視を切り替えます。なので、'list' オプションをオンにしてください。

set list

'list' オプションとかいいから常に表示しろよ、って場合は g:zenspace#default_mode を設定してください。

let g:zenspace#default_mode = 'on'

zenspace.vim は、常に表示する "on"、表示しない "off"、'list' オプションに追従する "list" の 3 つのモードがあり、ウィンドウ毎に独立しています。
デフォルト値を変える場合は上記の通り g:zenspace#default_mode を設定してください。個別のウィンドウで変更したい場合は :ZenSpaceOn :ZenSpaceOff :ZenSpaceList を使用してください。

Q & A

Q. 色を変えたいんだけど?
A. 赤に変える例を載せておきます。

augroup vimrc-highlight
  autocmd!
  autocmd ColorScheme * highlight ZenSpace ctermbg=Red guibg=Red
augroup END

Q. 普通のスペースとかは表示できないの?
A. 全角スペース専用です。通常のスペースやタブや行末スペースは、Vim 標準の 'listchars' オプションを設定するといい感じに表示できるので、そっちを使うことをオススメします。

Q. Conceal とか使って□で表示できない?
A. 検討したんですが、Conceal の制限がキツすぎて見送りました。詳細はこちらを読んでください。


Enjoy!

Boost.勉強会 #19 東京 に行ってきた

C++ まったく書いてないけどなぜか Boost.勉強会 #19 東京 に行ってきたよ。
C++ の最近の動向に軽く触れる感じでゆるーく感想書いてく。C++ よくわかってないのでよくわからない感想しか書けません。

Boostライブラリ一周の旅 1.59.0-1.60.0 - @

資料: http://www.slideshare.net/faithandbrave/boost-tour-1600
資料(マージ版): http://www.slideshare.net/faithandbrave/boost-tour-1600-merge

いつものやつ。
Boost.Test v3 で Power Assert 入ったのだいぶ強そうだった。

Effective Modern C++C++ Core Guidelines - @

資料: http://www.slideshare.net/ShintarouOkada/boost19-effective-modern-cc-core-guidelines

Effective Modern C++ 邦訳版を査読した話と、CppCoreGuidelines の話。
査読報酬で具体的な話が聞けたのだいぶ良さがあった。
CppCoreGuidelines は lint を提供することを想定しているとのことで、使えるようになったらとても便利そう。
詳しくない言語を書くとき、良い書き方なのかいつも気にしながら書こうとしてしまうのでそれだけでかなり時間がかかってしまうのだけど、lint があると安心して書いていけそう。

SIMD のゆるい話 - @

資料: http://www.slideshare.net/krustf/avx-simd

ゆるい話…なるほどわからん、という感じだった。すまぬ。

「女性のためのC++コミュニティ Ladies++ meetup #1」の紹介 - @

資料: http://www.slideshare.net/cocodrips/c-ladies

女性のためのC++コミュニティ Ladies++ meetup #1 の紹介。
コミュニティが始まったばかりで人脈があまりないので女性 C++er を紹介して欲しいとのことです。

クロスプラットフォームマルチメディアライブラリSDL2の紹介 - @

資料: http://www.slideshare.net/nyaocat/sdl2

SDL2 の紹介。

いつの間にかライセンスが zlib になっていたのと、Emscripten でブラウザにも対応していたの知らなかった。

クソザコ鳥頭が非順序連想コンテナに入門してみた - @

資料: http://www.slideshare.net/kariya_mitsuru/ss-55842496

非順序連想コンテナが仕様である処理時間を定数時間にするための実装の苦労がおもしろかった。

expectedによるエラーハンドリング - @

資料: http://www.slideshare.net/faithandbrave/error-handling-using-expected

expected の紹介。
Haskell の Either に成功/失敗の明確な意味を持たせたものと解釈した。
単に成功値か失敗値が欲しいというだけなら、variant でも目的は達成できるのかもしれないけど、インターフェースが成功/失敗に特化して作ってあるし、何より明確な意味があるのは便利。

Boost.Configについて - @

資料: http://www.flast.jp/article/boost-19-tokyo/index.html

コンパイラC++ のバージョンの差異を吸収するための中の人が使うライブラリの話。
なんというかやはり苦労してるんだな…という感じ。

メモリモデル再入門 - @

資料: https://docs.google.com/presentation/d/1ClVZvXo8lj7TE66hZ0eqNyVc1DBcRciJRXUxukhOb2k/pub#slide=id.p

スレッドとか Data Race とかの話。誰でもわかる基本的な例から徐々に複雑な例にしていく解説が上手いなと思った。
なかなか面白い話だったのでスライド最後まで完成させてほしかった…。

全体的な感想

割とわからないことだらけかなーとも思ってたけど、面白いセッションも結構あって良かった。
相変わらず C++ 書く機会ないですが、C++ は割と好きな言語なのでまた機会があれば参加したいです。

Vim に追加された assert 系の関数の紹介

この記事は Vim script Advent Calendar 2015 の 3 日目の記事です。

はじめに

先月末に、Vim の組み込み関数に assert 系の関数が追加されました。
今回はこの関数について紹介します。

歴史

割とどうでもいいので読み飛ばし可です。

2015-11-29 Vim 7.4.944

assertEqual()、assertTrue()、assertFalse() が追加されました。また、一緒に使うための v:errors も追加されました。

しかし、関数名について苦情が多く出ました
Vim script の組み込み関数の多くは、単語の区切りがなく1単語です。例えば deepcopy() や matchlist() などです。
一部の関数には _ が使われており、has_key() や complete_add() があります。
そして中には大文字を含む関数もありますが、今のところそれらは全て ID と言う単語に使われており、synID() や diff_hlID() などです。これらは例外と言えそうです。
つまり今まで camelCase が使われていなかったのにも関わらず突然 camelCase の関数が追加されたので、それはさすがにおかしいという話になりました。

2015-11-30 Vim 7.4.945

assertEqual()、assertTrue()、assertFalse() がそれぞれ assert_equal()、assert_true()、assert_false() にリネームされました。
また、先の話で別途提案された、各関数にエラー時のメッセージを渡すためのオプショナルの引数を追加するパッチが適用されました。
わずか1日でのスピード対応、ドキュメントもテストも修正されて完璧…と思いきや…肝心の実装が適用されておらず、関数名は変わっていませんでした…。

2015-12-01 Vim 7.4.946

無事実装も適用され、assert_equal()、assert_true()、assert_false() にリネームされました。

2015-12-03 Vim 7.4.950

一通り解決されたかに思いましたが、変数 v:errors が初期化されていないという問題が発見されました。
現在 vim_dev にパッチを投げています
恐らくすぐに適用されると思います。
適用されました

使い方

仕組みは単純です。
v:errors は配列になっていて、各 assert 関数が失敗すると、v:errors にメッセージが文字列として追加されます。
なので使用前にまず v:errors を初期化します。

let v:errors = []

続いて適当に assert 系の関数を呼びます。

function! Test() abort
  let actual = 2 + 2
  call assert_equal(5, actual)
endfunction

call Test()

v:errors を見ると、失敗の結果がわかります。

echo v:errors
" => ['function Test line 2: Expected 5 but got 4']

結果のメッセージの : の左側にある文字列は expand('') の結果に行番号の情報を足したものです。
メッセージの右側には失敗時の値が出ます。
また、assert 系の関数には追加でメッセージを渡すことができ、渡すと : の右側はこのメッセージが表示されます。

let v:errors = []
function! Test() abort
  let actual = 2 + 2
  call assert_equal(5, actual, 'test fail!')
endfunction

call Test()
echo v:errors
" => ['function Test line 2: ''test fail!''']

終わりに

見てきたように、非常に簡単な仕組みのもので、なんなら Vim script で自作もできそうな仕組みになっています。
今後、Vim 本体での Vim script 周りのテストはこの関数が使われるようになるようです。
本体以外でも、簡易なテストが書きたい場合には使えるかもしれません。

VimConf 2015 に行ってきた

VimConf 2015 に行ってきた。
昨年は主催的ポジションからの参加だったのだけど、今年は割と一般参加的ポジションで、一応スタッフって枠にはなってたけど大したことはしてないし、ラクさせてもらいました。

というわけで各発表についての感想を雑につらつらと。

Vimのgitへの移行について - @

まさかのスライドなし発表すごすぎた。
GitHub 移行は外からだと割とすんなり決まった印象があったけど、やはり裏では色々あったんですね。

deoplete ~ The dark powered auto completion plugin for neovim ~ - @

Shougo さんのバイタリティは相変わらずすごい。…すごい。

私はどのようにVim scriptを書くか? - @

Vim script 入門的な内容。これで Vim script 書く人増えるといいなー。
発表資料がすごく凝っててすごかった。

Hacking Libcall and Java - @

「一般的」の定義について考えさせられる内容だった。もはや持ちネタと言えるレベル。
発表中にツッコミが入ったりしてすごかった。

Vim on Browser - @

ブラウザ上で Vim を動かす話。
プラグインのデモがブラウザでできるってのはかなり便利そうさを感じた。
feature=small でしか動かせないのが残念だけど、普通に動いているのでとにかくすごい。

Vim + Clojure - @

VimClojure を書くのに便利なプラグイン群の紹介。
今年も去年同様 Civilization 5 の話題が盛り込まれており、すごかった。

Introduction to OmniSharp.vim - @

OmniSharp.vim について話した。
たいがいキョドってるのでちゃんと伝わったかすごい心配…。

海外のVimmerが開発したVim pluginの紹介 @

One more thing すごい。

ぼくのかんがえたさいきょうのぷらぐいん vim-gita - @

gita 存在は知ってたけど、改めて見るとやはりすごい便利そうだった。
プログラマじゃないのにこんなの作れるなんてすごい。
まだまだ開発版っぽいので今後はもっとすごくなるらしいので期待したい。

Vim script版 power-assert について - @

power-assert、仕組みまでは理解できても、実際に作ってしまうのはすごい。

切り刻め!HTML - @

フロントエンド周りはあまり詳しくないので、必要になったときにすごく参考になりそうな情報満載だった。

ヘルプを読む - @

逆引きヘルプの提案がすごく便利そうだった。

vim-jpのvimhelp, reading_vimrcのホスト関連のお話 - @

Docker やらその周辺の使いこなし具合がすごかった…。

5年間Yokohama.vimをやってみて感じた事 - @

継続されている Yokohama.vim は本当にすごい。次回も是非参加したいです。

AppVeyorを使ってみよう @

vimproc については私自身はバイナリが欲しかったので、テストはとりあえずいっか、って思っていたのを k_takata さんがやってくれて、すごくありがたかったです。
Cygwin が動かないのは本当に謎…なんなんだ…。

まとめ

つまりまとめると、すごい。

最後に

VimConf は多くの方の協力で成り立っています。まとめ役をやってくれた @ さん、会場提供をしてくれた株式会社ミクシィさんと、その連絡役をしてくれた@Kuniwak さん、発表者の皆さん、スタッフの皆さん、そしてもちろん参加してくれた全ての皆さん、ありがとうございます!
VimConf は恐らく来年も開催されるとは思いますが、イベントをよりよくしていくために、以下のページで KPT をやっています。(KPT はふりかえり手法の1つで、詳しくはググってください google:KPT)

KPT for 2016

この KPT は特にスタッフ専用ということはないので、一般参加者の方でも、なんなら参加したかったけどできなかった方でも、Keep だけでも Problem だけでも Try だけでも、なんでもいいので書いてみてください。
みんなの意見がよりよい VimConf を作っていくはずです。

イカリングを閲覧できる ikaring.vim を作った

Splatoon してますか? 本日また大きめのアップデートが入って、まだまだ熱は冷めそうにないですね。
さて、この Splatoon 専用の SNS として、イカリングというサービスがあります。

イカリング: https://splatoon.nintendo.net/

フレンドのオンライン状態を確認できたり、フレンド内での週間ランキングが閲覧できたり、自分やフレンドの装備や各ブキの塗り面積が確認できたり、ステージのスケジュールを知ることができて、とても便利です。
とても便利なのでいつでも見れると嬉しいですね。
というわけで Vim から見れるようにしました。

https://github.com/thinca/vim-ikaring

使い方

:Ikaring コマンドを実行すると開始できます。
初回は、Nintendo Network ID とパスワードを訊かれるので入力します。
ログイン後はクッキーをローカルに保存するので、セッションが切れるまでは再度入力する必要はありません。
もしくは以下のように変数を設定しておけば自動的に接続します。

let g:ikaring#username = 'thinca'
let g:ikaring#password = '******'

開くと以下のような画面になります。

…これはたまたま誰も遊んでないだけであって、フレンドがいないわけじゃないんだからね!
というわけでランキング画面を開いてみます。

:Ikaring ranking

ちゃんとフレンドいますね。
ナワバリバトルのポイントが0になってますが、これは単に勝率が0%なだけです。バグではないです。
次にプロフィール画面を開いてみます。

:Ikaring profile

出ましたね。ブキをとっかえひっかえしているせいでウデマエが全然上がりません。
一番下を見ると、塗りの合計も見れます。

ちなみに、フレンド画面やランキング画面でユーザー名の上で を押したり、:Ikaring profile [ユーザー名] (補完あり) を実行すると、フレンドのプロフィールが見れたりします。

最後にステージ情報です。

わかりやすい。

苦労した点について

実はこのプラグイン、8月上旬に作り始めて、早い段階で8割方できてたんですが、エラー処理とかその他細かい部分のブラッシュアップをしようと思ってたらこんな時期になってしまいました…反省…。
さて、ここからは作る上で苦労した点について書いていきます。

ログイン

当然ですが、イカリングには API なんてものはありません*1
じゃあどうするかというと、スクレイピングになります。
最初に試しに Ruby で Mechanize を使ってやってみたところ、当然のようにログインしてログイン後のページのデータが取れました。便利。
しかし、今回作るのは Vim プラグインなので、Vim でやりたいわけです。Ruby に頼るわけにはいかない。
というわけで、スクレイピングするようなライブラリを、今回やりたいことができる最低限の機能で作りました。
まだリリースしてませんが、vital.vim のライブラリとして作ってあります。ikaring.vim に含まれているので、気になる人は見てみてください。
この件についてはリリースも含めて後日また書きます(たぶん)。

ギアの名前

イカリングですが、ブキやギアは基本的に画像でしか表示されていません。名前のリソースが一切ないのです。
画像の方はどうやらブキやギアに対してハッシュ値の ID が振られていて、URL にはこの ID が使われているようです。
なので、ID とブキ/ギアの対応さえわかれば、表示できます。しかし、この ID を知るには、イカリング上で各ギアを表示する必要があるのです…。
ブキに関しては、実はフレンドにバトルの予定を知らせる機能の部分に一覧がありました。そもそもブキは入手も簡単なので ID を知るのは難しくないです。
ギアに関しては、1つずつ調べていくしかありません。ギアの ID を知るには、ギアをイカリング上に表示する必要があり、つまり自分もしくはフレンドの誰かが該当ギアを装備していなくてはいけません。
なので私は、ゲーム上で入手可能なほぼ全てのギア*2を入手し、装備してバトルを行い、数分後にイカリングに反映されているのを見ては ID をチェックする、という作業を繰り返して、ほぼ全てのギアの ID を調べることに成功しました!やった!!!
…と思ったら、そのうち来るとは思っていたけど、本日、無事アップデートで新ギアが追加されました。本当にありがとうございました。
もう私一人で全て調べるのは無理です*3
もしあなたが ikaring.vim を使っていて、本来ギアが表示されるはずの部分にハッシュ値が表示されたら、ぜひその ID とギアの名前を以下の Issue にコメントして教えてください。フレンドの持ってる名前の知らないギアの場合などもあると思いますが、しばらくすれば攻略 Wiki なんかで画像付きで名前が調べられると思うので、そちらも合わせてご確認頂けると助かります。ご協力をお願いします!

https://github.com/thinca/vim-ikaring/issues/1




と言うわけで ikaring.vim を作った話でした。イカよろしく〜

*1:一部の情報はログイン後なら JSON で取得できたので、そこは楽でした

*2:まだランク50になってないのででんせつのぼうしだけ持ってない

*3:もしくはとても時間がかかります。コンプガチャつらい

Vim script で AtCoder に参戦する方法

先週、進捗キャンプという知り合いで集まって進捗を出す会に行ってきて、そこで @ さんと表題の件について色々話した。
その後個人的に手法をカイゼンしたりしたので、結果をまとめておく。

AtCoder とは

http://atcoder.jp/
私自身も「とは」と言って語れるほど詳しくはないので簡単に説明すると、主に日本人向けの競技プログラミングサイト。
問題は全て日本語で、定期的に様々なコンテストが開催されている。問題の難易度が低い初心者向けのコンテスト(AtCoder Beginner Contest 通称 ABC)なんかもあるので、競技プログラミングの入口としては最適だ。

AtCoder の対応言語

競技プログラミングでは、ソースコードを提出し、提出されたコードが正しく動くかどうかサーバ側で実際に動かして検証を行う。つまり、サーバ側に各言語の処理系が必要であり、競技プログラミングで使用できる言語はその点で制約がかかる。
どの言語が使えるかは競技プログラミングによって様々であるが、AtCoder はかなり多くの言語に対応している。
しかし、Vim script は対応言語には含まれていない。
それもそのはず。Vim script は言語としてマイナーという点を除いても、とある問題がある。

競技プログラミングの問題形式と Vim script

競技プログラミングにおける問題は、多くの場合、標準入力と標準出力を使う。
標準入力から問題のデータを受け取り、問題を解いて、結果を標準出力に出力させる。
AtCoder も例に漏れずこの形式である。
標準入出力は多くの言語が標準で扱えるので、この形式にすることで多くの言語に対応することができるためだと思われる。
しかし、Vim script はテキストエディタ Vim を拡張するための言語。標準入出力を直接扱うことは、できない。

ではどうするか

AtCoder の対応言語の1つに、Bash がある。
Bash は、それ自身にもそれなりの演算能力はあるが、どちらかと言うと外部プログラムを呼び出す能力に長けている。
実際、awk や bc なんかがよく使われたりするらしい(よく知らないけど)。
つまり、この Bash の環境に Vim が入っていれば、あとは標準入出力を Bash 側で面倒を見ることによって、Vim script が使えることになる。
試したところ Vim はちゃんと入っているようだった*1。これで Vim script で問題が解ける。

Vim script で問題を解く

AtCoder には練習用のコンテストがあるので、そこにある練習問題を解いてみる。
http://practice.contest.atcoder.jp/tasks/practice_1

vim -u NONE -i NONE -N -n -e -s -S <(cat <<EOF
function! s:main(input) abort
  let a = a:input[0]
  let [b, c] = split(a:input[1], ' ')
  let s = a:input[2]
  return (a + b + c) . ' ' . s
endfunction

let s:input = getline(1, '$')
enew
put =s:main(s:input)
1 delete _
%print
EOF
) <(cat)

結果はこちら
ちゃんと解けてる。

何をしているのか

まず、このコード自体は Bash であるが、事実上その大部分は Vim script である。
ちょっとずつ分解していくと、まず vim を起動するコードは Vim script の部分を省略すると以下のようになる(... の部分が省略部分)。

vim -u NONE -i NONE -N -n -e -s -S <(...) <(cat)
  • -u NONE
    • vimrc ファイルを読み込まない。
  • -i NONE
    • vininfo ファイルを作成しない。
  • -N
    • vi 互換モードをOFFにして(つまりVimとして)起動する。
  • -n
  • -e -s
    • バッチモードで起動する。詳細は割愛。(気になる人は :help -s-ex を参照)
  • -S {ファイル}
    • 起動後にファイルを Vim script として実行する。
  • <(cat)
    • Vim で開くファイルの指定。<(...) はプロセス置換という bash の機能で、コマンドの結果をファイルとして渡すことができる。

さて、Vim を起動していることはわかったので、次は Vim script 部分。

function! s:main(input) abort
  let a = a:input[0]
  let [b, c] = split(a:input[1], ' ')
  let s = a:input[2]
  return (a + b + c) . ' ' . s
endfunction

let s:input = getline(1, '$')
enew
put =s:main(s:input)
1 delete _
%print

ちょっとずつ見ていく。

function! s:main(input) abort
  let a = a:input[0]
  let [b, c] = split(a:input[1], ' ')
  let s = a:input[2]
  return (a + b + c) . ' ' . s
endfunction

わかりやすくするため、問題を解く部分を関数に分けた。
引数の input は、入力のテキストが行単位で配列で渡ってくる。
結果を文字列か、行単位の配列で返す。

let s:input = getline(1, '$')

入力はファイルとして渡ってきており、バッファに開かれている。
バッファの全行を配列として s:input に保存している。

enew

output 用の新しいバッファを開く。

put =s:main(s:input)
1 delete _

結果をバッファに展開している。
空のバッファに put でテキストを置くと1行目に空行が残ってしまうので、delete でこの余計な空行を消している。

%print

バッチモードの Vim では、print などのいくつかのコマンドの結果は標準出力へ出される。
%print でバッファ全体を標準出力へ結果として出力している。
ちなみにこの方法だと、最後に改行のない出力が行えない*2が、大抵の競技プログラミングの問題は最後に改行を出力させるので、問題になることはないだろう。

解く環境を整える

テンプレート

こうして見るとわかる通り、実際に触るのは s:main() 関数の部分だけであり、他の部分はテンプレートだ。
テンプレート系のプラグインを使って、テンプレート化しておくと便利だろう。
ちなみに私は template.vim を使っている(宣伝)。

Vim script 部分だけ編集する

ファイル全体としては Bash だけど、編集したいのは Vim script だ。main の部分だけを、filetype=vim で編集したい。
そんな時に便利なのが partedit.vim だ。
バッファの一部を別のバッファで開き、別のファイルタイプで編集できる。保存すると元のバッファに適用される。

https://i.gyazo.com/14a70ff2a7148b9e1507170bda083710.gif

上の動作例では :Partedit コマンドを引数なしで動かしているが、そうするためにはオプションの設定が必要だ。

" 部分を編集するバッファを開くコマンドの指定。これは縦分割したい場合の例 (デフォルトだと現在のウィンドウにそのまま開かれる)
let g:partedit#opener = 'vsplit'
" 部分を編集するバッファの filetype。デフォルトだと元のバッファと同じ filetype が適用される。
let g:partedit#filetype = 'vim'

ファイルタイプは常に同じだと別のシーンで使いたい場合に困ると思うので、localrc.vim などを利用して、b:partedit_filetype を設定するといいだろう。

テストケースを実行したい その1

答えが正しいか、提出する前に手元で実行して確認したいだろう。
開いているファイルを実行したい、と来れば、quickrun.vim が便利だ。
問題の入力を標準入力で与える必要があるが、幸い quickrun.vim は標準入力にも対応している。input オプションを設定すればよい。

" input.txt ファイルの中身を標準入力として使う
QuickRun -input input.txt

" = で始めることでファイルではなく入力文字列を直接与えられる
QuickRun -input =input-text
" i レジスタの中身を標準入力として使う
QuickRun -input =@i
" b:input 変数の中身を標準入力として使う
QuickRun -input =%{b:input}

" 事前に設定しておけば毎回指定する必要はない
let g:quickrun_config = {'_': {}}
let g:quickrun_config._.input = '=@i'
let g:quickrun_config._.input = '=%{b:input}'

" 実行する際に変数に値を入れれば OK
let b:input = "1\n2 3\ntest\n"

print デバッグするには、put コマンドを使えばよい。
最終的なバッファの内容が出力結果になるので、put コマンドでバッファに行を足してやる。

let hoge = 'debug'
put =hoge
" put の中では " はコメント扱いになり使えないので注意
put ="foo"  これはコメントになるので動かない
put =\"foo\"  " これなら動く
テストケースを実行したい その2

実は、もっと楽な方法がある。ここで s:main() を切り分けておいたのが役に立つ。
単純に s:main() を呼んでしまえば良い。

function! s:main(input) abort
  let a = a:input[0]
  let [b, c] = split(a:input[1], ' ')
  let s = a:input[2]
  return (a + b + c) . ' ' . s
endfunction

echo s:main(['1', '2 3', 'test'])

これで OK だ。ただし、この状態で Bash としてこれを実行しても結果を見ることはできない。
しかし先ほどの partedit.vim で、このバッファは Vim script として独立している。そう。quickrun.vim を使えばよい。このバッファはファイルとして存在していないが、quickrun.vim であれば実行可能だ。

こちらの方法で print デバッグしたい場合は、echo コマンドを使えば OK だ。わかりやすい。

ちなみにこれらの echo の行は、Bash としての実行結果には一切影響を及ぼさないので、そのまま投稿することも可能だが、当然実行時間は長くなってしまうので注意が必要だ。

最後に

Vim script で AtCoder に参加する方法について紹介した。
今回の例では出てこなかったが、当然 main 以外の関数を定義することもできる。
また、よくある操作については vital.vim のコードが参考になる。Data.ListData.StringMath 辺りには便利関数が揃っている。NYSL なので、コピペして使ってもまったく問題ない。是非活用して欲しい。
Enjoy Programming!

*1:軽く調べてみたところ、練習用コンテストの Vim のバージョンは 7.3.429 だった。Ubuntu 12.04 の Vim がこのバージョンなので、この環境を使っている可能性が高い。コンテストによっては環境が異なる可能性があるので注意。

*2:一応別の方法で回避はできる。