emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] scratch/allow-custom-null-and-false-objects-in-jsonc 16a7b


From: João Távora
Subject: [Emacs-diffs] scratch/allow-custom-null-and-false-objects-in-jsonc 16a7bce 2/2: Merge branch 'master' into this scratch branch
Date: Sat, 9 Jun 2018 10:49:35 -0400 (EDT)

branch: scratch/allow-custom-null-and-false-objects-in-jsonc
commit 16a7bce700f4b90bd6b2d7ab4bb860a3c29cb764
Merge: 4aed7ee 38111b5
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>

    Merge branch 'master' into this scratch branch
    
    scratch/allow-custom-null-and-false-objects-in-jsonc
---
 doc/misc/flymake.texi     |   8 ++
 etc/NEWS                  |   8 ++
 lisp/files.el             |  55 +++++++--
 lisp/isearch.el           |   1 +
 lisp/progmodes/cc-mode.el | 214 ++++++++++++++++++++++----------
 lisp/progmodes/flymake.el |  10 +-
 src/.gdbinit              |   3 -
 src/buffer.c              |   2 +-
 src/bytecode.c            |   3 +-
 src/dispextern.h          |   9 +-
 src/dispnew.c             |   3 +-
 src/editfns.c             |  45 +++----
 src/eval.c                |   6 +
 src/font.c                |   4 +-
 src/fringe.c              |  22 +---
 src/ftfont.c              |   7 +-
 src/lisp.h                |  46 ++++---
 src/lread.c               |   6 +-
 src/msdos.c               |   8 +-
 src/term.c                |   8 +-
 src/window.c              |   2 +-
 src/xdisp.c               |  51 ++++----
 src/xfaces.c              | 303 ++++++++++++++++++++++++++++++++++++----------
 src/xwidget.c             |  20 +--
 24 files changed, 575 insertions(+), 269 deletions(-)

diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi
index e7f4da7..502d408 100644
--- a/doc/misc/flymake.texi
+++ b/doc/misc/flymake.texi
@@ -84,6 +84,10 @@ Syntax check is done ``on-the-fly''.  It is started whenever
 @code{flymake-start-on-flymake-mode} is nil;
 
 @item
+the buffer is saved, unless @code{flymake-start-on-save-buffer} is
+nil;
+
address@hidden
 a newline character is added to the buffer, unless
 @code{flymake-start-syntax-check-on-newline} is nil;
 
@@ -220,6 +224,10 @@ after a newline character is inserted into the buffer.
 A boolean flag indicating whether to start syntax check immediately
 after enabling @code{flymake-mode}.
 
address@hidden flymake-start-on-save-buffer
+A boolean flag indicating whether to start syntax check after saving
+the buffer.
+
 @item flymake-error
 A custom face for highlighting regions for which an error has been
 reported.
diff --git a/etc/NEWS b/etc/NEWS
index d6b7485..46a57b1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -183,6 +183,9 @@ shown in the currently selected window.
 You should instead set properties on known diagnostic symbols, like
 ':error' and ':warning', as demonstrated in the Flymake manual.
 
+*** New customizable variable 'flymake-start-on-save-buffer'
+Control whether Flymake starts checking the buffer on save.
+
 ** Package
 *** New 'package-quickstart' feature
 When 'package-quickstart' is non-nil, package.el precomputes a big autoloads
@@ -573,6 +576,11 @@ manual for more details.
 * Lisp Changes in Emacs 27.1
 
 +++
