彷徨えるフジワラ

年がら年中さまよってます

MBCS 文字列の折り返し - その 3

Mercurial のヘルプ表示に関しては、まずは「文字化けを無くす」ことが優先なのだけど、Python 標準の textwrap を使う限りでは(少なくとも日本語は)綺麗な行折り返しは実現できそうにないから、いずれは独自の行折り返し実装が必要になる感じ。

標準 textwrap は:

  1. 空白文字区切りで「語」単位に分割
  2. 行長が表示幅を超えない範囲で「語」を行に順次取り込む
  3. 行長を超える「語」が出現した段階で、その行に関する取り込みを中断
  4. 「語」が表示幅(≠ 残スペース)よりも大きい場合にのみ、その語を分割して行に取り込む
  5. それ以外の場合、その「語」は次の行に取り込む

という流れで処理をしているので、空白による文字区切りを前提としない日本語文字列、特に Mercurial のヘルプドキュメントの様な英単語混在形式の場合、(4) の単語分割が発生する条件と折り合わないケースが多々あるため、行末に妙な空白ができる結果に。
日本語の組版に関する拙い知識を元に考えた限りでは、与えられた文字列をいわゆる字句分解して:

  • 分割しては駄目な文字シーケンス(主に半角英単語)
  • 絶対必要な空白(半角英単語間の区切り等)
  • 任意の位置で分割して良い文字シーケンス(全角文字列)

上記3種類に分類した上で、最後の文字列からは任意の長さで切り出しを行う、という按配で折り返しを行う必要があると思われる。

半角/全角の境界部分は、空白が無くても分割できると嬉しい。その場合は自動的に空白を追加した方が良いかなぁ。半角/全角の連続をベタ組みするのはちょっと見辛い気が。

それから、標準の textwrap では配慮されていないけど、いわゆる禁則処理もやりたいところ。やっぱり、句読点が行頭に来たり、開き括弧が行末に来るのはちょとねぇ。

なんてことを考えると、やっぱり後から折り返し処理を差し替えられるようにしたくなるわけですよ。

そこで今回のパッチでは、関数を保持する wraptext および filltext 変数を i18n モジュールに導入して、後から処理を入れ替えることができるようにしてみた。

wraptext = lambda t, **kwa: MBTextWrapper(**kwa).wrap(t)
filltext = lambda t, **kwa: '\n'.join(MBTextWrapper(**kwa).wrap(t))

そもそもコマンドの簡易ヘルプ表示には、綺麗な行折り返しなんて期待されていない可能性もあるけど、こうしておけば各言語の固有事情も後から吸収しやすいのではないかと。っつーか、日本語以外の東アジア圏言語では、折り返しがどういうルールになっているのかすら知らないし。

そういえば、Martin Geisler 氏の言う所の「Henrik が TortoiseHg 向けに書いたカスタム textwrap」ってーのはどんな按配なんだろう?