summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Colascione <dancol@dancol.org>2018-06-07 16:20:06 -0700
committerDaniel Colascione <dancol@dancol.org>2018-06-07 21:03:30 -0700
commit9d447fa1562c69f2224bdefc37703c0eb0f7d7cf (patch)
treedd1d64edecb820298d433f5537e12835058db362
parente2a98002020369cf0c09c7acf8557290e867705f (diff)
downloademacs-9d447fa1562c69f2224bdefc37703c0eb0f7d7cf.tar.gz
Add support for per-window face remapping
Extend face specifications to support the notion of filtering to a specific context and add a filter that limits a face specification to windows having a certain parameter. * src/xfaces.c: (evaluate_face_filter,filter_face_ref): New functions. (merge_face_ref): Ignore filtered face specifications. (Fx_list_fonts,get_lface_attributes,merge_face_vectors) (merge_named_face,merge_face_ref,merge_face_ref) (Finternal_merge_in_global_face,Fface_font,lookup_named_face) (lookup_basic_face,Fface_attributes_as_vector) (x_supports_face_attributes_p) (Fdisplay_supports_face_attributes_p,realize_named_face) (compute_char_face,face_at_buffer_position) (face_at_buffer_position,face_at_buffer_position) (face_at_buffer_position) (face_for_overlay_string,face_at_string_position,merge_faces): Pass window to face machinery. (syms_of_xfaces): Add :window and :filtered * src/xdisp.c (init_iterator, handle_face_prop) (handle_single_display_spec, merge_escape_glyph_face) (merge_glyphless_glyph_face, get_next_display_element) (next_element_from_display_vector, append_space_for_newline) (extend_face_to_end_of_line,highlight_trailing_whitespace) (maybe_produce_line_number) (display_line, calc_line_height_property): Pass window to face machinery. * src/term.c (tty_menu_activate): Adjust to new face core function signature. * src/msdos.c (XMenuActivate): Adjust to new face core function signature. * src/fringe.c (draw_fringe_bitmap_1, Fset_fringe_bitmap_face): Pass window to face machinery. * src/font.c (font_range, Finternal_char_font): Pass window to face machinery. * src/dispnew.c (spec_glyph_lookup_face): Pass window to face machinery. * src/dispextern.h: (lookup_named_face,lookup_basic_face) (lookup_derived_face,merge_faces): Add struct window arguments to prototypes.
-rw-r--r--etc/NEWS5
-rw-r--r--src/dispextern.h9
-rw-r--r--src/dispnew.c3
-rw-r--r--src/font.c4
-rw-r--r--src/fringe.c22
-rw-r--r--src/msdos.c8
-rw-r--r--src/term.c8
-rw-r--r--src/xdisp.c51
-rw-r--r--src/xfaces.c304
9 files changed, 291 insertions, 123 deletions
diff --git a/etc/NEWS b/etc/NEWS
index d6b7485f7ff..3282340f914 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -573,6 +573,11 @@ manual for more details.
* Lisp Changes in Emacs 27.1
+++
+** Face specifications (of the kind used in `face-remapping-alist')
+ now support filters, allowing faces to vary between windows display
+ the same buffer.
+
++++
** New function assoc-delete-all.
+++
diff --git a/src/dispextern.h b/src/dispextern.h
index bc2a76f1e02..2180c9ae63f 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3429,11 +3429,12 @@ char *choose_face_font (struct frame *, Lisp_Object *, Lisp_Object,
#ifdef HAVE_WINDOW_SYSTEM
void prepare_face_for_display (struct frame *, struct face *);
#endif
-int lookup_named_face (struct frame *, Lisp_Object, bool);
-int lookup_basic_face (struct frame *, int);
+int lookup_named_face (struct window *, struct frame *, Lisp_Object, bool);
+int lookup_basic_face (struct window *, struct frame *, int);
int smaller_face (struct frame *, int, int);
int face_with_height (struct frame *, int, int);
-int lookup_derived_face (struct frame *, Lisp_Object, int, bool);
+int lookup_derived_face (struct window *, struct frame *,
+ Lisp_Object, int, bool);
void init_frame_faces (struct frame *);
void free_frame_faces (struct frame *);
void recompute_basic_faces (struct frame *);
@@ -3443,7 +3444,7 @@ int face_for_overlay_string (struct window *, ptrdiff_t, ptrdiff_t *, ptrdiff_t,
bool, Lisp_Object);
int face_at_string_position (struct window *, Lisp_Object, ptrdiff_t, ptrdiff_t,
ptrdiff_t *, enum face_id, bool);
-int merge_faces (struct frame *, Lisp_Object, int, int);
+int merge_faces (struct window *, Lisp_Object, int, int);
int compute_char_face (struct frame *, int, Lisp_Object);
void free_all_realized_faces (Lisp_Object);
extern char unspecified_fg[], unspecified_bg[];
diff --git a/src/dispnew.c b/src/dispnew.c
index b854d179d51..46e0c83ef6a 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -2508,8 +2508,7 @@ spec_glyph_lookup_face (struct window *w, GLYPH *glyph)
/* Convert the glyph's specified face to a realized (cache) face. */
if (lface_id > 0)
{
- int face_id = merge_faces (XFRAME (w->frame),
- Qt, lface_id, DEFAULT_FACE_ID);
+ int face_id = merge_faces (w, Qt, lface_id, DEFAULT_FACE_ID);
SET_GLYPH_FACE (*glyph, face_id);
}
}
diff --git a/src/font.c b/src/font.c
index 305bb14576a..c886c299d19 100644
--- a/src/font.c
+++ b/src/font.c
@@ -3810,7 +3810,7 @@ font_range (ptrdiff_t pos, ptrdiff_t pos_byte, ptrdiff_t *limit,
face_id =
NILP (Vface_remapping_alist)
? DEFAULT_FACE_ID
- : lookup_basic_face (f, DEFAULT_FACE_ID);
+ : lookup_basic_face (w, f, DEFAULT_FACE_ID);
face_id = face_at_string_position (w, string, pos, 0, &ignore,
face_id, false);
@@ -4559,7 +4559,7 @@ DEFUN ("internal-char-font", Finternal_char_font, Sinternal_char_font, 1, 2, 0,
CHECK_CHARACTER (ch);
c = XINT (ch);
f = XFRAME (selected_frame);
- face_id = lookup_basic_face (f, DEFAULT_FACE_ID);
+ face_id = lookup_basic_face (NULL, f, DEFAULT_FACE_ID);
pos = -1;
}
else
diff --git a/src/fringe.c b/src/fringe.c
index 85aa14da727..60691846814 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -587,8 +587,8 @@ draw_fringe_bitmap_1 (struct window *w, struct glyph_row *row, int left_p, int o
if (face_id == DEFAULT_FACE_ID)
{
Lisp_Object face = fringe_faces[which];
- face_id = NILP (face) ? lookup_named_face (f, Qfringe, false)
- : lookup_derived_face (f, face, FRINGE_FACE_ID, 0);
+ face_id = NILP (face) ? lookup_named_face (w, f, Qfringe, false)
+ : lookup_derived_face (w, f, face, FRINGE_FACE_ID, 0);
if (face_id < 0)
face_id = FRINGE_FACE_ID;
}
@@ -1633,20 +1633,10 @@ If FACE is nil, reset face to default fringe face. */)
if (!n)
error ("Undefined fringe bitmap");
- /* The purpose of the following code is to signal an error if FACE
- is not a face. This is for the caller's convenience only; the
- redisplay code should be able to fail gracefully. Skip the check
- if FRINGE_FACE_ID is unrealized (as in batch mode and during
- daemon startup). */
- if (!NILP (face))
- {
- struct frame *f = SELECTED_FRAME ();
-
- if (FACE_FROM_ID_OR_NULL (f, FRINGE_FACE_ID)
- && lookup_derived_face (f, face, FRINGE_FACE_ID, 1) < 0)
- error ("No such face");
- }
-
+ /* We used to check, as a convenience to callers, for basic face
+ validity here, but since validity can depend on the specific
+ _window_ in which this buffer is being displayed, defer the check
+ to redisplay, which can cope with bad face specifications. */
fringe_faces[n] = face;
return Qnil;
}
diff --git a/src/msdos.c b/src/msdos.c
index eedbf7b1a6c..6c0dfa0c46a 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -3063,15 +3063,15 @@ XMenuActivate (Display *foo, XMenu *menu, int *pane, int *selidx,
state = alloca (menu->panecount * sizeof (struct IT_menu_state));
screensize = screen_size * 2;
faces[0]
- = lookup_derived_face (sf, intern ("msdos-menu-passive-face"),
+ = lookup_derived_face (NULL, sf, intern ("msdos-menu-passive-face"),
DEFAULT_FACE_ID, 1);
faces[1]
- = lookup_derived_face (sf, intern ("msdos-menu-active-face"),
+ = lookup_derived_face (NULL, sf, intern ("msdos-menu-active-face"),
DEFAULT_FACE_ID, 1);
selectface = intern ("msdos-menu-select-face");
- faces[2] = lookup_derived_face (sf, selectface,
+ faces[2] = lookup_derived_face (NULL, sf, selectface,
faces[0], 1);
- faces[3] = lookup_derived_face (sf, selectface,
+ faces[3] = lookup_derived_face (NULL, sf, selectface,
faces[1], 1);
/* Make sure the menu title is always displayed with
diff --git a/src/term.c b/src/term.c
index 08d483f4fa0..bcd7dd82d6f 100644
--- a/src/term.c
+++ b/src/term.c
@@ -3132,15 +3132,15 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
SAFE_NALLOCA (state, 1, menu->panecount);
memset (state, 0, sizeof (*state));
faces[0]
- = lookup_derived_face (sf, intern ("tty-menu-disabled-face"),
+ = lookup_derived_face (NULL, sf, intern ("tty-menu-disabled-face"),
DEFAULT_FACE_ID, 1);
faces[1]
- = lookup_derived_face (sf, intern ("tty-menu-enabled-face"),
+ = lookup_derived_face (NULL, sf, intern ("tty-menu-enabled-face"),
DEFAULT_FACE_ID, 1);
selectface = intern ("tty-menu-selected-face");
- faces[2] = lookup_derived_face (sf, selectface,
+ faces[2] = lookup_derived_face (NULL, sf, selectface,
faces[0], 1);
- faces[3] = lookup_derived_face (sf, selectface,
+ faces[3] = lookup_derived_face (NULL, sf, selectface,
faces[1], 1);
/* Make sure the menu title is always displayed with
diff --git a/src/xdisp.c b/src/xdisp.c
index ad1c044557d..a2b6513e571 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2809,7 +2809,7 @@ init_iterator (struct it *it, struct window *w,
/* Perhaps remap BASE_FACE_ID to a user-specified alternative. */
if (! NILP (Vface_remapping_alist))
remapped_base_face_id
- = lookup_basic_face (XFRAME (w->frame), base_face_id);
+ = lookup_basic_face (w, XFRAME (w->frame), base_face_id);
/* Use one of the mode line rows of W's desired matrix if
appropriate. */
@@ -4060,7 +4060,7 @@ handle_face_prop (struct it *it)
might be a big deal. */
base_face_id = it->string_from_prefix_prop_p
? (!NILP (Vface_remapping_alist)
- ? lookup_basic_face (it->f, DEFAULT_FACE_ID)
+ ? lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID)
: DEFAULT_FACE_ID)
: underlying_face_id (it);
}
@@ -4988,7 +4988,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
struct face *f;
f = FACE_FROM_ID (it->f,
- lookup_basic_face (it->f, DEFAULT_FACE_ID));
+ lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID));
new_height = (XFLOATINT (it->font_height)
* XINT (f->lface[LFACE_HEIGHT_INDEX]));
}
@@ -5175,12 +5175,12 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
if (it)
{
- int face_id = lookup_basic_face (it->f, DEFAULT_FACE_ID);
+ int face_id = lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID);
if (CONSP (XCDR (XCDR (spec))))
{
Lisp_Object face_name = XCAR (XCDR (XCDR (spec)));
- int face_id2 = lookup_derived_face (it->f, face_name,
+ int face_id2 = lookup_derived_face (it->w, it->f, face_name,
FRINGE_FACE_ID, false);
if (face_id2 >= 0)
face_id = face_id2;
@@ -6985,7 +6985,7 @@ merge_escape_glyph_face (struct it *it)
else
{
/* Merge the `escape-glyph' face into the current face. */
- face_id = merge_faces (it->f, Qescape_glyph, 0, it->face_id);
+ face_id = merge_faces (it->w, Qescape_glyph, 0, it->face_id);
last_escape_glyph_frame = it->f;
last_escape_glyph_face_id = it->face_id;
last_escape_glyph_merged_face_id = face_id;
@@ -7010,7 +7010,7 @@ merge_glyphless_glyph_face (struct it *it)
else
{
/* Merge the `glyphless-char' face into the current face. */
- face_id = merge_faces (it->f, Qglyphless_char, 0, it->face_id);
+ face_id = merge_faces (it->w, Qglyphless_char, 0, it->face_id);
last_glyphless_glyph_frame = it->f;
last_glyphless_glyph_face_id = it->face_id;
last_glyphless_glyph_merged_face_id = face_id;
@@ -7184,7 +7184,7 @@ get_next_display_element (struct it *it)
}
face_id = (lface_id
- ? merge_faces (it->f, Qt, lface_id, it->face_id)
+ ? merge_faces (it->w, Qt, lface_id, it->face_id)
: merge_escape_glyph_face (it));
XSETINT (it->ctl_chars[0], g);
@@ -7199,7 +7199,7 @@ get_next_display_element (struct it *it)
if (nonascii_space_p && EQ (Vnobreak_char_display, Qt))
{
/* Merge `nobreak-space' into the current face. */
- face_id = merge_faces (it->f, Qnobreak_space, 0,
+ face_id = merge_faces (it->w, Qnobreak_space, 0,
it->face_id);
XSETINT (it->ctl_chars[0], ' ');
ctl_len = 1;
@@ -7212,7 +7212,7 @@ get_next_display_element (struct it *it)
if (nonascii_hyphen_p && EQ (Vnobreak_char_display, Qt))
{
/* Merge `nobreak-space' into the current face. */
- face_id = merge_faces (it->f, Qnobreak_hyphen, 0,
+ face_id = merge_faces (it->w, Qnobreak_hyphen, 0,
it->face_id);
XSETINT (it->ctl_chars[0], '-');
ctl_len = 1;
@@ -7232,7 +7232,7 @@ get_next_display_element (struct it *it)
}
face_id = (lface_id
- ? merge_faces (it->f, Qt, lface_id, it->face_id)
+ ? merge_faces (it->w, Qt, lface_id, it->face_id)
: merge_escape_glyph_face (it));
/* Draw non-ASCII space/hyphen with escape glyph: */
@@ -7860,7 +7860,7 @@ next_element_from_display_vector (struct it *it)
{
int lface_id = GLYPH_CODE_FACE (gc);
if (lface_id > 0)
- it->face_id = merge_faces (it->f, Qt, lface_id,
+ it->face_id = merge_faces (it->w, Qt, lface_id,
it->saved_face_id);
}
@@ -7889,7 +7889,7 @@ next_element_from_display_vector (struct it *it)
GLYPH_CODE_FACE (it->dpvec[it->current.dpvec_index + 1]);
if (lface_id > 0)
- next_face_id = merge_faces (it->f, Qt, lface_id,
+ next_face_id = merge_faces (it->w, Qt, lface_id,
it->saved_face_id);
}
}
@@ -20084,7 +20084,7 @@ append_space_for_newline (struct it *it, bool default_face_p)
/* If the default face was remapped, be sure to use the
remapped face for the appended newline. */
if (default_face_p)
- it->face_id = lookup_basic_face (it->f, DEFAULT_FACE_ID);
+ it->face_id = lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID);
else if (it->face_before_selective_p)
it->face_id = it->saved_face_id;
face = FACE_FROM_ID (it->f, it->face_id);
@@ -20231,8 +20231,9 @@ extend_face_to_end_of_line (struct it *it)
return;
/* The default face, possibly remapped. */
- default_face = FACE_FROM_ID_OR_NULL (f,
- lookup_basic_face (f, DEFAULT_FACE_ID));
+ default_face = FACE_FROM_ID_OR_NULL (
+ f,
+ lookup_basic_face (it->w, f, DEFAULT_FACE_ID));
/* Face extension extends the background and box of IT->face_id
to the end of the line. If the background equals the background
@@ -20486,11 +20487,12 @@ trailing_whitespace_p (ptrdiff_t charpos)
}
-/* Highlight trailing whitespace, if any, in ROW. */
+/* Highlight trailing whitespace, if any, in row at IT. */
static void
-highlight_trailing_whitespace (struct frame *f, struct glyph_row *row)
+highlight_trailing_whitespace (struct it *it)
{
+ struct glyph_row *row = it->glyph_row;
int used = row->used[TEXT_AREA];
if (used)
@@ -20535,7 +20537,7 @@ highlight_trailing_whitespace (struct frame *f, struct glyph_row *row)
&& glyph->u.ch == ' '))
&& trailing_whitespace_p (glyph->charpos))
{
- int face_id = lookup_named_face (f, Qtrailing_whitespace, false);
+ int face_id = lookup_named_face (it->w, it->f, Qtrailing_whitespace, false);
if (face_id < 0)
return;
@@ -21107,9 +21109,9 @@ maybe_produce_line_number (struct it *it)
char lnum_buf[INT_STRLEN_BOUND (ptrdiff_t) + 1];
bool beyond_zv = IT_BYTEPOS (*it) >= ZV_BYTE ? true : false;
ptrdiff_t lnum_offset = -1; /* to produce 1-based line numbers */
- int lnum_face_id = merge_faces (it->f, Qline_number, 0, DEFAULT_FACE_ID);
+ int lnum_face_id = merge_faces (it->w, Qline_number, 0, DEFAULT_FACE_ID);
int current_lnum_face_id
- = merge_faces (it->f, Qline_number_current_line, 0, DEFAULT_FACE_ID);
+ = merge_faces (it->w, Qline_number_current_line, 0, DEFAULT_FACE_ID);
/* Compute point's line number if needed. */
if ((EQ (Vdisplay_line_numbers, Qrelative)
|| EQ (Vdisplay_line_numbers, Qvisual)
@@ -21559,7 +21561,8 @@ display_line (struct it *it, int cursor_vpos)
portions of the screen will clear with the default face's
background color. */
if (row->reversed_p
- || lookup_basic_face (it->f, DEFAULT_FACE_ID) != DEFAULT_FACE_ID)
+ || lookup_basic_face (it->w, it->f, DEFAULT_FACE_ID)
+ != DEFAULT_FACE_ID)
extend_face_to_end_of_line (it);
break;
}
@@ -22192,7 +22195,7 @@ display_line (struct it *it, int cursor_vpos)
/* Highlight trailing whitespace. */
if (!NILP (Vshow_trailing_whitespace))
- highlight_trailing_whitespace (it->f, it->glyph_row);
+ highlight_trailing_whitespace (it);
/* Compute pixel dimensions of this line. */
compute_line_metrics (it);
@@ -27862,7 +27865,7 @@ calc_line_height_property (struct it *it, Lisp_Object val, struct font *font,
int face_id;
struct face *face;
- face_id = lookup_named_face (it->f, face_name, false);
+ face_id = lookup_named_face (it->w, it->f, face_name, false);
face = FACE_FROM_ID_OR_NULL (it->f, face_id);
if (face == NULL || ((font = face->font) == NULL))
return make_number (-1);
diff --git a/src/xfaces.c b/src/xfaces.c
index a9c2f37e9f2..1654f8610e8 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -350,7 +350,8 @@ static bool realize_default_face (struct frame *);
static void realize_named_face (struct frame *, Lisp_Object, int);
static struct face_cache *make_face_cache (struct frame *);
static void free_face_cache (struct face_cache *);
-static bool merge_face_ref (struct frame *, Lisp_Object, Lisp_Object *,
+static bool merge_face_ref (struct window *w,
+ struct frame *, Lisp_Object, Lisp_Object *,
bool, struct named_merge_point *);
static int color_distance (XColor *x, XColor *y);
@@ -1551,7 +1552,7 @@ the WIDTH times as wide as FACE on FRAME. */)
{
/* This is of limited utility since it works with character
widths. Keep it for compatibility. --gerd. */
- int face_id = lookup_named_face (f, face, false);
+ int face_id = lookup_named_face (NULL, f, face, false);
struct face *width_face = FACE_FROM_ID_OR_NULL (f, face_id);
if (width_face && width_face->font)
@@ -1907,19 +1908,22 @@ get_lface_attributes_no_remap (struct frame *f, Lisp_Object face_name,
return !NILP (lface);
}
-/* Get face attributes of face FACE_NAME from frame-local faces on frame
- F. Store the resulting attributes in ATTRS which must point to a
- vector of Lisp_Objects of size LFACE_VECTOR_SIZE. If FACE_NAME is an
- alias for another face, use that face's definition.
- If SIGNAL_P, signal an error if FACE_NAME does not name a face.
- Otherwise, return true iff FACE_NAME is a face. */
-
+/* Get face attributes of face FACE_NAME from frame-local faces on
+ frame F. Store the resulting attributes in ATTRS which must point
+ to a vector of Lisp_Objects of size LFACE_VECTOR_SIZE.
+ If FACE_NAME is an alias for another face, use that face's
+ definition. If SIGNAL_P, signal an error if FACE_NAME does not
+ name a face. Otherwise, return true iff FACE_NAME is a face. If W
+ is non-NULL, also consider remappings attached to the window.
+ */
static bool
-get_lface_attributes (struct frame *f, Lisp_Object face_name,
+get_lface_attributes (struct window *w,
+ struct frame *f, Lisp_Object face_name,
Lisp_Object attrs[LFACE_VECTOR_SIZE], bool signal_p,
struct named_merge_point *named_merge_points)
{
Lisp_Object face_remapping;
+ eassert (w == NULL || WINDOW_XFRAME (w) == f);
face_name = resolve_face_name (face_name, signal_p);
@@ -1939,7 +1943,7 @@ get_lface_attributes (struct frame *f, Lisp_Object face_name,
for (i = 1; i < LFACE_VECTOR_SIZE; ++i)
attrs[i] = Qunspecified;
- return merge_face_ref (f, XCDR (face_remapping), attrs,
+ return merge_face_ref (w, f, XCDR (face_remapping), attrs,
signal_p, named_merge_points);
}
}
@@ -2072,15 +2076,16 @@ merge_face_heights (Lisp_Object from, Lisp_Object to, Lisp_Object invalid)
/* Merge two Lisp face attribute vectors on frame F, FROM and TO, and
store the resulting attributes in TO, which must be already be
- completely specified and contain only absolute attributes. Every
- specified attribute of FROM overrides the corresponding attribute of
- TO; relative attributes in FROM are merged with the absolute value in
- TO and replace it. NAMED_MERGE_POINTS is used internally to detect
- loops in face inheritance/remapping; it should be 0 when called from
- other places. */
-
+ completely specified and contain only absolute attributes.
+ Every specified attribute of FROM overrides the corresponding
+ attribute of TO; relative attributes in FROM are merged with the
+ absolute value in TO and replace it. NAMED_MERGE_POINTS is used
+ internally to detect loops in face inheritance/remapping; it should
+ be 0 when called from other places. If window W is non-NULL, use W
+ to interpret face specifications. */
static void
-merge_face_vectors (struct frame *f, Lisp_Object *from, Lisp_Object *to,
+merge_face_vectors (struct window *w,
+ struct frame *f, Lisp_Object *from, Lisp_Object *to,
struct named_merge_point *named_merge_points)
{
int i;
@@ -2093,7 +2098,8 @@ merge_face_vectors (struct frame *f, Lisp_Object *from, Lisp_Object *to,
other code uses `unspecified' as a generic value for face attributes. */
if (!UNSPECIFIEDP (from[LFACE_INHERIT_INDEX])
&& !NILP (from[LFACE_INHERIT_INDEX]))
- merge_face_ref (f, from[LFACE_INHERIT_INDEX], to, false, named_merge_points);
+ merge_face_ref (w, f, from[LFACE_INHERIT_INDEX],
+ to, false, named_merge_points);
if (FONT_SPEC_P (from[LFACE_FONT_INDEX]))
{
@@ -2153,10 +2159,12 @@ merge_face_vectors (struct frame *f, Lisp_Object *from, Lisp_Object *to,
/* Merge the named face FACE_NAME on frame F, into the vector of face
attributes TO. Use NAMED_MERGE_POINTS to detect loops in face
inheritance. Return true if FACE_NAME is a valid face name and
- merging succeeded. */
+ merging succeeded. Window W, if non-NULL, is used to filter face
+ specifications. */
static bool
-merge_named_face (struct frame *f, Lisp_Object face_name, Lisp_Object *to,
+merge_named_face (struct window *w,
+ struct frame *f, Lisp_Object face_name, Lisp_Object *to,
struct named_merge_point *named_merge_points)
{
struct named_merge_point named_merge_point;
@@ -2166,11 +2174,11 @@ merge_named_face (struct frame *f, Lisp_Object face_name, Lisp_Object *to,
&named_merge_points))
{
Lisp_Object from[LFACE_VECTOR_SIZE];
- bool ok = get_lface_attributes (f, face_name, from, false,
+ bool ok = get_lface_attributes (w, f, face_name, from, false,
named_merge_points);
if (ok)
- merge_face_vectors (f, from, to, named_merge_points);
+ merge_face_vectors (w, f, from, to, named_merge_points);
return ok;
}
@@ -2178,6 +2186,111 @@ merge_named_face (struct frame *f, Lisp_Object face_name, Lisp_Object *to,
return false;
}
+/* Determine whether the face filter FILTER evaluated in window W
+ matches. W can be NULL if the window context is unknown.
+
+ A face filter is either nil, which always matches, or a list
+ (:window PARAMETER VALUE), which matches if the current window has
+ a PARAMETER EQ to VALUE.
+
+ If the filter is invalid, set *OK to false and, if ERR_MSGS is
+ true, log an error message. */
+static bool
+evaluate_face_filter (Lisp_Object filter, struct window *w,
+ bool *ok, bool err_msgs)
+{
+ Lisp_Object orig_filter = filter;
+
+ {
+ if (NILP (filter))
+ return true;
+
+ if (face_filters_always_match)
+ return true;
+
+ if (!CONSP (filter))
+ goto err;
+
+ if (!EQ (XCAR (filter), Qwindow_kw))
+ goto err;
+ filter = XCDR (filter);
+
+ Lisp_Object parameter = XCAR (filter);
+ filter = XCDR (filter);
+ if (!CONSP (filter))
+ goto err;
+
+ Lisp_Object value = XCAR (filter);
+ filter = XCDR (filter);
+ if (!NILP (filter))
+ goto err;
+
+ bool match = false;
+ if (w) {
+ Lisp_Object found = assq_no_quit (parameter, w->window_parameters);
+ if (!NILP (found) && EQ (XCDR (found), value))
+ match = true;
+ }
+
+ return match;
+ }
+
+ err:
+ if (err_msgs)
+ add_to_log ("Invalid face filter %S", orig_filter);
+ *ok = false;
+ return false;
+}
+
+/* Determine whether FACE_REF is a "filter" face specification (case
+ #4 in merge_face_ref). If it is, evaluate the filter, and if the
+ filter matches, return the filtered expression. Otherwise, return
+ the original expression.
+
+ On error, set *OK to false, having logged an error message if
+ ERR_MSGS is true, with return value unspecified.
+
+ W is either NULL or a window used to evaluate filters. If W is
+ null, no window-based face specification filter matches.
+*/
+static Lisp_Object
+filter_face_ref (Lisp_Object face_ref,
+ struct window *w,
+ bool *ok,
+ bool err_msgs)
+{
+ Lisp_Object orig_face_ref = face_ref;
+ if (!CONSP (face_ref))
+ return face_ref;
+
+ {
+ if (!EQ (XCAR (face_ref), Qfiltered_kw))
+ return face_ref;
+ face_ref = XCDR (face_ref);
+
+ if (!CONSP (face_ref))
+ goto err;
+ Lisp_Object filter = XCAR (face_ref);
+ face_ref = XCDR (face_ref);
+
+ if (!CONSP (face_ref))
+ goto err;
+ Lisp_Object filtered_face_ref = XCAR (face_ref);
+ face_ref = XCDR (face_ref);
+
+ if (!NILP (face_ref))
+ goto err;
+
+ return evaluate_face_filter (filter, w, ok, err_msgs)
+ ? filtered_face_ref : Qnil;
+ }
+
+ err:
+ if (err_msgs)
+ add_to_log ("Invalid face ref %S", orig_face_ref);
+ *ok = false;
+ return Qnil;
+}
/* Merge face attributes from the lisp `face reference' FACE_REF on
frame F into the face attribute vector TO. If ERR_MSGS,
@@ -2199,21 +2312,44 @@ merge_named_face (struct frame *f, Lisp_Object face_name, Lisp_Object *to,
(BACKGROUND-COLOR . COLOR) where COLOR is a color name. This is
for compatibility with 20.2.
+ 4. Conses of the form
+ (:filter (:window PARAMETER VALUE) FACE-SPECIFICATION),
+ which applies FACE-SPECIFICATION only if the
+ given face attributes are being evaluated in the context of a
+ window with a parameter named PARAMETER being EQ VALUE.
+
+ 5. nil, which means to merge nothing.
+
Face specifications earlier in lists take precedence over later
specifications. */
static bool
-merge_face_ref (struct frame *f, Lisp_Object face_ref, Lisp_Object *to,
+merge_face_ref (struct window *w,
+ struct frame *f, Lisp_Object face_ref, Lisp_Object *to,
bool err_msgs, struct named_merge_point *named_merge_points)
{
bool ok = true; /* Succeed without an error? */
+ Lisp_Object filtered_face_ref;
+
+ filtered_face_ref = face_ref;
+ do
+ {
+ face_ref = filtered_face_ref;
+ filtered_face_ref = filter_face_ref (face_ref, w, &ok, err_msgs);
+ } while (ok && !EQ (face_ref, filtered_face_ref));
+
+ if (!ok)
+ return false;
+
+ if (NILP (face_ref))
+ return true;
if (CONSP (face_ref))
{
Lisp_Object first = XCAR (face_ref);
if (EQ (first, Qforeground_color)
- || EQ (first, Qbackground_color))
+ || EQ (first, Qbackground_color))
{
/* One of (FOREGROUND-COLOR . COLOR) or (BACKGROUND-COLOR
. COLOR). COLOR must be a string. */
@@ -2400,7 +2536,7 @@ merge_face_ref (struct frame *f, Lisp_Object face_ref, Lisp_Object *to,
{
/* This is not really very useful; it's just like a
normal face reference. */
- if (! merge_face_ref (f, value, to,
+ if (! merge_face_ref (w, f, value, to,
err_msgs, named_merge_points))
err = true;
}
@@ -2424,16 +2560,16 @@ merge_face_ref (struct frame *f, Lisp_Object face_ref, Lisp_Object *to,
Lisp_Object next = XCDR (face_ref);
if (! NILP (next))
- ok = merge_face_ref (f, next, to, err_msgs, named_merge_points);
+ ok = merge_face_ref (w, f, next, to, err_msgs, named_merge_points);
- if (! merge_face_ref (f, first, to, err_msgs, named_merge_points))
+ if (! merge_face_ref (w, f, first, to, err_msgs, named_merge_points))
ok = false;
}
}
else
{
/* FACE_REF ought to be a face name. */
- ok = merge_named_face (f, face_ref, to, named_merge_points);
+ ok = merge_named_face (w, f, face_ref, to, named_merge_points);
if (!ok && err_msgs)
add_to_log ("Invalid face reference: %s", face_ref);
}
@@ -3701,7 +3837,7 @@ Default face attributes override any local face attributes. */)
/* Ensure that the face vector is fully specified by merging
the previously-cached vector. */
memcpy (attrs, oldface->lface, sizeof attrs);
- merge_face_vectors (f, lvec, attrs, 0);
+ merge_face_vectors (NULL, f, lvec, attrs, 0);
vcopy (local_lface, 0, attrs, LFACE_VECTOR_SIZE);
newface = realize_face (c, lvec, DEFAULT_FACE_ID);
@@ -3774,7 +3910,7 @@ return the font name used for CHARACTER. */)
else
{
struct frame *f = decode_live_frame (frame);
- int face_id = lookup_named_face (f, face, true);
+ int face_id = lookup_named_face (NULL, f, face, true);
struct face *fface = FACE_FROM_ID_OR_NULL (f, face_id);
if (! fface)
@@ -4432,10 +4568,12 @@ face_for_font (struct frame *f, Lisp_Object font_object, struct face *base_face)
/* Return the face id of the realized face for named face SYMBOL on
frame F suitable for displaying ASCII characters. Value is -1 if
the face couldn't be determined, which might happen if the default
- face isn't realized and cannot be realized. */
-
+ face isn't realized and cannot be realized. If window W is given,
+ consider face remappings specified for W or for W's buffer. If W is
+ NULL, consider only frame-level face configuration. */
int
-lookup_named_face (struct frame *f, Lisp_Object symbol, bool signal_p)
+lookup_named_face (struct window *w, struct frame *f,
+ Lisp_Object symbol, bool signal_p)
{
Lisp_Object attrs[LFACE_VECTOR_SIZE];
Lisp_Object symbol_attrs[LFACE_VECTOR_SIZE];
@@ -4448,11 +4586,11 @@ lookup_named_face (struct frame *f, Lisp_Object symbol, bool signal_p)
default_face = FACE_FROM_ID (f, DEFAULT_FACE_ID);
}
- if (! get_lface_attributes (f, symbol, symbol_attrs, signal_p, 0))
+ if (! get_lface_attributes (w, f, symbol, symbol_attrs, signal_p, 0))
return -1;
memcpy (attrs, default_face->lface, sizeof attrs);
- merge_face_vectors (f, symbol_attrs, attrs, 0);
+ merge_face_vectors (w, f, symbol_attrs, attrs, 0);
return lookup_face (f, attrs);
}
@@ -4462,10 +4600,10 @@ lookup_named_face (struct frame *f, Lisp_Object symbol, bool signal_p)
is FACE_ID. The return value will usually simply be FACE_ID, unless that
basic face has bee remapped via Vface_remapping_alist. This function is
conservative: if something goes wrong, it will simply return FACE_ID
- rather than signal an error. */
-
+ rather than signal an error. Window W, if non-NULL, is used to filter
+ face specifications for remapping. */
int
-lookup_basic_face (struct frame *f, int face_id)
+lookup_basic_face (struct window *w, struct frame *f, int face_id)
{
Lisp_Object name, mapping;
int remapped_face_id;
@@ -4505,7 +4643,7 @@ lookup_basic_face (struct frame *f, int face_id)
/* If there is a remapping entry, lookup the face using NAME, which will
handle the remapping too. */
- remapped_face_id = lookup_named_face (f, name, false);
+ remapped_face_id = lookup_named_face (w, f, name, false);
if (remapped_face_id < 0)
return face_id; /* Give up. */
@@ -4603,22 +4741,23 @@ face_with_height (struct frame *f, int face_id, int height)
attributes of the face FACE_ID for attributes that aren't
completely specified by SYMBOL. This is like lookup_named_face,
except that the default attributes come from FACE_ID, not from the
- default face. FACE_ID is assumed to be already realized. */
-
+ default face. FACE_ID is assumed to be already realized.
+ Window W, if non-NULL, filters face specifications. */
int
-lookup_derived_face (struct frame *f, Lisp_Object symbol, int face_id,
+lookup_derived_face (struct window *w,
+ struct frame *f, Lisp_Object symbol, int face_id,
bool signal_p)
{
Lisp_Object attrs[LFACE_VECTOR_SIZE];
Lisp_Object symbol_attrs[LFACE_VECTOR_SIZE];
struct face *default_face;
- if (!get_lface_attributes (f, symbol, symbol_attrs, signal_p, 0))
+ if (!get_lface_attributes (w, f, symbol, symbol_attrs, signal_p, 0))
return -1;
default_face = FACE_FROM_ID (f, face_id);
memcpy (attrs, default_face->lface, sizeof attrs);
- merge_face_vectors (f, symbol_attrs, attrs, 0);
+ merge_face_vectors (w, f, symbol_attrs, attrs, 0);
return lookup_face (f, attrs);
}
@@ -4630,7 +4769,8 @@ DEFUN ("face-attributes-as-vector", Fface_attributes_as_vector,
Lisp_Object lface;
lface = Fmake_vector (make_number (LFACE_VECTOR_SIZE),
Qunspecified);
- merge_face_ref (XFRAME (selected_frame), plist, XVECTOR (lface)->contents,
+ merge_face_ref (NULL, XFRAME (selected_frame),
+ plist, XVECTOR (lface)->contents,
true, 0);
return lface;
}
@@ -4714,7 +4854,7 @@ x_supports_face_attributes_p (struct frame *f,
memcpy (merged_attrs, def_attrs, sizeof merged_attrs);
- merge_face_vectors (f, attrs, merged_attrs, 0);
+ merge_face_vectors (NULL, f, attrs, merged_attrs, 0);
face_id = lookup_face (f, merged_attrs);
face = FACE_FROM_ID_OR_NULL (f, face_id);
@@ -4985,7 +5125,7 @@ face for italic. */)
for (i = 0; i < LFACE_VECTOR_SIZE; i++)
attrs[i] = Qunspecified;
- merge_face_ref (f, attributes, attrs, true, 0);
+ merge_face_ref (NULL, f, attributes, attrs, true, 0);
def_face = FACE_FROM_ID_OR_NULL (f, DEFAULT_FACE_ID);
if (def_face == NULL)
@@ -5354,7 +5494,7 @@ realize_named_face (struct frame *f, Lisp_Object symbol, int id)
/* Merge SYMBOL's face with the default face. */
get_lface_attributes_no_remap (f, symbol, symbol_attrs, true);
- merge_face_vectors (f, symbol_attrs, attrs, 0);
+ merge_face_vectors (NULL, f, symbol_attrs, attrs, 0);
/* Realize the face. */
realize_face (c, attrs, id);
@@ -5869,7 +6009,7 @@ compute_char_face (struct frame *f, int ch, Lisp_Object prop)
Lisp_Object attrs[LFACE_VECTOR_SIZE];
struct face *default_face = FACE_FROM_ID (f, DEFAULT_FACE_ID);
memcpy (attrs, default_face->lface, sizeof attrs);
- merge_face_ref (f, prop, attrs, true, 0);
+ merge_face_ref (NULL, f, prop, attrs, true, 0);
face_id = lookup_face (f, attrs);
}
@@ -5948,7 +6088,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
else if (NILP (Vface_remapping_alist))
face_id = DEFAULT_FACE_ID;
else
- face_id = lookup_basic_face (f, DEFAULT_FACE_ID);
+ face_id = lookup_basic_face (w, f, DEFAULT_FACE_ID);
default_face = FACE_FROM_ID (f, face_id);
}
@@ -5966,7 +6106,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
/* Merge in attributes specified via text properties. */
if (!NILP (prop))
- merge_face_ref (f, prop, attrs, true, 0);
+ merge_face_ref (w, f, prop, attrs, true, 0);
/* Now merge the overlay data. */
noverlays = sort_overlays (overlay_vec, noverlays, w);
@@ -5986,7 +6126,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
so discard the mouse-face text property, if any, and
use the overlay property instead. */
memcpy (attrs, default_face->lface, sizeof attrs);
- merge_face_ref (f, prop, attrs, true, 0);
+ merge_face_ref (w, f, prop, attrs, true, 0);
}
oend = OVERLAY_END (overlay_vec[i]);
@@ -6004,7 +6144,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
prop = Foverlay_get (overlay_vec[i], propname);
if (!NILP (prop))
- merge_face_ref (f, prop, attrs, true, 0);
+ merge_face_ref (w, f, prop, attrs, true, 0);
oend = OVERLAY_END (overlay_vec[i]);
oendpos = OVERLAY_POSITION (oend);
@@ -6065,12 +6205,12 @@ face_for_overlay_string (struct window *w, ptrdiff_t pos,
return DEFAULT_FACE_ID;
/* Begin with attributes from the default face. */
- default_face = FACE_FROM_ID (f, lookup_basic_face (f, DEFAULT_FACE_ID));
+ default_face = FACE_FROM_ID (f, lookup_basic_face (w, f, DEFAULT_FACE_ID));
memcpy (attrs, default_face->lface, sizeof attrs);
/* Merge in attributes specified via text properties. */
if (!NILP (prop))
- merge_face_ref (f, prop, attrs, true, 0);
+ merge_face_ref (w, f, prop, attrs, true, 0);
*endptr = endpos;
@@ -6149,7 +6289,7 @@ face_at_string_position (struct window *w, Lisp_Object string,
/* Merge in attributes specified via text properties. */
if (!NILP (prop))
- merge_face_ref (f, prop, attrs, true, 0);
+ merge_face_ref (w, f, prop, attrs, true, 0);
/* Look up a realized face with the given face attributes,
or realize a new one for ASCII characters. */
@@ -6159,7 +6299,7 @@ face_at_string_position (struct window *w, Lisp_Object string,
/* Merge a face into a realized face.
- F is frame where faces are (to be) realized.
+ W is a window in the frame where faces are (to be) realized.
FACE_NAME is named face to merge.
@@ -6173,9 +6313,10 @@ face_at_string_position (struct window *w, Lisp_Object string,
*/
int
-merge_faces (struct frame *f, Lisp_Object face_name, int face_id,
+merge_faces (struct window *w, Lisp_Object face_name, int face_id,
int base_face_id)
{
+ struct frame *f = WINDOW_XFRAME (w);
Lisp_Object attrs[LFACE_VECTOR_SIZE];
struct face *base_face;
@@ -6190,7 +6331,7 @@ merge_faces (struct frame *f, Lisp_Object face_name, int face_id,
face_name = lface_id_to_name[face_id];
/* When called during make-frame, lookup_derived_face may fail
if the faces are uninitialized. Don't signal an error. */
- face_id = lookup_derived_face (f, face_name, base_face_id, 0);
+ face_id = lookup_derived_face (w, f, face_name, base_face_id, 0);
return (face_id >= 0 ? face_id : base_face_id);
}
@@ -6199,7 +6340,7 @@ merge_faces (struct frame *f, Lisp_Object face_name, int face_id,
if (!NILP (face_name))
{
- if (!merge_named_face (f, face_name, attrs, 0))
+ if (!merge_named_face (w, f, face_name, attrs, 0))
return base_face_id;
}
else
@@ -6210,7 +6351,7 @@ merge_faces (struct frame *f, Lisp_Object face_name, int face_id,
face = FACE_FROM_ID_OR_NULL (f, face_id);
if (!face)
return base_face_id;
- merge_face_vectors (f, face->lface, attrs, 0);
+ merge_face_vectors (w, f, face->lface, attrs, 0);
}
/* Look up a realized face with the given face attributes,
@@ -6421,6 +6562,11 @@ syms_of_xfaces (void)
DEFSYM (Qunspecified, "unspecified");
DEFSYM (QCignore_defface, ":ignore-defface");
+ /* Used for limiting character attributes to windows with specific
+ characteristics. */
+ DEFSYM (Qwindow_kw, ":window");
+ DEFSYM (Qfiltered_kw, ":filtered");
+
/* The symbol `face-alias'. A symbol having that property is an
alias for another face. Value of the property is the name of
the aliased face. */
@@ -6496,6 +6642,10 @@ syms_of_xfaces (void)
defsubr (&Sdump_colors);
#endif
+ DEFVAR_BOOL ("face-filters-always-match", face_filters_always_match,
+ doc: /* Non-nil means that face filters are always deemed to
+match. Use only when evaluating face attributes. */);
+
DEFVAR_LISP ("face-new-frame-defaults", Vface_new_frame_defaults,
doc: /* List of global face definitions (for internal use only.) */);
Vface_new_frame_defaults = Qnil;
@@ -6544,7 +6694,8 @@ REPLACEMENT is a face specification, i.e. one of the following:
(1) a face name
(2) a property list of attribute/value pairs, or
- (3) a list in which each element has the form of (1) or (2).
+ (3)
+ (3) a list in which each element has one of the above forms.
List values for REPLACEMENT are merged to form the final face
specification, with earlier entries taking precedence, in the same way
@@ -6564,13 +6715,32 @@ causes EXTRA-FACE... or (FACE-ATTR VAL ...) to be _merged_ with the
existing definition of FACE. Note that this isn't necessary for the
default face, since every face inherits from the default face.
-If this variable is made buffer-local, the face remapping takes effect
-only in that buffer. For instance, the mode my-mode could define a
-face `my-mode-default', and then in the mode setup function, do:
+An entry in the list can also be a filtered face expression of the
+form:
+
+ (:filtered FILTER FACE-SPECIFICATION)
+
+This construct applies FACE-SPECIFICATION (which can have any of the
+forms allowed for face specifications generally) only if FILTER
+matches at the moment Emacs wants to draw text with the combined face.
+
+The only filters currently defined are NIL (which always matches) and
+(:window PARAMETER VALUE), which matches only in the context of a
+window with a parameter EQ-equal to VALUE.
+
+An entry in the face list can also be nil, which does nothing.
+
+If `face-remapping-alist' is made buffer-local, the face remapping
+takes effect only in that buffer. For instance, the mode my-mode
+could define a face `my-mode-default', and then in the mode setup
+function, do:
(set (make-local-variable \\='face-remapping-alist)
\\='((default my-mode-default)))).
+You probably want to use the face-remap package included in Emacs
+instead of manipulating face-remapping-alist directly.
+
Because Emacs normally only redraws screen areas when the underlying
buffer contents change, you may need to call `redraw-display' after
changing this variable for it to take effect. */);