改行コード
Unicodeにはプレーンテキスト的に改行として扱うべきコードポイントがいくつもあります。
- U+000D
\r
: Carriage return - U+000A
\n
: Line feed - U+000B
\v
: Vertical tab - U+000C
\f
: Form feed - U+0085: Next line
- U+2028: Line separator
- U+2029: Paragraph separator
なるほど、この7つのどれかがきたら改行すればいいんだな、と考えると残念ながら間違いです。
なんと、\r
と\n
の間では改行されません。
つまり以下の文字列
\r
- a
\n
- b
\0
からaを削除すると
\r\n
- b
\0
になります。
おや。改行コードを削除していないのに行数が減りました(楽しいですね)。
毎回全ての文字列を再描画するのなら問題はないのですが、何十万文字ものテキストを扱おうと思うとほんの少しだけ頭を動しておく必要があります。
TATEditorではこの対処のために少しだけ面倒な改行カウント機構が入ってます。
また、TATEditorではここで紹介した改行のコードポイントを等しく改行として扱っています。
(デフォルトではCRとLFとCR+LFは改行記号で判別できるようになってますが)
タイプライターを模擬していた頃はCarriage return (CR) は行末から行頭に、Line feed (LF) は次の行に移動するものでした。そのため、本来は2つで一つの改行動作でした。
なので私はCR+LF式のWindowsの改行が好きです。
おしまい。
折り返し
あ、もう少しだけ続けます。
テキストを表示する領域が狭い場合、
- 一部を表示する
- スクロールする
- 次の行に折り返す
という選択肢があります。
改行コードがデータに由来する論理的な改行なのに対し、折り返しは表示領域という制約に由来するものです。
そのため、描画条件を決めるまではどこで改行されるかはわかりません。
また、改行される場所によって文字の送り幅も変わりうるので事前に計算することもままなりません。
(もっと言えば、描画領域の大きさが変わると折り返し位置は変わり続けます。つまり描画領域が変わると、テキスト全体を再描画するまで様々な情報が取得できません。例えば、描画領域全体がどれくらいの大きさになるか、指定された文字列がどこにあるかといったことはテキスト全体を再描画しなければ概算しかわかりません)
さらに、こうした折り返しの処理では論理改行に比べ格段に多くのことに注意しなければなりません。
例えば
- 句読点の直前では改行されない
- parenthesisの内側では改行されない
- 英語などではハイフネーションすることも
- 単語の特定の箇所でハイフンを置いて折り返すこと
一方、日本語ではそのような制約はありませんが、長音記号やもしかすると小書き文字の前では改行しないかもしれません。さらに、句読点が本来の描画領域の外に描画されるぶら下がりという処理があることもあります。
大変ですね。
でも大抵のケースについてはUnicodeがカバーしてくれます。
おわりに
今日は改行って面倒だなという話をしました。
TATEditorではICUというライブラリを通してUnicodeの改行可能な場所を調べ、それとぶら下がり処理を組み合わせて折り返しを実装してます。
(ハイフネーションはいずれ実装したいですね)