とりあえず全ての変更を commit & push して他のマシンから作業を継続できるようにするスクリプトを書いた

wip というブランチを作成して、origin に commit & push するようにした。 ブランチ名が被らないように、サフィックスとして現在時刻を付与している。 サイズのデカいバイナリファイルや、 共有されたらまずいファイルといったものを追加してしまったらどうしようというのは考えたが、 その辺は何らかの仕組みで事前に対処されている前提と割り切った。

gist.github.com

コード中にも書いてあるように cURL & sh で実行すれば良い。

curl https://gist.githubusercontent.com/msh5/c73ac86e645deff00678ee843e889bda/raw/git-wip-push.sh | sh

ここからはメモ。

まず、普通に raw ファイルの URL を取得すると、その URL 内に commit のハッシュが含まれている。 常に最新のファイルをみるようにしたい場合には、commit の部分をまるっと消せば良い。

あと、Gist に貼ったスニペットをもっとカジュアルに管理 & 実行したいところだが、 ざっと調べたもののそういったソリューションは発明されていないようだ。

CLion でソースファイル自動生成時に挿入されるコピーライトの表記を変更する

f:id:msh5_h:20180418172739j:plain

CLion のデフォルトだと、cpp ファイルを自動生成するとコピーライトがこんなふうに挿入される。 これだと簡素すぎるのとそもそもチームのルールに合わないので、これを社内標準の表記に変えようと思った。

続きを読む

Jekyll の Auto-Generation と Live Reload と Incremental Build

Auto-Generation

ページのソースファイルが変更されると、自律的にサイトを再生成してサーバに反映する機能。
昔は auto というオプション名だったようだが、いまは watch というオプション名が付いている。

デフォルトで有効になっているが、 jekyll serve 実行時に --[no-]watch オプションを渡すことで明示的に設定できる。

$ jekyll serve --help | grep "\-w"
        -w, --[no-]watch   Watch for changes and rebuild

Live Reload

バージョン 3.7.0 から実装された比較的新しい機能。
livereload 用のサーバを立てるのと、各ページに livereload.js というスクリプトを仕込む。
これによって、ブラウザがサイトの変更を検出し、自動でページをリロードできるようになる。

jekyll serve 実行時に --livereload オプションを渡すことで有効にできる。

$ jekyll serve --help | grep "livereload"
        -l, --livereload   Use LiveReload to automatically refresh browsers
            --livereload-ignore ignore GLOB1[,GLOB2[,...]]  Files for LiveReload to ignore. Remember to quote the values so your shell won't expand them
            --livereload-min-delay [SECONDS]  Minimum reload delay
            --livereload-max-delay [SECONDS]  Maximum reload delay
            --livereload-port [PORT]  Port for LiveReload to listen on

Incremental Build

通常のビルド機能は、サイトに含まれるすべてのページを再生成しようとする。
一方のインクリメンタルビルドは、変更のあったページのみを再生成する。
速いことがメリットだが、特定のケースでは正しく生成されずサイトが壊れるらしい。
まだ実験的な機能らしく、無理して使う必要はないだろう。

jekyll build 実行時に --incremental オプションを渡すことで有効にできる。

$ jekyll build --help | grep "incremental"
        -I, --incremental  Enable incremental rebuild.

また、jekyll serve--incremental オプションに対応していて、
serve 中に走るビルドに対して、インクリメンタルビルドを有効にできる。

$ jekyll serve --help | grep "incremental"
        -I, --incremental  Enable incremental rebuild.

RubyMine や IntelliJ IDEA でも RuboCop を使いたい

IntelliJ の RuboCop サポート

RubyMine は RuboCop はネイティブでサポートしていて、2017.1 バージョンから実装されている。 IntelliJ IDEA はプラグインとして Ruby の機能を提供しているが、こちらも同様に RuboCop をサポートしていて、2017.1 バージョンから実装している。

余談だけど、ネイティブでサポートされるまでは、個人開発のプラグインがもてはやされていました。

コード検査機能への統合

RuboCop サポートは IntelliJ のコード検査機能(Inspection)の一つとして統合されている。 RuboCop はバックグラウンドで実行されて、エラーがある場合には電球アイコンで該当箇所が示される。

f:id:msh5_h:20180416233504p:plain

IntelliJ は RuboCop の検査結果をパースして、エディタ上でのグラフィカルな表示に変換している。 ちなみに、RuboCop の severity は IntelliJ のコード検査機能に対して以下のように割り当てられている。

