雑念日記

主に技術的なことをつらとらと(書ければいいな)。

Javaが書けない僕でもp5に貢献したい!

本投稿はProcessing Advent Calendar 2014の5日目の記事です。

Processing Advent Calendar 2014 : ATND

公私共にほとんど使わないのでJava力が底辺を這いずりまわっている僕ですが、Processingのバグ修正にちょっぴり貢献したので、その顛末を備忘録がてら記録したいと思います。
「これから本格的にProcessingへコントリビュートしたい!でもどうしたらいいかよくわからない!」という方がもしいれば少しでも参考になれば幸いです。
間違ったことを書いていたらご指摘願います。

目次

  • はじまり
  • 発起
  • Processingのソースコードはどこにあるか
  • ビルド環境を用意する
  • バグの原因を見つける
  • バグった経緯を調べる
  • バグを修正してみる
  • pull requestを送る
  • 結果
  • まとめ

はじまり

Processing-2.1のエディタ(PDE)で日本語文字列の表示がおかしいというバグがありました。
僕自身は外部エディタを使っているのですぐには気づかなかったのですが、ブログやTwitterGithubのIssueなどでちょろちょろ話題になっていました。

f:id:hoshi_sano:20141204022939p:plain

↑最初の報告者は中国の方でした。どうもマルチバイト文字だと問題が発生するようでした。
Issueがあがっているならすぐ解決されるか…と思いきや、開発陣には話がイマイチ伝わっていない感じ。
この手の問題はアルファベット圏の方々にはピンとこないかもしれませんね。
しかしこの問題、当時日本人は(少なくとも観測範囲内では)当然みんな嫌がっていて、このバグのためにわざわざ古いバージョン(2.0x系)を使っていた人が少なくなかったと思います。

発起

さて、このバグ、ざっくり言うと半角文字は重ならなくて全角文字は重なっちゃうというものでしたが、どういうコードでバグってるのかなんとなく予想がつきますね?
わりと簡単に修正できそうだけど放置してたらなかなか修正されなさそう、というような状況に見えたので、自分で修正してみようと思いました。
「修正する」というところまでいかずとも「ここがバグの原因だよ!」と誰かに伝えることだけでもできれば!
そんなわけでProcessingの修正作業が始まります。

Processingのソースコードはどこにあるか

ご存知の通り、Processingはオープンソースのプロジェクトなのでソースコードはすべて公開されています。
現在ではGithub上で日々開発が進められていますので、gitGithubが最低限使える必要があります。
git初心者はタイムリーなGit Advent Calendar 2014 初心者がプルリクまでに覚えるべきたった 9つの厳選 Gitコマンド とかを見るべし。


Processing · GitHub

大別すると、Processing本体がprocessing/processingで、リファレンスや公式WEBサイトがprocessing/processing-docsで管理されています。他にもAndroidモードサウンドライブラリなど、コマゴマとした機能毎のリポジトリもあります。

今回のバグはProcessing本体のエディタ(PDE)のバグなので、対象のリポジトリこちら

Githubにログインしている状態で、このリポジトリの「Fork」ボタンをクリックして、自分のアカウント上にprocessingのリポジトリを作成します。

↓これ

f:id:hoshi_sano:20141204023450p:plain

その後、git cloneでforkしたリポジトリを手元にコピーします。

  $ git clone https://github.com/<Githubアカウント名>/processing.git

processingのディレクトリ構成は2014-12-05時点では次のようになっています。

  processing/
  ├── README.md
  ├── app/
  ├── build/
  ├── core/
  ├── done.txt
  ├── java/
  ├── license.txt
  ├── pdex/
  └── todo.txt

それぞれざっくり以下のような感じです。

名前 説明
README.md リポジトリの概要が書かれたファイルです。一読しておきましょう。
app エディタ(PDE)に関するソースのディレクトリです。エディタの挙動を変えたい場合はここをいじります。
build 全体をビルドするために必要な諸々のディレクトリです。
core Processingの仕様に関するソースのディレクトリです。クラスやメソッドの挙動を変えたい場合はここをいじります。
done.txt 完了している機能追加やバグ修正がバージョンごとに一覧で記録してあります。
java 標準添付ライブラリのソースなどが置かれたディレクトリです。
license.txt ライセンス条項です。
pdex PDE Xモードに関するソースのディレクトリです。
todo.txt 今後追加すべき機能や修正すべきバグが一覧で記録してあります。

