]> git.eshelyaron.com Git - emacs.git/commitdiff
Support ':extend' in faces defined by list of key/value pairs
authorEli Zaretskii <eliz@gnu.org>
Tue, 26 Nov 2019 17:29:45 +0000 (19:29 +0200)
committerEli Zaretskii <eliz@gnu.org>
Tue, 26 Nov 2019 17:29:45 +0000 (19:29 +0200)
* src/xfaces.c: Update and improve commentary at the beginning
of the file.
(face_attr_sym): New static array.
(init_xfaces): Initialize 'face_attr_sym'.
(merge_face_ref): Handle the :extend attribute in faces
specified as lists of key/value pairs.  (Bug#37774)

src/xfaces.c

index c3b455c928e8f09a545d0febc5c3dbf357abc7bc..440cf4f1917622353a8a1e3d549082b7be3af5e5 100644 (file)
@@ -68,17 +68,25 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    On the other hand, if one of the other font-related attributes are
    specified, the corresponding specs in this attribute is set to nil.
 
-   15. A face name or list of face names from which to inherit attributes.
-
-   16. A specified average font width, which is invisible from Lisp,
-   and is used to ensure that a font specified on the command line,
-   for example, can be matched exactly.
+   16. A face name or list of face names from which to inherit attributes.
 
    17. A fontset name.  This is another special attribute.
 
    A fontset is a mappings from characters to font-specs, and the
    specs overwrite the font-spec in the 14th attribute.
 
+   18. A "distant background" color, to be used when the foreground is
+   too close to the background and is hard to read.
+
+   19. Whether to extend the face to end of line when the face
+   "covers" the newline that ends the line.
+
+   On the C level, a Lisp face is completely represented by its array
+   of attributes.  In that array, the zeroth element is Qface, and the
+   rest are the 19 face attributes described above.  The
+   lface_attribute_index enumeration, defined on dispextern.h, with
+   values given by the LFACE_*_INDEX constants, is used to reference
+   the individual attributes.
 
    Faces are frame-local by nature because Emacs allows you to define the
    same named face (face names are symbols) differently for different
@@ -100,10 +108,18 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
    The display style of a given character in the text is determined by
    combining several faces.  This process is called `face merging'.
-   Any aspect of the display style that isn't specified by overlays or
-   text properties is taken from the `default' face.  Since it is made
-   sure that the default face is always fully-specified, face merging
-   always results in a fully-specified face.
+   Face merging combines the attributes of each of the faces being
+   merged such that the attributes of the face that is merged later
+   override those of a face merged earlier in the process.  In
+   particular, this replaces any 'unspecified' attributes with
+   non-'unspecified' values.  Also, if a face inherits from another
+   (via the :inherit attribute), the attributes of the parent face,
+   recursively, are applied where the inheriting face doesn't specify
+   non-'unspecified' values.  Any aspect of the display style that
+   isn't specified by overlays or text properties is taken from the
+   'default' face.  Since it is made sure that the default face is
+   always fully-specified, face merging always results in a
+   fully-specified face.
 
 
    Face realization.
@@ -1604,6 +1620,9 @@ the WIDTH times as wide as FACE on FRAME.  */)
       && EQ (AREF (LFACE, 0), Qface))
 
 
+/* Face attribute symbols for each value of LFACE_*_INDEX.  */
+static Lisp_Object face_attr_sym[LFACE_VECTOR_SIZE];
+
 #ifdef GLYPH_DEBUG
 
 /* Check consistency of Lisp face attribute vector ATTRS.  */