RuboCop の severity IntelliJ の severity
Refactor and Convention Weak Warning
Warning Warning
Error and Fatal Error

RuboCop の検査を有効にする

CLion の GCC などと違って、RubyMine や Ruby プラグインに RuboCop はバンドルされていない。 なので、RuboCop をプロジェクトの SDK 環境に別途インストールする必要がある。

gem install rubocop

基本的に IntelliJ が自動的に RuboCop を認識するので、インストール後は何もしなくても使えるようになる。 機能をオフにしたい場合には、Settings(もしくは Preferences)> Editor > Inspections から RuboCop のチェックボックスを外せばよい。

f:id:msh5_h:20180416233539p:plain

注意点としては、RuboCop が SDK 環境以下にインストールされている必要があること。 IntelliJ はプロジェクトごとに SDK をパス指定する仕様になっているが、これが RuboCop をインストールした環境と違っている場合には、IntelliJ がこれを認識することができない。

この問題は bundler や rbenv などで、複数の環境を使い分けているプロジェクトやシステムで起こりやすい。 IntelliJ から見えていないことが原因なので、一般に SDK の指定を直したり bundler のインストールパスをプロジェクトの設定に追加することで対処する。

GitHub の Release へのアップロードに ghr を使う

GitHub の Release へカジュアルにアップロードする方法はないものかと考えて、CLI ツールを検討するに至った。 候補はいくつかみつかったが、シンプルで使いやすそうな ghr を試してみた。

github.com

一般的なアップロード方法

もっともベーシックな方法としては、Web UI からポチポチしてアップロードする。 利点としては、わかいやすいので権限さえ渡せば誰でもできること。 欠点としては、コマンドラインから操作できないことと自動化しにくいこと。

GitHub が提供するもうひとつの方法は、API 経由でアップロードすること。 認証には設定画面から発行可能なトークンを利用する。 利点としては、自動化できること。 欠点としては、REST API のドキュメントをよく確認した上でコマンドを打つことになるので、カジュアルには実行できないこと。

ghr とは

ghr は、メルカリインフラエンジニアの @deeeet さんが公開されているツール。 設計的には go-github というライブラリをラップしていて、挙動的には GitHubAPI を叩いている。

$ ghr \
    -t TOKEN \        # Set Github API Token
    -u USERNAME \     # Set Github username
    -r REPO \         # Set repository name
    -c COMMIT \       # Set target commitish, branch or commit SHA
    -b BODY \         # Set text describing the contents of the release
    -p NUM \          # Set amount of parallelism (Default is number of CPU)
    -delete \         # Delete release and its git tag in advance if it exists
    -draft \          # Release as draft (Unpublish)
    -prerelease \     # Crate prerelease
    TAG PATH

GitHub 上の USERNAME / REPO のリポジトリにアップロードされる。 ポイントは、PATH としてディレクトリも指定できること。 ディレクトリ内の複数のファイルを、一回のコマンドでアップロードできる。 これは、クロスコンパイルするなどしてアップロード対象のファイル複数ある場合に便利である。

あと地味にうれしいところを二つ。

  • GHE に対応していて、社内 GitHub にも適用可能。
  • brew でもパッケージ管理されているので、インストールだけでなくバージョン管理も便利。

まとめ

ghr 使いやすかったので、常用していきたい。 そもそもながら、hub コマンドにこういった機能が欲しいところではあります。

続・CentOS の Docker イメージ

前回の記事の続き。

msh5.hatenablog.jp

イメージの更新状況は、docker-library/official-images のヒストリをみれば良い。 前回の記事で言及したとおり、centos6, centos7 のイメージは 1ヶ月に 1回更新されていることがわかる。

github.com

イメージの中身は CentOS/sig-cloud-instance-images で管理されている。 どうやら更新のたびにブランチを再作成する方針らしく、常に最新のコミットが1つあるのみである。 再作成の理由は定かではないが、Git がバイナリファイルの扱いを苦手としていることが一因ではないかと推測する。

github.com

イメージ構築用の kickstart ファイルは、CentOS/sig-cloud-instance-build で管理されている。 kickstart ファイルの内容から、言語設定や無効化されているサービスといった、詳細なイメージの情報が読み取れる。 ちなみに、kickstart とは RedHat 系 OS で採用されている OS インストール自動化のための仕組みである。 管理者は kickstart ファイルを用意することで、静的にインストール工程を構築することができる。

