unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Buffer-local faces patch
@ 2004-05-03 11:28 Miles Bader
  2004-05-03 11:58 ` Kim F. Storm
  2004-05-03 18:43 ` Eli Zaretskii
  0 siblings, 2 replies; 4+ messages in thread
From: Miles Bader @ 2004-05-03 11:28 UTC (permalink / raw)


[-- Attachment #1: Type: text/plain, Size: 640 bytes --]

I tried hacking up my idea for implementing buffer-local faces, here's
a patch.

The user interface is the variable `face-remappings':

   Alist of face mappings.
   Each element is of the form:
      (FACE . NEW-FACE)
   or
      (FACE NEW-FACE MERGE-FACE...),
   which causes NEW-FACE to be used where FACE normally would.
   If present, MERGE-FACE... are merged during display with NEW-FACE.

E.g., try evaluating the following in some buffer:

  (set (make-local-variable 'face-remappings) '((default . bold)))

It works (or seems to!) with other special faces like mode-line, etc., too.

Here's the patch; lemme know what you think:



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: +face-remapping-20040503-0.patch --]
[-- Type: text/x-patch, Size: 15005 bytes --]

* looking for miles@gnu.org--gnu-2004/emacs--miles--0--patch-15 to compare with
* comparing to miles@gnu.org--gnu-2004/emacs--miles--0--patch-15
M  src/xfaces.c
M  src/dispextern.h
M  src/fontset.c
M  src/xdisp.c

* modified files

*** orig/src/dispextern.h
--- mod/src/dispextern.h
***************
*** 2838,2843 ****
--- 2838,2844 ----
  int xstricmp P_ ((const unsigned char *, const unsigned char *));
  int lookup_face P_ ((struct frame *, Lisp_Object *, int, struct face *));
  int lookup_named_face P_ ((struct frame *, Lisp_Object, int));
+ int lookup_basic_face P_ ((struct frame *, int));
  int smaller_face P_ ((struct frame *, int, int));
  int face_with_height P_ ((struct frame *, int, int));
  int lookup_derived_face P_ ((struct frame *, Lisp_Object, int, int));
***************
*** 2854,2859 ****
--- 2855,2862 ----
  extern char unspecified_fg[], unspecified_bg[];
  void free_realized_multibyte_face P_ ((struct frame *, int));
  
+ extern Lisp_Object Vface_remappings;
+ 
  /* Defined in xfns.c  */
  
  #ifdef HAVE_X_WINDOWS


*** orig/src/fontset.c
--- mod/src/fontset.c
***************
*** 1252,1258 ****
        CHECK_NATNUM (ch);
        c = XINT (ch);
        f = XFRAME (selected_frame);
!       face_id = DEFAULT_FACE_ID;
      }
    else
      {
--- 1252,1258 ----
        CHECK_NATNUM (ch);
        c = XINT (ch);
        f = XFRAME (selected_frame);
!       face_id = lookup_basic_face (f, DEFAULT_FACE_ID);
      }
    else
      {


*** orig/src/xdisp.c
--- mod/src/xdisp.c
***************
*** 2028,2033 ****
--- 2028,2034 ----
       enum face_id base_face_id;
  {
    int highlight_region_p;
+   enum face_id remapped_base_face_id = base_face_id;
  
    /* Some precondition checks.  */
    xassert (w != NULL && it != NULL);
***************
*** 2044,2049 ****
--- 2045,2054 ----
        free_all_realized_faces (Qnil);
      }
  
+   /* Perhaps remap BASE_FACE_ID to a user-specified alternative.  */
+   if (! NILP (Vface_remappings))
+     remapped_base_face_id = lookup_basic_face (XFRAME (w->frame), base_face_id);
+ 
    /* Use one of the mode line rows of W's desired matrix if
       appropriate.  */
    if (row == NULL)
***************
*** 2059,2065 ****
    bzero (it, sizeof *it);
    it->current.overlay_string_index = -1;
    it->current.dpvec_index = -1;
!   it->base_face_id = base_face_id;
    it->string = Qnil;
    IT_STRING_CHARPOS (*it) = IT_STRING_BYTEPOS (*it) = -1;
  
--- 2064,2070 ----
    bzero (it, sizeof *it);
    it->current.overlay_string_index = -1;
    it->current.dpvec_index = -1;
!   it->base_face_id = remapped_base_face_id;
    it->string = Qnil;
    IT_STRING_CHARPOS (*it) = IT_STRING_BYTEPOS (*it) = -1;
  
***************
*** 2243,2253 ****
      {
        struct face *face;
  
!       it->face_id = base_face_id;
  
        /* If we have a boxed mode line, make the first character appear
  	 with a left box line.  */
!       face = FACE_FROM_ID (it->f, base_face_id);
        if (face->box != FACE_NO_BOX)
  	it->start_of_box_run_p = 1;
  #ifdef HAVE_WINDOW_SYSTEM
--- 2248,2258 ----
      {
        struct face *face;
  
!       it->face_id = remapped_base_face_id;
  
        /* If we have a boxed mode line, make the first character appear
  	 with a left box line.  */
!       face = FACE_FROM_ID (it->f, remapped_base_face_id);
        if (face->box != FACE_NO_BOX)
  	it->start_of_box_run_p = 1;
  #ifdef HAVE_WINDOW_SYSTEM
***************
*** 3491,3497 ****
  	      /* Value is a multiple of the canonical char height.  */
  	      struct face *face;
  
! 	      face = FACE_FROM_ID (it->f, DEFAULT_FACE_ID);
  	      new_height = (XFLOATINT (it->font_height)
  			    * XINT (face->lface[LFACE_HEIGHT_INDEX]));
  	    }
--- 3496,3503 ----
  	      /* Value is a multiple of the canonical char height.  */
  	      struct face *face;
  
! 	      face = FACE_FROM_ID (it->f,
! 				   lookup_basic_face (it->f, DEFAULT_FACE_ID));
  	      new_height = (XFLOATINT (it->font_height)
  			    * XINT (face->lface[LFACE_HEIGHT_INDEX]));
  	    }
***************
*** 3591,3597 ****
  	      || EQ (XCAR (prop), Qright_fringe))
  	  && CONSP (XCDR (prop)))
  	{
! 	  unsigned face_id = DEFAULT_FACE_ID;
  
  	  /* Save current settings of IT so that we can restore them
  	     when we are finished with the glyph property value.  */
--- 3597,3603 ----
  	      || EQ (XCAR (prop), Qright_fringe))
  	  && CONSP (XCDR (prop)))
  	{
! 	  unsigned face_id = lookup_basic_face (it->f, DEFAULT_FACE_ID);
  
  	  /* Save current settings of IT so that we can restore them
  	     when we are finished with the glyph property value.  */


*** orig/src/xfaces.c
--- mod/src/xfaces.c
***************
*** 400,405 ****
--- 400,412 ----
  
  Lisp_Object Vface_new_frame_defaults;
  
+ /* Alist of face mappings.  Each element is either of the form
+    (FACE . NEW-FACE), or (FACE NEW-FACE MERGE-FACE...),
+    where FACE is the named used for lookups, and NEW-FACE is the name
+    that actually gets looked up.  If present, MERGE-FACE... are merged
+    during display of FACE, with NEW-FACE.  */
+ Lisp_Object Vface_remappings;
+ 
  /* The next ID to assign to Lisp faces.  */
  
  static int next_lface_id;
***************
*** 3196,3213 ****
     for another face, return that face's definition.  If SIGNAL_P is
     non-zero, signal an error if FACE_NAME is not a valid face name.
     If SIGNAL_P is zero, value is nil if FACE_NAME is not a valid face
!    name.  */
  
  static INLINE Lisp_Object
! lface_from_face_name (f, face_name, signal_p)
       struct frame *f;
       Lisp_Object face_name;
       int signal_p;
  {
    Lisp_Object lface;
  
    face_name = resolve_face_name (face_name);
  
    if (f)
      lface = assq_no_quit (face_name, f->face_alist);
    else
--- 3203,3271 ----
     for another face, return that face's definition.  If SIGNAL_P is
     non-zero, signal an error if FACE_NAME is not a valid face name.
     If SIGNAL_P is zero, value is nil if FACE_NAME is not a valid face
!    name.  If ATTRS is non-zero, then it is a lisp-face vector, and
!    successful lookups copy the lface vector into it, _along with any
!    merges from face mappings_ (so in that cases, the returned lisp
!    vector and the values in ATTRS may not be identical).  CYCLE_CHECK
!    is used internally to detect loops in face mapping; it can be Qnil
!    when called from other places that are not involved in mutual
!    recursion with this function.  */
  
  static INLINE Lisp_Object
! lface_from_face_name_with_attrs (f, face_name, attrs, signal_p, cycle_check)
       struct frame *f;
       Lisp_Object face_name;
+      Lisp_Object *attrs;
       int signal_p;
+      Lisp_Object cycle_check;
  {
    Lisp_Object lface;
+   Lisp_Object face_remapping;
  
    face_name = resolve_face_name (face_name);
  
+   /* See if SYMBOL has been remapped to some other face (usually this
+      is done buffer-locally).  */
+   face_remapping = assq_no_quit (face_name, Vface_remappings);
+   if (! NILP (face_remapping))
+     {
+       /* Make sure we're not in an mapping loop.  */
+       cycle_check = CYCLE_CHECK (cycle_check, face_name, 15);
+ 
+       if (! NILP (cycle_check))
+ 	{
+ 	  /* No cycle detected, lookup FACE_NAME's mapping instead.  */
+ 	  Lisp_Object merges;
+ 
+ 	  face_remapping = XCDR (face_remapping);
+ 
+ 	  /* A mapping may also contain a list of `merge faces', which
+ 	     we ignore in this function.  */
+ 	  if (CONSP (face_remapping) && SYMBOLP (XCAR (face_remapping)))
+ 	    {
+ 	      merges = XCDR (face_remapping);
+ 	      face_remapping = XCAR (face_remapping);
+ 	    }
+ 	  else
+ 	    merges = Qnil;
+ 
+ 	  /* Recursively lookup FACE_REMAPPING, if it's not obviously bogus.  */
+ 	  if (SYMBOLP (face_remapping)
+ 	      && !NILP (face_remapping)
+ 	      && !EQ (face_remapping, face_name))
+ 	    {
+ 	      lface = lface_from_face_name_with_attrs (f, face_remapping, attrs,
+ 						       signal_p, cycle_check);
+ 
+ 	      /* Merge in additional faces specified in the mapping.  */
+ 	      if (attrs && !NILP (lface) && !NILP (merges))
+ 		merge_face_inheritance (f, merges, attrs, cycle_check);
+ 
+ 	      return lface;
+ 	    }
+ 	}
+     }
+ 
    if (f)
      lface = assq_no_quit (face_name, f->face_alist);
    else