+** Face specifications (of the kind used in `face-remapping-alist')
+   now support filters, allowing faces to vary between windows display
+   the same buffer.
+
++++
 ** New function assoc-delete-all.
 
 +++
diff --git a/lisp/files.el b/lisp/files.el
index dbe95bb..3921040 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2014,17 +2014,47 @@ think it does, because \"free\" is pretty hard to 
define in practice."
   :version "25.1"
   :type '(choice integer (const :tag "Never issue warning" nil)))
 
-(defun abort-if-file-too-large (size op-type filename)
+(declare-function x-popup-dialog "menu.c" (position contents &optional header))
+
+(defun files--ask-user-about-large-file (size op-type filename offer-raw)
+  (let ((prompt (format "File %s is large (%s), really %s?"
+                       (file-name-nondirectory filename)
+                       (file-size-human-readable size) op-type)))
+    (if (not offer-raw)
+        (if (y-or-n-p prompt) nil 'abort)
+      (let* ((use-dialog (and (display-popup-menus-p)
+                              last-input-event
+                             (listp last-nonmenu-event)
+                             use-dialog-box))
+             (choice
+              (if use-dialog
+                  (x-popup-dialog t `(,prompt
+                                      ("Yes" . ?y)
+                                      ("No" . ?n)
+                                      ("Open in raw mode" . ?r)))
+                (read-char-choice
+                 (concat prompt " (y)es or (n)o or (r)aw ")
+                 '(?y ?Y ?n ?N ?r ?R)))))
+        (cond ((memq choice '(?y ?Y)) nil)
+              ((memq choice '(?r ?R)) 'raw)
+              (t 'abort))))))
+
+(defun abort-if-file-too-large (size op-type filename &optional offer-raw)
   "If file SIZE larger than `large-file-warning-threshold', allow user to 
abort.
-OP-TYPE specifies the file operation being performed (for message to user)."
-  (when (and large-file-warning-threshold size
-            (> size large-file-warning-threshold)
-             ;; No point in warning if we can't read it.
-             (file-readable-p filename)
-            (not (y-or-n-p (format "File %s is large (%s), really %s? "
-                                   (file-name-nondirectory filename)
-                                   (file-size-human-readable size) op-type))))
-    (user-error "Aborted")))
+OP-TYPE specifies the file operation being performed (for message
+to user).  If OFFER-RAW is true, give user the additional option
+to open the file in raw mode. If the user chooses this option,
+`abort-if-file-too-large' returns the symbol `raw'. Otherwise, it
+returns nil or exits non-locally."
+  (let ((choice (and large-file-warning-threshold size
+                    (> size large-file-warning-threshold)
+                     ;; No point in warning if we can't read it.
+                     (file-readable-p filename)
+                     (files--ask-user-about-large-file
+                      size op-type filename offer-raw))))
+    (when (eq choice 'abort)
+      (user-error "Aborted"))
+    choice))
 
 (defun warn-maybe-out-of-memory (size)
   "Warn if an attempt to open file of SIZE bytes may run out of memory."
@@ -2104,7 +2134,10 @@ the various files."
                  (setq buf other))))
        ;; Check to see if the file looks uncommonly large.
        (when (not (or buf nowarn))
-         (abort-if-file-too-large (nth 7 attributes) "open" filename)
+          (when (eq (abort-if-file-too-large
+                     (nth 7 attributes) "open" filename t)
+                    'raw)
+            (setf rawfile t))
          (warn-maybe-out-of-memory (nth 7 attributes)))
        (if buf
            ;; We are using an existing buffer.
diff --git a/lisp/isearch.el b/lisp/isearch.el
index feadf10..1e785a4 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -2036,6 +2036,7 @@ If search string is empty, just beep."
 (defun isearch-yank-kill ()
   "Pull string from kill ring into search string."
   (interactive)
+  (unless isearch-mode (isearch-mode t))
   (isearch-yank-string (current-kill 0)))
 
 (defun isearch-yank-pop ()
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index a1411ad..e619fac 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -1110,13 +1110,56 @@ Note that the style variables are always made local to 
the buffer."
     (goto-char start)
     (while (progn
             (parse-partial-sexp (point) end nil nil st-s 'syntax-table)
-            (c-clear-char-property (1- (point)) 'syntax-table)
+            (unless (bobp)
+              (c-clear-char-property (1- (point)) 'syntax-table))
             (setq st-pos (point))
             (and (< (point) end)
                  (not (eq (char-before) ?\")))))
     (goto-char (min no-st-pos st-pos))
     nil))
 
+(defun c-multiline-string-check-final-quote ()
+  ;; Check that the final quote in the buffer is correctly marked or not with
+  ;; a string-fence syntax-table text propery.  The return value has no
+  ;; significance.
+  (let (pos-ll pos-lt)
+    (save-excursion
+      (goto-char (point-max))
+      (skip-chars-backward "^\"")
+      (while
+         (and
+          (not (bobp))
+          (cond
+           ((progn
+              (setq pos-ll (c-literal-limits)
+                    pos-lt (c-literal-type pos-ll))
+              (memq pos-lt '(c c++)))
+            ;; In a comment.
+            (goto-char (car pos-ll)))
+           ((save-excursion
+              (backward-char)  ; over "
+              (eq (logand (skip-chars-backward "\\\\") 1) 1))
+            ;; At an escaped string.
+            (backward-char)
+            t)
+           (t
+            ;; At a significant "
+            (c-clear-char-property (1- (point)) 'syntax-table)
+            (setq pos-ll (c-literal-limits)
+                  pos-lt (c-literal-type pos-ll))
+            nil)))
+       (skip-chars-backward "^\""))
+      (cond
+       ((bobp))
+       ((eq pos-lt 'string)
+       (c-put-char-property (1- (point)) 'syntax-table '(15)))
+       (t nil)))))
+
+(defvar c-bc-changed-stringiness nil)
+;; Non-nil when, in a before-change function, the deletion of a range of text
+;; will change the "stringiness" of the subsequent text.  Only used when
+;; `c-multiline-sting-start-char' is a non-nil value which isn't a character.
+
 (defun c-before-change-check-unbalanced-strings (beg end)
   ;; If BEG or END is inside an unbalanced string, remove the syntax-table
   ;; text property from respectively the start or end of the string.  Also
@@ -1175,6 +1218,18 @@ Note that the style variables are always made local to 
the buffer."
               (< (point) (point-max))))))
       (setq c-new-END (max (point) c-new-END)))
 
+     (c-multiline-string-start-char
+      (setq c-bc-changed-stringiness
+           (not (eq (eq end-literal-type 'string)
+                    (eq beg-literal-type 'string))))
+      ;; Deal with deletion of backslashes before "s.
+      (goto-char end)
+      (if (and (looking-at "\\\\*\"")
+              (eq (logand (skip-chars-backward "\\\\" beg) 1) 1))
+         (setq c-bc-changed-stringiness (not c-bc-changed-stringiness)))
+      (if (eq beg-literal-type 'string)
+         (setq c-new-BEG (min (car beg-limits) c-new-BEG))))
+
      ((< c-new-END (point-max))
       (goto-char (1+ c-new-END))       ; might be a newline.
       ;; In the following regexp, the initial \n caters for a newline getting
@@ -1183,7 +1238,6 @@ Note that the style variables are always made local to 
the buffer."
                         nil t)
       ;; We're at an EOLL or point-max.
       (setq c-new-END (min (1+ (point)) (point-max)))
-      ;; FIXME!!!  Write a clever comment here.
       (goto-char c-new-END)
       (if (equal (c-get-char-property (1- (point)) 'syntax-table) '(15))
          (if (memq (char-before) '(?\n ?\r))
@@ -1202,14 +1256,16 @@ Note that the style variables are always made local to 
the buffer."
        (if (c-search-backward-char-property 'syntax-table '(15) c-new-BEG)
            (c-clear-char-property (point) 'syntax-table))))
 
-    (when (eq end-literal-type 'string)
-      (c-clear-char-property (1- (cdr end-limits)) 'syntax-table))
+    (unless (and c-multiline-string-start-char
+                (not (c-characterp c-multiline-string-start-char)))
+      (when (eq end-literal-type 'string)
+       (c-clear-char-property (1- (cdr end-limits)) 'syntax-table))
 
-    (when (eq beg-literal-type 'string)
-      (setq c-new-BEG (min c-new-BEG (car beg-limits)))
-      (c-clear-char-property (car beg-limits) 'syntax-table))))
+      (when (eq beg-literal-type 'string)
+       (setq c-new-BEG (min c-new-BEG (car beg-limits)))
+       (c-clear-char-property (car beg-limits) 'syntax-table)))))
 
-(defun c-after-change-re-mark-unbalanced-strings (beg _end _old-len)
+(defun c-after-change-re-mark-unbalanced-strings (beg end _old-len)
   ;; Mark any unbalanced strings in the region (c-new-BEG c-new-END) with
   ;; string fence syntax-table text properties.
   ;;
@@ -1218,66 +1274,90 @@ Note that the style variables are always made local to 
the buffer."
   ;;
   ;; This function is called exclusively as an after-change function via
   ;; `c-before-font-lock-functions'.
-  (c-save-buffer-state
-      ((cll (progn (goto-char c-new-BEG)
-                  (c-literal-limits)))
-       (beg-literal-type (and cll (c-literal-type cll)))
-       (beg-limits
-       (cond
-        ((and (eq beg-literal-type 'string)
-              (c-unescaped-nls-in-string-p (car cll)))
-         (cons
-          (car cll)
+  (if (and c-multiline-string-start-char
+          (not (c-characterp c-multiline-string-start-char)))
+      ;; Only the last " might need to be marked.
+      (c-save-buffer-state
+         ((beg-literal-limits
+           (progn (goto-char beg) (c-literal-limits)))
+          (beg-literal-type (c-literal-type beg-literal-limits))
+          end-literal-limits end-literal-type)
+       (when (and (eq beg-literal-type 'string)
+                  (c-get-char-property (car beg-literal-limits) 'syntax-table))
+         (c-clear-char-property (car beg-literal-limits) 'syntax-table)
+         (setq c-bc-changed-stringiness (not c-bc-changed-stringiness)))
+       (setq end-literal-limits (progn (goto-char end) (c-literal-limits))
+             end-literal-type (c-literal-type end-literal-limits))
+       ;; Deal with the insertion of backslashes before a ".
+       (goto-char end)
+       (if (and (looking-at "\\\\*\"")
+                (eq (logand (skip-chars-backward "\\\\" beg) 1) 1))
+           (setq c-bc-changed-stringiness (not c-bc-changed-stringiness)))
+       (when (eq (eq (eq beg-literal-type 'string)
+                     (eq end-literal-type 'string))
+                 c-bc-changed-stringiness)
+         (c-multiline-string-check-final-quote)))
+    ;; There could be several "s needing marking.
+    (c-save-buffer-state
+       ((cll (progn (goto-char c-new-BEG)
+                    (c-literal-limits)))
+        (beg-literal-type (and cll (c-literal-type cll)))
+        (beg-limits
+         (cond
+          ((and (eq beg-literal-type 'string)
+                (c-unescaped-nls-in-string-p (car cll)))
+           (cons
+            (car cll)
+            (progn
+              (goto-char (1+ (car cll)))
+              (search-forward-regexp
+               (cdr (assq (char-after (car cll)) c-string-innards-re-alist))
+               nil t)
+              (min (1+ (point)) (point-max)))))
+          ((and (null beg-literal-type)
+                (goto-char beg)
+                (eq (char-before) c-multiline-string-start-char)
+                (memq (char-after) c-string-delims))
+           (cons (point)
+                 (progn
+                   (forward-char)
+                   (search-forward-regexp
+                    (cdr (assq (char-before) c-string-innards-re-alist)) nil t)
+                   (1+ (point)))))
+          (cll)))
+        s)
+      (goto-char
+       (cond ((null beg-literal-type)
+             c-new-BEG)
+            ((eq beg-literal-type 'string)
+             (car beg-limits))
+            (t                         ; comment
+             (cdr beg-limits))))
+      (while
+         (and
+          (< (point) c-new-END)
           (progn
-            (goto-char (1+ (car cll)))
-            (search-forward-regexp
-             (cdr (assq (char-after (car cll)) c-string-innards-re-alist))
-             nil t)
-            (min (1+ (point)) (point-max)))))
-        ((and (null beg-literal-type)
-              (goto-char beg)
-              (eq (char-before) c-multiline-string-start-char)
-              (memq (char-after) c-string-delims))
-         (cons (point)
-               (progn
-                 (forward-char)
-                 (search-forward-regexp
-                  (cdr (assq (char-before) c-string-innards-re-alist)) nil t)
-                 (1+ (point)))))
-        (cll)))
-       s)
-    (goto-char
-     (cond ((null beg-literal-type)
-           c-new-BEG)
-          ((eq beg-literal-type 'string)
-           (car beg-limits))
-          (t                           ; comment
-           (cdr beg-limits))))
-    (while
-       (and
-        (< (point) c-new-END)
-        (progn
-          ;; Skip over any comments before the next string.
-          (while (progn
-                   (setq s (parse-partial-sexp (point) c-new-END nil
-                                               nil s 'syntax-table))
-                   (and (not (nth 3 s))
-                        (< (point) c-new-END)
-                        (not (memq (char-before) c-string-delims)))))
-          ;; We're at the start of a string.
-          (memq (char-before) c-string-delims)))
-      (if (c-unescaped-nls-in-string-p (1- (point)))
-         (looking-at "[^\"]*")
-       (looking-at (cdr (assq (char-before) c-string-innards-re-alist))))
-      (cond
-       ((memq (char-after (match-end 0)) '(?\n ?\r))
-       (c-put-char-property (1- (point)) 'syntax-table '(15))
-       (c-put-char-property (match-end 0) 'syntax-table '(15)))
-       ((or (eq (match-end 0) (point-max))
-           (eq (char-after (match-end 0)) ?\\)) ; \ at EOB
-       (c-put-char-property (1- (point)) 'syntax-table '(15))))
-      (goto-char (min (1+ (match-end 0)) (point-max)))
-      (setq s nil))))
+            ;; Skip over any comments before the next string.
+            (while (progn
+                     (setq s (parse-partial-sexp (point) c-new-END nil
+                                                 nil s 'syntax-table))
+                     (and (not (nth 3 s))
+                          (< (point) c-new-END)
+                          (not (memq (char-before) c-string-delims)))))
+            ;; We're at the start of a string.
+            (memq (char-before) c-string-delims)))
+       (if (c-unescaped-nls-in-string-p (1- (point)))
+           (looking-at "\\(\\\\\\(.\\|\n|\\\r\\)\\|[^\"]\\)*")
+         (looking-at (cdr (assq (char-before) c-string-innards-re-alist))))
+       (cond
+        ((memq (char-after (match-end 0)) '(?\n ?\r))
+         (c-put-char-property (1- (point)) 'syntax-table '(15))
+         (c-put-char-property (match-end 0) 'syntax-table '(15)))
+        ((or (eq (match-end 0) (point-max))
+             (eq (char-after (match-end 0)) ?\\)) ; \ at EOB
+         (c-put-char-property (1- (point)) 'syntax-table '(15))))
+       (goto-char (min (1+ (match-end 0)) (point-max)))
+       (setq s nil)))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Parsing of quotes.
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index d8959c8..fdb22cc 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -196,11 +196,17 @@ If nil, never start checking buffer automatically like 
this."
   'flymake-start-on-flymake-mode "26.1")
 
 (defcustom flymake-start-on-flymake-mode t
-  "Start syntax check when `flymake-mode' is enabled.
+  "If non-nil, start syntax check when `flymake-mode' is enabled.
 Specifically, start it when the buffer is actually displayed."
   :version "26.1"
   :type 'boolean)
 
+(defcustom flymake-start-on-save-buffer t
+  "If non-nil start syntax check when a buffer is saved.
+Specifically, start it when the saved buffer is actually displayed."
+  :version "27.1"
+  :type 'boolean)
+
 (defcustom flymake-log-level -1
   "Obsolete and ignored variable."
   :type 'integer)
@@ -962,7 +968,7 @@ Do it only if `flymake-no-changes-timeout' is non-nil."
     (flymake--schedule-timer-maybe)))
 
 (defun flymake-after-save-hook ()
-  (when flymake-mode
+  (when flymake-start-on-save-buffer
     (flymake-log :debug "starting syntax check as buffer was saved")
     (flymake-start t)))
 
diff --git a/src/.gdbinit b/src/.gdbinit
index 7a0cf02..67dcf71 100644
--- a/src/.gdbinit
+++ b/src/.gdbinit
@@ -1020,9 +1020,6 @@ define xpr
     if $misc == Lisp_Misc_Overlay
       xoverlay
     end
-#    if $misc == Lisp_Misc_Save_Value
-#      xsavevalue
-#    end
   end
   if $type == Lisp_Vectorlike
     set $size = ((struct Lisp_Vector *) $ptr)->header.size
diff --git a/src/buffer.c b/src/buffer.c
index 1483737..244c185 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1696,7 +1696,7 @@ cleaning up all windows currently displaying the buffer 
to be killed. */)
   {
     ptrdiff_t count = SPECPDL_INDEX ();
 
-    record_unwind_protect (save_excursion_restore, save_excursion_save ());
+    record_unwind_protect_excursion ();
     set_buffer_internal (b);
 
     /* First run the query functions; if any query is answered no,
diff --git a/src/bytecode.c b/src/bytecode.c
index 55b193f..772cc98 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -739,8 +739,7 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, 
Lisp_Object maxdepth,
          NEXT;
 
        CASE (Bsave_excursion):
-         record_unwind_protect (save_excursion_restore,
-                                save_excursion_save ());
+         record_unwind_protect_excursion ();
          NEXT;
 
        CASE (Bsave_current_buffer): /* Obsolete since ??.  */
diff --git a/src/dispextern.h b/src/dispextern.h
index bc2a76f..2180c9a 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 b854d17..46e0c83 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/editfns.c b/src/editfns.c
index 608304c..e672c0e 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -1016,37 +1016,30 @@ save_excursion_save (void)
 void
 save_excursion_restore (Lisp_Object info)
 {
-  Lisp_Object tem, tem1;
-
-  tem = Fmarker_buffer (XSAVE_OBJECT (info, 0));
+  Lisp_Object marker = XSAVE_OBJECT (info, 0);
+  Lisp_Object window = XSAVE_OBJECT (info, 2);
+  free_misc (info);
+  Lisp_Object buffer = Fmarker_buffer (marker);
   /* If we're unwinding to top level, saved buffer may be deleted.  This
-     means that all of its markers are unchained and so tem is nil.  */
-  if (NILP (tem))
-    goto out;
+     means that all of its markers are unchained and so BUFFER is nil.  */
+  if (NILP (buffer))
+    return;
 
-  Fset_buffer (tem);
+  Fset_buffer (buffer);
 
-  /* Point marker.  */
-  tem = XSAVE_OBJECT (info, 0);
-  Fgoto_char (tem);
-  unchain_marker (XMARKER (tem));
+  Fgoto_char (marker);
+  unchain_marker (XMARKER (marker));
 
   /* If buffer was visible in a window, and a different window was
      selected, and the old selected window is still showing this
      buffer, restore point in that window.  */
-  tem = XSAVE_OBJECT (info, 2);
-  if (WINDOWP (tem)
-      && !EQ (tem, selected_window)
-      && (tem1 = XWINDOW (tem)->contents,
-         (/* Window is live...  */
-          BUFFERP (tem1)
-          /* ...and it shows the current buffer.  */
-          && XBUFFER (tem1) == current_buffer)))
-    Fset_window_point (tem, make_number (PT));
-
- out:
-
-  free_misc (info);
+  if (WINDOWP (window) && !EQ (window, selected_window))
+    {
+      /* Set window point if WINDOW is live and shows the current buffer.  */
+      Lisp_Object contents = XWINDOW (window)->contents;
+      if (BUFFERP (contents) && XBUFFER (contents) == current_buffer)
+       Fset_window_point (window, make_number (PT));
+    }
 }
 
 DEFUN ("save-excursion", Fsave_excursion, Ssave_excursion, 0, UNEVALLED, 0,
@@ -1068,7 +1061,7 @@ usage: (save-excursion &rest BODY)  */)
   register Lisp_Object val;
   ptrdiff_t count = SPECPDL_INDEX ();
 
-  record_unwind_protect (save_excursion_restore, save_excursion_save ());
+  record_unwind_protect_excursion ();
 
   val = Fprogn (args);
   return unbind_to (count, val);
@@ -3242,7 +3235,7 @@ buffer stay intact.  */)
 
   Fundo_boundary ();
   ptrdiff_t count = SPECPDL_INDEX ();
-  record_unwind_protect (save_excursion_restore, save_excursion_save ());
+  record_unwind_protect_excursion ();
 
   ptrdiff_t i = size_a;
   ptrdiff_t j = size_b;
diff --git a/src/eval.c b/src/eval.c
index 90d8c33..86011a2 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -3415,6 +3415,12 @@ record_unwind_protect_int (void (*function) (int), int 
arg)
 }
 
 void
+record_unwind_protect_excursion (void)
+{
+  record_unwind_protect (save_excursion_restore, save_excursion_save ());
+}
+
+void
 record_unwind_protect_void (void (*function) (void))
 {
   specpdl_ptr->unwind_void.kind = SPECPDL_UNWIND_VOID;
diff --git a/src/font.c b/src/font.c
index 305bb14..c886c29 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 85aa14d..6069184 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/ftfont.c b/src/ftfont.c
index 9a8777e..a534670 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -1131,16 +1131,19 @@ ftfont_open2 (struct frame *f,
          return Qnil;
        }
     }
-  set_save_integer (val, 1, XSAVE_INTEGER (val, 1) + 1);
   size = XINT (AREF (entity, FONT_SIZE_INDEX));
   if (size == 0)
     size = pixel_size;
   if (FT_Set_Pixel_Sizes (ft_face, size, size) != 0)
     {
       if (XSAVE_INTEGER (val, 1) == 0)
-       FT_Done_Face (ft_face);
+       {
+         FT_Done_Face (ft_face);
+         cache_data->ft_face = NULL;
+       }
       return Qnil;
     }
+  set_save_integer (val, 1, XSAVE_INTEGER (val, 1) + 1);
 
   ASET (font_object, FONT_FILE_INDEX, filename);
   font = XFONT_OBJECT (font_object);
diff --git a/src/lisp.h b/src/lisp.h
index ee2e72d..293cf27 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -228,8 +228,8 @@ extern bool suppress_checking EXTERNALLY_VISIBLE;
 
    USE_LSB_TAG not only requires the least 3 bits of pointers returned by
    malloc to be 0 but also needs to be able to impose a mult-of-8 alignment
-   on the few static Lisp_Objects used, all of which are aligned via
-   'char alignas (GCALIGNMENT) gcaligned;' inside a union.  */
+   on some non-GC Lisp_Objects, all of which are aligned via
+   GCALIGNED_UNION at the end of a union.  */
 
 enum Lisp_Bits
   {
@@ -277,6 +277,12 @@ DEFINE_GDB_SYMBOL_END (VALMASK)
 error !;
 #endif
 
+#if USE_LSB_TAG
+# define GCALIGNED_UNION char alignas (GCALIGNMENT) gcaligned;
+#else
+# define GCALIGNED_UNION
+#endif
+
 /* Lisp_Word is a scalar word suitable for holding a tagged pointer or
    integer.  Usually it is a pointer to a deliberately-incomplete type
    'union Lisp_X'.  However, it is EMACS_INT when Lisp_Objects and
@@ -776,10 +782,10 @@ struct Lisp_Symbol
       /* Next symbol in obarray bucket, if the symbol is interned.  */
       struct Lisp_Symbol *next;
     } s;
-    char alignas (GCALIGNMENT) gcaligned;
+    GCALIGNED_UNION
   } u;
 };
-verify (alignof (struct Lisp_Symbol) % GCALIGNMENT == 0);
+verify (!USE_LSB_TAG || alignof (struct Lisp_Symbol) % GCALIGNMENT == 0);
 
 /* Declare a Lisp-callable function.  The MAXARGS parameter has the same
    meaning as in the DEFUN macro, and is used to construct a prototype.  */
@@ -890,9 +896,9 @@ union vectorlike_header
         Current layout limits the pseudovectors to 63 PVEC_xxx subtypes,
         4095 Lisp_Objects in GC-ed area and 4095 word-sized other slots.  */
     ptrdiff_t size;
-    char alignas (GCALIGNMENT) gcaligned;
+    GCALIGNED_UNION
   };
-verify (alignof (union vectorlike_header) % GCALIGNMENT == 0);
+verify (!USE_LSB_TAG || alignof (union vectorlike_header) % GCALIGNMENT == 0);
 
 INLINE bool
 (SYMBOLP) (Lisp_Object x)
@@ -1250,10 +1256,10 @@ struct Lisp_Cons
        struct Lisp_Cons *chain;
       } u;
     } s;
-    char alignas (GCALIGNMENT) gcaligned;
+    GCALIGNED_UNION
   } u;
 };
-verify (alignof (struct Lisp_Cons) % GCALIGNMENT == 0);
+verify (!USE_LSB_TAG || alignof (struct Lisp_Cons) % GCALIGNMENT == 0);
 
 INLINE bool
 (NILP) (Lisp_Object x)
@@ -1372,10 +1378,10 @@ struct Lisp_String
       unsigned char *data;
     } s;
     struct Lisp_String *next;
-    char alignas (GCALIGNMENT) gcaligned;
+    GCALIGNED_UNION
   } u;
 };
-verify (alignof (struct Lisp_String) % GCALIGNMENT == 0);
+verify (!USE_LSB_TAG || alignof (struct Lisp_String) % GCALIGNMENT == 0);
 
 INLINE bool
 STRINGP (Lisp_Object x)
@@ -3977,6 +3983,7 @@ extern void record_unwind_protect (void (*) 
(Lisp_Object), Lisp_Object);
 extern void record_unwind_protect_ptr (void (*) (void *), void *);
 extern void record_unwind_protect_int (void (*) (int), int);
 extern void record_unwind_protect_void (void (*) (void));
+extern void record_unwind_protect_excursion (void);
 extern void record_unwind_protect_nothing (void);
 extern void clear_unwind_protect (ptrdiff_t);
 extern void set_unwind_protect (ptrdiff_t, void (*) (Lisp_Object), 
Lisp_Object);
@@ -4680,13 +4687,14 @@ extern void *record_xmalloc (size_t) 
ATTRIBUTE_ALLOC_SIZE ((1));
 #define SAFE_ALLOCA_LISP(buf, nelt) SAFE_ALLOCA_LISP_EXTRA (buf, nelt, 0)
 
 
-/* If USE_STACK_LISP_OBJECTS, define macros that and functions that allocate
-   block-scoped conses and strings.  These objects are not
-   managed by the garbage collector, so they are dangerous: passing them
-   out of their scope (e.g., to user code) results in undefined behavior.
-   Conversely, they have better performance because GC is not involved.
+/* If USE_STACK_LISP_OBJECTS, define macros and functions that
+   allocate some Lisp objects on the C stack.  As the storage is not
+   managed by the garbage collector, these objects are dangerous:
+   passing them to user code could result in undefined behavior if the
+   objects are in use after the C function returns.  Conversely, these
+   objects have better performance because GC is not involved.
 
-   This feature is experimental and requires careful debugging.
+   While debugging you may want to disable allocation on the C stack.
    Build with CPPFLAGS='-DUSE_STACK_LISP_OBJECTS=0' to disable it.  */
 
 #if (!defined USE_STACK_LISP_OBJECTS \
@@ -4751,7 +4759,8 @@ enum
    Take its unibyte value from the null-terminated string STR,
    an expression that should not have side effects.
    STR's value is not necessarily copied.  The resulting Lisp string
-   should not be modified or made visible to user code.  */
+   should not be modified or given text properties or made visible to
+   user code.  */
 
 #define AUTO_STRING(name, str) \
   AUTO_STRING_WITH_LEN (name, str, strlen (str))
@@ -4760,7 +4769,8 @@ enum
    Take its unibyte value from the null-terminated string STR with length LEN.
    STR may have side effects and may contain null bytes.
    STR's value is not necessarily copied.  The resulting Lisp string
-   should not be modified or made visible to user code.  */
+   should not be modified or given text properties or made visible to
+   user code.  */
 
 #define AUTO_STRING_WITH_LEN(name, str, len)                           \
   Lisp_Object name =                                                   \
diff --git a/src/lread.c b/src/lread.c
index 239c66c..d2c7eae2 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1976,11 +1976,11 @@ readevalloop (Lisp_Object readcharfun,
       if (!NILP (start))
        {
          /* Switch to the buffer we are reading from.  */
-         record_unwind_protect (save_excursion_restore, save_excursion_save 
());
+         record_unwind_protect_excursion ();
          set_buffer_internal (b);
 
          /* Save point in it.  */
-         record_unwind_protect (save_excursion_restore, save_excursion_save 
());
+         record_unwind_protect_excursion ();
          /* Save ZV in it.  */
          record_unwind_protect (save_restriction_restore, 
save_restriction_save ());
          /* Those get unbound after we read one expression.  */
@@ -2137,7 +2137,7 @@ This function preserves the position of point.  */)
 
   specbind (Qeval_buffer_list, Fcons (buf, Veval_buffer_list));
   specbind (Qstandard_output, tem);
-  record_unwind_protect (save_excursion_restore, save_excursion_save ());
+  record_unwind_protect_excursion ();
   BUF_TEMP_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
   specbind (Qlexical_binding, lisp_file_lexically_bound_p (buf) ? Qt : Qnil);
   BUF_TEMP_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
diff --git a/src/msdos.c b/src/msdos.c
index eedbf7b..6c0dfa0 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 08d483f..bcd7dd8 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/window.c b/src/window.c
index f654d87..2c6ff01 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5656,7 +5656,7 @@ scroll_command (Lisp_Object window, Lisp_Object n, int 
direction)
      the moment.  But don't screw up if window_scroll gets an error.  */
   if (XBUFFER (w->contents) != current_buffer)
     {
-      record_unwind_protect (save_excursion_restore, save_excursion_save ());
+      record_unwind_protect_excursion ();
       Fset_buffer (w->contents);
     }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index ad1c044..a2b6513 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 a9c2f37..961bef7 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;
@@ -6544,7 +6694,7 @@ REPLACEMENT is a face specification, i.e. one of the 
following:
 
   (1) a face name
   (2) a property list of attribute/value pairs, or
-  (3) a list in which each element has the form of (1) or (2).
+  (3) a list in which each element has one of the above forms.
 
 List values for REPLACEMENT are merged to form the final face
 specification, with earlier entries taking precedence, in the same way
@@ -6564,13 +6714,32 @@ causes EXTRA-FACE... or (FACE-ATTR VAL ...) to be 
_merged_ with the
 existing definition of FACE.  Note that this isn't necessary for the
 default face, since every face inherits from the default face.
 
-If this variable is made buffer-local, the face remapping takes effect
-only in that buffer.  For instance, the mode my-mode could define a
-face `my-mode-default', and then in the mode setup function, do:
+An entry in the list can also be a filtered face expression of the
+form:
+
+  (:filtered FILTER FACE-SPECIFICATION)
+
+This construct applies FACE-SPECIFICATION (which can have any of the
+forms allowed for face specifications generally) only if FILTER
+matches at the moment Emacs wants to draw text with the combined face.
+
+The only filters currently defined are NIL (which always matches) and
+(:window PARAMETER VALUE), which matches only in the context of a
+window with a parameter EQ-equal to VALUE.
+
+An entry in the face list can also be nil, which does nothing.
+
+If `face-remapping-alist' is made buffer-local, the face remapping
+takes effect only in that buffer.  For instance, the mode my-mode
+could define a face `my-mode-default', and then in the mode setup
+function, do:
 
    (set (make-local-variable \\='face-remapping-alist)
        \\='((default my-mode-default)))).
 
+You probably want to use the face-remap package included in Emacs
+instead of manipulating face-remapping-alist directly.
+
 Because Emacs normally only redraws screen areas when the underlying
 buffer contents change, you may need to call `redraw-display' after
 changing this variable for it to take effect.  */);
diff --git a/src/xwidget.c b/src/xwidget.c
index 32022ab..5f26512 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -749,8 +749,10 @@ argument procedure FUN.*/)
   /* JavaScript execution happens asynchronously.  If an elisp
      callback function is provided we pass it to the C callback
      procedure that retrieves the return value.  */
+  gchar *script_string
+    = XSAVE_POINTER (XCAR (AREF (xw->script_callbacks, idx)), 0);
   webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw->widget_osr),
-                                  XSAVE_POINTER (XCAR (AREF 
(xw->script_callbacks, idx)), 0),
+                                 script_string,
                                   NULL, /* cancelable */
                                   webkit_javascript_finished_cb,
                                  (gpointer) idx);
@@ -1221,15 +1223,13 @@ kill_buffer_xwidgets (Lisp_Object buffer)
             gtk_widget_destroy (xw->widgetwindow_osr);
           }
        if (!NILP (xw->script_callbacks))
-         {
-           ptrdiff_t idx;
-           for (idx = 0; idx < ASIZE (xw->script_callbacks); idx++)
-             {
-               if (!NILP (AREF (xw->script_callbacks, idx)))
-                 xfree (XSAVE_POINTER (XCAR (AREF (xw->script_callbacks, 
idx)), 0));
-               ASET (xw->script_callbacks, idx, Qnil);
-             }
-         }
+         for (ptrdiff_t idx = 0; idx < ASIZE (xw->script_callbacks); idx++)
+           {
+             Lisp_Object cb = AREF (xw->script_callbacks, idx);
+             if (!NILP (cb))
+               xfree (XSAVE_POINTER (XCAR (cb), 0));
+             ASET (xw->script_callbacks, idx, Qnil);
+           }
       }
     }
 }



reply via email to

[Prev in Thread] Current Thread [Next in Thread]