github.com

最後に、CentOS 7 をコンテナとして扱う場合は、systemd が無効化されているので注意すること。 やや手間ではあるものの、Docker Store のドキュメンテーション通りに地道にやることで、 systemd を有効化してコンテナを立ち上げることができる。

12436288584_94d6bc46d2_b.jpg
The official build of CentOS.

CentOS の Docker イメージ

CentOS は、他のメジャーな Linux システムと同様に、コンテナ用のオフィシャルなイメージが Docker Store で配布されている。

現在、CentOS 6 と CentOS 7 を2つのイメージが配布されていて、 セキュリティアップデートを除いて、1ヶ月に1度定期的に更新される。 更新というのは、カーネルおよびインストールパッケージが最新になるということである。 タグでいうと、centos6, centos7 がこれにあたる。

これとは別に、定期更新が行われない固定されたイメージも配布されている。 タグでいうと、centos6.x, centos7.x.xxx がこれにあたる。 固定されているため、カーネルやパッケージの更新は行われない。 これは、あるアプリが依存パッケージの更新によってある日突然動かなくなる、 といった心配がない点で嬉しい。 一方、このイメージにはセキュリティアップデートも反映されないため、 この点で自己責任となる。 ドキュメントでは yum update が推奨されているが、 これを実行してしまうことで全てのパッケージが更新されてしまい、 固定されたイメージを使う意義がなくなってしまう。

という訳で、定期更新のイメージを使うか、 セキュリティアップデートをあてにせずに固定されたイメージを使うかは、 ユーザが用途に合わせて決める必要がある。

追記:続きを書いた。

msh5.hatenablog.jp

.bashrc に処理時間を報告させる

ある時ふと「.bashrc と .bash_profile を区別する必要ははたしてあるのか」ということを思って、それからというもの、スタートアップの処理は全て .bashrc に書いている。 見通しが良いしリロードも簡単に出来るのですこぶる良かったのだが、とうとう重たくなってきてしまった。 その時点でのマシンの状態にもよるが、端末を立ち上げるたびに数秒待たされている。 ボトルネックを探してなんとかしようと思うが、 とりあえず .bashrc の最後で全体の処理にかかった時間を報告させることにした。

ちなみに私の環境の .bashrc では、常に末尾でメッセージを echo させるようにしている。

$ . ~/.bashrc 
... /home/minagoro/.bashrc is loaded

これは元々 .bashrc と .bash_profile の実行されるタイミングの違いを覚えるために、ずっと前に始めたものだ。 どこかのブログに書いてあったのだけど、実用性のある出力ではないので、多くの人はすぐに止めると思う。 だけど、実際シェルなので1行くらい出力が出ても邪魔にならないし、きちんと動作しているのがわかるので、私はずっとそのままにしている。

という訳で、すでに出力しているこのメッセージに、全体の処理にかかった時間を含めることにした。 イメージとしてはこんな感じで、必要な精度はミリ秒とした。

... /home/minagoro/.bashrc is loaded (XXX msec)

まず、.bashrc の冒頭でその時点での時間を取得する。 シェルが立ち上がってからの秒数を記録する SECONDS 環境変数というものがあるが、 これでは精度が足りない。 今回は date コマンドを使用して、ナノ秒までをとることとした。

# Start measuring the processing time for this script
start_time="`date +%s.%N`"

ちなみに、date コマンドの BSD 実装は %N に対応しておらず、秒までの精度しかとれない。 macOSGNU 実装のコマンドを利用するには、GNU coreutils パッケージをインストールするのが常套手段である。 BSD 実装との区別のためにコマンド名の頭には全て g がつけられていて、 date コマンドの場合は gdate コマンドとして存在する。

$ date +%s.%N
1506684842.N
// BSD 実装では %N に対応していないので、ナノ秒ではなく N として表示される。

$ brew install coreutils
$ gdate %s.%N
1506684908.032411000
// GNU 実装では %N がナノ秒に変換される。

続いて、.bashrc の最後で処理時間を計測する。 この時点での時間を取得して、冒頭で取得した時間の差から全体の処理時間を求める。 少数の計算が必要になるが、ここでは古き良き bc コマンドを利用した。

