Implement pseudo-value 'reset' of face attrributes

* doc/lispref/display.texi (Face Attributes):
* etc/NEWS: Document the new pseudo-value 'reset'.

* src/xfaces.c (realize_named_face, lookup_derived_face)
(gui_supports_face_attributes_p, lookup_named_face)
(Finternal_merge_in_global_face, merge_named_face, merge_faces):
Handle the 'reset' pseudo-value of a face's attribute.
(syms_of_xfaces): New symbol 'reset'.
(RESET_P): New macro.
(check_lface_attrs, Finternal_set_lisp_face_attribute): Allow
'reset' as a value of any attribute except ':inherit'.
(Bug#38771)
This commit is contained in:
Eli Zaretskii 2022-07-03 12:30:24 +03:00
parent 94d43f4942
commit 45badeceec
3 changed files with 158 additions and 26 deletions

View file

@ -2474,6 +2474,7 @@ Otherwise, it returns @code{nil}.
The following table lists all the face attributes, their possible
values, and their effects.
@cindex unspecified, face attribute value
Apart from the values given below, each face attribute can have the
value @code{unspecified}. This special value means that the face
doesn't specify that attribute directly. An @code{unspecified}
@ -2482,7 +2483,13 @@ description @code{:inherit} attribute below); or, failing that, to an
underlying face (@pxref{Displaying Faces}). (However,
@code{unspecified} is not a valid value in @code{defface}.)
The @code{default} face must specify all attributes.
@cindex reset, face attribute value
A face attribute can also have the value @code{reset}. This special
value stands for the value of the corresponding attribute of the
@code{default} face.
The @code{default} face must explicitly specify all attributes, and
cannot use the special value @code{reset}.
Some of these attributes are meaningful only on certain kinds of
displays. If your display cannot handle a certain attribute, the

View file

@ -515,6 +515,12 @@ mixed.
This inherits from the 'mode-line' face, but is the face actually used
on the mode lines (along with 'mode-line-inactive').
+++
** New face attribute pseudo-value 'reset'.
This value stands for the value of the corresponding attribute of the
'default' face. It can be used to reset attribute values produced by
inheriting from other faces.
+++
** New function 'buffer-text-pixel-size'.
This is similar to 'window-text-pixel-size', but can be used when the

View file

