From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.ciao.gmane.io!not-for-mail From: =?UTF-8?Q?Cl=C3=A9ment?= Pit-Claudel Newsgroups: gmane.emacs.bugs Subject: bug#41200: Displaying a tooltip with x-show-tip gets very slow as more faces are defined Date: Fri, 15 May 2020 14:50:17 -0400 Message-ID: <53252e93-085c-477f-21df-c909082dffc2@gmail.com> References: <8fd8896a-cd5c-66f4-4792-f65cac4dc4f5@gmail.com> <83lflx896q.fsf@gnu.org> <837dxd31cb.fsf@gnu.org> <789d786d-a07a-65c1-c0e4-433e4c18d64e@gmail.com> <835zcxgrby.fsf@gnu.org> <833681glas.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------BA64282E6E8C5FE96DAD52F3" Injection-Info: ciao.gmane.io; posting-host="ciao.gmane.io:159.69.161.202"; logging-data="97140"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.7.0 Cc: 41200@debbugs.gnu.org, Juri Linkov To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Fri May 15 20:51:15 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jZfQQ-000P8V-N7 for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 15 May 2020 20:51:14 +0200 Original-Received: from localhost ([::1]:52298 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jZfQP-0004Cp-LE for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 15 May 2020 14:51:13 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:54548) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jZfQE-00048P-0z for bug-gnu-emacs@gnu.org; Fri, 15 May 2020 14:51:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:55417) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jZfQD-0007LJ-LQ for bug-gnu-emacs@gnu.org; Fri, 15 May 2020 14:51:01 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1jZfQD-00067l-Jw for bug-gnu-emacs@gnu.org; Fri, 15 May 2020 14:51:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: =?UTF-8?Q?Cl=C3=A9ment?= Pit-Claudel Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 15 May 2020 18:51:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 41200 X-GNU-PR-Package: emacs Original-Received: via spool by 41200-submit@debbugs.gnu.org id=B41200.158956863023486 (code B ref 41200); Fri, 15 May 2020 18:51:01 +0000 Original-Received: (at 41200) by debbugs.gnu.org; 15 May 2020 18:50:30 +0000 Original-Received: from localhost ([127.0.0.1]:38730 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jZfPe-00066g-47 for submit@debbugs.gnu.org; Fri, 15 May 2020 14:50:30 -0400 Original-Received: from mail-qv1-f47.google.com ([209.85.219.47]:45126) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jZfPc-00066R-Nt for 41200@debbugs.gnu.org; Fri, 15 May 2020 14:50:25 -0400 Original-Received: by mail-qv1-f47.google.com with SMTP id z9so1584290qvi.12 for <41200@debbugs.gnu.org>; Fri, 15 May 2020 11:50:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=ZKChxk8dDfiR/B45KMT10NPSrZp6JyXHhv8oJUDW8Ak=; b=fRkvf61L4ALbptU2JIIBHvhu9bgswrg785RhoaNaImA49mkZiwWhn9zxPyPn1VdSKP sDxrFhUancWF5PGY65lAhVydUKYnEbYVktTL0Apz4oa8uYi65vwsLx2fneytKEF040dh rhdOI5rZTfo4+aDtC1reL89hVRB5WaCfvLqsbkKeuqXsyR+JcyZc8kcA33S6OjVM1UWH b94zU7yhqqtYd8peBb7xaYE6UB7m1I8PvIyk95xsYsB6/W7nX/KmFmgycUeGVThHL4YO JQH7MZdPWbQKjfqAzgKeLBHAJmBNS06XeffJuWjCb/oTY5ZdQGQplaSHSyrH5fopvn/w DWag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=ZKChxk8dDfiR/B45KMT10NPSrZp6JyXHhv8oJUDW8Ak=; b=ss2JFCSqs3w6bSSyERTfjOA+nrTSUYvsNhy0l0bNaz6NZ5r5/Y0djXQ9g5/OFSNkMI ZJyt6Xj0virqe6YU5RTDNh9hoXJxYShSGQa4hiRVMIRN5ICcUaOsOjs9WbHkpXyWrO0i qFyMIGD6kDu4OmVeJMqsUS4v1bYNq4dsw4Tg4oRLzHZuAdMURd3CffNJqdBofDBwUtBH Cj51YwkleqkwstdxAUnHBZQtQJdG6y4GRRcN3rDC6MKHWqWEfR6d0U9IZkYn+BWHMnts jjKDIalwZ2u1By28URaLzqt3zEIUR2I8RoqR3g9qjHQOK4WlOa82UBXCUGwiTO0nQsV+ aN9g== X-Gm-Message-State: AOAM533PUknQv++J3D7G88x9IDJ37WcyERjqJ4tTMouilKvmD5n3uzxM yuLMj4/DhqO+NDwlNrmj+9M= X-Google-Smtp-Source: ABdhPJx6YtoyOjE2KvHK36ZGGveXP1MvwK+l2koOwEl2mNH4EkCaYWIZlLnjJ+/n8cOAA2nSHST5IA== X-Received: by 2002:a0c:ec44:: with SMTP id n4mr4859433qvq.237.1589568619183; Fri, 15 May 2020 11:50:19 -0700 (PDT) Original-Received: from ?IPv6:2601:184:4180:66e7:4d17:b25e:8d9:2188? ([2601:184:4180:66e7:4d17:b25e:8d9:2188]) by smtp.googlemail.com with ESMTPSA id e27sm2148759qkl.47.2020.05.15.11.50.17 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 15 May 2020 11:50:18 -0700 (PDT) In-Reply-To: <833681glas.fsf@gnu.org> Content-Language: en-GB X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:180350 Archived-At: This is a multi-part message in MIME format. --------------BA64282E6E8C5FE96DAD52F3 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit On 15/05/2020 13.28, Eli Zaretskii wrote: >> Cc: 41200@debbugs.gnu.org, Anders Lindgren >> From: Clément Pit-Claudel >> Date: Fri, 15 May 2020 12:22:53 -0400 >> >>>>> face_hash_table? >>>> >>>> Thanks, good idea. I also liked Stefan's frame_face_map. >>> >>> "Map" is too general, IMO, it doesn't tell enough about the kind of >>> object it is. >> >> Got it. Is face_table better? (that was Stefan's other suggestion) > > Is anything wrong with face_hash_table? Nope; I've attached a new patch. >> Btw, I have one more question: some callers of face-list seems to rely on the order of faces added to it, so it would be better to preserve that order. Since hash-tables are not necessarily ordered, should I sort faces by face-id before returning them? Done in the attached patch as well. >>> I think the problem is that tab-line is declared a basic face, but its >>> defface form is not in faces.el. >> >> Ah, good catch. Current there's a defface for tab-bar in lisp/tab-bar, and since that's preloaded it works, but the defface for tab-line is in lisp/tab-line.el and so isn't preloaded. >> Should I move both to faces.el? >> > Yes, I think so. Thanks. I will ask Juri to confirm before moving them, because I realize now that they have a custom group. Juri (CC'd; hi Juri!), was there a reason to make tab-bar and tab-line basic faces? I see they are both in their own files and groups, instead of being in faces.el. >>>>> I'm not sure I understand why do you need to look at the existing >>>>> face's 'face' property? The original code didn't. >>>> >>>> The original code iterated over face-frame-alist in the order in which entries were added to it. If I understand correctly, iteration order isn't guaranteed on hash tables (is it?), so I had to find a different source of truth for these. >>> >>> Maybe we should store the ID with the face? I think we wanted to get >>> rid of the 'face' property of the faces at some point. >> >> Sounds reasonable; but where would we store it? Right now faces are just symbols, right? > > Don't hash-tables allow us to store more than one item in each slot? They do, so I can certainly store (face-id . face-vector) in the hash table. Done in the attached patch. I only do this in Vface_new_frame_defaults, not in frame->face_hash_table. >>> No, I was asking why not start with it as nil and actually make a >>> hash-table when we first need it. >> >> Oh, I thought it would be simpler to always have a hash table instead of having to sanity check every time. > > What is the default size of a hash-table? 65 entries, I think. --------------BA64282E6E8C5FE96DAD52F3 Content-Type: text/x-patch; charset=UTF-8; name="0001-Store-frame-faces-in-hash-tables-instead-of-alists.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Store-frame-faces-in-hash-tables-instead-of-alists.patc"; filename*1="h" >From be915d6d2ea7a739026dc4a53fab64f78efab2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Tue, 12 May 2020 21:48:32 -0400 Subject: [PATCH] Store frame faces in hash tables instead of alists * src/frame.h (struct frame): Add face_hash_table, remove face_alist. (fset_face_hash_table): New function. (fset_face_alist): Remove. * src/frame.c (make_frame): Initialize f->face_hash_table. (Fmake_terminal_frame): Update to work with hash tables instead of alists. * src/xfaces.c (lface_from_face_name_no_resolve): (Finternal_make_lisp_face): (update_face_from_frame_parameter): Update to work with hash tables instead of alists. (Fframe_face_hash_table): New function. (Fframe_face_alist): Move to faces.el as frame-face-alist. (syms_of_xfaces): Add frame_face_hash_table. * lisp/emacs-lisp/edebug.el (edebug-eval-defun): * lisp/progmodes/elisp-mode.el (elisp--eval-defun-1): * lisp/frame.el (frame-set-background-mode): Update to work with hash tables instead of alists. * lisp/faces.el (face-new-frame-defaults): Mark obsolete. (face-list): Update to use face--new-frame-defaults. (frame-face-alist): Moved here from src/xfaces.c. --- lisp/custom.el | 2 +- lisp/emacs-lisp/edebug.el | 3 +- lisp/faces.el | 23 ++++++++-- lisp/frame.el | 2 +- lisp/progmodes/elisp-mode.el | 3 +- src/frame.c | 20 ++++++--- src/frame.h | 8 ++-- src/xfaces.c | 82 ++++++++++++++++++------------------ 8 files changed, 83 insertions(+), 60 deletions(-) diff --git a/lisp/custom.el b/lisp/custom.el index 885c486c5e..3ac8bac40f 100644 --- a/lisp/custom.el +++ b/lisp/custom.el @@ -898,7 +898,7 @@ custom-push-theme ;; the value to a fake theme, `changed'. If the theme is ;; later disabled, we use this to bring back the old value. ;; - ;; For faces, we just use `face-new-frame-defaults' to + ;; For faces, we just use `face--new-frame-defaults' to ;; recompute when the theme is disabled. (when (and (eq prop 'theme-value) (boundp symbol)) diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el index 78461185d3..76a75c04df 100644 --- a/lisp/emacs-lisp/edebug.el +++ b/lisp/emacs-lisp/edebug.el @@ -488,8 +488,7 @@ edebug-eval-defun (set-default (nth 1 form) (eval (nth 2 form) lexical-binding))) ((eq (car form) 'defface) ;; Reset the face. - (setq face-new-frame-defaults - (assq-delete-all (nth 1 form) face-new-frame-defaults)) + (remhash (nth 1 form) face--new-frame-defaults) (put (nth 1 form) 'face-defface-spec nil) (put (nth 1 form) 'face-documentation (nth 3 form)) ;; See comments in `eval-defun-1' for purpose of code below diff --git a/lisp/faces.el b/lisp/faces.el index e707f6f4b6..bb51797a38 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -176,10 +176,27 @@ face-font-registry-alternatives ;;; Creation, copying. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(make-obsolete-variable + 'face-new-frame-defaults + "use `face--new-frame-defaults' or `face-alist' instead." + "28.1") + +(defun frame-face-alist (&optional frame) + "Return an alist of frame-local faces defined on FRAME. +This alist is a copy of the contents of `frame--face-hash-table'. +For internal use only." + (declare (obsolete frame--face-hash-table "28.1")) + (let ((faces)) + (maphash (lambda (face spec) (push `(,(car spec) ,face . ,(cdr spec)) faces)) + (frame--face-hash-table frame)) + (mapcar #'cdr (sort faces (lambda (f1 f2) (< (car f1) (car f2))))))) (defun face-list () "Return a list of all defined faces." - (mapcar #'car face-new-frame-defaults)) + (let ((faces)) + (maphash (lambda (face spec) (push `(,(car spec) . ,face) faces)) + face--new-frame-defaults) + (mapcar #'cdr (sort faces (lambda (f1 f2) (< (car f1) (car f2))))))) (defun make-face (face) "Define a new face with name FACE, a symbol. @@ -2097,7 +2114,7 @@ x-create-frame-with-faces (defun face-set-after-frame-default (frame &optional parameters) "Initialize the frame-local faces of FRAME. Calculate the face definitions using the face specs, custom theme -settings, X resources, and `face-new-frame-defaults'. +settings, X resources, and `face--new-frame-defaults'. Finally, apply any relevant face attributes found amongst the frame parameters in PARAMETERS." ;; The `reverse' is so that `default' goes first. @@ -2106,7 +2123,7 @@ face-set-after-frame-default (progn ;; Initialize faces from face spec and custom theme. (face-spec-recalc face frame) - ;; Apply attributes specified by face-new-frame-defaults + ;; Apply attributes specified by face--new-frame-defaults (internal-merge-in-global-face face frame)) ;; Don't let invalid specs prevent frame creation. (error nil))) diff --git a/lisp/frame.el b/lisp/frame.el index 6c2f774709..66b5c6aa8d 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -1227,7 +1227,7 @@ frame-set-background-mode ;; during startup with -rv on the command ;; line for the initial frame, because frames ;; are not recorded in the pdump file. - (assq face (frame-face-alist)) + (gethash face (frame--face-hash-table)) (face-spec-match-p face (face-user-default-spec face) ;; FIXME: why selected-frame and diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index b737134f90..75054b8818 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -1304,8 +1304,7 @@ elisp--eval-defun-1 ((eq (car form) 'custom-declare-face) ;; Reset the face. (let ((face-symbol (eval (nth 1 form) lexical-binding))) - (setq face-new-frame-defaults - (assq-delete-all face-symbol face-new-frame-defaults)) + (remhash face--new-frame-defaults face-symbol) (put face-symbol 'face-defface-spec nil) (put face-symbol 'face-override-spec nil)) form) diff --git a/src/frame.c b/src/frame.c index c871e4fd99..0843d53559 100644 --- a/src/frame.c +++ b/src/frame.c @@ -946,6 +946,10 @@ make_frame (bool mini_p) rw->total_lines = mini_p ? 9 : 10; rw->pixel_height = rw->total_lines * FRAME_LINE_HEIGHT (f); + fset_face_hash_table + (f, make_hash_table (hashtest_eq, DEFAULT_HASH_SIZE, DEFAULT_REHASH_SIZE, + DEFAULT_REHASH_THRESHOLD, Qnil, false)); + if (mini_p) { mw->top_line = rw->total_lines; @@ -1254,7 +1258,7 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, { struct frame *f; struct terminal *t = NULL; - Lisp_Object frame, tem; + Lisp_Object frame; struct frame *sf = SELECTED_FRAME (); #ifdef MSDOS @@ -1336,14 +1340,16 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, store_in_alist (&parms, Qminibuffer, Qt); Fmodify_frame_parameters (frame, parms); - /* Make the frame face alist be frame-specific, so that each + /* Make the frame face hash be frame-specific, so that each frame could change its face definitions independently. */ - fset_face_alist (f, Fcopy_alist (sf->face_alist)); - /* Simple Fcopy_alist isn't enough, because we need the contents of - the vectors which are the CDRs of associations in face_alist to + fset_face_hash_table (f, Fcopy_hash_table (sf->face_hash_table)); + /* Simple copy_hash_table isn't enough, because we need the contents of + the vectors which are the values in face_hash_table to be copied as well. */ - for (tem = f->face_alist; CONSP (tem); tem = XCDR (tem)) - XSETCDR (XCAR (tem), Fcopy_sequence (XCDR (XCAR (tem)))); + ptrdiff_t idx = 0; + struct Lisp_Hash_Table *table = XHASH_TABLE (f->face_hash_table); + for (idx = 0; idx < table->count; ++idx) + set_hash_value_slot (table, idx, Fcopy_sequence (HASH_VALUE (table, idx))); f->can_set_window_size = true; f->after_make_frame = true; diff --git a/src/frame.h b/src/frame.h index 476bac67fa..9761253085 100644 --- a/src/frame.h +++ b/src/frame.h @@ -158,8 +158,8 @@ #define EMACS_FRAME_H There are four additional elements of nil at the end, to terminate. */ Lisp_Object menu_bar_items; - /* Alist of elements (FACE-NAME . FACE-VECTOR-DATA). */ - Lisp_Object face_alist; + /* Hash table of FACE-NAME keys and FACE-VECTOR-DATA values. */ + Lisp_Object face_hash_table; /* A vector that records the entire structure of this frame's menu bar. For the format of the data, see extensive comments in xmenu.c. @@ -661,9 +661,9 @@ fset_condemned_scroll_bars (struct frame *f, Lisp_Object val) f->condemned_scroll_bars = val; } INLINE void -fset_face_alist (struct frame *f, Lisp_Object val) +fset_face_hash_table (struct frame *f, Lisp_Object val) { - f->face_alist = val; + f->face_hash_table = val; } #if defined (HAVE_WINDOW_SYSTEM) INLINE void diff --git a/src/xfaces.c b/src/xfaces.c index 7d7aff95c1..eae991d1c2 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -95,9 +95,10 @@ Copyright (C) 1993-1994, 1998-2020 Free Software Foundation, Inc. with the symbol `face' in slot 0, and a slot for each of the face attributes mentioned above. - There is also a global face alist `Vface_new_frame_defaults'. Face - definitions from this list are used to initialize faces of newly - created frames. + There is also a global face map `Vface_new_frame_defaults', + containing conses of (FACE_ID . FACE_DEFINITION). Face definitions + from this table are used to initialize faces of newly created + frames. A face doesn't have to specify all attributes. Those not specified have a value of `unspecified'. Faces specifying all attributes but @@ -1843,13 +1844,11 @@ lface_from_face_name_no_resolve (struct frame *f, Lisp_Object face_name, Lisp_Object lface; if (f) - lface = assq_no_quit (face_name, f->face_alist); + lface = Fgethash (face_name, f->face_hash_table, Qnil); else - lface = assq_no_quit (face_name, Vface_new_frame_defaults); + lface = CDR (Fgethash (face_name, Vface_new_frame_defaults, Qnil)); - if (CONSP (lface)) - lface = XCDR (lface); - else if (signal_p) + if (signal_p && NILP (lface)) signal_error ("Invalid face", face_name); check_lface (lface); @@ -2734,11 +2733,6 @@ DEFUN ("internal-make-lisp-face", Finternal_make_lisp_face, /* Add a global definition if there is none. */ if (NILP (global_lface)) { - global_lface = make_vector (LFACE_VECTOR_SIZE, Qunspecified); - ASET (global_lface, 0, Qface); - Vface_new_frame_defaults = Fcons (Fcons (face, global_lface), - Vface_new_frame_defaults); - /* Assign the new Lisp face a unique ID. The mapping from Lisp face id to Lisp face is given by the vector lface_id_to_name. The mapping from Lisp face to Lisp face id is given by the @@ -2748,9 +2742,14 @@ DEFUN ("internal-make-lisp-face", Finternal_make_lisp_face, xpalloc (lface_id_to_name, &lface_id_to_name_size, 1, MAX_FACE_ID, sizeof *lface_id_to_name); + Lisp_Object face_id = make_fixnum (next_lface_id); lface_id_to_name[next_lface_id] = face; - Fput (face, Qface, make_fixnum (next_lface_id)); + Fput (face, Qface, face_id); ++next_lface_id; + + global_lface = make_vector (LFACE_VECTOR_SIZE, Qunspecified); + ASET (global_lface, 0, Qface); + Fputhash (face, Fcons (face_id, global_lface), Vface_new_frame_defaults); } else if (f == NULL) for (i = 1; i < LFACE_VECTOR_SIZE; ++i) @@ -2763,7 +2762,7 @@ DEFUN ("internal-make-lisp-face", Finternal_make_lisp_face, { lface = make_vector (LFACE_VECTOR_SIZE, Qunspecified); ASET (lface, 0, Qface); - fset_face_alist (f, Fcons (Fcons (face, lface), f->face_alist)); + Fputhash (face, lface, f->face_hash_table); } else for (i = 1; i < LFACE_VECTOR_SIZE; ++i) @@ -2924,7 +2923,7 @@ DEFUN ("internal-set-lisp-face-attribute", Finternal_set_lisp_face_attribute, f = NULL; lface = lface_from_face_name (NULL, face, true); - /* When updating face-new-frame-defaults, we put :ignore-defface + /* When updating face--new-frame-defaults, we put :ignore-defface where the caller wants `unspecified'. This forces the frame defaults to ignore the defface value. Otherwise, the defface will take effect, which is generally not what is intended. @@ -3508,7 +3507,7 @@ update_face_from_frame_parameter (struct frame *f, Lisp_Object param, /* If there are no faces yet, give up. This is the case when called from Fx_create_frame, and we do the necessary things later in face-set-after-frame-defaults. */ - if (NILP (f->face_alist)) + if (XFIXNAT (Fhash_table_count (f->face_hash_table)) == 0) return; if (EQ (param, Qforeground_color)) @@ -4174,14 +4173,13 @@ DEFUN ("internal-lisp-face-empty-p", Finternal_lisp_face_empty_p, return i == LFACE_VECTOR_SIZE ? Qt : Qnil; } - -DEFUN ("frame-face-alist", Fframe_face_alist, Sframe_face_alist, +DEFUN ("frame--face-hash-table", Fframe_face_hash_table, Sframe_face_hash_table, 0, 1, 0, - doc: /* Return an alist of frame-local faces defined on FRAME. + doc: /* Return a hash table of frame-local faces defined on FRAME. For internal use only. */) (Lisp_Object frame) { - return decode_live_frame (frame)->face_alist; + return decode_live_frame (frame)->face_hash_table; } @@ -6678,30 +6676,32 @@ DEFUN ("show-face-resources", Fshow_face_resources, Sshow_face_resources, #ifdef HAVE_PDUMPER /* All the faces defined during loadup are recorded in - face-new-frame-defaults, with the last face first in the list. We - need to set next_lface_id to the next face ID number, so that any - new faces defined in this session will have face IDs different from - those defined during loadup. We also need to set up the - lface_id_to_name[] array for the faces that were defined during - loadup. */ + face-new-frame-defaults. We need to set next_lface_id to the next + face ID number, so that any new faces defined in this session will + have face IDs different from those defined during loadup. We also + need to set up the lface_id_to_name[] array for the faces that were + defined during loadup. */ void init_xfaces (void) { - if (CONSP (Vface_new_frame_defaults)) + int nfaces = XFIXNAT (Fhash_table_count (Vface_new_frame_defaults)); + if (nfaces > 0) { /* Allocate the lface_id_to_name[] array. */ - lface_id_to_name_size = next_lface_id = - XFIXNAT (Flength (Vface_new_frame_defaults)); + lface_id_to_name_size = next_lface_id = nfaces; lface_id_to_name = xnmalloc (next_lface_id, sizeof *lface_id_to_name); /* Store the faces. */ - Lisp_Object tail; - int i = next_lface_id - 1; - for (tail = Vface_new_frame_defaults; CONSP (tail); tail = XCDR (tail)) + struct Lisp_Hash_Table* table = XHASH_TABLE (Vface_new_frame_defaults); + for (ptrdiff_t idx = 0; idx < nfaces; ++idx) { - Lisp_Object lface = XCAR (tail); - eassert (i >= 0); - lface_id_to_name[i--] = XCAR (lface); + Lisp_Object lface = HASH_KEY (table, idx); + Lisp_Object face_id = CAR (HASH_VALUE (table, idx)); + if (FIXNATP (face_id)) { + int id = XFIXNAT (face_id); + eassert (id >= 0); + lface_id_to_name[id] = lface; + } } } face_attr_sym[0] = Qface; @@ -6855,7 +6855,7 @@ syms_of_xfaces (void) defsubr (&Sinternal_copy_lisp_face); defsubr (&Sinternal_merge_in_global_face); defsubr (&Sface_font); - defsubr (&Sframe_face_alist); + defsubr (&Sframe_face_hash_table); defsubr (&Sdisplay_supports_face_attributes_p); defsubr (&Scolor_distance); defsubr (&Sinternal_set_font_selection_order); @@ -6879,9 +6879,11 @@ syms_of_xfaces (void) the "specifity" of a face specification and should be let-bound only for this purpose. */); - 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; + DEFVAR_LISP ("face--new-frame-defaults", Vface_new_frame_defaults, + doc: /* Hash table of global face definitions (for internal use only.) */); + Vface_new_frame_defaults = + make_hash_table (hashtest_eq, DEFAULT_HASH_SIZE, DEFAULT_REHASH_SIZE, + DEFAULT_REHASH_THRESHOLD, Qnil, false); DEFVAR_LISP ("face-default-stipple", Vface_default_stipple, doc: /* Default stipple pattern used on monochrome displays. -- 2.17.1 --------------BA64282E6E8C5FE96DAD52F3--