# Finish measuring the processing time for this script
finish_time=`date +%S.%N`
elapsed_time=`echo "(${finish_time} - ${start_time}) * 1000" | bc`

echo "... ${HOME}/.bashrc is loaded (${elapsed_time%.*} msec)"

最後に、.bashrc を動かしてみる。

$ . ~/.bashrc 
... /home/minagoro/.bashrc is loaded (3103msec)

無事、処理時間が報告されるようになった。 私の環境では、シェルが立ち上がるまでに3秒近くかかるようだ。👎

ghq(1) は Git プロジェクト以外にも使える

github.com

作業ディレクトリの管理に ghq を取り入れている。 もともと $HOME/work 以下に作業ディレクトリを切っていたが、 配下のディレクトリが日に日に増えていくのを見るのが辛かった。 ghq を使えば、作業ディレクトリがリポジトリのURL に基づいて整理される。 不便もあるが、何より秩序が生まれることが気持ちいい。

ところで、巷では peco との連携が必要以上に取り上げられているが、無理に併せて使う必要はない。 相性がいいのは確かであるが、とりあえず grep でしばらく使ってみるのがよろしい。 個人的には、ghq look がファジー検索に対応すれば、それだけで解決するのではないかと思う。 ちなみに、どうせ使うのであれば、別の対話型 grep ツールをお薦めする。 例えば、世界的に最もシェアの高い fzf や、percol(peco は percol を再実装したもの)である。

ghq は、Git 以外のバージョン管理システムVCS)にも対応している。 対応している VCS は、Git, Subversion, Mercurial, Darcs の4つである。 1世代前のシステムとも互換性が取られているというのは、ツールとして素晴らしい。 ところで、ソースをみる限り ghq は、各 VCS のクライアント機能を自前で実装しているわけではない。 アプリ上から子プロセスを生成して、システムにインストールされているコマンドを実行するだけのようだ。 なので、実際に get するには、対応するクライアントツールがシステムにインストールされている必要がある。

試しに Subversion プロジェクトを get してみる。 この時、コマンドオプションや設定ファイルで、特別な指定を与える必要ない。 ghq には、対応する VCS をツール内で検知するような実装がなされている。 もちろん指定することも可能で、これには README.md の Configuration の項が参考になる。 指定してやることで、冗長なサーバ問い合わせをスキップすることが出来る。

$ ghq get {プロジェクト URL}/trunk
    clone {プロジェクトURL}/trunk
      git ls-remote {プロジェクトURL}/trunk
      hg identify {プロジェクトURL}/trunk
     svn info {プロジェクトURL}/trunk
     svn checkout {プロジェクトURL}/trunk
...
リビジョン XXX をチェックアウトしました
$ ghq list
...
{プロジェクトパス}/trunk

ん、ディレクトリ名が trunk ...。 コマンドオプションが足りていないかとも思ったが、そういう訳でもないようだ。 致命的な問題でもないが、余裕のできたタイミングで ghqリポジトリに issue を上げてみようと思う。 ちなみに、git-svn にも対応しているようなので、慣れている人はそちらも評価してみて欲しい。

GNU locate

GNU locate は、システムに存在するあらゆるファイルに対して、 ファイルのフルパスを対象としたパターンマッチング検索を行うためのツールである。 検索はあらかじめ作成しておいたデータベースを利用するため、非常に高速である。 データベースの圧縮には、インクリメンタルエンコーディングが用いられている。

検索アルゴリズムのデフォルトは、部分一致検索である。 検索文字列をフルパスの一部に含むファイルをリストアップする。 一方、検索文字列にメタキャラクタが含まれる場合は、パターンマッチングに切り替わる。 対応するメタキャラクタは * と ? と [] で、シェルで馴染み深い glob が実装されている。

GNU locate は、locate コマンドと updatedb コマンドの2つで構成される。 検索には locate コマンド、データベースの作成および更新には updatedb コマンドを用いる。 GNU locate は、一時ディレクトリとネットワークディレクトリを除いた全てのファイルを検索対象とする。 明示的に検索範囲を指定するには、updatedb のコマンドオプションを利用するとよい。 それ以外には、チューニングに関するパラメータ(例えば、データベースの圧縮率の変更など)は特に存在しない。

