unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [RFC PATCH] Per-window face support
@ 2018-06-07 23:42 dancol
  2018-06-08  2:57 ` Stefan Monnier
  2018-06-08  9:15 ` Eli Zaretskii
  0 siblings, 2 replies; 31+ messages in thread
From: dancol @ 2018-06-07 23:42 UTC (permalink / raw)
  To: emacs-devel

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;





^ permalink raw reply related	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2018-06-19  1:21 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-06-07 23:42 [RFC PATCH] Per-window face support dancol
2018-06-08  2:57 ` 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

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).