***************
*** 3219,3227 ****
--- 3277,3306 ----
      signal_error ("Invalid face", face_name);
  
    check_lface (lface);
+ 
+   if (attrs)
+     bcopy (XVECTOR (lface)->contents, attrs, LFACE_VECTOR_SIZE * sizeof *attrs);
+ 
    return lface;
  }
  
+ /* Return the face definition of FACE_NAME on frame F.  F null means
+    return the definition for new frames.  FACE_NAME may be a string or
+    a symbol (apparently Emacs 20.2 allowed strings as face names in
+    face text properties; Ediff uses that).  If FACE_NAME is an alias
+    for another face, return that face's definition.  If SIGNAL_P is
+    non-zero, signal an error if FACE_NAME is not a valid face name.
+    If SIGNAL_P is zero, value is nil if FACE_NAME is not a valid face
+    name.  */
+ static INLINE Lisp_Object
+ lface_from_face_name (f, face_name, signal_p)
+      struct frame *f;
+      Lisp_Object face_name;
+      int signal_p;
+ {
+   return lface_from_face_name_with_attrs (f, face_name, 0, signal_p, Qnil);
+ }
+ 
  
  /* Get face attributes of face FACE_NAME from frame-local faces on
     frame F.  Store the resulting attributes in ATTRS which must point
***************
*** 3237,3255 ****
       int signal_p;
  {
    Lisp_Object lface;
!   int success_p;
! 
!   lface = lface_from_face_name (f, face_name, signal_p);
!   if (!NILP (lface))
!     {
!       bcopy (XVECTOR (lface)->contents, attrs,
! 	     LFACE_VECTOR_SIZE * sizeof *attrs);
!       success_p = 1;
!     }
!   else
!     success_p = 0;
! 
!   return success_p;
  }
  
  
--- 3316,3323 ----
       int signal_p;
  {
    Lisp_Object lface;
!   lface = lface_from_face_name_with_attrs (f, face_name, attrs, signal_p, Qnil);
!   return !NILP (lface);
  }
  
  
***************
*** 5779,5789 ****
     face couldn't be determined, which might happen if the default face
     isn't realized and cannot be realized.  */
  