というわけで、今回のバグの修正対象はappの下の何かです。

ビルド環境を用意する

バグを修正するには自前でprocessingをビルドして挙動を確認できる環境が必要になります。
以下にビルドに関する情報が書かれている(上述のgit cloneの話も含む)ので、それに従いましょう。

2014-12-05現在では以下がインストールされている必要があります。

Antとはビルドツールのことです。 http://ant.apache.org/
@reona396さんの2日目の記事にもちらっと出てきました。

ビルドには、ソースコードや設定ファイルなどの構成要素から実行可能なファイルを生成するまで複雑な手順が必要なのですが、それらを自動化してくれるのがAntなどのビルドツールです。

ここまで用意できたら、あとはcloneしたリポジトリから以下を実行すればビルド開始です。

  $ cd build
  $ ant run

うまくいけばPDEが起動します。
うまくいかない場合は上述のwikiにトラブルシューティング的な記述があるので参考にしましょう。
tyfkdaさん(processingの日本語化をしてくださった方です)の「Processingをビルドする」も参考になるかもです。
Eclipseを使ったビルドについてもwikiに書かれているので、Eclipse使いの人も安心です。

さて、ここまでで修正の準備ができました。

バグの原因を見つける

次は問題となっているコード探しです。
processingのソースコードに精通していて、どのファイルのどのあたりが原因か思い浮かぶ、というのが理想的ですが、ほとんどソースコードを読んだことがない状態だと、問題のあるコードがどこにあるか見当もつきませんね。少なくとも当時の僕はそうでした。

そのため、「全角文字が重なってしまう」というバグを、「文字の横幅の計算がミスってて、配置がおかしくなってる」という現象であるとアタリをつけて(それ以外あまり思い浮かばないですけど)、「font」「char」「width」など関連がありそうな単語でgrepしまくりました。
(今考えるとかなり頭の悪い方法です。もう少し冷静になって、それぞれのファイルが何を担うものかざっくり調べても良かったと思います。)

そして以下のコードにたどり着きました。

https://github.com/processing/processing/blob/processing-0223-2.1/app/src/processing/app/syntax/TextAreaPainter.java#L764-L768

  protected int paintSyntaxLine(Graphics gfx, Segment line, int x, int y, 
                                Token tokens, SyntaxStyle[] styles) {
    ...
      int w = fm.charWidth(' '); // 問題はコレ
      for (int i = 0; i < line.count; i++) {
        gfx.drawChars(line.array, line.offset+i, 1, x, y);
        x += w;
      }
      //x += fm.charsWidth(line.array, line.offset, line.count);
      //x += fm.charWidth(' ') * line.count;
      line.offset += length;

      tokens = tokens.next;
    }

    return x;
  }

文字の幅を一律半角スペースの幅固定にして、配置を決めていました。そりゃあ全角文字は重なっちゃうね。

バグった経緯を調べる

問題がある場所はわかったのはいいですが、以前のバージョンでは問題なかったという点が気になります。
修正方針を決めるためにも、エンバグした経緯を調べる必要がありました。
そんなわけで過去のバージョン、問題なかったという2.03ではどうなっているかを見てみました。git(Github)を使えば過去の変更もわりと簡単に確認できます。

このあたりですね。

javax.swing.text.Utilities.drawTabbedTextに丸投げしていた処理をこのへんのコミットで自前の実装に変更しているみたいでした。
コミットメッセージを見る限り、Eclipseでも問題なく使えるように、ということのようです。

バグを修正してみる

問題点も経緯も判明したので、次はコードの修正です。

バグ自体が難しいものではなく、また周辺に参考となるコードが大量にあるので、以下のような修正でとりあえず想定している通りに動く状態にすることができました。