使用上の注意としては、検索結果はデータベースを更新したタイミングに依存するということである。 データベース更新以降に作成されたファイルは、検索してもヒットしない。 逆に、データベース更新以降に削除されたファイルが、検索するとヒットしてしまう。 この問題には、locate コマンドの --exiting オプションが有効である。 --existing オプションを与えることで、locate コマンドは検索処理に存在確認を組み合わせる。 これによって、存在しないファイルは検索結果として表示されない。 ただし、検索結果が多い時には存在確認がボトルネックとなって、重くなってしまうので注意すること。

インストール方法について触れておく。 CentOS 7 であれば、yum インストールが簡単である。 ただし、登録されているのは mlocate パッケージなので、パッケージ名もそのように指定すること。 mlocate パッケージは、GNU locate に機能拡張を施したものである。 具体的には、前回の結果をキャッシュとして利用することで、データベース更新の高速化を実現している。 データベースの更新は夜間に定期実行するのが一般的であるが、 mlocate のように効率化された実装であれば hourly でも十分機能するだろう。

macOS であれば、brew インストールが簡単である。 findutils フォーミュラに組み込まれているので、フォーミュラ名にはこれを指定すること。 しかし findutils フォーミュラで提供されるのは、GNU locate ではなく BSD locate である。 注意点は、updatedb コマンドが locate.updadb と違っていることである。 また、コマンドのインストール先が PATH の通っていない /usr/libexec であることである。 不便であれば、スタートアップスクリプトエイリアスを作るとよい。

alias updatedb='sudo /usr/libexec/locate.updatedb'

最後に、man の情報は一部古いようである。 コマンドに対応する man を見てみると、言及されていないコマンドオプションが目立つ。 正確な情報が知りたい場合は、GNU findutils のドキュメントを参考にするとよい。 この記事の内容も、もともとは man の情報を出典にしていたが、 途中からそれに気づいて GNU findutils のドキュメントの内容を元に書き直した。 なお、GNU findutils は、find や locate といったファイル検索コマンドを管理する GNU プロジェクトである。 GNU locate はどこかのタイミングで、このプロジェクトに統合されたようだ。

www.gnu.org

hacking パッケージを pip インストールすると Flake8 のバージョンが巻き戻る

hacking パッケージは、Flake8 プラグインとして提供されるスタイルチェックツールである。

github.com

その特徴は、Google Python Style Guide を元にした スタイルチェックを提供する点である。 実際に使ってみても、Python 3.x 系と互換性がとれるような実装を提案するなど、 実用性を感じられるものとなっている。

興味を惹かれる hacking だが、最新バージョンの 1.0.0 現在、残念なことに Flake8 3.x 系に対応していない。 問題を報告するチケット は存在するが、1年近く放置されている。 メンテが止まっている訳ではないのだが、ここまで放置されている状況を見ると、対応に関して消極的であるのは確かである。

さらに hacking を pip インストールすると、既存の Flake8 のパッケージが 2.5.5 まで戻されてしまう。 これは、pip の依存性解決の実装に基づいた挙動である。 pip install を実行すると、pip は依存パッケージのインストールも併せて行う。 ここでバージョンが新しすぎるパッケージが既にインストールされていると、 pip はそのパッケージのバージョンを戻してしまうようだ。 特にドキュメントでは触れられていないが、 実際にそういった挙動をする。

Flake8 のバージョンが戻ることの弊害を考えてみる。 Flake8 のリリースノート をみる限り、2.5.5 から目立った変更はないように見える。 Flake8 がスタイルチェックツールであることを踏まえると、たとえ古いバージョンでも所望のチェックができるなら良いかもしれない。 ただし問題は、Flake8 の hacking 以外のプラグインへの影響である。 Flake8 パッケージのバージョンが1年前に戻ってしまうことで、多くの別のプラグインも1年前のバージョンに戻ってしまう。 hacking 単体での採用を考えるプロジェクトは少ないだろうし、 複数のプラグインの提供する静的解析を組み合わせられないというのは、Flake8 の特徴をスポイルしてしまっている。

おそらくこの問題に関する唯一の解決策は、自身で hacking パッケージを Flake8 3.x 対応することだろう。 調べても hacking の代替となるスタイルチェックツールは見つからない。 もしくは、hacking の提供するようなスタイルチェックを諦めてしまうか。 とにもかくにも一般論として、今後のプロジェクトへの hacking 導入は慎重に考えるべきである。

コマンドラインツールでタイトルケース変換したい

