-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathface-explorer.el
4647 lines (3912 loc) · 168 KB
/
face-explorer.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; face-explorer.el --- Tools for faces and text properties. -*- lexical-binding: t; -*-
;; Copyright (C) 2014-2017,2019,2024-2025 Anders Lindgren
;; Author: Anders Lindgren
;; Keywords: faces
;; Version: 0.0.6
;; Created: 2017-02-24 (Based code from e2ansi created 2014-12-09)
;; URL: https://github.com/Lindydancer/face-explorer
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Library and tools for faces and text properties.
;;
;; This library is useful for packages that convert syntax highlighted
;; buffers to other formats. The functions can be used to determine
;; how a face or a face text property looks, in terms of primitive
;; face attributes (e.g. foreground and background colors). Two sets
;; of functions are provided, one for existing frames and one for
;; fictitious displays, like 8 color tty.
;;
;; In addition, the following tools are provided:
;;
;; - `face-explorer-list-faces' -- list all available faces. Like
;; `list-faces-display' but with information on how a face is
;; defined. In addition, a sample for the selected frame and for a
;; fictitious display is shown.
;;
;; - `face-explorer-describe-face' -- Print detailed information on
;; how a face is defined, and list all underlying definitions.
;;
;; - `face-explorer-describe-face-prop' -- Describe the `face' text
;; property at the point in terms of primitive face attributes.
;; Also show how it would look on a fictitious display.
;;
;; - `face-explorer-list-display-features' -- Show which features a
;; display supports. Most graphical displays support all, or most,
;; features. However, many tty:s don't support, for example,
;; strike-through. Using specially constructed faces, the resulting
;; buffer will render differently in different displays, e.g. a
;; graphical frame and a tty connected using `emacsclient -nw'.
;;
;; - `face-explorer-list-face-prop-examples' -- Show a buffer with an
;; assortment of `face' text properties. A sample text is shown in
;; four variants: Native, a manually maintained reference vector,
;; the result of `face-explorer-face-prop-attributes' and
;; `face-explorer-face-prop-attributes-for-fictitious-display'. Any
;; package that convert a buffer to another format (like HTML, ANSI,
;; or LaTeX) could use this buffer to ensure that everything work as
;; intended.
;;
;; - `face-explorer-list-overlay-examples' -- Show a buffer with a
;; number of examples of overlays, some are mixed with `face' text
;; properties. Any package that convert a buffer to another format
;; (like HTML, ANSI, or LaTeX) could use this buffer to ensure that
;; everything work as intended.
;;
;; - `face-explorer-tooltip-mode' -- Minor mode that shows tooltips
;; containing text properties and overlays at the mouse pointer.
;;
;; - `face-explorer-simulate-display-mode' -- Minor mode for make a
;; buffer look like it would on a fictitious display. Using this
;; you can, for example, see how a theme would look in using dark or
;; light background, a 8 color tty, or on a grayscale graphical
;; monitor.
;; Fictitious displays:
;;
;; Emacs supports a variety of displays, from graphical frames to
;; terminals with 8 colors. Emacs itself provides query functions for
;; existing displays. This library provides query functions for any
;; kind of display, allowing you to play the "what if" game. (For
;; example, how would a face look on a grayscale graphical display or
;; an 8 color tty.)
;;
;; It is possible to use the query functions for fictitious displays
;; in batch mode, when the normal face and colors system are severely
;; restricted.
;; Library functions:
;;
;; Function for existing frames:
;;
;; - `face-explorer-face-attributes' -- The face attributes
;; of a face, after expanding all inherited faces.
;;
;; - `face-explorer-face-prop-attributes' -- The primitive face
;; attributes of a face specification, as used by the `face' text
;; property. Effectively, this can tell how a piece of text look to
;; the user, expressed in terms of foreground color, background
;; color, underline etc. Face remappings performed by
;; `face-remapping-alist' are handled.
;;
;; - `face-explorer-face-attributes-at' -- The primitive face
;; attributes at a specific position in a buffer.
;;
;; Support for a fictitious display:
;;
;; The following variables defines a fictitious display. The
;; face-explorer tools use the global variants of these variables.
;; However, when calling the functions in the library, it's possible
;; to dynamically bind them using `let'.
;;
;; - `face-explorer-number-of-colors' -- Number of colors.
;;
;; - `face-explorer-background-mode' -- The background mode, either
;; `light' or `dark'.
;;
;; - `face-explorer-color-class' -- The color class: `color',
;; `grayscale', or `mono'.
;;
;; - `face-explorer-window-system-type' -- The window system type,
;; either a symbol like `tty' or a list like `(graphic ns)'.
;;
;; - `face-explorer-match-supports-function' -- A function to call to
;; determine the features a display supports. By default, a
;; graphical display supports everything and a tty supports things
;; like underline and inverse video.
;;
;; The following functions can be used to query face-related
;; information for fictitious displays:
;;
;; - `face-explorer-face-attributes-for-fictitious-display'
;; -- The face attributes of a face, after expanding all inherited
;; faces, for a fictitious display.
;;
;; - `face-explorer-face-prop-attributes-for-fictitious-display' --
;; The primitive face attributes of a face specification, as used by
;; the `face' text property, for a fictitious display. Effectively,
;; this can tell how a piece of text look to the user, expressed in
;; terms of foreground color, background color, underline etc.
;;
;; - `face-explorer-face-attributes-for-fictitious-display-at' -- The
;; primitive face attributes at a specific position in a buffer, for
;; a fictitious display.
;; Face-related tools:
;;
;; This package provide a number of face-related tools. Most of them
;; display information about a face both in the selected frame and as
;; it would look on a fictitious display.
;;
;; Key bindings:
;;
;; In the tool buffers, you can use the following keys to change the
;; settings:
;;
;; - `-', `+', and `#' -- Decrease, increase, and set the number of
;; colors of the fictitious display. The increase and decrease
;; commands use 8, 16, 256, and "infinite" colors.
;;
;; - `b' -- Toggle between light and dark background mode.
;;
;; - `c' -- Step to the next color class.
;;
;; - `r' -- Reset the fictitious display to match the selected frame.
;;
;; - `g' -- Toggle the window system between that of the selected frame
;; and a terminal.
;; Face verifiers:
;;
;; The `face-explorer-list-faces' and `face-explorer-describe-face'
;; tools warn about inappropriately defined faces. You can use the
;; following keys to handle these warnings:
;;
;; - `wd' -- Disable verifier
;;
;; - `we' -- Enable verifier
;;
;; - `wa' -- Enable all verifiers
;;
;; - `wn' -- Disable all verifiers
;;
;; - `wx' -- Describe verifier.
;; The `face-explorer-list-faces' tool:
;;
;; List all available faces. Like `list-faces-display' but with
;; information on how a face is defined. In addition, a sample for
;; the both the selected frame and for the current fictitious display
;; is shown.
;;
;; Additional keys:
;;
;; - `RET' -- Open the `face-explorer-describe-face' tool for the face
;; on the line of the cursor.
;; The `face-explorer-describe-face' tool:
;;
;; Display information about a face. This includes:
;;
;; - The documentation
;;
;; - The face attributes in the selected frame
;;
;; - Ditto, but with inheritances resolved
;;
;; - Primitive face attributes and samples for the current fictitious display
;;
;; - Samples for a number of typical fictitious displays
;;
;; - The face specifications used to define the face, originating from
;; `defface', `custom-theme-set-faces' etc.
;; The `face-explorer-describe-face-prop' tool:
;;
;; Display information about a `face' text property. This includes:
;;
;; - The value of the property
;;
;; - The corresponding primitive face attributes in the selected frame,
;; with samples.
;;
;; - Ditto for the current fictitious display
;;
;; - Samples how the `face' text property would look in a number of
;; typical fictitious displays.
;; The `face-explorer-list-display-features' tool:
;;
;; Display a buffer contains text using specially constructed faces
;; that will look differently depending on available display features.
;; For example, if you run `emacsclient -nw' from a terminal, this
;; buffer will look differently than it does in a graphical frame.
;; The `face-explorer-list-face-prop-examples' tool:
;;
;; List sample text with face text properties in various variants.
;;
;; This is useful for two reasons:
;;
;; - It can be used to investigate how Emacs, rally, displays various
;; faces and text properties.
;;
;; - It can be used to test packages that convert text with text
;; properties to various other format, like PostScript, HTML, ANSI,
;; LaTeX etc.
;; The `face-explorer-list-overlay-examples' tool:
;;
;; List sample text with overlays in various variants.
;; The `face-explorer-tooltip-mode' tool:
;;
;; A minor mode that shows information about text
;; properties and overlays in a tooltip.
;;
;; This is enabled in all buffers displayed by the tools in this
;; module.
;;
;; `face-explorer-tooltip-global-mode' can be used to enable this mode
;; for all buffers.
;; The `face-explorer-simulate-display-mode' tool:
;;
;; A minor mode used to show how the current buffer would look in a
;; fictitious display. This can be used, for example, to check if a
;; face theme would look good in a 8 color tty or in a grayscale
;; graphical display.
;;
;; The key bindings describe above above are available after the
;; prefix `C-c !'.
;;
;; Note: The mode will not restrict colors to the fictitious display.
;; For example, if a face is defined as "red" it will be shown in red
;; even when the fictitious display is set to `mono' or `grayscale'.
;;
;; `face-explorer-simulate-display-global-mode' can be used to enable
;; this mode for all buffers.
;; Vocabulary:
;;
;; Primitive face attributes:
;;
;; Normally, a face or face specification can be quite complex, for
;; example a face can inherit from other faces and a face
;; specification can contain several faces. "Primitive face
;; attributes" corresponds to how the face or face specification will
;; look to the user, the foreground and background color it has,
;; whether it is bold or italic etc. Unspecified properties are not
;; included. Properties like :height could be relative (like 1.2) or
;; absolute (like 10). Primitive face attributes never contain the
;; `:inherit' attribute.
;; Technical background:
;;
;; Face definitions:
;;
;; The attributes associated with a face can originate from the
;; following locations:
;;
;; - `defface' -- This construct defines a customizable face. The
;; information is stored in the property `face-default-spec' in the
;; face symbol. Normally it is accessed using the function
;; `face-default-spec'.
;;
;; - Customize -- A user can customize a face using
;; `custom-theme-set-faces'. The `theme-face' property of the face
;; symbol contains an alist from active themes to display
;; requirements face specifications.
;;
;; - Overrides -- Face attributes can be overridden using the function
;; `face-spec-set'. This information is stored in the
;; `face-override-spec' property in the face symbol.
;;
;; - Future frames -- The function `set-face-attribute' can specify
;; that an attribute should be set for future frames, by passing nil
;; or `t' as FRAME. This information can be accessed using
;; `face-attribute' by passing `t' as the FRAME.
;;
;; - Existing frames -- A face attribute can be changed in existing
;; frames by `set-face-attribute' by passing a frame or nil as
;; FRAME. This information can be accessed using `face-attribute'.
;;
;; Face aliases:
;;
;; The `face-alias' property of the face symbol can contain another
;; symbol which the face is aliased to.
;;
;; Distant foreground:
;;
;; A "distant foreground" is an alternative color used to render the
;; foreground when the normal foreground color would be too close to
;; the background. Unfortunately, there is no clear definition of
;; what "too close" really means, so it is hard to simulate it.
;; Currently, this package return both the `:foreground' and
;; `:distant-foreground' attributes. Hopefully, in the future, it
;; might be possible to deduce the color that is shown and use
;; `:foreground' to represent that color.
;; Other Font Lock Tools:
;;
;; This package is part of a suite of font-lock tools. The other
;; tools in the suite are:
;;
;;
;; Font Lock Studio:
;;
;; Interactive debugger for font-lock keywords (Emacs syntax
;; highlighting rules).
;;
;; Font Lock Studio lets you *single-step* Font Lock keywords --
;; matchers, highlights, and anchored rules, so that you can see what
;; happens when a buffer is fontified. You can set *breakpoints* on
;; or inside rules and *run* until one has been hit. When inside a
;; rule, matches are *visualized* using a palette of background
;; colors. The *explainer* can describe a rule in plain-text English.
;; Tight integration with *Edebug* allows you to step into Lisp
;; expressions that are part of the Font Lock keywords.
;;
;;
;; Font Lock Profiler:
;;
;; A profiler for font-lock keywords. This package measures time and
;; counts the number of times each part of a font-lock keyword is
;; used. For matchers, it counts the total number and the number of
;; successful matches.
;;
;; The result is presented in table that can be sorted by count or
;; time. The table can be expanded to include each part of the
;; font-lock keyword.
;;
;; In addition, this package can generate a log of all font-lock
;; events. This can be used to verify font-lock implementations,
;; concretely, this is used for back-to-back tests of the real
;; font-lock engine and Font Lock Studio, an interactive debugger for
;; font-lock keywords.
;;
;;
;; Highlight Refontification:
;;
;; Minor mode that visualizes how font-lock refontifies a buffer.
;; This is useful when developing or debugging font-lock keywords,
;; especially for keywords that span multiple lines.
;;
;; The background of the buffer is painted in a rainbow of colors,
;; where each band in the rainbow represent a region of the buffer
;; that has been refontified. When the buffer is modified, the
;; rainbow is updated.
;;
;;
;; Faceup:
;;
;; Emacs is capable of highlighting buffers based on language-specific
;; `font-lock' rules. This package makes it possible to perform
;; regression test for packages that provide font-lock rules.
;;
;; The underlying idea is to convert text with highlights ("faces")
;; into a plain text representation using the Faceup markup
;; language. This language is semi-human readable, for example:
;;
;; «k:this» is a keyword
;;
;; By comparing the current highlight with a highlight performed with
;; stable versions of a package, it's possible to automatically find
;; problems that otherwise would have been hard to spot.
;;
;; This package is designed to be used in conjunction with Ert, the
;; standard Emacs regression test system.
;;
;; The Faceup markup language is a generic markup language, regression
;; testing is merely one way to use it.
;;
;;
;; Font Lock Regression Suite:
;;
;; A collection of example source files for a large number of
;; programming languages, with ERT tests to ensure that syntax
;; highlighting does not accidentally change.
;;
;; For each source file, font-lock reference files are provided for
;; various Emacs versions. The reference files contains a plain-text
;; representation of source file with syntax highlighting, using the
;; format "faceup".
;;
;; Of course, the collection source file can be used for other kinds
;; of testing, not limited to font-lock regression testing.
;; History:
;;
;; Part of the code of this package was originally published as part
;; of the package `e2ansi', a package that can emit highlighted text
;; to a terminal using ANSI sequences. `e2ansi' can be configured to
;; be used with the command line command `more' and `less' to syntax
;; highlight anything viewed using those command.
;;; Code:
;; References:
;;
;; - `defface': "For backward compatibility, elements of SPEC can be
;; written as (DISPLAY ATTS) instead of (DISPLAY . ATTS)." However,
;; in most cases, it looks like the former is used, at least for
;; `custom-theme-set-faces', for all themes shipped with Emacs 25.
;; Unfortunately, it affects how the property `theme-faces' look.
;;
;; - The two variables below are automatically applied to
;; `get-text-property' (even though it's documentation doesn't
;; mention this).
;;
;; `char-property-alias-alist': Alist of alternative face properties
;; (e.g. `((face font-lock-face))'.
;;
;; `default-text-properties': Property list of default text
;; properties (e.g. `(face (:underline t))').
;;
;; WARNING: This is also active when getting accessing text
;; properties of STRINGS, hence the return value will depend on the
;; value if this variable in the buffer that happens to be current,
;; unless these variables explicitly are let-bound.
;;
;; - `get-char-property' is returns the value of text properties or
;; overlays. However, it only returns the value of one text
;; property/overlay, which makes it unsuitable for the `face'
;; property, as Emacs joins all occurrences when displaying the
;; result. This package provide `face-explorer-face-text-props-at'
;; that returns the combined value.
;;
;; - `next-single-property-change' finds the next text property change
;; and `next-char-property-change' the same for text properties or
;; overlays. Note that they behave differently when the text
;; property stretch to the end of the buffer, the former return
;; `nil' whereas the latter `point-max'.
;;
;; - A "display requirement" is an alist of the form `((DISPLAY-REQ
;; . (ATTR VALUE ...) ...)' which is the same as `((DISPLAY-REQ
;; ATTR VALUE ...) ...)'. DISPLAY-REQ is a form specifying
;; conditions to match a display. The first element may use
;; `default' as DISPLAY-REQ, meaning that the attributes it
;; specifies default values for the following elements.
;; Emacs bugs:
;;
;; The documentation of `defface' doesn't mention that `tty' is a
;; valid value for `type'.
;;
;; Manual specifies that all attributes of the `default' face should
;; be defined, but :distant-foreground isn't.
;;
;; When a face with a :distant-foreground is themed,
;; :distant-foreground is incorrectly retained.
;;
;; Re-evaluating a theme definition (?) hides it from `disable-theme'.
;; Bad faces:
;;
;; The following is a list of faces I've noticed contain a
;; questionable definition. The list is by no means complete.
;;
;; - `font-lock-keyword-face' use "cyan" as foreground color in light
;; displays in 8 color mode.
;;
;; - `font-lock-variable-name-face' use "yellow" as foreground color
;; in light displays in 8 color mode.
(defgroup face-explorer nil
"Library and tools for investigating faces."
:group 'faces)
(defcustom face-explorer-sample-text "Abc01234il()"
"Sample text for `face-explorer' commands."
:type 'string
:group 'face-explorer)
(defvar face-explorer-light-mode-colors '(default ("black" . "white"))
"List describing how to set sample colors in light background mode.
Each entry in the list can be:
- A face -- this face is used, but only if it provides different
colors in light and dark background mode.
- A cons pair of colors (strings) -- the first element is the
foreground color and the second the background.")
(defvar face-explorer-dark-mode-colors '(default ("white" . "black"))
"List describing how to set sample colors in dark background mode.
See `face-explorer-light-mode-colors' for more information.")
;; -------------------------------------------------------------------
;; Support functions.
;;
(defalias 'face-explorer-user-error
(if (fboundp 'user-error)
'user-error
'error))
(defun face-explorer-plist-set (plist attr value)
"Change value in PLIST of ATTR to VALUE, nondestructively.
Like `plist-put', but does not destructively change PLIST."
(let ((rest (plist-member plist attr)))
(if rest
(let ((new-plist (cons attr (cons value (cdr (cdr rest)))))
(head '()))
(while (not (eq plist rest))
(push (pop plist) head)
(push (pop plist) head))
(nconc (nreverse head) new-plist))
(cons attr (cons value plist)))))
(defun face-explorer-join-face-attributes (plist1 plist2)
"Join face attributes PLIST1 and PLIST2, PLIST1 takes precedence."
(while plist2
(let ((key (pop plist2))
(value (pop plist2)))
;; Height:s are accumulative.
(if (and (eq key :height)
(floatp value)
(floatp (plist-get plist1 :height)))
(setq plist1
(face-explorer-plist-set
plist1 :height (* value
(plist-get plist1 :height))))
(unless (plist-member plist1 key)
(setq plist1 (cons key (cons value plist1)))))))
plist1)
(defun face-explorer-join-face-xattributes (xplist1 xplist2)
"Join XPLIST1 and XPLIST2, XPLIST1 takes precedence.
Both arguments are on the form (FLAG . PLIST), where FLAG is a
non-nil if the face inherited (directly or indirectly) from the
`default' face, and PLIST is a property list of primitive face
attributes."
(if (car xplist1)
xplist1
(cons (car xplist2)
(face-explorer-join-face-attributes (cdr xplist1)
(cdr xplist2)))))
(defun face-explorer-keep-well-formed-attributes (plist)
"Remove incorrectly formed entries from PLIST.
Only plists entries where the key is a keywords (:name) are retained.
Return a plist, the returned plist may be completely or partially
the same as the original PLIST."
(if (null plist)
plist
(if (keywordp (car plist))
(let* ((orig-rest (cdr-safe (cdr plist)))
(rest (face-explorer-keep-well-formed-attributes orig-rest)))
(if (eq orig-rest rest)
plist
`(,(nth 0 plist) ,(nth 1 plist) ,@rest)))
;; Incorrectly formed entry, drop it.
(face-explorer-keep-well-formed-attributes (cdr-safe (cdr plist))))))
;; C.f. `custom-fix-face-spec'.
(defun face-explorer-fix-face-attributes (plist)
"Convert deprecated face attributes in PLIST to modern variants."
(if (null plist)
plist
(let ((rest (cdr (cdr plist))))
(let ((new-rest (face-explorer-fix-face-attributes rest))
(key (nth 0 plist))
(value (nth 1 plist)))
(cond ((eq key :italic)
`(:slant ,(if value 'italic 'normal) ,@new-rest))
((eq key :bold)
`(:weight ,(if value 'bold 'normal) ,@new-rest))
((eq key :reverse-video)
`(:inverse-video ,value ,@new-rest))
((and (eq key :box)
(eq value t))
`(:box 1 ,@new-rest))
((eq rest new-rest)
;; No change. Use original plist to avoid consing.
plist)
(t
`(,key ,value ,@new-rest)))))))
(defun face-explorer-face-attributes-for-future-frames (face)
"The primitive face attributes of FACE specified for future frames."
(let ((plist '()))
(dolist (attribute-pair custom-face-attributes)
(let ((attr (car attribute-pair)))
(let ((value (face-attribute face attr t t)))
(unless (or (null value)
(eq value 'unspecified))
(push value plist)
(push attr plist)))))
plist))
(defun face-explorer-not-disjunct-p (list1 list2)
"Non-nil, if LIST1 and LIST2 contain at least one common element."
(let ((res nil))
(while list1
(when (memq (pop list1) list2)
(setq res t)
(setq list1 nil)))
res))
(defun face-explorer-remove-plist-duplicates (plist)
"Remove duplicate properties from PLIST, nondestructively.
Entries towards the end of the the list take precedence."
(and plist
(let ((attr (nth 0 plist))
(rest (face-explorer-remove-plist-duplicates (cdr (cdr plist)))))
(if rest
(if (plist-member rest (car plist))
;; ATTR already present.
rest
;; Keep attr. (Optimized to avoid re-consing the entire list.)
(if (eq rest (cdr (cdr plist)))
plist
(cons attr (cons (nth 1 plist) rest))))
plist))))
(defun face-explorer-plist-delete (key plist)
"Delete KEY from property list PLIST, nondestructively.
Return a new property list."
(if (null plist)
plist
(let ((head-key (nth 0 plist))
(head-value (nth 1 plist))
(rest (cdr (cdr plist))))
(if (eq head-key key)
(face-explorer-plist-delete key rest)
(let ((new-rest (face-explorer-plist-delete key rest)))
(if (eq rest new-rest)
;; No change. Use original plist to avoid consing.
plist
`(,head-key ,head-value ,@new-rest)))))))
(defun face-explorer-prop-valid-value (attr value)
"For face attribute ATTR, return non-nil if VALUE is a valid value.
Currently, this is only implemented for some properties."
(cond ((memq attr '(:family :foundry :foreground :distant-foreground
:background))
(stringp value))
((eq attr :inherit)
(or (symbolp value)
(listp value)))
(t
t)))
(defun face-explorer-alias-of-face (face)
"Return FACE, or a face it is aliased to.
Face aliases are defined using `define-obsolete-face-alias'."
(let (new-face)
(while (and (setq new-face (get face 'face-alias))
(facep new-face))
(setq face new-face)))
face)
(defun face-explorer-parse-face-prop (face-prop
face-xattributes-function
&optional extra-arguments)
"Parse FACE-PROP, a face specification used by the `face' text property.
Face specification can be a single face or a (possibly nested)
list of faces and property value pairs. For
example `(font-lock-waning-face :underline t)'. Entries earlier
in the list take precedence.
Return (FLAG . PLIST) where FLAG is a non-nil if the face
inherited (directly or indirectly) from the `default' face, and
PLIST is a property list of primitive face attributes.
The returned PLIST contains the face properties needed to render
text the same way as FACE-PROP. However, it does not include
face properties inherited from the `default' face.
No face property is included twice. The :inherit face property
is never included, but face properties of the faces it has
inherited from are.
The actual properties of a face are determined by the function
bound to FACE-XATTRIBUTES-FUNCTION. It is passed a face, t (to
use `face-remapping-alist'), and the arguments in EXTRA-ARGUMENTS
and it should return (FLAG . PLIST) as described above.
Face remappings performed by `face-remapping-alist' are handled."
(unless (listp face-prop)
(setq face-prop (list face-prop)))
(let ((res-xattrs '(nil)))
(while face-prop
(let ((face-xplist
(cond ((keywordp (car face-prop))
;; Once a keyword is found, the rest of the list
;; is treated as a plist.
(let ((plist-xplist '(nil . ())))
(while face-prop
(let ((attr (pop face-prop))
(value (pop face-prop)))
(setq plist-xplist
(face-explorer-join-face-xattributes
(cond ((eq attr :inherit)
;; `value' is a face or list of faces.
(face-explorer-parse-face-prop
value
face-xattributes-function
extra-arguments))
((and (keywordp attr)
(face-explorer-prop-valid-value
attr value))
(list nil attr value))
(t
'(nil . ())))
plist-xplist))))
;; `face-prop' empty, so the outer loop will
;; break after this.
plist-xplist))
((eq (car face-prop) 'foreground-color)
(let ((color (cdr face-prop)))
(setq face-prop nil)
`(nil . (:foreground ,color))))
((eq (car face-prop) 'background-color)
(let ((color (cdr face-prop)))
(setq face-prop nil)
`(nil . (:background ,color))))
((or (symbolp (car face-prop))
(stringp (car face-prop)))
(let ((face (pop face-prop)))
(when (stringp face)
(setq face (intern face)))
(setq face (face-explorer-alias-of-face face))
(cond ((not (facep face))
'(nil . ()))
((eq face 'default)
'(t . ()))
(t
(face-explorer-parse-face-remapping-alist
face
face-xattributes-function
extra-arguments)))))
((listp (car face-prop))
(face-explorer-parse-face-prop
(pop face-prop)
face-xattributes-function
extra-arguments))
(t
(error "Unexpected face specification")))))
(setq res-xattrs (face-explorer-join-face-xattributes
res-xattrs
face-xplist))))
;; Strip away properties explicitly set to `unspecified'.
;;
;; For example: `((:foreground unspecified) green-face)'.
(let ((flag (car res-xattrs))
(plist (cdr res-xattrs))
(res-plist '()))
(while plist
(let ((attr (pop plist))
(value (pop plist)))
(unless (eq value 'unspecified)
(push attr res-plist)
(push value res-plist))))
(cons flag (nreverse res-plist)))))
(defun face-explorer-expand-inherit (plist use-remapping
face-xattributes-function
&optional extra-arguments)
"Expand :inherit in property list PLIST.
When USE-REMAPPING is non-nil, `face-remapping-alist' is
consulted when expanding inherited faces.
Return (FLAG . PLIST) where FLAG is a non-nil if the face
inherited (directly or indirectly) from the `default' face, and
PLIST is a property list of primitive face attributes.
Properties inherited from the `default' face are not included in
PLIST.
The actual properties of a face are determined by the function
bound to FACE-XATTRIBUTES-FUNCTION. It is passed a face,
USE-REMAPPING, and the arguments in EXTRA-ARGUMENTS and it should
return (FLAG . PLIST) as described above."
;; In a face attribute list, the position of the :inherit property
;; does not matter. (However, in `face' text properties, it does
;; matter.)
(let ((xplist '(nil . ()))
(inherit (plist-get plist :inherit)))
;; Get all inherited properties.
(when inherit
(unless (consp inherit)
(setq inherit (list inherit)))
(dolist (face inherit)
(when (facep face)
(setq face (face-explorer-alias-of-face face))
(if (eq face 'default)
(setq xplist
(cons t
(cdr xplist)))
(setq xplist
(face-explorer-join-face-xattributes
xplist
(if use-remapping
(face-explorer-parse-face-remapping-alist
face
face-xattributes-function
extra-arguments)
(apply face-xattributes-function
face
use-remapping
extra-arguments))))))))
;; Override with properties explicitly defined.
(face-explorer-join-face-xattributes
(cons nil (face-explorer-plist-delete :inherit plist))
xplist)))
(defun face-explorer-maybe-set-inherit-from-default-flag (xplist is-default)
"Set FLAG field in XPLIST to t if IS-DEFAULT is non-nil, nondestructively.
XPLIST is in the form (FLAG . PLIST)."
(cond ((null is-default)
xplist)
((car xplist)
xplist)
(t
(cons t (cdr xplist)))))
(defvar face-explorer--seen-faces '()
"Faces seen by `face-explorer-parse-face-remapping-alist'.
This is used to break loops when parsing `face-remapping-alist'.
This variable is typically `let'-bound.")
(defun face-explorer-parse-face-remapping-alist
(face face-xattributes-function &optional extra-arguments)
"The primitive face attributes of FACE, including face remappings.
Return (FLAG . PLIST) where FLAG is a non-nil if the face
inherited (directly or indirectly) from the `default' face, and
PLIST is a property list of primitive face attributes.
Properties inherited from the `default' face are not included in
PLIST.
See `face-explorer-parse-face-prop' for information about
FACE-XATTRIBUTES-FUNCTION and EXTRA-ARGUMENTS.
See `face-remapping-alist' for information on face remappings."
(let ((entry (assq face face-remapping-alist)))
(if (or (null entry)
(memq face face-explorer--seen-faces))
;; Use the non-aliased value.
(apply face-xattributes-function
face
t
extra-arguments)
(let ((face-explorer--seen-faces
(cons face face-explorer--seen-faces)))
(face-explorer-parse-face-prop
(cdr entry)
face-xattributes-function
extra-arguments)))))
(defun face-explorer-next-face-property-change (pos &optional limit)
"Next position after POS where the `face' property change.
Both text properties and overlays are searched.
Search up to LIMIT (defaults to `point-max').
If POS is nil and the first character has a `face' property,
return `point-min'.
If last character contains a face property, return `point-max'."
(unless limit
(setq limit (point-max)))
(if (equal pos limit)
;; Last search returned `limit'. There is no more to search for.
nil
(if (and (null pos)
(get-text-property (point-min) 'face))
;; `pos' is `nil' and the character at `point-min' contains a
;; face property, return `point-min'.
(point-min)
(unless pos
;; Start from the beginning.
(setq pos (point-min)))
(next-char-property-change pos limit))))
(defun face-explorer-overlays-at (pos)
"The overlays at POS.
In newer Emacs versions, the list is sorted by decreasing priority."
(condition-case nil
(with-no-warnings
(overlays-at pos :sorted))
(error
;; Note: `reverse' is needed to preserve the original order for
;; otherwise equal overlays. In addition, as it creates a new
;; list, it is used to ensure that `sort' doesn't destructively
;; modify the original list returned by `overlays-at'.
(let ((seq (reverse (overlays-at pos))))
(setq seq
(sort seq (lambda (a b)
(let ((a-prio (or (overlay-get a 'priority)
0))
(b-prio (or (overlay-get b 'priority)
0)))
;; Priorities are typically numbers, but some
;; internal overlays use other, undocumented,
;; values.
(if (not (equal a-prio b-prio))
(and (numberp a-prio)
(numberp b-prio)
(< a-prio b-prio))
(let ((a-start (overlay-start a))
(b-start (overlay-start b)))
(if (not (eq a-start b-start))
(< a-start b-start)
(let ((a-end (overlay-end a))
(b-end (overlay-end b)))
(if (not (eq a-end b-end))
(> a-end b-end)
nil)))))))))
seq))))
(defun face-explorer-face-text-props-at (&optional pos)
"The active face text properties at POS.