@ -295,6 +295,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#define IGNORE_DEFFACE_P(ATTR) EQ ((ATTR), QCignore_defface)
/* True if face attribute ATTR is `reset'. */
#define RESET_P(ATTR) EQ ((ATTR), Qreset)
/* Size of hash table of realized faces in face caches (should be a
prime number). */
@ -1757,57 +1761,72 @@ check_lface_attrs (Lisp_Object attrs[LFACE_VECTOR_SIZE])
{
eassert (UNSPECIFIEDP (attrs[LFACE_FAMILY_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_FAMILY_INDEX])
|| RESET_P (attrs[LFACE_FAMILY_INDEX])
|| STRINGP (attrs[LFACE_FAMILY_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_FOUNDRY_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_FOUNDRY_INDEX])
|| RESET_P (attrs[LFACE_FOUNDRY_INDEX])
|| STRINGP (attrs[LFACE_FOUNDRY_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_SWIDTH_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_SWIDTH_INDEX])
|| RESET_P (attrs[LFACE_SWIDTH_INDEX])
|| SYMBOLP (attrs[LFACE_SWIDTH_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_HEIGHT_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_HEIGHT_INDEX])
|| RESET_P (attrs[LFACE_HEIGHT_INDEX])
|| NUMBERP (attrs[LFACE_HEIGHT_INDEX])
|| FUNCTIONP (attrs[LFACE_HEIGHT_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_WEIGHT_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_WEIGHT_INDEX])
|| RESET_P (attrs[LFACE_WEIGHT_INDEX])
|| SYMBOLP (attrs[LFACE_WEIGHT_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_SLANT_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_SLANT_INDEX])
|| RESET_P (attrs[LFACE_SLANT_INDEX])
|| SYMBOLP (attrs[LFACE_SLANT_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_UNDERLINE_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_UNDERLINE_INDEX])
|| RESET_P (attrs[LFACE_UNDERLINE_INDEX])
|| SYMBOLP (attrs[LFACE_UNDERLINE_INDEX])
|| STRINGP (attrs[LFACE_UNDERLINE_INDEX])
|| CONSP (attrs[LFACE_UNDERLINE_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_EXTEND_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_EXTEND_INDEX])
|| RESET_P (attrs[LFACE_EXTEND_INDEX])
|| SYMBOLP (attrs[LFACE_EXTEND_INDEX])
|| STRINGP (attrs[LFACE_EXTEND_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_OVERLINE_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_OVERLINE_INDEX])
|| RESET_P (attrs[LFACE_OVERLINE_INDEX])
|| SYMBOLP (attrs[LFACE_OVERLINE_INDEX])
|| STRINGP (attrs[LFACE_OVERLINE_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_STRIKE_THROUGH_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_STRIKE_THROUGH_INDEX])
|| RESET_P (attrs[LFACE_STRIKE_THROUGH_INDEX])
|| SYMBOLP (attrs[LFACE_STRIKE_THROUGH_INDEX])
|| STRINGP (attrs[LFACE_STRIKE_THROUGH_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_BOX_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_BOX_INDEX])
|| RESET_P (attrs[LFACE_BOX_INDEX])
|| SYMBOLP (attrs[LFACE_BOX_INDEX])
|| STRINGP (attrs[LFACE_BOX_INDEX])
|| FIXNUMP (attrs[LFACE_BOX_INDEX])
|| CONSP (attrs[LFACE_BOX_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_INVERSE_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_INVERSE_INDEX])
|| RESET_P (attrs[LFACE_INVERSE_INDEX])
|| SYMBOLP (attrs[LFACE_INVERSE_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_FOREGROUND_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_FOREGROUND_INDEX])
|| RESET_P (attrs[LFACE_FOREGROUND_INDEX])
|| STRINGP (attrs[LFACE_FOREGROUND_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_DISTANT_FOREGROUND_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_DISTANT_FOREGROUND_INDEX])
|| RESET_P (attrs[LFACE_DISTANT_FOREGROUND_INDEX])
|| STRINGP (attrs[LFACE_DISTANT_FOREGROUND_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_BACKGROUND_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_BACKGROUND_INDEX])
|| RESET_P (attrs[LFACE_BACKGROUND_INDEX])
|| STRINGP (attrs[LFACE_BACKGROUND_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_INHERIT_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_INHERIT_INDEX])
@ -1817,13 +1836,16 @@ check_lface_attrs (Lisp_Object attrs[LFACE_VECTOR_SIZE])
#ifdef HAVE_WINDOW_SYSTEM
eassert (UNSPECIFIEDP (attrs[LFACE_STIPPLE_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_STIPPLE_INDEX])
|| RESET_P (attrs[LFACE_STIPPLE_INDEX])
|| SYMBOLP (attrs[LFACE_STIPPLE_INDEX])
|| !NILP (Fbitmap_spec_p (attrs[LFACE_STIPPLE_INDEX])));
eassert (UNSPECIFIEDP (attrs[LFACE_FONT_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_FONT_INDEX])
|| RESET_P (attrs[LFACE_FONT_INDEX])
|| FONTP (attrs[LFACE_FONT_INDEX]));
eassert (UNSPECIFIEDP (attrs[LFACE_FONTSET_INDEX])
|| STRINGP (attrs[LFACE_FONTSET_INDEX])
|| RESET_P (attrs[LFACE_FONTSET_INDEX])
|| NILP (attrs[LFACE_FONTSET_INDEX]));
#endif
}
@ -2083,7 +2105,7 @@ lface_fully_specified_p (Lisp_Object attrs[LFACE_VECTOR_SIZE])
#ifdef HAVE_WINDOW_SYSTEM
/* Set font-related attributes of Lisp face LFACE from FONT-OBJECT.
If FORCE_P, set only unspecified attributes of LFACE. The
If FORCE_P is zero, set only unspecified attributes of LFACE. The
exception is `font' attribute. It is set to FONT_OBJECT regardless
of FORCE_P. */
@ -2339,6 +2361,14 @@ merge_named_face (struct window *w,
Lisp_Object from[LFACE_VECTOR_SIZE], val;
bool ok = get_lface_attributes (w, f, face_name, from, false,
named_merge_points);
if (ok && !EQ (face_name, Qdefault))
{
struct face *deflt = FACE_FROM_ID (f, DEFAULT_FACE_ID);
int i;
for (i = 1; i < LFACE_VECTOR_SIZE; i++)
if (EQ (from[i], Qreset))
from[i] = deflt->lface[i];
}
if (ok && (attr_filter == 0 /* No filter. */
|| (!NILP (from[attr_filter]) /* Filter, but specified. */
@ -3087,7 +3117,9 @@ FRAME 0 means change the face on all frames, and change the default
if (EQ (attr, QCfamily))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
CHECK_STRING (value);
if (SCHARS (value) == 0)
@ -3099,7 +3131,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCfoundry))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
CHECK_STRING (value);
if (SCHARS (value) == 0)
@ -3111,7 +3145,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCheight))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
if (EQ (face, Qdefault))
{
@ -3139,7 +3175,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCweight))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
CHECK_SYMBOL (value);
if (FONT_WEIGHT_NAME_NUMERIC (value) < 0)
@ -3151,7 +3189,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCslant))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
CHECK_SYMBOL (value);
if (FONT_SLANT_NAME_NUMERIC (value) < 0)
@ -3165,7 +3205,7 @@ FRAME 0 means change the face on all frames, and change the default
{
bool valid_p = false;
if (UNSPECIFIEDP (value) || IGNORE_DEFFACE_P (value))
if (UNSPECIFIEDP (value) || IGNORE_DEFFACE_P (value) || RESET_P (value))
valid_p = true;
else if (NILP (value) || EQ (value, Qt))
valid_p = true;
@ -3223,7 +3263,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCoverline))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
if ((SYMBOLP (value)
&& !EQ (value, Qt)
&& !NILP (value))
@ -3237,7 +3279,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCstrike_through))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
if ((SYMBOLP (value)
&& !EQ (value, Qt)
&& !NILP (value))
@ -3258,7 +3302,7 @@ FRAME 0 means change the face on all frames, and change the default
if (EQ (value, Qt))
value = make_fixnum (1);
if (UNSPECIFIEDP (value) || IGNORE_DEFFACE_P (value))
if (UNSPECIFIEDP (value) || IGNORE_DEFFACE_P (value) || RESET_P (value))
valid_p = true;
else if (NILP (value))
valid_p = true;
@ -3320,7 +3364,9 @@ FRAME 0 means change the face on all frames, and change the default
else if (EQ (attr, QCinverse_video)
|| EQ (attr, QCreverse_video))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
CHECK_SYMBOL (value);
if (!EQ (value, Qt) && !NILP (value))
@ -3331,7 +3377,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCextend))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
CHECK_SYMBOL (value);
if (!EQ (value, Qt) && !NILP (value))
@ -3345,7 +3393,9 @@ FRAME 0 means change the face on all frames, and change the default
/* Compatibility with 20.x. */
if (NILP (value))
value = Qunspecified;
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
/* Don't check for valid color names here because it depends
on the frame (display) whether the color will be valid
@ -3362,7 +3412,9 @@ FRAME 0 means change the face on all frames, and change the default
/* Compatibility with 20.x. */
if (NILP (value))
value = Qunspecified;
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
/* Don't check for valid color names here because it depends
on the frame (display) whether the color will be valid
@ -3379,7 +3431,9 @@ FRAME 0 means change the face on all frames, and change the default
/* Compatibility with 20.x. */
if (NILP (value))
value = Qunspecified;
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
/* Don't check for valid color names here because it depends
on the frame (display) whether the color will be valid
@ -3394,7 +3448,9 @@ FRAME 0 means change the face on all frames, and change the default
else if (EQ (attr, QCstipple))
{
#if defined (HAVE_WINDOW_SYSTEM)
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value)
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value)
&& !NILP (value)
&& NILP (Fbitmap_spec_p (value)))
signal_error ("Invalid stipple attribute", value);
@ -3404,7 +3460,9 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCwidth))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
CHECK_SYMBOL (value);
if (FONT_WIDTH_NAME_NUMERIC (value) < 0)
@ -3419,7 +3477,9 @@ FRAME 0 means change the face on all frames, and change the default
#ifdef HAVE_WINDOW_SYSTEM
if (EQ (frame, Qt) || FRAME_WINDOW_P (f))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if (!UNSPECIFIEDP (value)
&& !IGNORE_DEFFACE_P (value)
&& !RESET_P (value))
{
struct frame *f1;
@ -3479,9 +3539,12 @@ FRAME 0 means change the face on all frames, and change the default
Lisp_Object tmp;
old_value = LFACE_FONTSET (lface);
tmp = Fquery_fontset (value, Qnil);
if (NILP (tmp))
signal_error ("Invalid fontset name", value);
if (!RESET_P (value))
{
tmp = Fquery_fontset (value, Qnil);
if (NILP (tmp))
signal_error ("Invalid fontset name", value);
}
ASET (lface, LFACE_FONTSET_INDEX, value = tmp);
}
#endif /* HAVE_WINDOW_SYSTEM */
@ -3503,14 +3566,20 @@ FRAME 0 means change the face on all frames, and change the default
else if (EQ (attr, QCbold))
{
old_value = LFACE_WEIGHT (lface);
ASET (lface, LFACE_WEIGHT_INDEX, NILP (value) ? Qnormal : Qbold);
if (RESET_P (value))
ASET (lface, LFACE_WEIGHT_INDEX, value);
else
ASET (lface, LFACE_WEIGHT_INDEX, NILP (value) ? Qnormal : Qbold);
prop_index = FONT_WEIGHT_INDEX;
}
else if (EQ (attr, QCitalic))
{
attr = QCslant;
old_value = LFACE_SLANT (lface);
ASET (lface, LFACE_SLANT_INDEX, NILP (value) ? Qnormal : Qitalic);
if (RESET_P (value))
ASET (lface, LFACE_SLANT_INDEX, value);
else
ASET (lface, LFACE_SLANT_INDEX, NILP (value) ? Qnormal : Qitalic);
prop_index = FONT_SLANT_INDEX;
}
else
@ -4120,6 +4189,13 @@ 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);
/* Make explicit any attributes whose value is 'reset'. */
int i;
for (i = 1; i < LFACE_VECTOR_SIZE; i++)
if (EQ (lvec[i], Qreset))
lvec[i] = attrs[i];
merge_face_vectors (NULL, f, lvec, attrs, 0);
vcopy (local_lface, 0, attrs, LFACE_VECTOR_SIZE);
newface = realize_face (c, lvec, DEFAULT_FACE_ID);
@ -4886,6 +4962,13 @@ lookup_named_face (struct window *w, struct frame *f,
return -1;
memcpy (attrs, default_face->lface, sizeof attrs);
/* Make explicit any attributes whose value is 'reset'. */
int i;
for (i = 1; i < LFACE_VECTOR_SIZE; i++)
if (EQ (symbol_attrs[i], Qreset))
symbol_attrs[i] = attrs[i];
merge_face_vectors (w, f, symbol_attrs, attrs, 0);
return lookup_face (f, attrs);
@ -5056,6 +5139,13 @@ lookup_derived_face (struct window *w,
default_face = FACE_FROM_ID (f, face_id);
memcpy (attrs, default_face->lface, sizeof attrs);
/* Make explicit any attributes whose value is 'reset'. */
int i;
for (i = 1; i < LFACE_VECTOR_SIZE; i++)
if (EQ (symbol_attrs[i], Qreset))
symbol_attrs[i] = attrs[i];
merge_face_vectors (w, f, symbol_attrs, attrs, 0);
return lookup_face (f, attrs);
}
@ -5104,6 +5194,12 @@ gui_supports_face_attributes_p (struct frame *f,
{
Lisp_Object *def_attrs = def_face->lface;
/* Make explicit any attributes whose value is 'reset'. */
int i;
for (i = 1; i < LFACE_VECTOR_SIZE; i++)
if (EQ (attrs[i], Qreset))
attrs[i] = def_attrs[i];
/* Check that other specified attributes are different from the
default face. */
if ((!UNSPECIFIEDP (attrs[LFACE_UNDERLINE_INDEX])
@ -5811,8 +5907,16 @@ realize_named_face (struct frame *f, Lisp_Object symbol, int id)
lface = Finternal_make_lisp_face (symbol, frame);
}
/* Merge SYMBOL's face with the default face. */
get_lface_attributes_no_remap (f, symbol, symbol_attrs, true);
/* Handle the 'reset' pseudo-value of any attribute by replacing it
with the corresponding value of the default face. */
int i;
for (i = 1; i < LFACE_VECTOR_SIZE; i++)
if (EQ (symbol_attrs[i], Qreset))
symbol_attrs[i] = attrs[i];
/* Merge SYMBOL's face with the default face. */
merge_face_vectors (NULL, f, symbol_attrs, attrs, 0);
/* Realize the face. */
@ -6750,7 +6854,21 @@ merge_faces (struct window *w, Lisp_Object face_name, int face_id,
if (!face)
return base_face_id;
merge_face_vectors (w, f, face->lface, attrs, 0);
if (face_id != DEFAULT_FACE_ID)
{
struct face *deflt = FACE_FROM_ID (f, DEFAULT_FACE_ID);
Lisp_Object lface_attrs[LFACE_VECTOR_SIZE];
int i;
memcpy (lface_attrs, face->lface, LFACE_VECTOR_SIZE);
/* Make explicit any attributes whose value is 'reset'. */
for (i = 1; i < LFACE_VECTOR_SIZE; i++)
if (EQ (lface_attrs[i], Qreset))
lface_attrs[i] = deflt->lface[i];
merge_face_vectors (w, f, lface_attrs, attrs, 0);
}
else
merge_face_vectors (w, f, face->lface, attrs, 0);
}
/* Look up a realized face with the given face attributes,
@ -7019,6 +7137,7 @@ syms_of_xfaces (void)
DEFSYM (Qblack, "black");
DEFSYM (Qoblique, "oblique");
DEFSYM (Qitalic, "italic");
DEFSYM (Qreset, "reset");
/* The symbols `foreground-color' and `background-color' which can be
used as part of a `face' property. This is for compatibility with