! int
! lookup_named_face (f, symbol, c)
       struct frame *f;
       Lisp_Object symbol;
       int c;
  {
    Lisp_Object attrs[LFACE_VECTOR_SIZE];
    Lisp_Object symbol_attrs[LFACE_VECTOR_SIZE];
--- 5847,5858 ----
     face couldn't be determined, which might happen if the default face
     isn't realized and cannot be realized.  */
  
! static int
! lookup_named_face_1 (f, symbol, c, signal_p)
       struct frame *f;
       Lisp_Object symbol;
       int c;
+      int signal_p;
  {
    Lisp_Object attrs[LFACE_VECTOR_SIZE];
    Lisp_Object symbol_attrs[LFACE_VECTOR_SIZE];
***************
*** 5796,5807 ****
        default_face = FACE_FROM_ID (f, DEFAULT_FACE_ID);
      }
  
!   get_lface_attributes (f, symbol, symbol_attrs, 1);
    bcopy (default_face->lface, attrs, sizeof attrs);
    merge_face_vectors (f, symbol_attrs, attrs, Qnil);
    return lookup_face (f, attrs, c, NULL);
  }
  
  
  /* Return the ID of the realized ASCII face of Lisp face with ID
     LFACE_ID on frame F.  Value is -1 if LFACE_ID isn't valid.  */