日本人にとって馴染みのないものに、タイトルケースがある。 タイトルケースというのは、大文字小文字の使い分け規則のひとつで、 英語圏で新聞や広告の見出しのタイポグラフィなどに使われる。 名詞を中心に単語の先頭文字を大文字に変換するのだが、例えば、"This is a pen.“ を "This is a Pen.” といった風に変換する。 すべてを先頭大文字にするわけでも、すべてを小文字にするわけでもなく、 いい感じにキャピタライズされるので、見た目がサマになる。

このタイトルケースだが、変換規則が標準化されていない。 実質的な標準といわれるものすらなく、書く人によってキャピタライズが変わってくる。 標準化されていないというよりも、もともとそういうラフなケースのようだ。

いい感じにかっこよくなるので、自分は README の見出しに使うことがある。 ただ、どうしても自分で変換していると、明らかに間違うようなこともある。 兼ねてから自動化が必要だと考えていたので、調べてみた。

タイトルケースの生成の多くは、Web サービスにおいてサポートされている。 title case で検索すれば、多くの変換サービスがヒットする。 しかし、Web サービスなのでセキュリティ的な問題もあるし、Web 広告も目に付く。

自分は Atom を常用しているが、初めはこれのプラグインパッケージとして使えるものを探そうとした。 ただし、これの弱点はポータブルでないことである。 SNS などブラウザ上で文章の編集が必要なことが最近は少なくないし、そのうち別のエディタに乗り換えるかもしれない。 非常にプレーンな解決策である、コマンドラインツールを探すことにした。

一番最初に思いついたのは ICU だ。 Unicode の標準化団体が管理する、文字を扱う上では最強のライブラリである。 たしか、タイトルケースに変換する関数も提供されていた。 ICU がライブラリの一部としてコマンドラインツールも提供していると、自分の望みは満たせる。 調べてみると、ICU は確かにタイトルケースへの変換はサポートしていた。 しかし、残念なことにコマンドラインツールを提供していなかった。

次に、node の change-case を調べた。 このライブラリをバインドした Atomプラグインもあるので、共通して使える。 期待したものの、すべてを先頭大文字にするという残念な仕様だった。

$ vi try.js 
const titleCase = require('title-case')

const sample = 'the vitamins are in my fresh california raisins.'
console.log(titleCase(sample))
$ node try.js 
The Vitamins Are In My Fresh California Raisins
// ところで、ピリオドが消えているのはいいのだろうか・・・。

ふと思い立って、ICU でタイトルケース変換するプログラムを書いてみた。

$ vi try.cc 
#include <stdio.h>
#include <unicode/unistr.h>

int main(int argc, char *argv[]) {
  icu::UnicodeString ustr("This is a pen.", "utf8");
  ustr = ustr.toTitle(NULL);
  std::string buf;
  ustr.toUTF8String(buf);
  printf("%s\n", buf.c_str());
}
$ c++ -licuuc try.cc && ./a.out 
This Is A Pen.

あれ、ICU でも同じじゃん・・・。

残念ながら、気の利いたタイトルケース変換を提供するコマンドラインツールは見つからなかったが、一般的なライブラリにおいて、タイトルケースは「全ての単語の先頭文字を大文字に変換」で実装されていることがわかった。

Lint with Atom

Atom でコーディングスタイルをチェックするための方法および一連の仕組みと、 スタイルチェックプラグインとしてデファクトスタンダードな linter(という名前の)プラグインについて、 俯瞰で理解する。

Atom のスタイルチェック

Atom はシンプルなエディタとして設計されているので、 一般的な IDE のようなスタイルチェック機能はデフォルトでは実装されていない。

ただし、Atom の提供する柔軟なプラグイン機構に基づいて、 スタイルチェック機能を提供する複数のプラグインサードパーティより提供されていて、これを導入することで、スタイルチェック機能を Atom に載せることが出来る。

多くのスタイルチェックプラグインは、ファイルの保存時にスタイルチェックを適用するようになっていて、 スタイルのルールに関する違反が見つかった場合には、これを Atom の UI を通して報告する。

プラグインごとに対応するコード(言語やフォーマット)やスタイルルールが違っているので、 ユーザは導入に際してプラグインの機能をよく理解するとともに、 どういったルールが自身のコーディングスタイルや環境(所属するプロジェクトなど)に合っているかを考えて、 場合によってはルールを選択する(※1)といったことが必要になる。

  • ※1: 多くのスタイルチェックプラグインは、設定項目として特定のルールのみを適用、もしくは除外する機能を提供している。

