mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-05-31 01:32:00 -07:00
Introduce 'margin' face for window margin background
A new basic face 'margin' is used for text displayed in the left and right margin areas, i.e., the areas typically used by VCS and LSP packages for per-line annotations. Its background defaults to the frame default, preserving existing behavior for users who do not customize it. * etc/NEWS: Document the new 'margin' face. * lisp/faces.el (margin): Add 'margin' face, inheriting from 'default'. * src/dispextern.h (face_id): Add MARGIN_FACE_ID. * src/xdisp.c (face_at_pos): Use 'margin' as the base face for strings displayed in margin areas so that they inherit the gutter background by default. (extend_face_to_end_of_line): Compute 'margin_fill_face_id' from the 'margin' face. Use while loops to explicitly fill all empty character slots in both left and right margins for both GUI and TTY branches. (display_line): Call 'extend_face_to_end_of_line' for beyond-EOB rows when the window has margins. Also extend the existing condition for text rows with empty margins to trigger when the 'margin' face background differs from the frame default, not only when the default face is remapped. * src/xfaces.c (realize_basic_faces): Realize 'margin' as a basic face to support face-remapping and efficient lookup. (Bug#80693)
This commit is contained in:
parent
c878753d39
commit
d24b10ca75
6 changed files with 184 additions and 51 deletions
|
|
@ -3817,6 +3817,11 @@ Basic faces used for the corresponding decorations of GUI frames.
|
|||
@item cursor
|
||||
The basic face used for the text cursor.
|
||||
|
||||
@item margin
|
||||
The basic face used for window margins, both on the left and on the
|
||||
right. It is commonly used to customize the background of the empty
|
||||
areas of the margins. It inherits from the @code{default} face.
|
||||
|
||||
@item mouse
|
||||
The basic face used for displaying mouse-sensitive text when the mouse
|
||||
pointer is on that text.
|
||||
|
|
@ -5909,12 +5914,14 @@ that text, put on that text an overlay with a @code{before-string}
|
|||
property, and put the margin display specification on the contents of
|
||||
the before-string.
|
||||
|
||||
Note that if the string to be displayed in the margin doesn't
|
||||
specify a face, its face is determined using the same rules and
|
||||
priorities as it is for strings displayed in the text area
|
||||
(@pxref{Displaying Faces}). If this results in undesirable
|
||||
``leaking'' of faces into the margin, make sure the string has an
|
||||
explicit face specified for it.
|
||||
Note that if the string to be displayed in the margin doesn't fully
|
||||
specify its face, the nonspecified attributes are inherited from the
|
||||
@code{margin} face (@pxref{Basic Faces}). The face merging mechanism
|
||||
ensures that the margin background remains consistent when margin
|
||||
annotations specify only a foreground color. If you want a margin
|
||||
string to have a specific appearance independent of the @code{margin}
|
||||
face, make sure the string has a face specifying all required
|
||||
attributes.
|
||||
|
||||
Before the display margins can display anything, you must give
|
||||
them a nonzero width. The usual way to do that is to set these
|
||||
|
|
|
|||
8
etc/NEWS
8
etc/NEWS
|
|
@ -24,6 +24,14 @@ applies, and please also update docstrings as needed.
|
|||
|
||||
* Installation Changes in Emacs 31.1
|
||||
|
||||
+++
|
||||
** New face 'margin' for the window margin background.
|
||||
A new basic face 'margin' is used by default for text displayed in the
|
||||
left and right margin areas, i.e., the areas reserved by packages such as
|
||||
git-gutter, lsp-mode, and hl-diff for per-line annotations. Its
|
||||
background defaults to the frame default, so existing behavior is
|
||||
unchanged for users who do not customize it.
|
||||
|
||||
+++
|
||||
** Unexec dumper removed.
|
||||
The traditional unexec dumper, deprecated since Emacs 27, has been
|
||||
|
|
|
|||
|
|
@ -2913,6 +2913,13 @@ used to display the prompt text."
|
|||
(setq minibuffer-prompt-properties
|
||||
(append minibuffer-prompt-properties (list 'face 'minibuffer-prompt)))
|
||||
|
||||
(defface margin
|
||||
'((t :inherit default))
|
||||
"Basic face for window margins (both left and right).
|
||||
This face is used to customize the appearance of the margin areas."
|
||||
:version "31.1"
|
||||
:group 'basic-faces)
|
||||
|
||||
(defface fringe
|
||||
'((((class color) (background light))
|
||||
:background "grey95")
|
||||
|
|
|
|||
|
|
@ -1966,6 +1966,7 @@ enum face_id
|
|||
TAB_BAR_FACE_ID,
|
||||
TAB_LINE_ACTIVE_FACE_ID,
|
||||
TAB_LINE_INACTIVE_FACE_ID,
|
||||
MARGIN_FACE_ID,
|
||||
BASIC_FACE_ID_SENTINEL
|
||||
};
|
||||
|
||||
|
|
|
|||
198
src/xdisp.c
198
src/xdisp.c
|
|
@ -4874,6 +4874,17 @@ face_at_pos (const struct it *it, enum lface_attribute_index attr_filter)
|
|||
: underlying_face_id (it);
|
||||
}
|
||||
|
||||
/* For strings displayed in a margin area via a 'display' property,
|
||||
use the realized 'margin' face as the base so that unspecified
|
||||
attributes (notably background) are inherited from 'margin'
|
||||
rather than from 'default' or the buffer face at point. This
|
||||
allows packages to specify only a foreground color for a margin
|
||||
annotation and have the margin background fill in automatically. */
|
||||
if (it->string_from_display_prop_p
|
||||
&& it->area != TEXT_AREA
|
||||
&& it->w)
|
||||
base_face_id = lookup_basic_face (it->w, it->f, MARGIN_FACE_ID);
|
||||
|
||||
return face_at_string_position (it->w,
|
||||
it->string,
|
||||
IT_STRING_CHARPOS (*it),
|
||||
|
|
@ -24285,13 +24296,14 @@ append_space_for_newline (struct it *it, bool default_face_p)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Extend the face of the last glyph in the text area of IT->glyph_row
|
||||
to the end of the display line. Called from display_line. If the
|
||||
glyph row is empty, add a space glyph to it so that we know the
|
||||
face to draw. Set the glyph row flag fill_line_p. If the glyph
|
||||
row is R2L, prepend a stretch glyph to cover the empty space to the
|
||||
left of the leftmost glyph. */
|
||||
/* Extend the face of the last glyph in the text area of IT->glyph_row
|
||||
to the end of the display line. Also fill the window margins with
|
||||
the 'margin' face. If the text area is empty, a space glyph is
|
||||
added to it to carry the face used for clearing the line. In the
|
||||
margin areas, empty cells are explicitly filled with space glyphs
|
||||
(TTY) or a single stretch glyph (GUI). Set the glyph row flag
|
||||
fill_line_p. If the glyph row is R2L, prepend a stretch glyph to
|
||||
cover the empty space to the left of the leftmost glyph. */
|
||||
|
||||
static void
|
||||
extend_face_to_end_of_line (struct it *it)
|
||||
|
|
@ -24340,6 +24352,18 @@ extend_face_to_end_of_line (struct it *it)
|
|||
? it->saved_face_id
|
||||
: extend_face_id));
|
||||
|
||||
/* Use the 'margin' face to fill empty cells in the left and right
|
||||
margin areas. That face defaults to the frame default, so it is a
|
||||
no-op unless the user customizes its background. This is the only
|
||||
way to give the margin area below point-max (where no overlay can
|
||||
place glyphs) a non-default background. The approach is analogous
|
||||
to 'maybe_produce_line_number' for the line-number area. */
|
||||
int margin_fill_face_id = lookup_basic_face (it->w, f, MARGIN_FACE_ID);
|
||||
|
||||
/* Skip if none of the conditions that require extending the face to the
|
||||
end of line are met: text face extends to EOL, box/underline/etc are
|
||||
drawn, fill-column indicator is shown, or the 'margin' face has a
|
||||
non-default background in a window that has margins. */
|
||||
if (FRAME_WINDOW_P (f)
|
||||
&& MATRIX_ROW_DISPLAYS_TEXT_P (it->glyph_row)
|
||||
&& face->box == FACE_NO_BOX
|
||||
|
|
@ -24351,7 +24375,11 @@ extend_face_to_end_of_line (struct it *it)
|
|||
&& !face->stipple
|
||||
#endif
|
||||
&& !it->glyph_row->reversed_p
|
||||
&& !display_fill_column_indicator)
|
||||
&& !display_fill_column_indicator
|
||||
&& !(FACE_FROM_ID (f, margin_fill_face_id)->background
|
||||
!= FRAME_BACKGROUND_PIXEL (f)
|
||||
&& (WINDOW_LEFT_MARGIN_WIDTH (it->w) > 0
|
||||
|| WINDOW_RIGHT_MARGIN_WIDTH (it->w) > 0)))
|
||||
return;
|
||||
|
||||
/* Set the glyph row flag indicating that the face of the last glyph
|
||||
|
|
@ -24391,23 +24419,81 @@ extend_face_to_end_of_line (struct it *it)
|
|||
#endif
|
||||
))
|
||||
{
|
||||
/* The third condition is a safety bound preventing writes
|
||||
past the end of the left-margin glyph array. In the
|
||||
non-window-system branch the equivalent bound is expressed
|
||||
via a pointer 'g' initialized to the first empty slot and
|
||||
advanced by 'g++', so the limit arises naturally from the
|
||||
iteration. Here we index by 'n = used' directly and
|
||||
recompute the equivalent pointer bound inline. */
|
||||
/* Left Margin GUI; GUI-specific optimization: use one stretch glyph
|
||||
to fill the rest. */
|
||||
if (WINDOW_LEFT_MARGIN_WIDTH (it->w) > 0
|
||||
&& it->glyph_row->used[LEFT_MARGIN_AREA] == 0)
|
||||
&& (it->glyph_row->used[LEFT_MARGIN_AREA]
|
||||
< WINDOW_LEFT_MARGIN_WIDTH (it->w)))
|
||||
{
|
||||
it->glyph_row->glyphs[LEFT_MARGIN_AREA][0] = space_glyph;
|
||||
it->glyph_row->glyphs[LEFT_MARGIN_AREA][0].face_id =
|
||||
default_face->id;
|
||||
it->glyph_row->glyphs[LEFT_MARGIN_AREA][0].frame = f;
|
||||
it->glyph_row->used[LEFT_MARGIN_AREA] = 1;
|
||||
int used = it->glyph_row->used[LEFT_MARGIN_AREA];
|
||||
int remaining_pixels = (WINDOW_LEFT_MARGIN_WIDTH (it->w)
|
||||
* FRAME_COLUMN_WIDTH (f));
|
||||
|
||||
/* Subtract width of existing glyphs. */
|
||||
struct glyph *g = it->glyph_row->glyphs[LEFT_MARGIN_AREA];
|
||||
for (int i = 0; i < used; ++i)
|
||||
remaining_pixels -= (g++)->pixel_width;
|
||||
|
||||
if (remaining_pixels > 0)
|
||||
{
|
||||
int saved_face_id = it->face_id;
|
||||
enum glyph_row_area saved_area = it->area;
|
||||
struct text_pos saved_pos = it->position;
|
||||
|
||||
it->face_id = margin_fill_face_id;
|
||||
it->area = LEFT_MARGIN_AREA;
|
||||
/* Set position to 0 for the filler glyph. */
|
||||
clear_position (it);
|
||||
|
||||
append_stretch_glyph (it, Qnil, remaining_pixels,
|
||||
it->ascent + it->descent, it->max_ascent);
|
||||
|
||||
it->position = saved_pos;
|
||||
it->face_id = saved_face_id;
|
||||
it->area = saved_area;
|
||||
}
|
||||
}
|
||||
|
||||
/* Right Margin GUI; GUI-specific optimization: use one stretch glyph
|
||||
to fill the rest. */
|
||||
if (WINDOW_RIGHT_MARGIN_WIDTH (it->w) > 0
|
||||
&& it->glyph_row->used[RIGHT_MARGIN_AREA] == 0)
|
||||
&& (it->glyph_row->used[RIGHT_MARGIN_AREA]
|
||||
< WINDOW_RIGHT_MARGIN_WIDTH (it->w)))
|
||||
{
|
||||
it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0] = space_glyph;
|
||||
it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0].face_id =
|
||||
default_face->id;
|
||||
it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0].frame = f;
|
||||
it->glyph_row->used[RIGHT_MARGIN_AREA] = 1;
|
||||
int used = it->glyph_row->used[RIGHT_MARGIN_AREA];
|
||||
int remaining_pixels = (WINDOW_RIGHT_MARGIN_WIDTH (it->w)
|
||||
* FRAME_COLUMN_WIDTH (f));
|
||||
|
||||
/* Subtract width of existing glyphs. */
|
||||
struct glyph *g = it->glyph_row->glyphs[RIGHT_MARGIN_AREA];
|
||||
for (int i = 0; i < used; ++i)
|
||||
remaining_pixels -= (g++)->pixel_width;
|
||||
|
||||
if (remaining_pixels > 0)
|
||||
{
|
||||
int saved_face_id = it->face_id;
|
||||
enum glyph_row_area saved_area = it->area;
|
||||
struct text_pos saved_pos = it->position;
|
||||
|
||||
it->face_id = margin_fill_face_id;
|
||||
it->area = RIGHT_MARGIN_AREA;
|
||||
/* Set position to 0 for the filler glyph. */
|
||||
clear_position (it);
|
||||
|
||||
append_stretch_glyph (it, Qnil, remaining_pixels,
|
||||
it->ascent + it->descent, it->max_ascent);
|
||||
|
||||
it->position = saved_pos;
|
||||
it->face_id = saved_face_id;
|
||||
it->area = saved_area;
|
||||
}
|
||||
}
|
||||
|
||||
struct font *font = (default_face->font
|
||||
|
|
@ -24576,11 +24662,16 @@ extend_face_to_end_of_line (struct it *it)
|
|||
it->c = it->char_to_display = ' ';
|
||||
it->len = 1;
|
||||
|
||||
/* Fill the left margin if it is only partially covered by glyphs and
|
||||
the margin face or the extended face differ from the frame default.
|
||||
Mode-line rows are excluded because they have no margin area. */
|
||||
if (WINDOW_LEFT_MARGIN_WIDTH (it->w) > 0
|
||||
&& (it->glyph_row->used[LEFT_MARGIN_AREA]
|
||||
< WINDOW_LEFT_MARGIN_WIDTH (it->w))
|
||||
&& !it->glyph_row->mode_line_p
|
||||
&& face->background != FRAME_BACKGROUND_PIXEL (f))
|
||||
&& (face->background != FRAME_BACKGROUND_PIXEL (f)
|
||||
|| FACE_FROM_ID (f, margin_fill_face_id)->background
|
||||
!= FRAME_BACKGROUND_PIXEL (f)))
|
||||
{
|
||||
struct glyph *g = it->glyph_row->glyphs[LEFT_MARGIN_AREA];
|
||||
struct glyph *e = g + it->glyph_row->used[LEFT_MARGIN_AREA];
|
||||
|
|
@ -24593,7 +24684,7 @@ extend_face_to_end_of_line (struct it *it)
|
|||
it->wrap_prefix_width = it->current_x;
|
||||
|
||||
it->area = LEFT_MARGIN_AREA;
|
||||
it->face_id = default_face->id;
|
||||
it->face_id = margin_fill_face_id;
|
||||
while (it->glyph_row->used[LEFT_MARGIN_AREA]
|
||||
< WINDOW_LEFT_MARGIN_WIDTH (it->w)
|
||||
&& g < it->glyph_row->glyphs[TEXT_AREA])
|
||||
|
|
@ -24655,7 +24746,9 @@ extend_face_to_end_of_line (struct it *it)
|
|||
&& (it->glyph_row->used[RIGHT_MARGIN_AREA]
|
||||
< WINDOW_RIGHT_MARGIN_WIDTH (it->w))
|
||||
&& !it->glyph_row->mode_line_p
|
||||
&& face->background != FRAME_BACKGROUND_PIXEL (f))
|
||||
&& (face->background != FRAME_BACKGROUND_PIXEL (f)
|
||||
|| FACE_FROM_ID (f, margin_fill_face_id)->background
|
||||
!= FRAME_BACKGROUND_PIXEL (f)))
|
||||
{
|
||||
struct glyph *g = it->glyph_row->glyphs[RIGHT_MARGIN_AREA];
|
||||
struct glyph *e = g + it->glyph_row->used[RIGHT_MARGIN_AREA];
|
||||
|
|
@ -24664,7 +24757,7 @@ extend_face_to_end_of_line (struct it *it)
|
|||
it->current_x += g->pixel_width;
|
||||
|
||||
it->area = RIGHT_MARGIN_AREA;
|
||||
it->face_id = default_face->id;
|
||||
it->face_id = margin_fill_face_id;
|
||||
while (it->glyph_row->used[RIGHT_MARGIN_AREA]
|
||||
< WINDOW_RIGHT_MARGIN_WIDTH (it->w)
|
||||
&& g < it->glyph_row->glyphs[LAST_AREA])
|
||||
|
|
@ -25915,17 +26008,21 @@ display_line (struct it *it, int cursor_vpos)
|
|||
it->font_height = Qnil;
|
||||
it->voffset = 0;
|
||||
row->ends_at_zv_p = true;
|
||||
/* A row that displays right-to-left text must always have
|
||||
its last face extended all the way to the end of line,
|
||||
even if this row ends in ZV, because we still write to
|
||||
the screen left to right. We also need to extend the
|
||||
last face if the default face is remapped to some
|
||||
different face, otherwise the functions that clear
|
||||
portions of the screen will clear with the default face's
|
||||
background color. */
|
||||
/* A row that displays right-to-left text must always have its
|
||||
last face extended all the way to the end of line, even if
|
||||
this row ends in ZV, because we still write to the screen
|
||||
left to right. We also need to extend the last face if the
|
||||
default face is remapped to some different face, otherwise
|
||||
the functions that clear portions of the screen will clear
|
||||
with the default face's background color. We also call
|
||||
extend_face_to_end_of_line when the window has a left or
|
||||
right margin so that the empty areas of the margins are
|
||||
filled with the 'margin' face background. */
|
||||
if (row->reversed_p
|
||||
|| lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID)
|
||||
!= DEFAULT_FACE_ID)
|
||||
!= DEFAULT_FACE_ID
|
||||
|| WINDOW_LEFT_MARGIN_WIDTH (it->w) > 0
|
||||
|| WINDOW_RIGHT_MARGIN_WIDTH (it->w) > 0)
|
||||
extend_face_to_end_of_line (it);
|
||||
break;
|
||||
}
|
||||
|
|
@ -26502,18 +26599,6 @@ display_line (struct it *it, int cursor_vpos)
|
|||
}
|
||||
it->hpos = hpos_before;
|
||||
}
|
||||
/* If the default face is remapped, and the window has
|
||||
display margins, and no glyphs were written yet to the
|
||||
margins on this screen line, we must add one space
|
||||
glyph to the margin area to make sure the margins use
|
||||
the background of the remapped default face. */
|
||||
if (lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID)
|
||||
!= DEFAULT_FACE_ID /* default face is remapped */
|
||||
&& ((WINDOW_LEFT_MARGIN_WIDTH (it->w) > 0
|
||||
&& it->glyph_row->used[LEFT_MARGIN_AREA] == 0)
|
||||
|| (WINDOW_RIGHT_MARGIN_WIDTH (it->w) > 0
|
||||
&& it->glyph_row->used[RIGHT_MARGIN_AREA] == 0)))
|
||||
extend_face_to_end_of_line (it);
|
||||
}
|
||||
else if (IT_OVERFLOW_NEWLINE_INTO_FRINGE (it))
|
||||
{
|
||||
|
|
@ -26536,6 +26621,29 @@ display_line (struct it *it, int cursor_vpos)
|
|||
it->hpos = hpos_before;
|
||||
}
|
||||
|
||||
/* If the default face is remapped or the 'margin' face has a
|
||||
non-default background, and the window has display margins,
|
||||
and no glyphs were written yet to the margins on this screen
|
||||
line, fill the margin area so that the margins use the
|
||||
correct background. Placed here, after the if/else-if chain
|
||||
above, so it fires for all three truncation paths: TTY/no-fringe
|
||||
truncation glyph, GUI newline-overflow-into-fringe, and GUI
|
||||
regular truncation where the indicator is drawn as a fringe
|
||||
bitmap. */
|
||||
{
|
||||
int margin_face_id =
|
||||
lookup_basic_face (it->w, it->f, MARGIN_FACE_ID);
|
||||
if ((lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID)
|
||||
!= DEFAULT_FACE_ID
|
||||
|| FACE_FROM_ID (it->f, margin_face_id)->background
|
||||
!= FRAME_BACKGROUND_PIXEL (it->f))
|
||||
&& ((WINDOW_LEFT_MARGIN_WIDTH (it->w) > 0
|
||||
&& it->glyph_row->used[LEFT_MARGIN_AREA] == 0)
|
||||
|| (WINDOW_RIGHT_MARGIN_WIDTH (it->w) > 0
|
||||
&& it->glyph_row->used[RIGHT_MARGIN_AREA] == 0)))
|
||||
extend_face_to_end_of_line (it);
|
||||
}
|
||||
|
||||
row->truncated_on_right_p = true;
|
||||
it->continuation_lines_width = 0;
|
||||
reseat_at_next_visible_line_start (it, false);
|
||||
|
|
|
|||
|
|
@ -5211,6 +5211,7 @@ lookup_basic_face (struct window *w, struct frame *f, int face_id)
|
|||
case WINDOW_DIVIDER_LAST_PIXEL_FACE_ID: name = Qwindow_divider_last_pixel; break;
|
||||
case INTERNAL_BORDER_FACE_ID: name = Qinternal_border; break;
|
||||
case CHILD_FRAME_BORDER_FACE_ID: name = Qchild_frame_border; break;
|
||||
case MARGIN_FACE_ID: name = Qmargin; break;
|
||||
|
||||
default:
|
||||
emacs_abort (); /* the caller is supposed to pass us a basic face id */
|
||||
|
|
@ -5978,6 +5979,7 @@ realize_basic_faces (struct frame *f)
|
|||
realize_named_face (f, Qtab_bar, TAB_BAR_FACE_ID);
|
||||
realize_named_face (f, Qtab_line_active, TAB_LINE_ACTIVE_FACE_ID);
|
||||
realize_named_face (f, Qtab_line_inactive, TAB_LINE_INACTIVE_FACE_ID);
|
||||
realize_named_face (f, Qmargin, MARGIN_FACE_ID);
|
||||
unbind_to (count, Qnil);
|
||||
|
||||
/* Reflect changes in the `menu' face in menu bars. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue