Previous: , Up: Display   [Contents][Index]


40.26 双方向テキストの表示

Emacsはアラビア語、ペルシア語、ヘブライ語のような水平方向テキストの自然な表示順がR2L(right-to-left: 右から左)に実行されるようなスクリプトで記述されたテキストを表示できます。さらにL2R(right-to-left: 左から右)のテキストに埋め込まれたR2Lスクリプト(例: プログラムソースファイル内のアラビア語やヘブライ語のコメント)は適宜右から左にR2Lに表示される一方、ラテンスクリプト部やR2Lテキストに埋め込まれた数字はL2Rで表示されます。そのようなL2RとR2Lが混交されたテキストを、わたしたちは双方向テキスト(bidirectional text)と呼んでいます。このセクションでは双方向テキストの編集と表示にたいする機能とオプションについて説明します。

テキストはロジカルな順序(または読込順)、すなわち人間が各文字を読み込むであろう順序でテキストをEmacsバッファーや文字列に格納します。R2Lおよび双方向テキストでは、スクリーン上で文字が表示される順序(ビジュアル順と呼ばれる)はロジカル順と同一ではありません。それら各文字のスクリーン位置は文字列やバッファー位置により単調に増加しません。この双方向の並べ替え(bidirectional reordering)を処理を行う際に、EmacsはUnicode双方向アルゴリズム(UBA: Unicode Bidirectional Algorithm)にしたがいます(https://www.unicode.org/reports/tr9/)。EmacsはUnicode Standard v9.0の要件に合致するUBAの“Full Bidirectionality(完全な双方向性)”を提供します。とはいえテキストがパラグラフの基本方向と逆方向なときにEmacsが継続行を表示する方法は、表示するテキストの再並び換えの前に行の折り返しを要求するUBAからは逸脱していることに注意してください。

Variable: bidi-display-reordering

このバッファーローカル変数の値が非nil (デフォルト)なら、Emacsは表示で双方向の並べ替えを行う。この並べ替えはバッファーテキスト、同様に文字列表示やバッファー内のテキストプロパティやオーバーレイプロパティ由来のオーバーレイ文字列に効果を及ぼす(Overlay PropertiesおよびDisplay Propertyを参照)。値がnilならEmacsはバッファー内での双方向の並べ替えを行わない。

bidi-display-reorderingのデフォルト値は、モードライン内に表示されるテキスト(Mode Line Formatを参照)、およびヘッダー行(Header Linesを参照)を含む、バッファーにより直接提供されない文字列の並べ替えを制御する。

たとえバッファーのbidi-display-reorderingが非nilでも、Emacsがユニバイトバッファーのテキストの並べ替えを行うことはありません。これはユニバイトバッファーに含まれるのが文字ではなくrawバイトであり、並べ替えに要する方向的なプロパティを欠くからです。したがってあるバッファーのテキストが並べ替えられるかどうかテストするには、bidi-display-reorderingのテスト単独では不十分です。正しいテストは以下のようになります:

 (if (and enable-multibyte-characters
          bidi-display-reordering)
     ;; 表示時にバッファーは並べ替えられるだろう
   )

とはいえ親バッファーが並べ替えられた際には、ユニバイト表示やオーバーレイ文字列は並べ替えられます。これはEmacsによりプレーンASCII文字列がユニバイト文字列に格納されるからです。ユニバイト表示やオーバーレイ文字列が非ASCII文字列を含むなら、それらの文字はL2Rの方向をもつとみなされます。

テキストプロパティdisplay、値が文字列であるようなdisplayプロパティによるオーバーレイ、バッファーテキストを置換するその他任意のプロパティにカバーされたテキストは表示時の並べ替えの際には単一の単位として扱われます。つまりこれらのプロパティにカバーされたテキストのchunk全体が一緒に並べ替えられます。さらにそのようなテキストchunk内の文字の双方向的なプロパティは無視されて、Emacsはあたかもそれらがオブジェクト置換文字(Object Replacement Character)として知られる単一文字で置換されたかのように並べ替えます。これはテキスト範囲上にdisplayプロパティを配置することにより、表示時に周辺テキストを並べ替える方法が変更され得ることを意味しています。このような予期せぬ効果を防ぐには、常に周辺テキストと等しい方向のテキストにたいしてそのようなプロパティを配置してください。

双方向テキストのパラグラフはそれぞれ、R2LかL2Rいずれかの基本方向(base direction)をもちます。L2Rパラグラフはウィンドウの左マージンを先頭に表示され、そのテキストが右マージンに達したら切り詰めや継続されます。R2Lパラグラフはウィンドウの右マージンを先頭に表示され、そのテキストが左マージンに達したら切り詰めや継続されます。

EmacsのUBA実装の目的におけるパラグラフの開始および終了の正確な位置は、以下の2つのローカル変数により決定されます(paragraph-startparagraph-separateに効果はないことに注意)。デフォルトではこれらの変数はnilであり、パラグラフは空行(改行を後に併なう0個以上の空白文字)で囲まれます。

Variable: bidi-paragraph-start-re

この変数の値が非nilならパラグラフの開始か2つのパラグラフを分割する行にマッチする正規表現であること。この正規表現は常に改行の後にマッチするので、それをアンカーにする("^"で開始する)のが最善である。

Variable: bidi-paragraph-separate-re

この変数の値が非nilなら2つのパラグラフを分割する行にマッチする正規表現であること。この正規表現は常に改行の後にマッチするので、それをアンカーにする("^"で開始する)のが最善である。

これら2つの変数 のいずれかを変更する場合には、整合性のあるパラグラフの記述を保証するために、通常は両方を変更するべきです。たとえば双方向の並べ替え目的のために各改行を新たなパラグラフの開始とするには両方の変数に"^"をセットしてください。

デフォルトではEmacsはテキスト先頭を調べることにより各パラグラフの基本方向を判断します。基本方向の精細な決定手法はUBAにより指定されており、簡潔に言うとその明示にな方向生をもつそのパラグラフ内の最初の文字がパラグラフの基本方向を決定します。とはいえ、あるバッファーが自身のパラグラフにたいして特定の基本方向の強制を要する場合もあります。たとえばプログラムソースコードを含むバッファーは、すべてのパラグラフがL2Rで表示されるよう強制されるべきでしょう。これを行うために以下の変数を使用できます:

User Option: bidi-paragraph-direction

このバッファーローカル変数の値がright-to-leftleft-to-rightいずれかのシンボルなら、そのバッファー内のすべてのパラグラフがその指定された方向をもつとみなされる。その他すべての値はnil (デフォルト)と等価であり、それは各パラグラフの基本方向が内容により判断されることを意味する。

プログラムソースコードにたいするモードは、これをleft-to-rightにセットすること。Progモードはデフォルトでこれを行うので、Progモードから派生したモードは明示的にセットする必要はない(Basic Major Modesを参照)。

Function: current-bidi-paragraph-direction &optional buffer

この関数はbufferという名前のバッファーのポイント位置のパラグラフ方向をリターンする。リターンされる値はleft-to-rightright-to-leftいずれかのシンボルである。bufferが省略またはnilの場合のデフォルトはカレントバッファー。変数bidi-paragraph-directionのバッファーローカル値が非nilなら、リターンされる値はその値と等しくなるだろう。それ以外ならリターンされる値はEmacsにより動的に決定されたパラグラフの方向を反映する。bidi-display-reorderingの値がnilのバッファー、同様にユニバイトバッファーにたいしては、この関数は常にleft-to-rightをリターンする。

バッファーのカレントのスクリーン位置にたいして、ビジュアル順にL2RかR2Lいずれかの方向に厳密なポイント移動を要す場合があります。Emacsはこれを行うためのプリミティブを提供します。

Function: move-point-visually direction

この関数は、カレントで選択されたウィンドウのバッファーにたいしてポイントを、スクリーン上ですぐ右か左のポイントへ移動する。directionが正ならスクリーン位置は右、それ以外ならスクリーン位置は左へ移動するだろう。周囲の双方向コンテキストに依存して、これは潜在的に多くのバッファーのポイントを移動し得ることに注意。スクリーン行終端で呼び出された場合には、この関数はdirectionに応じて適宜、次行か前行の右端か左端のスクリーン位置にポイントを移動する。

この関数は値として新たなバッファー位置をリターンする。

バッファー内で双方向の内容をもつ2つの文字列が並置されているときや、プログラムで1つのテキスト文字列に結合した場合には、双方向の並べ替えは以外かつ不快な効果を与える可能性があります。典型的な問題ケースはBuffer MenuモードやRmail Summaryモードのようにバッファーがスペースや区切り文字分割されたテキストのフィールドのシーケンスで構成されているときです。それはセパレーターとして使用されている区切り文字が弱い方向性をもち、周囲のテキストの方向を採用するためです。結果として双方向の内容のフィールドが後続する数値フィールドは、先行するフィールドヘ左方向に表示され、期待したレイアウトを破壊してしまいます。この問題を回避するための方法がいくつかあります:

Function: bidi-string-mark-left-to-right string

この関数は結果を安全に他の文字列に結合できるよう、あるいはこの文字列とスクリーン上で次行となる行に関連するレイアウトを乱すことなくバッファー内の他の文字列に並置できるよう、自身への引数stringを恐らく変更してリターンする。この関数がリターンする文字列がR2Lパラグラフの一部として表示される文字列なら、それは常に後続するテキストの左に出現するだろう。この関数は自身の引数の文字を検証することにより機能して、もしそれらの文字のいずれかがディスプレイ上の並べ替えを発生し得るなら、この関数はその文字列にLRM文字を付加する。付加されたLRM文字はテキストプロパティinvisibletを与えることにより不可視にできる(Invisible Textを参照)。

並べ替えアルゴリズムはbidi-classプロパティとして格納された文字の双方向プロパティを使用します(Character Propertiesを参照)。Lispプログラムはput-char-code-property関数を呼び出すことにより、これらのプロパティを変更できます。しかしこれを行うにはUBAの完全な理解が要求されるので推奨しません。ある文字の双方向プロパティにたいする任意の変更はグローバルな効果をもちます。これらはEmacsのフレームのすべてのフレームとウィンドウに影響します。

同様にmirroringプロパティは並べ替えられたテキスト内の適切にミラーされた文字の表示に使用されます。Lispプログラムはこのプロパティを変更することにより、ミラーされた表示に影響を与えることができます。繰り返しますがそのような変更はEmacsのすべての表示に影響を与えます。

スペシャル双方向制御文字LEFT-TO-RIGHT OVERRIDE (LRO)とRIGHT-TO-LEFT OVERRIDE (RLO)をテキストに挿入することにより、文字の双方向プロパティをオーバーライドできます。RLOと改行かPOP DIRECTIONAL FORMATTING (PDF)のいずれか先にある文字間のすべての文字は、それらが強いR2Lであるかのように表示されます(反転して表示される)。同様にLROPDFか改行の間のすべての文字は、それらがたとえ強いR2Lであっても強いL2Rであるかのように反転して表示されません

これらのオーバーライドは、あるテキストを並び替えアルゴリズムの影響を受けずに、直接表示順を制御したいときに有用です。しかしこれらはフィッシング(phishing)として知られるような悪意のある用途にも使用されます。特にウェブ上のURLやemailメッセージ内のリンクは真のリンク先はまったく異なるのに、ブラウザによる論理順で解釈される外観を認識不能に操作したり、何らかの著名で安全なリンク先に偽装される可能性があります。

Emacsはアプリケーションが使用するために、双方向プロパティでL2R文字をR2L、またはその逆にするようにオーバーライドされたテキストのインスタンスを検知するプリミティブを提供します。

Function: bidi-find-overridden-directionality from to &optional object

この関数はobjectで指定されたテキストのfrom (含む)とto (含まず)の間のテキストを調べてR2Lの文字であるかのように表示が強制されている双方向プロパティの強いL2R文字、L2Rの文字であるかのように表示が強制されている強いR2L文字の最初の位置をリターンする。指定されたテキストリージョンでそのような文字が見つからなければnilをリターンする。

オプション引数objectは検索するテキストを指定して、デフォルトはカレントバッファー。objectが非nilなら別のバッファーや文字列、またはウィンドウかもしれない。文字列ならこの関数はその文字列を検索する。ウィンドウならこの関数はそのウィンドウが表示するバッファーを検索する。検査したいテキストをもつバッファーが何らかのウィンドウに表示されていれば、この関数にバッファーを渡すのではなくそのウィンドウの指定を推奨する。これはウィンドウ固有のオーバーレイにカバーされたバッファーのテキストでは関数の結果が変化し得るが、関数にウィンドウ固有のオーバーレイを正しく考慮するように指示するからである。

テキストがR2L文字とL2R文字の混交を含み、かつ双方向制御が別の場所にコピーされる際には、その視覚的外見は変化するかもしれず、コピー先の周辺テキストの視覚的外見にも影響するかもしれません。これはUBAで指定される双方向テキストの並び替えでは、コピーされるテキストとそれを取り囲む周辺テキストの両方が非自明かつコンテキスト依存の効果をもつからです。

コピーされるテキストとコピー先周辺のテキストの視覚的外見をLispプロパティが保証することが必要なときがあるかもしれません。この効果を達成するためにLispプログラムは以下の関数を使用できます。

Function: buffer-substring-with-bidi-context start end &optional no-properties

この関数はbuffer-substring (Buffer Contentsを参照)と同様に機能するが、テキストが別の場所にコピーされる際に視覚的外見を保つために必要な双方向制御文字を前や後に付加する点が異なる。オプション引数no-propertiesが非nilなら、それはテキストのコピーからテキストプロパティを削除することを意味する。