linter プラグイン

Atom のスタイルチェックまわりは良くできていて、それのベースを提供しているのが linter プラグインである。 steelbrain さんという Epic Games 所属(※1)のエンジニアによって提供されているもので、 Atom のスタイルチェックにおいて、実質的なデフォルトスタンダードのプラグインとなっている。

仕組みとしては、linter プラグインはプラットフォームとしての機能のみを提供していて、 具体的には、スタイルチェックのトリガー監視やエラー報告の仕組みを提供している。 実際にスタイルチェックを走らせるには、対応する言語のプラグイン(※2)をインストールする。 linter プラグインが実質的なデフォルトスタンダードとなっていることもあって、豊富なプラグインが多くの開発者によって提供されている。 プラグインは linter-xxx という名前で提供されていて、Atomプラグインインストール画面から検索かければすぐに見つかるだろう。 また、http://atomlinter.github.io/ では、言語/フォーマット別に linter-xxx プラグインがリストアップされている。

プラットフォームが同じというところで、どの言語のスタイルチェックを利用する場合にも、操作性および視覚的な部分で統一されるので、非常に快適。 また、linter-xxx プラグインには珍しい言語に対応するものもあり、第三者が言語対応部分を拡張できるようにした仕組みが、こういった部分でも活きているものと思われる。

linter プラグインのデメリットについても触れておくと、まず linter プラグイン自体の学習コストがかかること。 調べればすぐわかるものだが、何も考えずに導入とはいかないので、初心者にはとっつきづらいだろうと思う。 あと、環境構築が若干わずらわしい。 例えば、ひとつのフォーマットのスタイルチェックをやるにも最低でも2つのプラグイン(linter プラグインと linter-xxx プラグイン)をインストールしないといけない。(※3)

ちなみに linter-xxx プラグインは、AtomLinter と呼ばれる団体?によって取りまとめられている。 この団体の素性がよくわからなくて、特に記載がみつからないところから、非公式なコミュニティなのではと推測している。

  • ※1: steelbrain さんが Epic Games 所属というのは、GitHub アカウントのプロフィールから推測した。
  • ※2: linter プラグインは、こういった linter 上で動くプラグインのことを provider と定義している。ただし、直観的にわかりづらい気がするので、本エントリでは通して linter-xxx プラグインと呼ぶことにする。
  • ※3: でも linter-xxx だけインストールすれば、依存性で linter もインストールされるのか。

linter プラグインの依存プラグイン

linter プラグインをインストールすると、依存性の解決でぽろぽろとプラグインが落ちてくる。

  • linter-ui-default
  • intentions
  • busy-signal

linter-ui-default は UI を提供するプラグイン。 互換性のあるプラグインを入れることで、UI の変更および拡張が可能である。(※1)

また、intentions は、サジェストのための小さい窓を出す API を提供するプラグイン、busy-signal は、進捗を表す “ぐるぐる” 回る UI をステータスバーに出す API を提供するだけのプラグインである。 こういったプラグイン展開を見る限り、どうやら steelbrain さんは、実装を小さい部品に分けるのが好きなようだ。

最後に大事なこととして、linter プラグインが依存していることを忘れて、これらのプラグインを disable にしてしまうと、該当する機能が動作しなくなるので気をつけること。

  • ※1: 肝心の互換性のあるプラグインが linter-ui-plus しか存在せず、linter-ui-plus 自体もアルファリリースで未完成のため、実質 linter-ui-default しか選択肢はない。

linter 以外のスタイルチェックプラグイン

色々すっとばして linter プラグインの紹介をしてしまったので、補足として linter 以外のプラグインについても、調査結果を残しておく。 linter 以外のプラグインで見つけたものは、Atom-Lint と Nuclide の2つ。

Atom-Lint (atom-lint)。 linter プラグインと名前が似ているが別物。 複数のプログラミング言語をサポートしているので、これさえ入れればサポート対象の言語の lint を全て一任できる。 メジャーなプログラミング言語が抑えられているので、割と使えるかも。 https://atom.io/packages/atom-lint

Nuclide (nuclide)。 Facebook 製のプラグインで、AtomIDE 化をうたったもの。 特筆すべき機能が見当たらないのと、IDE が使いたいなら EclipseIntelliJ をあたった方が早い気がする。 https://atom.io/packages/Nuclide