Vim x C言語で書くプロジェクトの良さと(私的)つらみについて

この記事は Vim Advent Calendar 2016 の16日目の記事です。
15日目の前日は shinespark氏の「Vimでtreeっぽくディレクトリ構成を書きたい、そんなアナタの為のVim Plugin、できてます。」でした。


この前の新Mac騒動でCtrl+]を初めて知った@MNukazawaです。
しかし相変わらずESCを連打しています。


追記 > 投稿してから文字サイズがおかしいことに気づいたのですが、ざっくり対処しておきますので、多少おかしくても勘弁いただければと思います。

まえがき


家でも仕事でも、C言語を書くときは(C言語を書くときに限らず)Vimを使っています。
ライトVimmerにとって、Vimは最高ですが、完璧ではないです。
C言語での開発とVimとの相性は最高ですが、光のあるところには影もあり、以下に書いてあるアレコレのつらみに、私は今も苦しんでいます。

今回挙げた中には、受け入れていかなければならないものや、いかにも井の中の蛙っぽい悩みも含まれており、解決策やベストプラクティスが無いはずがないと思えるものもあります。
「プラグイン忌避を克服しろ」「Vimの基本コマンドくらい覚えろ」等々、厳しくツッコミを入れて頂けると、正直とても嬉しいです。


私は現在、Vecterion(べくてりおん)という名前のベクターグラフィック・エディタを書いています。
今回のVim記事も、VecterionをVimで書いているときの話をするので、Vecterionのスペックをざっくり挙げておきます。
* GUIアプリケーション(ベクターグラフィック・エディタ)
* C言語/GTK3で書いている。
* googletest使用
* GNUMake(生Makefile)でビルド
* 中規模アプリケーション(1人で開発。アプリケーション本体のヘッダとソースが50個ずつ有る)

リリースはまだしていませんが、昨日もリリースブロッカを一つ潰したので、来年のどこかでは...。
Vecterionについては、Vimに関わらない C言語 Advent Calendar 2016 の12日目の記事「Cで書く中規模GUIアプリケーションから得た知見(初稿) 」でざっくり書いたので、よければどうぞ。





つらみ達

# ディレクトリ構成

中規模アプリケーションは、ヘッダとソースが別パスになる。
=== Vecterionのヘッダ一覧 ==
:~/etaion_vge$ ls include/
et_canvas.h             et_error.h             et_snap.h          pv_bezier.h           pv_io.h
et_canvas_collection.h  et_etaion.h            et_snap_panel.h    pv_cairo.h            pv_render_context.h
et_color_panel.h        et_key_action.h        et_state.h         pv_color.h            pv_render_option.h
et_define.h             et_layer_view.h        et_stroke_panel.h  pv_element.h          pv_renderer.h
et_doc.h                et_mouse_action.h      et_thumbnail.h     pv_element_general.h  pv_rotate.h
et_doc_history.h        et_mouse_cursor.h      et_tool_id.h       pv_element_info.h     pv_stroke.h
et_doc_history_hive.h   et_mouse_util.h        et_tool_info.h     pv_error.h            pv_svg_info.h
et_doc_id.h             et_pointing_manager.h  et_tool_panel.h    pv_file_format.h      pv_type.h
et_doc_manager.h        et_position_panel.h    pv_anchor_point.h  pv_focus.h            pv_vg.h
et_doc_relation.h       et_renderer.h          pv_appearance.h    pv_general.h

===


ソースは source/ 以下にある。

* ヘッダとソースを別パスにすると、Vimでソースヘッダ間の移動が面倒になる

imでソースヘッダ間の移動で有名な 
`:sp %<.h`
 が効かなくなるため、移動が面倒。
仕方なしにヘッダからソースへの移動は、目に付いた適当な関数を叩いて飛んでいる。
というか、ソースへのアクセスもとりあえずヘッダを開いてctagsでソースへ飛び、いつでもctagsのスタックでヘッダへ飛べるようにしておく、という運用をしている。

 * Vimに必須なctagsが絡むと、ディレクトリが深くなりタイプ量が増える

 ctagsは、tagsファイル生成と利用のために、vimのカレントディレクトリを、常にプロジェクトのソース先頭にしておかないと都合が悪い。
そうでなくても、ソースとヘッダの間を行き来することを考えると、Tab補完はあるものの、ディレクトリの分だけタイプ数が増えることを受け入れなければならない。

