Emacs では、アラビア語、ペルシア語、ヘブライ語など、横書き表示時の自然な順序が右から左となる文字体系で書かれたテキストを表示できます。さらに、右から左へ書かれるテキストに埋め込まれたラテン文字や数字の部分は左から右へ表示され、左から右へ書かれるテキストに埋め込まれた右から左へ書かれる文字の部分(例:コメントやプログラムソースファイル内の文字列に含まれるアラビア語やヘブライ語のテキスト)は、適切に右から左へ表示されます。このような左から右と右から左のテキストが混在したものを、双方向テキストと呼びます。本節では、双方向テキストを編集・表示するための機能とオプションについて説明します。
テキストはEmacsバッファに、文字列は論理的(または読み取り)順、すなわち人間が各文字を読み取る順序で格納されます。右から左へ、あるいは双方向のテキストでは、画面上の文字の表示順(視覚的順序と呼ばれます)は論理的順序とは異なります。文字の画面上の位置は、文字列やバッファの位置に伴って単調に増加することはありません。この双方向再順序付けを行う際、EmacsはUnicode標準の附属書#9(https://www.unicode.org/reports/tr9/)に記載されているUnicode双方向アルゴリズム(別名UBA)に従います。Emacsは、Unicode標準 v9.0の要件に準拠した、UBAの「Full Bidirectionality」クラス実装を提供しています。ただし、テキストの方向がベースとなる段落の方向と逆である場合の継続行の表示方法は、表示用のテキスト再順序付けの前に行折り返しを行うことを要求するUBAとは異なっている点に注意してください。
このバッファローカル変数の値が nil 以外(デフォルト)の場合、Emacs
は表示用に双方向の並べ替えを行います。この並べ替えは、バッファ内のテキストだけでなく、バッファ内のテキストおよびオーバーレイプロパティに基づく表示文字列やオーバーレイ文字列にも適用されます(オーバーレイプロパティ および display
プロパティ を参照)。値が nil の場合、Emacs
はバッファ内で双方向の並べ替えを行いません。
bidi-display-reordering
のデフォルト値は、モードラインに表示されるテキスト(モードライン形式を参照)やヘッダー行(ウィンドウヘッダー行を参照)など、バッファから直接提供されない文字列の並べ替えを制御します。
Emacsは、バッファ内で bidi-display-reordering が nil
でない場合であっても、ユニバイトバッファのテキストを再配置することはありません。これは、ユニバイトバッファが文字ではなく生のバイトを保持しており、再配置に必要な方向性の特性を備えていないためです。したがって、バッファ内のテキストが表示用に再配置されるかどうかを判定するには、bidi-display-reordering
の値だけを確認するのでは不十分です。正しい判定方法は以下の通りです。
(if (and enable-multibyte-characters
bidi-display-reordering)
;; Buffer is being reordered for display
)
ただし、ユニバイトの表示文字列やオーバーレイ文字列は、親バッファが再配置されると同様に再配置されます。これは、プレーンなASCII文字列がEmacsによってユニバイト文字列として格納されるためです。ユニバイトの表示文字列やオーバーレイ文字列にASCII以外の文字が含まれている場合、それらの文字は左から右への方向を持つものとみなされます。
display テキストプロパティ、値が文字列である display
プロパティによるオーバーレイ、およびバッファテキストを置換するその他のプロパティが適用されるテキストは、表示用に並べ替えられる際、単一の単位として扱われます。つまり、これらのプロパティが適用されるテキストチャンク全体がまとめて並べ替えられます。さらに、このようなテキストチャンク内の文字の双方向プロパティは無視され、Emacs
はそれらを オブジェクト置換文字 として知られる単一の文字 U+FFFC
で置換されたかのように並べ替えます。これは、テキストの一部に表示プロパティを配置すると、周囲のテキストの表示上の並べ替え方が変わる可能性があることを意味します。このような予期せぬ影響を防ぐため、こうしたプロパティは常に、周囲のテキストと方向性が同一であるテキストに配置してください。
双方向テキストの各段落には、右から左、または左から右のいずれかの基本方向があります。左から右の段落はウィンドウの左端から表示され、テキストが右端に達すると切り詰められるか、あるいは続きが表示されます。右から左の段落は右端から表示され、左端で続きが表示されるか、あるいは切り詰められます。
EmacsのUBA実装において、段落が具体的にどこで始まりどこで終わるかは、以下の2つのバッファローカル変数によって決定されます(なお、paragraph-startおよびparagraph-separateはこれに影響しません)。デフォルトではこれら両方の変数はnilとなっており、段落は空行、すなわち0個以上の空白文字のみで構成され、その後に改行が続く行によって区切られます。
nil
以外の場合、この変数の値は、段落の開始または2つの段落を区切る行にマッチする正規表現である必要があります。この正規表現は常に改行の後にマッチするため、アンカーを付ける(つまり、"^" で始める)のが最適です。
nil
以外の場合、この変数の値は、2つの段落を区切る行にマッチする正規表現である必要があります。この正規表現は常に改行の後にマッチするため、アンカーを付ける(つまり、"^" で始める)のが最適です。
これら2つの変数のいずれかを変更する場合は、通常、両者を併せて変更し、段落の記述に一貫性を持たせるようにしてください。例えば、双方向再順序付けのために各改行を新しい段落の開始とするには、両方の変数を
"^" に設定してください。
デフォルトでは、Emacsは各段落の冒頭にあるテキストを参照して、その段落の基本方向を決定します。基本方向を決定する具体的な手法はUBAによって規定されており、端的に言えば、明示的な方向性を持つ段落の最初の文字が、その段落の基本方向を決定します。ただし、バッファによっては、段落に特定の基本方向を強制的に適用させる必要がある場合があります。例えば、プログラムのソースコードを含むバッファでは、すべての段落を左から右へ表示させる必要があります。これを実現するには、以下の変数を使用できます:
このバッファローカル変数の値がシンボル right-to-left または left-to-right
である場合、バッファ内のすべての段落がその指定された方向を持つものとみなされます。それ以外の値は nil(デフォルト)と同等であり、これは各段落の基本方向をその内容から決定することを意味します。
プログラムソースコード用のモードでは、これを left-to-right
に設定する必要があります。Progモードではデフォルトでこの設定が行われるため、Progモードから派生したモードで明示的に設定する必要はありません(基本メジャーモードを参照)。
この関数は、指定された buffer
におけるポイントの段落方向を返します。返される値はシンボルであり、left-to-right または
right-to-left のいずれかです。buffer が省略されているか、あるいは nil
である場合は、現在のバッファがデフォルトとなります。変数 bidi-paragraph-direction のバッファローカルな値が nil 以外である場合、返される値はその値と同一になります。それ以外の場合、返される値は Emacs
によって動的に決定された段落方向を反映します。bidi-display-reordering の値が
nil であるバッファおよびユニバイトバッファの場合、この関数は常に left-to-right を返します。
時には、ポイントを厳密な視覚的順序に従って、現在の画面位置から左または右に移動させる必要がある場合があります。Emacsには、それを行うためのプリミティブが用意されています。
この関数は、現在選択されているウィンドウのポイントを、画面上のポイントのすぐ右または左に位置するバッファ位置に移動します。direction が正の場合、ポイントは画面位置1つ分右に移動し、そうでない場合は画面位置1つ分左に移動します。なお、周囲の双方向コンテキストによっては、ポイントがバッファ位置単位で大きく移動する可能性がある点に注意してください。画面行の末尾で呼び出された場合、この関数は direction の値に応じて、次または前の画面行の最も右または最も左の画面位置にポイントを移動します。
この関数は、新しいバッファ位置を値として返します。
双方向の並べ替えは、双方向コンテンツを持つ2つの文字列がバッファ内で隣接して配置されたり、あるいはプログラム的に連結されてテキスト文字列となったりした場合に、予期せぬ好ましくない影響を及ぼすことがあります。典型的な問題例としては、Buffer MenuモードやRmail Summary Modeのように、バッファが空白文字や句読点で区切られたテキストフィールドのシーケンスで構成されている場合が挙げられます。区切り文字として使用される句読点は方向性が弱いため、周囲のテキストの方向性を引き継ぐことになります。その結果、双方向コンテンツを持つフィールドの後に続く数値フィールドが、直前のフィールドの左側に表示され、想定していたレイアウトが崩れることがあります。この問題を回避する方法はいくつかあります:
bidi-string-mark-left-to-right
が便利です。(右から左へ記述する段落では、代わりに U+200F RIGHT-TO-LEFT
MARK(または RLM)を使用してください。)これはUBAが推奨する解決策の一つです。
display プロパティで区切るか、(space
. PROPS) 形式のプロパティ値でオーバーレイします(指定スペースを参照)。Emacs はこの表示指定を 段落区切り として扱い、両側のテキストを個別に並べ替えます。
この関数は、引数 string
を(必要に応じて変更した上で)返します。これにより、結果を別の文字列と安全に連結したり、バッファ内で別の文字列と隣接させたりしても、画面上のこの文字列と次の文字列の相対的な配置が崩れることはありません。この関数が返す文字列が左から右へ流れる段落の一部として表示される場合、それは常に、後続のテキストの左側に表示されます。この関数は、引数の文字を検査し、それらの文字のいずれかが画面上の順序変更を引き起こす可能性がある場合、文字列に
LRM 文字を付加することで動作します。付加された LRM 文字は、invisible
のテキストプロパティに t を設定することで不可視になります(不可視テキスト を参照)。
並べ替えアルゴリズムは、bidi-classプロパティとして格納されている文字の双方向プロパティを利用します(文字プロパティを参照)。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) 制御文字のいずれか早い方までの間にある文字は、すべて強い右から左への文字であるかのように表示されます。つまり、表示上は反転します。同様に、LRO と PDF または改行までの間にある文字は、すべて強い左から右への文字であるかのように表示され、たとえ強い右から左への文字であっても反転しません。
これらのオーバーライドは、一部のテキストを並べ替えアルゴリズムの影響を受けないようにし、代わりに表示順序を直接制御したい場合に有用です。しかし、これらはフィッシングとして知られる悪意のある目的に利用されることもあります。具体的には、ウェブページ上のURLやメールメッセージ内のリンクを操作して、見た目を判別不能にしたり、よく知られた無害な場所に似せたりしつつ、ブラウザが論理的な順序で解釈した実際の場所は全く異なるものにすることが可能です。
Emacsは、左から右へ書く文字を右から左へ書く文字であるかのように、あるいはその逆に表示させるために、双方向性のプロパティが上書きされたテキストの箇所をアプリケーションが検出できるプリミティブを提供しています。
この関数は、指定された object の位置 from(含む)から to(含まない)までのテキストを参照し、方向特性によって右から左への表示が強制された強い左から右への文字、または左から右への表示が強制された強い右から左への文字が最初に見つかった位置を返します。指定されたテキスト領域内にそのような文字が見つからない場合は、nil を返します。
オプション引数 object
で検索対象のテキストを指定します。デフォルトでは現在のバッファが指定されます。object が nil
以外の場合、別のバッファ、あるいは文字列やウィンドウを指定できます。文字列の場合はその文字列を検索し、ウィンドウの場合はそのウィンドウに表示されているバッファを検索します。テキストを確認したいバッファが何らかのウィンドウに表示されている場合は、バッファを関数に渡すのではなく、そのウィンドウを指定して呼び出すことを推奨します。これは、ウィンドウ情報を関数に伝えることで、ウィンドウ固有のオーバーレイを正しく考慮できるためです。バッファ内のテキストがオーバーレイで覆われている場合、関数の結果が変わってしまう可能性があるためです。
右から左、および左から右への文字と双方向制御要素が混在するテキストを別の場所にコピーすると、その視覚的な表示が変化することがあり、またコピー先の周囲のテキストの視覚的な表示にも影響を及ぼすことがあります。これは、UBAで指定された双方向テキストの順序変更が、コピーされたテキストと、それを囲むことになるコピー先のテキストの両方に対して、文脈に依存した無視できない影響を及ぼすためです。
Lispプログラムでは、コピー先のテキストや、その周囲のテキストの正確な見た目を保持する必要がある場合があります。Lispプログラムでは、以下の関数を使用することでこの処理を実現できます。