--- 5865,5933 ----
        default_face = FACE_FROM_ID (f, DEFAULT_FACE_ID);
      }
  
!   if (! get_lface_attributes (f, symbol, symbol_attrs, signal_p))
!     return -1;
! 
    bcopy (default_face->lface, attrs, sizeof attrs);
    merge_face_vectors (f, symbol_attrs, attrs, Qnil);
+ 
    return lookup_face (f, attrs, c, NULL);
  }
  
+ /* Return the face id of the realized face for named face SYMBOL on
+    frame F suitable for displaying character C.  Value is -1 if the
+    face couldn't be determined, which might happen if the default face
+    isn't realized and cannot be realized.  */
+ 
+ int
+ lookup_named_face (f, symbol, c)
+      struct frame *f;
+      Lisp_Object symbol;
+      int c;
+ {
+   return lookup_named_face_1 (f, symbol, c);
+ }
+ 
+ 
+ /* Return the display face-id of the basic face who's canonical face-id
+    is FACE_ID.  The return value will usually simply be FACE_ID, unless that
+    basic face has bee remapped via Vface_remappings.  This function is
+    conservative: if something goes wrong, it will simply return FACE_ID
+    rather than signal an error.   */
+ 
+ int
+ lookup_basic_face (f, face_id)
+      struct frame *f;
+      int face_id;
+ {
+   Lisp_Object name, mapping;
+   int remapped_face_id;
+ 
+   if (NILP (Vface_remappings))
+     return face_id;		/* Nothing to do.  */
+ 
+   switch (face_id)
+     {
+     case DEFAULT_FACE_ID:		name = Qdefault;		break;
+     case MODE_LINE_FACE_ID:		name = Qmode_line;		break;
+     case MODE_LINE_INACTIVE_FACE_ID:	name = Qmode_line_inactive;	break;
+     case HEADER_LINE_FACE_ID:		name = Qheader_line;		break;
+ 
+     default:
+       return face_id;		/* Give up.  */
+     }
+ 
+   mapping = assq_no_quit (name, Vface_remappings);
+   if (NILP (mapping))
+     return face_id;		/* Give up.  */
+ 
+   remapped_face_id = lookup_named_face_1 (f, name, 0, 0);
+   if (remapped_face_id < 0)
+     return face_id;		/* Give up. */
+ 
+   return remapped_face_id;
+ }
+ 
  
  /* Return the ID of the realized ASCII face of Lisp face with ID
     LFACE_ID on frame F.  Value is -1 if LFACE_ID isn't valid.  */