# 今どきな長めの名前で、インテリセンス無しで書く

 今どきの関数・変数名は、省略を避けるなど、なんとなく長い名前になりがち。
 長い名前をインテリセンス無しで書くのはつらい。
 (名前が長いこと自体は、自分が悪い気がしないでもないが。)
 Vimは最高のテキストエディタだが、最高のIDEであるとは限らない。

 ex. `>-------pv_anchor_point_set_handle(&dst_aps[2], PvAnchorPointIndex_HandlePrev, p1_handle_prev_dst);`

 インテリセンスのプラグインに決定打がない。ついでにVimプラグインはインストールがつらい。


# リファクタリング的な置換ができる命名

「リファクタリング」というほど格好良くなくても、開発していて名前を変えたくなることは多い。

 * Vimのリネームは1ファイル内のみ

プロジェクト内でファイルを跨いだ命名変更がしたいとき、Vimは無力。
 ヘッダ・ソース合わせて100ファイルを超えているので手作業は嫌。
 なので、
`sed -i 's/AA/BB/g' */*.[hc]`
を使っている。

 * sedでリネームしやすい名前を付けている(付けなければならない)

 つまり`sed`しやすい名前を付けなければならない。
 EtElement構造体のオブジェクトの変数は"element", "layer_element"などと名付ける。
リネームしずらいので、"elem"とか"e"とは付けない。
 (というか初期に一部のコードでそういった変数名を付けてしまったので、後で直さないと...)
 関数の引数を変更したいとき、引数に折り返しがあるとsedできなくてつらい。(これはIDEでも難しいだろうけれど)

ファイルの"path"(filepath)と被るのが怖くて、アンカーポイントで構成される線分"path"(ahchorpath)と名づけるのを避けて、変な名前を付けたりしていた。

# ctagsでstatic関数定義へ飛ぶとき

中規模以上のC言語プロジェクトでは、ファイルローカルなstatic関数を定義する。
そしてコーディングルールによったり、その他の都合で、ソースの先頭近くでファイルローカルstatic関数の定義をすることになる。
そしてVimを使うならば、ctagsで関数間をジャンプする。

 * ctagsは最初の候補としてstatic関数の宣言へ飛ぶが、私は定義を見たい

いつもCtrl+]とCtrl+Tを使っている。Vimのタグジャンプ自体はとても便利で多用するので、いつも常に:tsと打って候補一覧を見てから数字キーで飛び先を指定する、なんてことはしたくない。

 * そもそも開発中に関数を追加しながら毎度`:!ctags -R`するのがつらい。

コミット前のコードは、書きかけの関数が消えたり、名前が変わったりが頻繁に起こる。
`ctags -R`を何度か打つことになる。が、ディレクトリに複数のソースファイルを抱えたプロジェクトで、再帰的にソースを探索させるとctagsも2~3秒かかることもある。(いま以上に大きくなったらどうなるか不安)

# インデント整形(gg=G)にコーディング規約を合わせる

 * Vimはデフォルトでインデント整形できる機能が用意されているのがすばらしい

 しかし完璧ではないので、以下のような考慮すべき点がある。

## switch case{}とLinuxコーディング規約の衝突

Linuxコーディング規約よりインデントが1段深い。
8tabを使っていると8文字分の横幅を使われる。
他の開発者に触ってもらうなどを考えると、Vimのデフォルトから.vimrcで変えたくなかった。
一人で開発しているだけなら、そのうち慣れたので大きな問題でもない。

## extern C{} (c++対応)

* extern C{}がVimの自動インデントで引っかかる。ヘッダ全体にインデントが一段かかってしまう。

 googletestでテストを書こうとするとこれに悩む。
 googletest側でextern C{}することで解決。
 CヘッダがC++に考慮するのが間違いと思うことにする。
 せいぜいC++テストコード内のinclude行だけの問題になる。




以上です。



あとがき

今更ですが、「つらみ」ってこういう使い方で良かったのでしょうかね?

もうそろそろ「ライトVimmerの世界 - 驚きのプラグイン無し生活 -」みたいな記事を書いたほうが良い気がしています。
それよりVimコマンドを覚えたほうが有意義そうですが。

水の中の魚は水のことを意識することがありません。Vimを使ってC言語でコードを書いていると、ctagsの有り難みをつい忘れてしまいがちです(Windows上のAtomでソースを開いたときに思い出す)。
それは必ずしも悪いことではないのだと思います。が、快適なVimコーディングも、使い続けていると細かいところでつらみが意識されるようになるのも、自然なことで、だからこそ先達たちは多用なVimプラグインを生み出してきたのかもしれません。


この記事は Vim Advent Calendar 2016 の16日目の記事でした。
17日目の明日はanekos氏の「Vim の下着の話」です。

Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換する

 Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換することができるが、上手く変換されない場合がある。   以前作った魔法陣イラスト素材をイラスト素材ストックサイトへ登録しようと思い立ち、改めて素材用にラスタライズ変換をかけようとした。   ラスタライズ変換...