42.27 Двунаправленный вывод

Emacs может отображать текст, написанный с использованием письменностей, таких как арабская, фарси и иврит, естественный порядок которых при горизонтальном выводе текста идет справа налево. Кроме того, сегменты латиницы и цифр, встроенные в текст, идущий справа налево, выводятся слева направо, а сегменты письменности, идущей справа налево, встроенные в текст, идущий слева направо (например, арабский или иврит в комментариях или строках в файле исходного кода программы), правильно выводятся справа налево. Такие смеси текста, идущего слева направо и справа налево, называются двунаправленным текстом. В этом разделе описываются средства и параметры для редактирования и отображения двунаправленного текста.

Текст в буферах и строках Emacs хранится в логическом (или читаемом) порядке, то есть в порядке, в котором человек читал бы каждый символ. В тексте, идущем справа налево, и в двунаправленном тексте порядок, в котором символы отображаются на экране (называемый визуальным порядком), не совпадает с логическим порядком; позиции символов на экране не возрастают монотонно с позицией в строке или буфере. При выполнении этой двунаправленной перестановки Emacs следует Двунаправленному алгоритму Юникода (также известному как UBA), который описан в Приложении №9 стандарта Юникод (https://www.unicode.org/reports/tr9/). Emacs предоставляет реализацию UBA класса “Полная двунаправленность”, соответствующую требованиям Стандарта Юникод версии 9.0. Обратите внимание, однако, что способ, которым Emacs отображает строки продолжения, когда направление текста противоположно базовому направлению абзаца, отклоняется от UBA, который требует выполнения переноса строк перед перестановкой текста для отображения.

Переменная: bidi-display-reordering

Если значение этой буферно-локальной переменной отлично от nil (значение по умолчанию), Emacs выполняет двунаправленную перестановку для вывода. Перестановка влияет на текст буфера, а также на строки вывода и строки наложения из текстовых свойств и свойств наложения в буфере (см. Свойства наложений и см. Свойство display). Если значение равно nil, Emacs не выполняет двунаправленную перестановку в буфере.

Значение по умолчанию bidi-display-reordering управляет перестановкой строк, которые не предоставлены непосредственно буфером, включая текст, отображаемый в строках режима (см. Формат строки режима) и заголовках окон (см. Заголовки окон).

Emacs никогда не переставляет текст однобайтового буфера, даже если bidi-display-reordering в этом буфере не равно nil. Это связано с тем, что однобайтовые буферы содержат необработанные байты, а не символы, и поэтому им не хватает свойств направленности, необходимых для перестановки. Следовательно, чтобы проверить, будет ли текст в буфере переставлен для вывода, недостаточно проверить только значение bidi-display-reordering. Правильная проверка выглядит так:

 (if (and enable-multibyte-characters
          bidi-display-reordering)
     ;; Буфер переставляется для вывода
   )

Однако однобайтовые строки вывода и наложения переставляются, если их родительский буфер переставляется. Это связано с тем, что простые ASCII-строки хранятся Emacs как однобайтовые строки. Если однобайтовая строка вывода или наложения содержит не-ASCII символы, эти символы считаются имеющими направление слева направо.

Текст, охваченный текстовыми свойствами display, наложениями со свойствами display, значение которых является строкой, и любыми другими свойствами, заменяющими текст буфера, обрабатывается как единое целое при его перестановке для вывода. То есть весь фрагмент текста, охваченный этими свойствами, переставляется вместе. Более того, двунаправленные свойства символов в таком фрагменте текста игнорируются, и Emacs переставляет их так, как если бы они были заменены одним символом U+FFFC, известным как Символ замены объекта. Это означает, что размещение свойства display над частью текста может изменить способ перестановки окружающего текста для вывода. Чтобы предотвратить этот неожиданный эффект, всегда размещайте такие свойства на тексте, направленность которого идентична направленности окружающего его текста.

Каждый абзац двунаправленного текста имеет базовое направление — либо справа налево, либо слева направо. Абзацы, идущие слева направо, отображаются, начиная с левого поля окна, и обрезаются или переносятся, когда текст достигает правого поля. Абзацы, идущие справа налево, отображаются, начиная с правого поля, и переносятся или обрезаются на левом поле.

То, где именно начинаются и заканчиваются абзацы для целей реализации UBA в Emacs, определяется следующими двумя буферно-локальными переменными (обратите внимание, что paragraph-start и paragraph-separate не влияют на это). По умолчанию обе эти переменные имеют значение nil, и абзацы ограничены пустыми строками, то есть строками, состоящими полностью из нуля или более пробельных символов, за которыми следует символ новой строки.

Переменная: bidi-paragraph-start-re

Если не равно nil, значение этой переменной должно быть регулярным выражением, соответствующим строке, которая начинает или разделяет два абзаца. Регулярное выражение всегда сопоставляется после символа новой строки, поэтому его лучше привязывать, то есть начинать с "^".

Переменная: bidi-paragraph-separate-re

Если не равно nil, значение этой переменной должно быть регулярным выражением, соответствующим строке, разделяющей два абзаца. Регулярное выражение всегда сопоставляется после символа новой строки, поэтому его лучше привязывать, то есть начинать с "^".

Если вы изменяете любую из этих двух переменных, обычно следует изменять обе, чтобы гарантировать согласованное описание абзацев. Например, чтобы каждая новая строка начинала новый абзац для целей двунаправленной перестановки, установите обе переменные в "^".

По умолчанию Emacs определяет базовое направление каждого абзаца, анализируя текст в его начале. Точный метод определения базового направления задается UBA; вкратце, первый символ в абзаце, имеющий явную направленность, определяет базовое направление абзаца. Однако иногда буферу может потребоваться принудительно задать определенное базовое направление для своих абзацев. Например, буферы, содержащие исходный код программ, должны принудительно отображать все абзацы слева направо. Для этого можно использовать следующую переменную:

Пользовательская опция: bidi-paragraph-direction

Если значение этой буферно-локальной переменной — символ right-to-left или left-to-right, все абзацы в буфере считаются имеющими указанное направление. Любое другое значение эквивалентно nil (значение по умолчанию), что означает определение базового направления каждого абзаца на основе его содержимого.

Режимы для исходного кода программ должны устанавливать это значение в left-to-right. Режим Prog делает это по умолчанию, поэтому режимы, производные от Prog mode, не должны устанавливать это явно (см. Базовые основные режимы).

Функция: current-bidi-paragraph-direction &optional buffer

Эта функция возвращает направление абзаца в точке в указанном buffer. Возвращаемое значение — символ, либо left-to-right, либо right-to-left. Если buffer опущен или равен nil, по умолчанию используется текущий буфер. Если буферно-локальное значение переменной bidi-paragraph-direction не равно nil, возвращаемое значение будет идентично этому значению; в противном случае возвращаемое значение отражает направление абзаца, определяемое динамически Emacs. Для буферов, у которых значение bidi-display-reordering равно nil, а также для однобайтовых буферов, эта функция всегда возвращает left-to-right.

Иногда возникает необходимость перемещать точку в строгом визуальном порядке — либо влево, либо вправо от ее текущей позиции на экране. Emacs предоставляет примитив для этого.

Функция: move-point-visually direction

Эта функция перемещает точку в текущем выбранном окне в позицию буфера, которая отображается непосредственно справа или слева от точки на экране. Если direction положителен, точка переместится на одну позицию экрана вправо, в противном случае — на одну позицию экрана влево. Обратите внимание, что в зависимости от окружающего двунаправленного контекста это потенциально может переместить точку на много позиций буфера. Если функция вызвана в конце строки экрана, она перемещает точку в крайнюю правую или крайнюю левую позицию экрана следующей или предыдущей строки экрана, в зависимости от значения direction.

Функция возвращает новую позицию в буфере в качестве своего значения.

Двунаправленная перестановка может иметь неожиданные и неприятные последствия, когда две строки с двунаправленным содержимым помещаются рядом в буфере или иным образом программно объединяются в строку текста. Типичный проблемный случай — когда буфер состоит из последовательностей текстовых полей, разделенных пробелами или знаками препинания, как в режиме меню буферов или режиме сводки Rmail. Поскольку знаки препинания, используемые в качестве разделителей, имеют слабую направленность, они приобретают направленность окружающего текста. В результате числовое поле, следующее за полем с двунаправленным содержимым, может отображаться слева от предыдущего поля, нарушая ожидаемую разметку. Существует несколько способов избежать этой проблемы:

Функция: bidi-string-mark-left-to-right string

Эта функция возвращает свой аргумент string, возможно, измененный, таким образом, чтобы результат можно было безопасно объединить с другой строкой или поместить рядом с другой строкой в буфере без нарушения относительного расположения этой строки и следующей за ней при выводе. Если строка, возвращаемая этой функцией, отображается как часть абзаца, идущего слева направо, она всегда будет отображаться слева от следующего за ней текста. Функция работает путем анализа символов своего аргумента, и если какой-либо из этих символов может вызвать перестановку при выводе, функция добавляет в конец строки символ LRM. Добавленный символ LRM делается невидимым путем присвоения ему текстового свойства invisible со значением t (см. Невидимый текст).

Алгоритм перестановки использует двунаправленные свойства символов, хранящиеся как их свойство bidi-class (см. Свойства символов). Lisp-программы могут изменять эти свойства, вызывая функцию put-char-code-property. Однако для этого требуется глубокое понимание UBA, поэтому это не рекомендуется. Любые изменения двунаправленных свойств символа имеют глобальный эффект: они влияют на все кадры и окна Emacs.

Аналогично, свойство mirroring используется для отображения соответствующего зеркального символа в переставленном тексте. Lisp-программы могут влиять на зеркальное отображение, изменяя это свойство. Опять же, любые такие изменения влияют на весь вывод Emacs.

Двунаправленные свойства символов можно переопределить, вставляя в текст специальные управляющие символы направления: ПЕРЕОПРЕДЕЛЕНИЕ СЛЕВА-НАПРАВО (LRO) и ПЕРЕОПРЕДЕЛЕНИЕ СПРАВА-НАЛЕВО (RLO). Любые символы между RLO и следующим символом новой строки или управляющим символом ОТМЕНА НАПРАВЛЕННОГО ФОРМАТИРОВАНИЯ (PDF), в зависимости от того, что встретится раньше, будут отображаться так, как если бы они были сильными символами справа налево, то есть они будут перевернуты при выводе. Аналогично, любые символы между LRO и PDF или новой строкой будут отображаться так, как если бы они были сильными символами слева направо, и не будут перевернуты, даже если они являются сильными символами справа налево.

Эти переопределения полезны, когда вы хотите сделать некоторый текст независимым от алгоритма перестановки и вместо этого напрямую управлять порядком вывода. Но они также могут использоваться в злонамеренных целях, известных как фишинг. В частности, URL-адрес на веб-странице или ссылка в сообщении электронной почты могут быть изменены так, чтобы их визуальное отображение стало неузнаваемым или похожим на какое-то популярное безопасное место, в то время как реальное место, интерпретируемое браузером в логическом порядке, совершенно другое.

Emacs предоставляет примитив, который приложения могут использовать для обнаружения случаев, когда двунаправленные свойства текста были переопределены таким образом, что символ, идущий слева направо, отображается как символ, идущий справа налево, или наоборот.

Функция: bidi-find-overridden-directionality from to &optional object

Эта функция анализирует текст указанного object между позициями from (включительно) и to (исключительно) и возвращает первую позицию, в которой обнаруживает сильный символ слева направо, чьи свойства направленности были принудительно изменены для отображения символа как идущего справа налево, или сильный символ справа налево, который был принудительно изменен для отображения как идущий слева направо. Если таких символов в указанной области текста не найдено, возвращает nil.

Необязательный аргумент object определяет, какой текст искать, и по умолчанию используется текущий буфер. Если object не равно nil, это может быть другой буфер, строка или окно. Если это строка, функция ищет в этой строке. Если это окно, функция ищет в буфере, отображаемом в этом окне. Если буфер, текст которого вы хотите проверить, отображается в некотором окне, мы рекомендуем указывать его через это окно, а не передавать буфер функции. Это связано с тем, что передача функции сведений об окне позволяет ей правильно учитывать специфичные для окна наложения, которые могут изменить результат функции, если часть текста в буфере покрыта наложениями.

Когда текст, содержащий смесь символов справа налево и слева направо и двунаправленные управляющие символы, копируется в другое место, его визуальное отображение может измениться, а также может повлиять на визуальное отображение окружающего текста в месте назначения. Это связано с тем, что перестановка двунаправленного текста, задаваемая UBA, имеет нетривиальные контекстно-зависимые эффекты как на копируемый текст, так и на текст в месте назначения, который будет его окружать.

Иногда Lisp-программе может потребоваться сохранить точное визуальное отображение копируемого текста в месте назначения и текста, окружающего копию. Lisp-программы могут использовать следующую функцию для достижения этого эффекта.

Функция: buffer-substring-with-bidi-context start end &optional no-properties

Эта функция работает аналогично buffer-substring (см. Проверка содержимого буфера), но добавляет в начало и конец копируемого текста управляющие символы двунаправленного направления, необходимые для сохранения визуального отображения текста при его вставке в другое место. Необязательный аргумент no-properties, если не равен nil, означает удаление текстовых свойств из копии текста.