***************
*** 7372,7384 ****
  
    *endptr = endpos;
  
!   default_face = FACE_FROM_ID (f, DEFAULT_FACE_ID);
  
    /* Optimize common cases where we can use the default face.  */
    if (noverlays == 0
        && NILP (prop)
        && !(pos >= region_beg && pos < region_end))
!     return DEFAULT_FACE_ID;
  
    /* Begin with attributes from the default face.  */
    bcopy (default_face->lface, attrs, sizeof attrs);
--- 7498,7515 ----
  
    *endptr = endpos;
  
! 
!   /* Perhaps remap BASE_FACE_ID to a user-specified alternative.  */
!   if (NILP (Vface_remappings))
!     default_face = FACE_FROM_ID (f, DEFAULT_FACE_ID);
!   else
!     default_face = FACE_FROM_ID (f, lookup_basic_face (f, DEFAULT_FACE_ID));
  
    /* Optimize common cases where we can use the default face.  */
    if (noverlays == 0
        && NILP (prop)
        && !(pos >= region_beg && pos < region_end))
!     return default_face->id;
  
    /* Begin with attributes from the default face.  */
    bcopy (default_face->lface, attrs, sizeof attrs);
***************
*** 7839,7844 ****
--- 7970,7985 ----
  ignore.  */);
    Vface_ignored_fonts = Qnil;
  