--- a/app/src/processing/app/syntax/TextAreaPainter.java
+++ b/app/src/processing/app/syntax/TextAreaPainter.java
@@ -761,10 +761,9 @@ public class TextAreaPainter extends JComponent implements TabExpander {
       // doesn't respect mono metrics, insists on spacing w/ fractional or something
 //      x = Utilities.drawTabbedText(line, x, y, gfx, this, 0);
 //      gfx.drawChars(line.array, line.offset, line.count, x, y);
-      int w = fm.charWidth(' ');
       for (int i = 0; i < line.count; i++) {
         gfx.drawChars(line.array, line.offset+i, 1, x, y);
-        x += w;
+        x += fm.charWidth(line.array[line.offset+i]);
       }
       //x += fm.charsWidth(line.array, line.offset, line.count);
       //x += fm.charWidth(' ') * line.count;

文字1個1個に対して幅を計算してるので処理に時間が掛かるのでは、とか、もともと固定幅(半角スペースの幅)で計算していたところが、プロポーショナルフォントの場合は文字毎に幅が違っちゃうけどいいのかな、とかいろいろ疑問に残るところはありましたが、ひとまずここまでで

  • 問題のあるコード
  • 問題の経緯
  • 修正案(あくまで"案")

が用意出来ました。

本当はここで更に単体テストが通るかどうかを確認するのが良いのですが、processingではあまりテストが書かれない傾向があるのと、今最新のリビジョンで試してみたら一部通らないテストがあり、手順が悪いのか手元の環境のせいなのかそもそも今は通らない状態なのかが判別できないため、時間切れで割愛します。進展があったら後日記事にまとめたいと思います。
(どなたか詳細ご存知の方がいたらご教授ください...)

pull requestを送る

ここまできて僕は

  • ソースコードをずっと追いかけた訳ではないのでバックグラウンドを理解していない
  • それに加えてJava力が低いので上記の修正が適切かわからない

という点から若干尻込みして、Issue #2173 に報告するだけで終わりにしようかなーとか思ってました。

しかし「綺麗なpull requestを送るための3つのポイント」という記事の中で「pull requestはラブレター」であるという記述を見て、pull requestを送ることにしました。

こういう意見もありますし。

修正案がイマイチならリジェクトされるだけですし、問題の箇所が伝われば誰かが適切な修正をしてくれますしね。

というわけで送ったpull requestがこちらです。

fix #2173 Multi byte character overlapping by hoshi-sano · Pull Request #2318 · processing/processing · GitHub

残念な英語には目をつむってください。
pull requestの送り方は上記の「綺麗なpull requestを送るための3つのポイント」が参考になります。
また、今回のケースは既にIssueがあることがわかっていましたが、pull requestを送る前に同じ内容に関するIssueやpull requestが既に発行されていないかどうかは確認しておきましょう。

結果

修正箇所が他のpull requestとバッティングしたりとその後も紆余曲折ありましたが、無事修正は取り込まれてバージョン2.2からは日本語が正常に表示されるようになりました。めでたしめでたし。

まとめ

gitとGithubを使えるようになろう

これができないとprocessingの開発の輪には入れません。

antを使えるようになろう

これも現状必須ですね。

英語を読めて、少し書けるようになろう

開発者向けのドキュメントやソースコード中のコメントやIssueを読んだり、バグ報告、Issue内でのやりとりするためには必要です。書く方は多少覚束なくても、コードの断片やキャプチャ画像なんかで補足することは可能です。翻訳サービスも利用しまくりましょう。

Java力は後からついてくる

Java力あるに越したことはないのは当然ですが、今回みたいな瑣末なバグ修正とかならコードがある程度読めてリファレンスを引ける力があればなんとかなるのではないでしょうか。修正や機能追加などの具体的な貢献には至らなくても、興味のある所のコードを読んでみたり、ちょっとお遊びで改造してみたりしていれば力がついていく気がします。

pull requestはラブレター

開発者にとってpull requestは嬉しいもののはずです。僕だったら嬉しいです。心のハードルを下げましょう。
ただしこの言葉を免罪符に、相手の気持ちを考えない無茶苦茶なラブレターを送るようなことはしないようにしましょう。


Processingの作品やライブラリ開発の紹介なんかはよく目にするのですが、Processing本体の開発についての日本語情報はあまり見ないので(僕のアンテナが拾ってないだけですかね...?)自分で書いてみました。
この記事を見て「戦闘力たったの5か...ゴミめ...」と思ったスゴウデProcessingコントリビュータの方々は、開発に関する情報発信をしてくださると僕が喜びます。
Processingの開発に興味を持った方は、まずは以下なんかを読むといいと思います。
https://github.com/processing/processing/wiki#contribute
今後、公式ドキュメントの多言語対応などで日本のP5erの力が発揮できる機会ができることを期待しつつ、おわり。