From: dancol@dancol.org
To: emacs-devel@gnu.org
Subject: [RFC PATCH] Per-window face support
Date: Thu, 7 Jun 2018 16:42:30 -0700 [thread overview]
Message-ID: <5e08587a56ad528599ed5fb259be6335.squirrel@dancol.org> (raw)
This patch lets us actually vary faces on a window-by-window basis. It
works by allowing face-remapping-alist entries to apply only to windows
with specific parameters set. A package that wants to apply a face
remapping to all windows with certain characteristics should use
face-remapping to add a remapping in all buffers, then set window
parameters to make that remapping take effect some of the time.
The existing support for per-window overlays isn't sufficient, since 1)
overlays affect only buffer text, and so can't affect non-text
face-configured regions like the beyond EOB text area.
diff --git a/src/dispextern.h b/src/dispextern.h
index bc2a76f1e0..2180c9ae63 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 b854d179d5..46e0c83ef6 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 305bb14576..c886c299d1 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 85aa14da72..6069184681 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 eedbf7b1a6..6c0dfa0c46 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 08d483f4fa..bcd7dd82d6 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 1299ba38e3..594a41f401 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 a9c2f37e9f..bf0b1ea04f 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;
next reply other threads:[~2018-06-07 23:42 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-06-07 23:42 dancol [this message]
2018-06-08 2:57 ` [RFC PATCH] Per-window face support Stefan Monnier
2018-06-08 3:18 ` Daniel Colascione
2018-06-08 3:53 ` Stefan Monnier
2018-06-08 9:15 ` Eli Zaretskii
2018-06-16 10:29 ` Eli Zaretskii
2018-06-16 14:24 ` Daniel Colascione
2018-06-16 15:06 ` Eli Zaretskii
2018-06-16 15:30 ` Daniel Colascione
2018-06-16 16:09 ` Eli Zaretskii
2018-06-16 17:35 ` Stefan Monnier
2018-06-16 21:03 ` Daniel Colascione
2018-06-17 18:20 ` Stefan Monnier
2018-06-17 18:48 ` Daniel Colascione
2018-06-17 19:03 ` Stefan Monnier
2018-06-17 19:21 ` Daniel Colascione
2018-06-17 20:51 ` Stefan Monnier
2018-06-17 21:16 ` Daniel Colascione
2018-06-17 21:34 ` Paul Eggert
2018-06-17 22:25 ` Aliasing EQ to EQL (was: [RFC PATCH] Per-window face support) Stefan Monnier
2018-06-17 23:49 ` Daniel Colascione
2018-06-18 2:14 ` Aliasing EQ to EQL Stefan Monnier
2018-06-18 3:48 ` Daniel Colascione
2018-06-18 13:56 ` Stefan Monnier
2018-06-17 19:23 ` [RFC PATCH] Per-window face support Stefan Monnier
2018-06-17 19:25 ` Daniel Colascione
2018-06-17 19:32 ` Stefan Monnier
2018-06-17 19:49 ` Daniel Colascione
2018-06-17 20:57 ` Stefan Monnier
2018-06-18 23:22 ` Richard Stallman
2018-06-19 1:21 ` Stefan Monnier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5e08587a56ad528599ed5fb259be6335.squirrel@dancol.org \
--to=dancol@dancol.org \
--cc=emacs-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.