+   DEFVAR_LISP ("face-remappings", &Vface_remappings,
+ 	       doc: /* Alist of face mappings.
+ Each element is of the form:
+    (FACE . NEW-FACE)
+ or
+    (FACE NEW-FACE MERGE-FACE...),
+ which causes NEW-FACE to be used where FACE normally would.
+ If present, MERGE-FACE... are merged during display with NEW-FACE.  */);
+   Vface_remappings = Qnil;
+ 
    DEFVAR_LISP ("face-font-rescale-alist", &Vface_font_rescale_alist,
  	       doc: /* Alist of fonts vs the rescaling factors.
  Each element is a cons (FONT-NAME-PATTERN . RESCALE-RATIO), where




[-- Attachment #3: Type: text/plain, Size: 54 bytes --]




-Miles
-- 
`Life is a boundless sea of bitterness'

[-- Attachment #4: Type: text/plain, Size: 141 bytes --]

_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel

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

* Re: Buffer-local faces patch
  2004-05-03 11:28 Buffer-local faces patch Miles Bader
@ 2004-05-03 11:58 ` Kim F. Storm
  2004-05-03 18:43 ` Eli Zaretskii
  1 sibling, 0 replies; 4+ messages in thread
From: Kim F. Storm @ 2004-05-03 11:58 UTC (permalink / raw)
  Cc: emacs-devel

Miles Bader <miles@gnu.org> writes:

> I tried hacking up my idea for implementing buffer-local faces, here's
> a patch.
> 
> The user interface is the variable `face-remappings':
> 
>    Alist of face mappings.
>    Each element is of the form:
>       (FACE . NEW-FACE)
>    or
>       (FACE NEW-FACE MERGE-FACE...),
>    which causes NEW-FACE to be used where FACE normally would.
>    If present, MERGE-FACE... are merged during display with NEW-FACE.
> 
> E.g., try evaluating the following in some buffer:
> 
>   (set (make-local-variable 'face-remappings) '((default . bold)))
> 
> It works (or seems to!) with other special faces like mode-line, etc., too.
> 
> Here's the patch; lemme know what you think:
> 

Looks like a very useful feature -- and quite non-intrusive as well.
I think we can install it, and see how it works out.  

I'm not sure about the name of the variable.  It seems that there is
a preference to have -alist as part of alist varibles...

So what about face-map-alist or face-remap-alist ?


-- 
Kim F. Storm <storm@cua.dk> http://www.cua.dk

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

* Re: Buffer-local faces patch
  2004-05-03 11:28 Buffer-local faces patch Miles Bader
  2004-05-03 11:58 ` Kim F. Storm
@ 2004-05-03 18:43 ` Eli Zaretskii
  2004-05-03 19:14   ` Miles Bader
  1 sibling, 1 reply; 4+ messages in thread
From: Eli Zaretskii @ 2004-05-03 18:43 UTC (permalink / raw)
  Cc: emacs-devel

> From: Miles Bader <miles@gnu.org>
> Date: 03 May 2004 20:28:18 +0900
> 
> Here's the patch; lemme know what you think:

I didn't yet try to use this, only read through the patch, so the
following question might be annoyingly stupid: I see DEFAULT_FACE_ID
being referenced in lots of the current source files besides the ones
you modified.  Is this patch supposed to work on all types of displays
supported by Emacs, or just on X?

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

* Re: Buffer-local faces patch
  2004-05-03 18:43 ` Eli Zaretskii
@ 2004-05-03 19:14   ` Miles Bader
  0 siblings, 0 replies; 4+ messages in thread
From: Miles Bader @ 2004-05-03 19:14 UTC (permalink / raw)
  Cc: emacs-devel, Miles Bader

On Mon, May 03, 2004 at 08:43:58PM +0200, Eli Zaretskii wrote:
> I didn't yet try to use this, only read through the patch, so the
> following question might be annoyingly stupid: I see DEFAULT_FACE_ID
> being referenced in lots of the current source files besides the ones
> you modified.  Is this patch supposed to work on all types of displays
> supported by Emacs, or just on X?

I don't know.  I didn't pay much attention to non-X stuff, but the changes
outside of xfaces.c are very small and I think they're all in generic code,
so it may just work.

I tried to avoid changing all the uses of DEFAULT_FACE_ID because as you say
there are lots of them.

The cases where DEFAULT_FACE_ID need _not_ be changed are:

  (1) Where the face-id is passed (eventually) to init_iterator, as
      init_iterator will do the work (the face passed to init_iterator is not
      a `real' face-id anyway, it is required to be one of the basic
      face-ids).

  (2) Where the code really wants the underlying default face, not the
      buffer's default face.  An example of this might be where it's going to
      test whether it can just draw the background color -- in this case you
      want to treat a remapped default-face as just another face (i.e., not
      the background).

Those two exceptions cover many, many, uses.

There are a few I'm not sure about, e.g., the fringe munging code, but the
patch tends to fail conservatively, so the only effect might be that
remapping doesn't take in some cases (like the fringe face).

-Miles
-- 
[|nurgle|]  ddt- demonic? so quake will have an evil kinda setting? one that
            will  make every christian in the world foamm at the mouth?
[iddt]      nurg, that's the goal

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

end of thread, other threads:[~2004-05-03 19:14 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-05-03 11:28 Buffer-local faces patch Miles Bader
2004-05-03 11:58 ` Kim F. Storm
2004-05-03 18:43 ` Eli Zaretskii
2004-05-03 19:14   ` Miles Bader

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).