1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-04-27 16:51:06 -07:00

Add mirroring for special glyphs (bug#80440)

See initial discussion here:
https://lists.gnu.org/archive/html/emacs-devel/2026-02/msg00400.html

* lisp/international/characters.el (pairs): Populate
special-mirror-table.
* src/xdisp.c (produce_special_glyphs): Add two arguments.  One
to identify the paragraph direction and one to identify that the
glyph is produced on the left hand side of a window.  Mirror
glyph defined in the display table according to the new
special-mirror-table.  Bidi mirroring always takes precedence.
(init_iterator, insert_left_trunc_glyphs, display_line)
(display_string): Call 'produce_special_glyphs' with new arguments.
(syms_of_xdisp) <special-mirror-table>: New char-table.
* etc/NEWS: Announce the change.
This commit is contained in:
Manuel Giraud 2026-02-18 18:56:02 +01:00 committed by Eli Zaretskii
parent b5e2b0bec1
commit d272dedf8c
3 changed files with 199 additions and 25 deletions

View file

@ -143,6 +143,12 @@ instead of its now-obsolete variable.
** Network Security Manager (NSM) is now more strict.
---
** Add the 'special-mirror-table' char-table.
This char-table is used to mirror special characters (truncation and
continuation) when the user has defined an alternative representation
for those characters via display table.
*** NSM warns about TLS 1.1 by default.
It has been deprecated by RFC 8996, published in 2021.

View file

@ -2009,6 +2009,28 @@ visual representation of these characters."
:group 'display)
;;; Special mirror. Only populate table here, its definition is in
;;; xdisp.c.
(let ((pairs '(;; Some truncation examples.
(?← . ?→)
(?↜ . ?↝)
(?↞ . ?↠)
(?↢ . ?↣)
(?↤ . ?↦)
(?↼ . ?⇀)
(?↽ . ?⇁)
(?⇇ . ?⇉)
(?⇐ . ?⇒)
(?⇠ . ?⇢)
(?⇦ . ?⇨)
;; Some continuation examples.
(?↩ . ?↪)
(?↫ . ?↬)
(?↲ . ?↳))))
(dolist (pair pairs)
(aset special-mirror-table (car pair) (cdr pair))
(aset special-mirror-table (cdr pair) (car pair))))
;;; Setting word boundary.
(setq word-combining-categories

View file

@ -1229,7 +1229,8 @@ static void get_cursor_offset_for_mouse_face (struct window *w,
int *offset);
#endif /* HAVE_WINDOW_SYSTEM */
static void produce_special_glyphs (struct it *, enum display_element_type);
static void produce_special_glyphs (struct it *, enum display_element_type,
bidi_dir_t direction, bool left_edge_p);
static void pad_mode_line (struct it *, bool);
static void show_mouse_face (Mouse_HLInfo *, enum draw_glyphs_face, bool);
static bool coords_in_mouse_face_p (struct window *, int, int);
@ -3423,19 +3424,29 @@ init_iterator (struct it *it, struct window *w,
frame parameter. */
if (!it->f->no_special_glyphs)
{
int width;
/* For each special glyph, we get the dimensions in the L2R and in
the R2L case and use the maximum produced width. This is
because the glyph can come from a different font in each
case. */
if (it->line_wrap == TRUNCATE)
{
/* We will need the truncation glyph. */
eassert (it->glyph_row == NULL);
produce_special_glyphs (it, IT_TRUNCATION);
it->truncation_pixel_width = it->pixel_width;
produce_special_glyphs (it, IT_TRUNCATION, L2R, false);
width = it->pixel_width;
produce_special_glyphs (it, IT_TRUNCATION, R2L, false);
it->truncation_pixel_width = max (width, it->pixel_width);
}
else
{
/* We will need the continuation glyph. */
eassert (it->glyph_row == NULL);
produce_special_glyphs (it, IT_CONTINUATION);
it->continuation_pixel_width = it->pixel_width;
produce_special_glyphs (it, IT_CONTINUATION, L2R, false);
width = it->pixel_width;
produce_special_glyphs (it, IT_CONTINUATION, R2L, false);
it->continuation_pixel_width = max (width, it->pixel_width);
}
}
@ -23815,7 +23826,8 @@ insert_left_trunc_glyphs (struct it *it)
truncate_it.glyph_row->used[TEXT_AREA] = 0;
CHARPOS (truncate_it.position) = BYTEPOS (truncate_it.position) = -1;
truncate_it.object = Qnil;
produce_special_glyphs (&truncate_it, IT_TRUNCATION);
produce_special_glyphs (&truncate_it, IT_TRUNCATION,
it->bidi_it.paragraph_dir, true);
/* Overwrite glyphs from IT with truncation glyphs. */
if (!it->glyph_row->reversed_p)
@ -26191,7 +26203,8 @@ display_line (struct it *it, int cursor_vpos)
glyphs like in 20.x. */
while (row->glyphs[TEXT_AREA] + row->used[TEXT_AREA]
< row->glyphs[1 + TEXT_AREA])
produce_special_glyphs (it, IT_CONTINUATION);
produce_special_glyphs (it, IT_CONTINUATION,
it->bidi_it.paragraph_dir, false);
row->continued_p = true;
it->current_x = x_before;
@ -26246,7 +26259,8 @@ display_line (struct it *it, int cursor_vpos)
if ((row->reversed_p
? WINDOW_LEFT_FRINGE_WIDTH (it->w)
: WINDOW_RIGHT_FRINGE_WIDTH (it->w)) == 0)
produce_special_glyphs (it, IT_CONTINUATION);
produce_special_glyphs (it, IT_CONTINUATION,
it->bidi_it.paragraph_dir, false);
it->continuation_lines_width += it->last_visible_x;
row->ends_in_middle_of_char_p = true;
row->continued_p = true;
@ -26273,7 +26287,8 @@ display_line (struct it *it, int cursor_vpos)
|| (row->reversed_p
? WINDOW_LEFT_FRINGE_WIDTH (it->w)
: WINDOW_RIGHT_FRINGE_WIDTH (it->w)) == 0)
produce_special_glyphs (it, IT_CONTINUATION);
produce_special_glyphs (it, IT_CONTINUATION,
it->bidi_it.paragraph_dir, false);
row->continued_p = true;
extend_face_to_end_of_line (it);
@ -26463,13 +26478,15 @@ display_line (struct it *it, int cursor_vpos)
for (n = row->used[TEXT_AREA]; i < n; ++i)
{
row->used[TEXT_AREA] = i;
produce_special_glyphs (it, IT_TRUNCATION);
produce_special_glyphs (it, IT_TRUNCATION,
it->bidi_it.paragraph_dir, false);
}
}
else
{
row->used[TEXT_AREA] = i;
produce_special_glyphs (it, IT_TRUNCATION);
produce_special_glyphs (it, IT_TRUNCATION,
it->bidi_it.paragraph_dir, false);
}
it->hpos = hpos_before;
}
@ -29988,10 +30005,13 @@ display_string (const char *string, Lisp_Object lisp_string, Lisp_Object face_st
if (row->mode_line_p)
pad_mode_line (it, mode_line_p);
else
produce_special_glyphs (it, IT_TRUNCATION);
produce_special_glyphs (it, IT_TRUNCATION,
it->bidi_it.paragraph_dir,
false);
}
}
produce_special_glyphs (it, IT_TRUNCATION);
produce_special_glyphs (it, IT_TRUNCATION,
it->bidi_it.paragraph_dir, false);
}
row->truncated_on_right_p = true;
}
@ -32785,17 +32805,20 @@ produce_stretch_glyph (struct it *it)
it->nglyphs = width;
}
/* Get information about special display element WHAT in an
environment described by IT. WHAT is one of IT_TRUNCATION or
IT_CONTINUATION. Maybe produce glyphs for WHAT if IT has a
non-null glyph_row member. This function ensures that fields like
face_id, c, len of IT are left untouched. */
/* Get information about special display element WHAT in an environment
described by IT. WHAT is one of IT_TRUNCATION or IT_CONTINUATION.
Maybe produce glyphs for WHAT if IT has a non-null glyph_row member.
DIRECTION is the paragraph direction. LEFT_EDGE_P tells whether the
glyph is to be produced on the left side of the window. This
function ensures that fields like face_id, c, len of IT are left
untouched. */
static void
produce_special_glyphs (struct it *it, enum display_element_type what)
produce_special_glyphs (struct it *it, enum display_element_type what,
bidi_dir_t direction, bool left_edge_p)
{
struct it temp_it;
Lisp_Object gc;
Lisp_Object gc, val;
GLYPH glyph;
/* Take face-remapping into consideration. */
int face_id = lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID);
@ -32807,28 +32830,129 @@ produce_special_glyphs (struct it *it, enum display_element_type what)
if (what == IT_CONTINUATION)
{
/* Continuation glyph. For R2L lines, we mirror it by hand. */
if (it->bidi_it.paragraph_dir == R2L)
if (direction == R2L)
SET_GLYPH (glyph, '/', face_id);
else
SET_GLYPH (glyph, '\\', face_id);
/* Is there a display table entry for the continuation glyph? */
if (it->dp
&& (gc = DISP_CONTINUE_GLYPH (it->dp), GLYPH_CODE_P (gc)))
{
/* FIXME: Should we mirror GC for R2L lines? */
SET_GLYPH_FROM_GLYPH_CODE (glyph, gc);
/* Mirror for R2L. */
if (direction == R2L)
{
/* Try bidi mirroring first. */
int c = bidi_mirror_char (GLYPH_CODE_CHAR (gc));
/* If there was no bidi mirroring, try
special_mirror_table. */
if (c == GLYPH_CODE_CHAR (gc))
{
val = CHAR_TABLE_REF (Vspecial_mirror_table,
GLYPH_CODE_CHAR (gc));
if (FIXNUMP (val))
{
c = XFIXNUM (val);
/* If something goes wrong defaults to '/'. */
if (CHAR_VALID_P (c))
SET_GLYPH (glyph, c, face_id);
else
SET_GLYPH (glyph, '/', face_id);
}
}
else
/* Bidi mirroring. */
SET_GLYPH (glyph, c, face_id);
}
else
/* No mirroring. */
SET_GLYPH_FROM_GLYPH_CODE (glyph, gc);
/* Make sure the glyph face is realized. */
spec_glyph_lookup_face (it->w, &glyph);
#ifdef HAVE_WINDOW_SYSTEM
/* Adjust face ID for a non-ASCII character. */
if (FRAME_WINDOW_P (it->f))
{
int c = GLYPH_CHAR (glyph);
face_id = GLYPH_FACE (glyph);
/* Find or create the face ID for displaying the
character. */
int new_id = FACE_FOR_CHAR (it->f, FACE_FROM_ID (it->f, face_id),
c, -1, Qnil);
/* Update the face in the glyph. */
SET_GLYPH_FACE (glyph, new_id);
}
#endif
}
}
else if (what == IT_TRUNCATION)
{
/* Truncation glyph. */
SET_GLYPH (glyph, '$', face_id);
/* Is there a display table entry for the truncation glyph? */
if (it->dp
&& (gc = DISP_TRUNC_GLYPH (it->dp), GLYPH_CODE_P (gc)))
{
/* FIXME: Should we mirror GC for R2L lines? */
SET_GLYPH_FROM_GLYPH_CODE (glyph, gc);
/* Mirror for R2L on the right hand side of the window and for
L2R on the left hand side. */
if (((it->bidi_it.paragraph_dir == R2L) && !left_edge_p) ||
((it->bidi_it.paragraph_dir == L2R) && left_edge_p))
{
/* Try bidi mirroring first. */
int c = bidi_mirror_char (GLYPH_CODE_CHAR (gc));
/* If there was no bidi mirroring, try
special_mirror_table. */
if (c == GLYPH_CODE_CHAR (gc))
{
val = CHAR_TABLE_REF (Vspecial_mirror_table,
GLYPH_CODE_CHAR (gc));
if (FIXNUMP (val))
{
c = XFIXNUM (val);
/* If something goes wrong defaults to '$'. */
if (CHAR_VALID_P (c))
SET_GLYPH (glyph, c, face_id);
else
SET_GLYPH (glyph, '$', face_id);
}
}
else
{
struct face *face = FACE_FROM_ID (it->f, face_id);
int id = FACE_FOR_CHAR (it->f, face, c, -1, Qnil);
/* Bidi mirroring. */
SET_GLYPH (glyph, c, id);
}
}
else
/* No mirroring. */
SET_GLYPH_FROM_GLYPH_CODE (glyph, gc);
/* Make sure the glyph face is realized. */
spec_glyph_lookup_face (it->w, &glyph);
#ifdef HAVE_WINDOW_SYSTEM
/* Adjust face ID for a non-ASCII character. */
if (FRAME_WINDOW_P (it->f))
{
int c = GLYPH_CHAR (glyph);
face_id = GLYPH_FACE (glyph);
/* Find or create the face ID for displaying the
character. */
int new_id = FACE_FOR_CHAR (it->f, FACE_FROM_ID (it->f, face_id),
c, -1, Qnil);
/* Update the face in the glyph. */
SET_GLYPH_FACE (glyph, new_id);
}
#endif
}
}
else
@ -38864,6 +38988,28 @@ display table takes effect; in this case, Emacs does not consult
Fset_char_table_extra_slot (Vglyphless_char_display, make_fixnum (0),
Qempty_box);
DEFVAR_LISP ("special-mirror-table", Vspecial_mirror_table,
doc: /* Char-table used to mirror special characters.
This table is used to mirror special characters (truncation and
continuation) under certain conditions. For example, if a user modifies
the display table to use a different continuation character like this:
(set-display-table-slot standard-display-table 'wrap #x21A9)
then, using this table, this character is mirrored accordingly when
displaying R2L text. The same applies to the truncation character which
is mirrored depending if it appears on the right or on the left hand
side of a window.
This table comes pre-populated with some Unicode arrow chars but you can
customize it by adding a character and its mirror both way like in the
following example with the Pilcrow sign:
(aset special-mirror-table #xB6 #x204B)
(aset special-mirror-table #x204B #xB6) */);
Vspecial_mirror_table = Fmake_char_table (Qnil, Qnil);
DEFVAR_LISP ("debug-on-message", Vdebug_on_message,
doc: /* If non-nil, debug if a message matching this regexp is displayed. */);
Vdebug_on_message = Qnil;