@@ -2397,6 +2416,58 @@ merge_face_ref (struct window *w,
               && *SDATA (SYMBOL_NAME (first)) == ':')
        {
          /* Assume this is the property list form.  */
+         if (attr_filter > 0)
+           {
+             eassert (attr_filter < LFACE_VECTOR_SIZE);
+             /* ATTR_FILTER positive means don't merge this face if
+                the corresponding attribute is nil, or not mentioned,
+                or if it's unspecified and the face doesn't inherit
+                from a face whose attribute is non-nil.  The code
+                below determines whether a face given as a property
+                list shall be merged.  */
+             Lisp_Object parent_face = Qnil;
+             bool attr_filter_seen = false;
+             Lisp_Object face_ref_tem = face_ref;
+             while (CONSP (face_ref_tem) && CONSP (XCDR (face_ref_tem)))
+               {
+                 Lisp_Object keyword = XCAR (face_ref_tem);
+                 Lisp_Object value = XCAR (XCDR (face_ref_tem));
+
+                 if (EQ (keyword, face_attr_sym[attr_filter])
+                     || (attr_filter == LFACE_INVERSE_INDEX
+                         && EQ (keyword, QCreverse_video)))
+                   {
+                     attr_filter_seen = true;
+                     if (NILP (value))
+                       return true;
+                   }
+                 else if (EQ (keyword, QCinherit))
+                   parent_face = value;
+                 face_ref_tem = XCDR (XCDR (face_ref_tem));
+               }
+             if (!attr_filter_seen)
+               {
+                 if (NILP (parent_face))
+                   return true;
+
+                 Lisp_Object scratch_attrs[LFACE_VECTOR_SIZE];
+                 int i;
+
+                 scratch_attrs[0] = Qface;
+                 for (i = 1; i < LFACE_VECTOR_SIZE; i++)
+                   scratch_attrs[i] = Qunspecified;
+                 if (!merge_face_ref (w, f, parent_face, scratch_attrs,
+                                      err_msgs, named_merge_points, 0))
+                   {
+                     add_to_log ("Invalid face attribute %S %S",
+                                 QCinherit, parent_face);
+                     return false;
+                   }
+                 if (NILP (scratch_attrs[attr_filter])
+                     || UNSPECIFIEDP (scratch_attrs[attr_filter]))
+                   return true;
+               }
+           }
          while (CONSP (face_ref) && CONSP (XCDR (face_ref)))
            {
              Lisp_Object keyword = XCAR (face_ref);
@@ -6590,6 +6661,25 @@ init_xfaces (void)
          lface_id_to_name[i--] = XCAR (lface);
        }
     }
+  face_attr_sym[0] = Qface;
+  face_attr_sym[LFACE_FOUNDRY_INDEX] = QCfoundry;
+  face_attr_sym[LFACE_SWIDTH_INDEX] = QCwidth;
+  face_attr_sym[LFACE_HEIGHT_INDEX] = QCheight;
+  face_attr_sym[LFACE_WEIGHT_INDEX] = QCweight;
+  face_attr_sym[LFACE_SLANT_INDEX] = QCslant;
+  face_attr_sym[LFACE_UNDERLINE_INDEX] = QCunderline;
+  face_attr_sym[LFACE_INVERSE_INDEX] = QCinverse_video;
+  face_attr_sym[LFACE_FOREGROUND_INDEX] = QCforeground;
+  face_attr_sym[LFACE_BACKGROUND_INDEX] = QCbackground;
+  face_attr_sym[LFACE_STIPPLE_INDEX] = QCstipple;
+  face_attr_sym[LFACE_OVERLINE_INDEX] = QCoverline;
+  face_attr_sym[LFACE_STRIKE_THROUGH_INDEX] = QCstrike_through;
+  face_attr_sym[LFACE_BOX_INDEX] = QCbox;
+  face_attr_sym[LFACE_FONT_INDEX] = QCfont;
+  face_attr_sym[LFACE_INHERIT_INDEX] = QCinherit;
+  face_attr_sym[LFACE_FONTSET_INDEX] = QCfontset;
+  face_attr_sym[LFACE_DISTANT_FOREGROUND_INDEX] = QCdistant_foreground;
+  face_attr_sym[LFACE_EXTEND_INDEX] = QCextend;
 }
 #endif