aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Kangas2021-03-08 03:29:42 +0100
committerStefan Kangas2021-03-08 04:23:08 +0100
commit4a112fd7a6f0dcbd1b99b811b324123f5699bdfb (patch)
treeb3816a8ab7f3a05785ead1a590d0ab2dc5cd2921
parent8e103ebef12bb723723c7e6ec8e1053e86878a5b (diff)
downloademacs-4a112fd7a6f0dcbd1b99b811b324123f5699bdfb.tar.gz
emacs-4a112fd7a6f0dcbd1b99b811b324123f5699bdfb.zip
Add new face 'help-key-binding' for keybindings in help
* lisp/faces.el (help-key-binding): New face. * lisp/help.el (help-for-help): Rename from 'help-for-help-internal'. Use 'substitute-command-keys' syntax. (help): Make into alias for 'help-for-help'. (help-for-help-internal): Make into obsolete alias for 'help-for-help'. (help--key-description-fontified): New function to add the 'help-key-binding' face. (help-key-description, substitute-command-keys) (describe-map-tree, help--describe-command) (help--describe-translation, describe-map): * lisp/help-fns.el (help-fns--key-bindings, describe-mode): Use above new function. * lisp/isearch.el (isearch-help-for-help-internal): Use `substitute-command-keys' syntax. * lisp/help-macro.el (make-help-screen): Use 'substitute-command-keys' and 'help--key-description-fontified'. Simplify. * src/keymap.c (describe_key_maybe_fontify): New function to add the 'help-key-binding' face to keybindings. (describe_vector): Use above new keybinding. (syms_of_keymap) <Qfont_lock_face, Qhelp_key_binding>: New DEFSYMs. (fontify_key_properties): New static variable. * lisp/tooltip.el (tooltip-show): Avoid overriding faces in specified tooltip text. * test/lisp/help-tests.el (with-substitute-command-keys-test): Don't test for text properties. (help-tests-substitute-command-keys/add-key-face) (help-tests-substitute-command-keys/add-key-face-listing): New tests.
-rw-r--r--etc/NEWS9
-rw-r--r--lisp/faces.el17
-rw-r--r--lisp/help-fns.el21
-rw-r--r--lisp/help-macro.el224
-rw-r--r--lisp/help.el153
-rw-r--r--lisp/isearch.el8
-rw-r--r--lisp/tooltip.el7
-rw-r--r--src/keymap.c25
-rw-r--r--test/lisp/help-tests.el24
9 files changed, 289 insertions, 199 deletions
diff --git a/etc/NEWS b/etc/NEWS
index cf21a7b0f18..3d94a0325d4 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -920,6 +920,15 @@ skipped.
920** Help 920** Help
921 921
922--- 922---
923*** Keybindings in 'help-mode' use the new 'help-key-binding' face.
924This face is added by 'substitute-command-keys' to any "\[command]"
925substitution. The return value of that function should consequently
926be assumed to be a propertized string.
927
928Note that the new face will also be used in tooltips. When using the
929GTK toolkit, this is only true if 'x-gtk-use-system-tooltips' is t.
930
931---
923*** 'g' ('revert-buffer') in 'help-mode' no longer requires confirmation. 932*** 'g' ('revert-buffer') in 'help-mode' no longer requires confirmation.
924 933
925+++ 934+++
diff --git a/lisp/faces.el b/lisp/faces.el
index 90f11bbe3bb..b2d47edca0f 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -2815,6 +2815,23 @@ Note: Other faces cannot inherit from the cursor face."
2815 "Face to highlight argument names in *Help* buffers." 2815 "Face to highlight argument names in *Help* buffers."
2816 :group 'help) 2816 :group 'help)
2817 2817
2818(defface help-key-binding
2819 '((((class color) (min-colors 88) (background light)) :foreground "ForestGreen")
2820 (((class color) (min-colors 88) (background dark)) :foreground "#44bc44")
2821 (((class color grayscale) (background light)) :foreground "grey15")
2822 (((class color grayscale) (background dark)) :foreground "grey85")
2823 (t :foreground "ForestGreen"))
2824 "Face for keybindings in *Help* buffers.
2825
2826This face is added by `substitute-command-keys', which see.
2827
2828Note that this face will also be used for key bindings in
2829tooltips. This means that, for example, changing the :height of
2830this face will increase the height of any tooltip containing key
2831bindings. See also the face `tooltip'."
2832 :version "28.1"
2833 :group 'help)
2834
2818(defface glyphless-char 2835(defface glyphless-char
2819 '((((type tty)) :inherit underline) 2836 '((((type tty)) :inherit underline)
2820 (((type pc)) :inherit escape-glyph) 2837 (((type pc)) :inherit escape-glyph)
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index 01d3756bf0c..c27cdb5aa45 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -466,13 +466,16 @@ suitable file is found, return nil."
466 ;; If lots of ordinary text characters run this command, 466 ;; If lots of ordinary text characters run this command,
467 ;; don't mention them one by one. 467 ;; don't mention them one by one.
468 (if (< (length non-modified-keys) 10) 468 (if (< (length non-modified-keys) 10)
469 (princ (mapconcat #'key-description keys ", ")) 469 (with-current-buffer standard-output
470 (insert (mapconcat #'help--key-description-fontified
471 keys ", ")))
470 (dolist (key non-modified-keys) 472 (dolist (key non-modified-keys)
471 (setq keys (delq key keys))) 473 (setq keys (delq key keys)))
472 (if keys 474 (if keys
473 (progn 475 (with-current-buffer standard-output
474 (princ (mapconcat #'key-description keys ", ")) 476 (insert (mapconcat #'help--key-description-fontified
475 (princ ", and many ordinary text characters")) 477 keys ", "))
478 (insert ", and many ordinary text characters"))
476 (princ "many ordinary text characters")))) 479 (princ "many ordinary text characters"))))
477 (when (or remapped keys non-modified-keys) 480 (when (or remapped keys non-modified-keys)
478 (princ ".") 481 (princ ".")
@@ -1824,10 +1827,12 @@ documentation for the major and minor modes of that buffer."
1824 (save-excursion 1827 (save-excursion
1825 (re-search-backward (substitute-command-keys "`\\([^`']+\\)'") 1828 (re-search-backward (substitute-command-keys "`\\([^`']+\\)'")
1826 nil t) 1829 nil t)
1827 (help-xref-button 1 'help-function-def mode file-name))))) 1830 (help-xref-button 1 'help-function-def mode file-name)))))
1828 (princ ":\n") 1831 (let ((fundoc (help-split-fundoc (documentation major-mode) nil 'doc)))
1829 (princ (help-split-fundoc (documentation major-mode) nil 'doc)) 1832 (with-current-buffer standard-output
1830 (princ (help-fns--list-local-commands))))) 1833 (insert ":\n")
1834 (insert fundoc)
1835 (insert (help-fns--list-local-commands)))))))
1831 ;; For the sake of IELM and maybe others 1836 ;; For the sake of IELM and maybe others
1832 nil) 1837 nil)
1833 1838
diff --git a/lisp/help-macro.el b/lisp/help-macro.el
index 791b10a878f..72371a87278 100644
--- a/lisp/help-macro.el
+++ b/lisp/help-macro.el
@@ -92,119 +92,117 @@ If HELP-TEXT contains the sequence `%THIS-KEY%', that is replaced
92with the key sequence that invoked FNAME. 92with the key sequence that invoked FNAME.
93When FNAME finally does get a command, it executes that command 93When FNAME finally does get a command, it executes that command
94and then returns." 94and then returns."
95 (let ((doc-fn (intern (concat (symbol-name fname) "-doc")))) 95 (declare (indent defun))
96 `(progn 96 `(defun ,fname ()
97 (defun ,doc-fn () ,help-text nil) 97 "Help command."
98 (defun ,fname () 98 (interactive)
99 "Help command." 99 (let ((line-prompt
100 (interactive) 100 (substitute-command-keys ,help-line)))
101 (let ((line-prompt 101 (when three-step-help
102 (substitute-command-keys ,help-line))) 102 (message "%s" line-prompt))
103 (when three-step-help 103 (let* ((help-screen ,help-text)
104 (message "%s" line-prompt)) 104 ;; We bind overriding-local-map for very small
105 (let* ((help-screen (documentation (quote ,doc-fn))) 105 ;; sections, *excluding* where we switch buffers
106 ;; We bind overriding-local-map for very small 106 ;; and where we execute the chosen help command.
107 ;; sections, *excluding* where we switch buffers 107 (local-map (make-sparse-keymap))
108 ;; and where we execute the chosen help command. 108 (new-minor-mode-map-alist minor-mode-map-alist)
109 (local-map (make-sparse-keymap)) 109 (prev-frame (selected-frame))
110 (new-minor-mode-map-alist minor-mode-map-alist) 110 config new-frame key char)
111 (prev-frame (selected-frame)) 111 (when (string-match "%THIS-KEY%" help-screen)
112 config new-frame key char) 112 (setq help-screen
113 (when (string-match "%THIS-KEY%" help-screen) 113 (replace-match (help--key-description-fontified
114 (setq help-screen 114 (substring (this-command-keys) 0 -1))
115 (replace-match (key-description 115 t t help-screen)))
116 (substring (this-command-keys) 0 -1)) 116 (unwind-protect
117 t t help-screen))) 117 (let ((minor-mode-map-alist nil))
118 (unwind-protect 118 (setcdr local-map ,helped-map)
119 (let ((minor-mode-map-alist nil)) 119 (define-key local-map [t] 'undefined)
120 (setcdr local-map ,helped-map) 120 ;; Make the scroll bar keep working normally.
121 (define-key local-map [t] 'undefined) 121 (define-key local-map [vertical-scroll-bar]
122 ;; Make the scroll bar keep working normally. 122 (lookup-key global-map [vertical-scroll-bar]))
123 (define-key local-map [vertical-scroll-bar] 123 (if three-step-help
124 (lookup-key global-map [vertical-scroll-bar])) 124 (progn
125 (if three-step-help 125 (setq key (let ((overriding-local-map local-map))
126 (progn 126 (read-key-sequence nil)))
127 (setq key (let ((overriding-local-map local-map)) 127 ;; Make the HELP key translate to C-h.
128 (read-key-sequence nil))) 128 (if (lookup-key function-key-map key)
129 ;; Make the HELP key translate to C-h. 129 (setq key (lookup-key function-key-map key)))
130 (if (lookup-key function-key-map key) 130 (setq char (aref key 0)))
131 (setq key (lookup-key function-key-map key))) 131 (setq char ??))
132 (setq char (aref key 0))) 132 (when (or (eq char ??) (eq char help-char)
133 (setq char ??)) 133 (memq char help-event-list))
134 (when (or (eq char ??) (eq char help-char) 134 (setq config (current-window-configuration))
135 (memq char help-event-list)) 135 (pop-to-buffer " *Metahelp*" nil t)
136 (setq config (current-window-configuration)) 136 (and (fboundp 'make-frame)
137 (pop-to-buffer " *Metahelp*" nil t) 137 (not (eq (window-frame)
138 (and (fboundp 'make-frame) 138 prev-frame))
139 (not (eq (window-frame) 139 (setq new-frame (window-frame)
140 prev-frame)) 140 config nil))
141 (setq new-frame (window-frame) 141 (setq buffer-read-only nil)
142 config nil)) 142 (let ((inhibit-read-only t))
143 (setq buffer-read-only nil) 143 (erase-buffer)
144 (let ((inhibit-read-only t)) 144 (insert (substitute-command-keys help-screen)))
145 (erase-buffer) 145 (let ((minor-mode-map-alist new-minor-mode-map-alist))
146 (insert help-screen)) 146 (help-mode)
147 (let ((minor-mode-map-alist new-minor-mode-map-alist)) 147 (setq new-minor-mode-map-alist minor-mode-map-alist))
148 (help-mode) 148 (goto-char (point-min))
149 (setq new-minor-mode-map-alist minor-mode-map-alist)) 149 (while (or (memq char (append help-event-list
150 (goto-char (point-min)) 150 (cons help-char '(?? ?\C-v ?\s ?\177 delete backspace vertical-scroll-bar ?\M-v))))
151 (while (or (memq char (append help-event-list 151 (eq (car-safe char) 'switch-frame)
152 (cons help-char '(?? ?\C-v ?\s ?\177 delete backspace vertical-scroll-bar ?\M-v)))) 152 (equal key "\M-v"))
153 (eq (car-safe char) 'switch-frame) 153 (condition-case nil
154 (equal key "\M-v")) 154 (cond
155 (condition-case nil 155 ((eq (car-safe char) 'switch-frame)
156 (cond 156 (handle-switch-frame char))
157 ((eq (car-safe char) 'switch-frame) 157 ((memq char '(?\C-v ?\s))
158 (handle-switch-frame char)) 158 (scroll-up))
159 ((memq char '(?\C-v ?\s)) 159 ((or (memq char '(?\177 ?\M-v delete backspace))
160 (scroll-up)) 160 (equal key "\M-v"))
161 ((or (memq char '(?\177 ?\M-v delete backspace)) 161 (scroll-down)))
162 (equal key "\M-v")) 162 (error nil))
163 (scroll-down))) 163 (let ((cursor-in-echo-area t)
164 (error nil)) 164 (overriding-local-map local-map))
165 (let ((cursor-in-echo-area t) 165 (setq key (read-key-sequence
166 (overriding-local-map local-map)) 166 (format "Type one of the options listed%s: "
167 (setq key (read-key-sequence 167 (if (pos-visible-in-window-p
168 (format "Type one of the options listed%s: " 168 (point-max))
169 (if (pos-visible-in-window-p 169 "" ", or SPACE or DEL to scroll")))
170 (point-max)) 170 char (aref key 0)))
171 "" ", or SPACE or DEL to scroll"))) 171
172 char (aref key 0))) 172 ;; If this is a scroll bar command, just run it.
173 173 (when (eq char 'vertical-scroll-bar)
174 ;; If this is a scroll bar command, just run it. 174 (command-execute (lookup-key local-map key) nil key))))
175 (when (eq char 'vertical-scroll-bar) 175 ;; We don't need the prompt any more.
176 (command-execute (lookup-key local-map key) nil key)))) 176 (message "")
177 ;; We don't need the prompt any more. 177 ;; Mouse clicks are not part of the help feature,
178 (message "") 178 ;; so reexecute them in the standard environment.
179 ;; Mouse clicks are not part of the help feature, 179 (if (listp char)
180 ;; so reexecute them in the standard environment. 180 (setq unread-command-events
181 (if (listp char) 181 (cons char unread-command-events)
182 (setq unread-command-events 182 config nil)
183 (cons char unread-command-events) 183 (let ((defn (lookup-key local-map key)))
184 config nil) 184 (if defn
185 (let ((defn (lookup-key local-map key))) 185 (progn
186 (if defn 186 (when config
187 (progn 187 (set-window-configuration config)
188 (when config 188 (setq config nil))
189 (set-window-configuration config) 189 ;; Temporarily rebind `minor-mode-map-alist'
190 (setq config nil)) 190 ;; to `new-minor-mode-map-alist' (Bug#10454).
191 ;; Temporarily rebind `minor-mode-map-alist' 191 (let ((minor-mode-map-alist new-minor-mode-map-alist))
192 ;; to `new-minor-mode-map-alist' (Bug#10454). 192 ;; `defn' must make sure that its frame is
193 (let ((minor-mode-map-alist new-minor-mode-map-alist)) 193 ;; selected, so we won't iconify it below.
194 ;; `defn' must make sure that its frame is 194 (call-interactively defn))
195 ;; selected, so we won't iconify it below. 195 (when new-frame
196 (call-interactively defn)) 196 ;; Do not iconify the selected frame.
197 (when new-frame 197 (unless (eq new-frame (selected-frame))
198 ;; Do not iconify the selected frame. 198 (iconify-frame new-frame))
199 (unless (eq new-frame (selected-frame)) 199 (setq new-frame nil)))
200 (iconify-frame new-frame)) 200 (ding)))))
201 (setq new-frame nil))) 201 (when config
202 (ding))))) 202 (set-window-configuration config))
203 (when config 203 (when new-frame
204 (set-window-configuration config)) 204 (iconify-frame new-frame))
205 (when new-frame 205 (setq minor-mode-map-alist new-minor-mode-map-alist))))))
206 (iconify-frame new-frame))
207 (setq minor-mode-map-alist new-minor-mode-map-alist))))))))
208 206
209(provide 'help-macro) 207(provide 'help-macro)
210 208
diff --git a/lisp/help.el b/lisp/help.el
index 084e941549e..94073e5730a 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -104,8 +104,8 @@
104 (define-key map "R" 'info-display-manual) 104 (define-key map "R" 'info-display-manual)
105 (define-key map "s" 'describe-syntax) 105 (define-key map "s" 'describe-syntax)
106 (define-key map "t" 'help-with-tutorial) 106 (define-key map "t" 'help-with-tutorial)
107 (define-key map "w" 'where-is)
108 (define-key map "v" 'describe-variable) 107 (define-key map "v" 'describe-variable)
108 (define-key map "w" 'where-is)
109 (define-key map "q" 'help-quit) 109 (define-key map "q" 'help-quit)
110 map) 110 map)
111 "Keymap for characters following the Help key.") 111 "Keymap for characters following the Help key.")
@@ -187,64 +187,58 @@ Do not call this in the scope of `with-help-window'."
187;; So keyboard macro definitions are documented correctly 187;; So keyboard macro definitions are documented correctly
188(fset 'defining-kbd-macro (symbol-function 'start-kbd-macro)) 188(fset 'defining-kbd-macro (symbol-function 'start-kbd-macro))
189 189
190(defalias 'help 'help-for-help-internal) 190(defalias 'help 'help-for-help)
191;; find-function can find this. 191(make-help-screen help-for-help
192(defalias 'help-for-help 'help-for-help-internal)
193;; It can't find this, but nobody will look.
194(make-help-screen help-for-help-internal
195 (purecopy "Type a help option: [abcCdefFgiIkKlLmnprstvw.] C-[cdefmnoptw] or ?") 192 (purecopy "Type a help option: [abcCdefFgiIkKlLmnprstvw.] C-[cdefmnoptw] or ?")
196 ;; Don't purecopy this one, because it's not evaluated (it's
197 ;; directly used as a docstring in a function definition, so it'll
198 ;; be moved to the DOC file anyway: no need for purecopying it).
199 "You have typed %THIS-KEY%, the help character. Type a Help option: 193 "You have typed %THIS-KEY%, the help character. Type a Help option:
200\(Use SPC or DEL to scroll through this text. Type \\<help-map>\\[help-quit] to exit the Help command.) 194\(Use SPC or DEL to scroll through this text. Type \\<help-map>\\[help-quit] to exit the Help command.)
201 195
202a PATTERN Show commands whose name matches the PATTERN (a list of words 196\\[apropos-command] PATTERN Show commands whose name matches the PATTERN (a list of words
203 or a regexp). See also the `apropos' command. 197 or a regexp). See also \\[apropos].
204b Display all key bindings. 198\\[describe-bindings] Display all key bindings.
205c KEYS Display the command name run by the given key sequence. 199\\[describe-key-briefly] KEYS Display the command name run by the given key sequence.
206C CODING Describe the given coding system, or RET for current ones. 200\\[describe-coding-system] CODING Describe the given coding system, or RET for current ones.
207d PATTERN Show a list of functions, variables, and other items whose 201\\[apropos-documentation] PATTERN Show a list of functions, variables, and other items whose
208 documentation matches the PATTERN (a list of words or a regexp). 202 documentation matches the PATTERN (a list of words or a regexp).
209e Go to the *Messages* buffer which logs echo-area messages. 203\\[view-echo-area-messages] Go to the *Messages* buffer which logs echo-area messages.
210f FUNCTION Display documentation for the given function. 204\\[describe-function] FUNCTION Display documentation for the given function.
211F COMMAND Show the Emacs manual's section that describes the command. 205\\[Info-goto-emacs-command-node] COMMAND Show the Emacs manual's section that describes the command.
212g Display information about the GNU project. 206\\[describe-gnu-project] Display information about the GNU project.
213h Display the HELLO file which illustrates various scripts. 207\\[view-hello-file] Display the HELLO file which illustrates various scripts.
214i Start the Info documentation reader: read included manuals. 208\\[info] Start the Info documentation reader: read included manuals.
215I METHOD Describe a specific input method, or RET for current. 209\\[describe-input-method] METHOD Describe a specific input method, or RET for current.
216k KEYS Display the full documentation for the key sequence. 210\\[describe-key] KEYS Display the full documentation for the key sequence.
217K KEYS Show the Emacs manual's section for the command bound to KEYS. 211\\[Info-goto-emacs-key-command-node] KEYS Show the Emacs manual's section for the command bound to KEYS.
218l Show last 300 input keystrokes (lossage). 212\\[view-lossage] Show last 300 input keystrokes (lossage).
219L LANG-ENV Describes a specific language environment, or RET for current. 213\\[describe-language-environment] LANG-ENV Describes a specific language environment, or RET for current.
220m Display documentation of current minor modes and current major mode, 214\\[describe-mode] Display documentation of current minor modes and current major mode,
221 including their special commands. 215 including their special commands.
222n Display news of recent Emacs changes. 216\\[view-emacs-news] Display news of recent Emacs changes.
223o SYMBOL Display the given function or variable's documentation and value. 217\\[describe-symbol] SYMBOL Display the given function or variable's documentation and value.
224p TOPIC Find packages matching a given topic keyword. 218\\[finder-by-keyword] TOPIC Find packages matching a given topic keyword.
225P PACKAGE Describe the given Emacs Lisp package. 219\\[describe-package] PACKAGE Describe the given Emacs Lisp package.
226r Display the Emacs manual in Info mode. 220\\[info-emacs-manual] Display the Emacs manual in Info mode.
227R Prompt for a manual and then display it in Info mode. 221\\[info-display-manual] Prompt for a manual and then display it in Info mode.
228s Display contents of current syntax table, plus explanations. 222\\[describe-syntax] Display contents of current syntax table, plus explanations.
229S SYMBOL Show the section for the given symbol in the Info manual 223\\[info-lookup-symbol] SYMBOL Show the section for the given symbol in the Info manual
230 for the programming language used in this buffer. 224 for the programming language used in this buffer.
231t Start the Emacs learn-by-doing tutorial. 225\\[help-with-tutorial] Start the Emacs learn-by-doing tutorial.
232v VARIABLE Display the given variable's documentation and value. 226\\[describe-variable] VARIABLE Display the given variable's documentation and value.
233w COMMAND Display which keystrokes invoke the given command (where-is). 227\\[where-is] COMMAND Display which keystrokes invoke the given command (where-is).
234. Display any available local help at point in the echo area. 228\\[display-local-help] Display any available local help at point in the echo area.
235 229
236C-a Information about Emacs. 230\\[about-emacs] Information about Emacs.
237C-c Emacs copying permission (GNU General Public License). 231\\[describe-copying] Emacs copying permission (GNU General Public License).
238C-d Instructions for debugging GNU Emacs. 232\\[view-emacs-debugging] Instructions for debugging GNU Emacs.
239C-e External packages and information about Emacs. 233\\[view-external-packages] External packages and information about Emacs.
240C-f Emacs FAQ. 234\\[view-emacs-FAQ] Emacs FAQ.
241C-m How to order printed Emacs manuals. 235C-m How to order printed Emacs manuals.
242C-n News of recent Emacs changes. 236C-n News of recent Emacs changes.
243C-o Emacs ordering and distribution information. 237\\[describe-distribution] Emacs ordering and distribution information.
244C-p Info about known Emacs problems. 238\\[view-emacs-problems] Info about known Emacs problems.
245C-s Search forward \"help window\". 239\\[search-forward-help-for-help] Search forward \"help window\".
246C-t Emacs TODO list. 240\\[view-emacs-todo] Emacs TODO list.
247C-w Information on absence of warranty for GNU Emacs." 241\\[describe-no-warranty] Information on absence of warranty for GNU Emacs."
248 help-map) 242 help-map)
249 243
250 244
@@ -492,6 +486,15 @@ To record all your input, use `open-dribble-file'."
492 486
493;; Key bindings 487;; Key bindings
494 488
489(defun help--key-description-fontified (keys &optional prefix)
490 "Like `key-description' but add face for \"*Help*\" buffers."
491 ;; We add both the `font-lock-face' and `face' properties here, as this
492 ;; seems to be the only way to get this to work reliably in any
493 ;; buffer.
494 (propertize (key-description keys prefix)
495 'font-lock-face 'help-key-binding
496 'face 'help-key-binding))
497
495(defun describe-bindings (&optional prefix buffer) 498(defun describe-bindings (&optional prefix buffer)
496 "Display a buffer showing a list of all defined keys, and their definitions. 499 "Display a buffer showing a list of all defined keys, and their definitions.
497The keys are displayed in order of precedence. 500The keys are displayed in order of precedence.
@@ -511,7 +514,6 @@ or a buffer name."
511 (with-current-buffer (help-buffer) 514 (with-current-buffer (help-buffer)
512 (describe-buffer-bindings buffer prefix)))) 515 (describe-buffer-bindings buffer prefix))))
513 516
514;; This function used to be in keymap.c.
515(defun describe-bindings-internal (&optional menus prefix) 517(defun describe-bindings-internal (&optional menus prefix)
516 "Show a list of all defined keys, and their definitions. 518 "Show a list of all defined keys, and their definitions.
517We put that list in a buffer, and display the buffer. 519We put that list in a buffer, and display the buffer.
@@ -559,7 +561,8 @@ If INSERT (the prefix arg) is non-nil, insert the message in the buffer."
559 (let* ((remapped (command-remapping symbol)) 561 (let* ((remapped (command-remapping symbol))
560 (keys (where-is-internal 562 (keys (where-is-internal
561 symbol overriding-local-map nil nil remapped)) 563 symbol overriding-local-map nil nil remapped))
562 (keys (mapconcat 'key-description keys ", ")) 564 (keys (mapconcat #'help--key-description-fontified
565 keys ", "))
563 string) 566 string)
564 (setq string 567 (setq string
565 (if insert 568 (if insert
@@ -587,11 +590,11 @@ If INSERT (the prefix arg) is non-nil, insert the message in the buffer."
587 nil) 590 nil)
588 591
589(defun help-key-description (key untranslated) 592(defun help-key-description (key untranslated)
590 (let ((string (key-description key))) 593 (let ((string (help--key-description-fontified key)))
591 (if (or (not untranslated) 594 (if (or (not untranslated)
592 (and (eq (aref untranslated 0) ?\e) (not (eq (aref key 0) ?\e)))) 595 (and (eq (aref untranslated 0) ?\e) (not (eq (aref key 0) ?\e))))
593 string 596 string
594 (let ((otherstring (key-description untranslated))) 597 (let ((otherstring (help--key-description-fontified untranslated)))
595 (if (equal string otherstring) 598 (if (equal string otherstring)
596 string 599 string
597 (format "%s (translated from %s)" string otherstring)))))) 600 (format "%s (translated from %s)" string otherstring))))))
@@ -979,7 +982,7 @@ is currently activated with completion."
979 "Substitute key descriptions for command names in STRING. 982 "Substitute key descriptions for command names in STRING.
980Each substring of the form \\\\=[COMMAND] is replaced by either a 983Each substring of the form \\\\=[COMMAND] is replaced by either a
981keystroke sequence that invokes COMMAND, or \"M-x COMMAND\" if COMMAND 984keystroke sequence that invokes COMMAND, or \"M-x COMMAND\" if COMMAND
982is not on any keys. 985is not on any keys. Keybindings will use the face `help-key-binding'.
983 986
984Each substring of the form \\\\={MAPVAR} is replaced by a summary of 987Each substring of the form \\\\={MAPVAR} is replaced by a summary of
985the value of MAPVAR as a keymap. This summary is similar to the one 988the value of MAPVAR as a keymap. This summary is similar to the one
@@ -999,7 +1002,7 @@ into the output, \\\\==\\[ puts \\[ into the output, and \\\\==\\=` puts \\=` in
999output. 1002output.
1000 1003
1001Return the original STRING if no substitutions are made. 1004Return the original STRING if no substitutions are made.
1002Otherwise, return a new string (without any text properties)." 1005Otherwise, return a new string."
1003 (when (not (null string)) 1006 (when (not (null string))
1004 ;; KEYMAP is either nil (which means search all the active 1007 ;; KEYMAP is either nil (which means search all the active
1005 ;; keymaps) or a specified local map (which means search just that 1008 ;; keymaps) or a specified local map (which means search just that
@@ -1053,12 +1056,16 @@ Otherwise, return a new string (without any text properties)."
1053 (where-is-internal fun keymap t)))) 1056 (where-is-internal fun keymap t))))
1054 (if (not key) 1057 (if (not key)
1055 ;; Function is not on any key. 1058 ;; Function is not on any key.
1056 (progn (insert "M-x ") 1059 (let ((op (point)))
1057 (goto-char (+ end-point 3)) 1060 (insert "M-x ")
1058 (delete-char 1)) 1061 (goto-char (+ end-point 3))
1062 (add-text-properties op (point)
1063 '( face help-key-binding
1064 font-lock-face help-key-binding))
1065 (delete-char 1))
1059 ;; Function is on a key. 1066 ;; Function is on a key.
1060 (delete-char (- end-point (point))) 1067 (delete-char (- end-point (point)))
1061 (insert (key-description key))))) 1068 (insert (help--key-description-fontified key)))))
1062 ;; 1D. \{foo} is replaced with a summary of the keymap 1069 ;; 1D. \{foo} is replaced with a summary of the keymap
1063 ;; (symbol-value foo). 1070 ;; (symbol-value foo).
1064 ;; \<foo> just sets the keymap used for \[cmd]. 1071 ;; \<foo> just sets the keymap used for \[cmd].
@@ -1172,7 +1179,7 @@ Any inserted text ends in two newlines (used by
1172 (concat title 1179 (concat title
1173 (if prefix 1180 (if prefix
1174 (concat " Starting With " 1181 (concat " Starting With "
1175 (key-description prefix))) 1182 (help--key-description-fontified prefix)))
1176 ":\n")) 1183 ":\n"))
1177 "key binding\n" 1184 "key binding\n"
1178 "--- -------\n"))) 1185 "--- -------\n")))
@@ -1228,7 +1235,11 @@ Return nil if the key sequence is too long."
1228 (= help--previous-description-column 32))) 1235 (= help--previous-description-column 32)))
1229 32) 1236 32)
1230 (t 16)))) 1237 (t 16))))
1231 (indent-to description-column 1) 1238 ;; Avoid using the `help-keymap' face.
1239 (let ((op (point)))
1240 (indent-to description-column 1)
1241 (set-text-properties op (point) '( face nil
1242 font-lock-face nil)))
1232 (setq help--previous-description-column description-column) 1243 (setq help--previous-description-column description-column)
1233 (cond ((symbolp definition) 1244 (cond ((symbolp definition)
1234 (insert (symbol-name definition) "\n")) 1245 (insert (symbol-name definition) "\n"))
@@ -1240,7 +1251,11 @@ Return nil if the key sequence is too long."
1240 1251
1241(defun help--describe-translation (definition) 1252(defun help--describe-translation (definition)
1242 ;; Converted from describe_translation in keymap.c. 1253 ;; Converted from describe_translation in keymap.c.
1243 (indent-to 16 1) 1254 ;; Avoid using the `help-keymap' face.
1255 (let ((op (point)))
1256 (indent-to 16)
1257 (set-text-properties op (point) '( face nil
1258 font-lock-face nil)))
1244 (cond ((symbolp definition) 1259 (cond ((symbolp definition)
1245 (insert (symbol-name definition) "\n")) 1260 (insert (symbol-name definition) "\n"))
1246 ((or (stringp definition) (vectorp definition)) 1261 ((or (stringp definition) (vectorp definition))
@@ -1351,9 +1366,9 @@ TRANSL, PARTIAL, SHADOW, NOMENU, MENTION-SHADOW are as in
1351 (setq end (caar vect)))) 1366 (setq end (caar vect))))
1352 ;; Now START .. END is the range to describe next. 1367 ;; Now START .. END is the range to describe next.
1353 ;; Insert the string to describe the event START. 1368 ;; Insert the string to describe the event START.
1354 (insert (key-description (vector start) prefix)) 1369 (insert (help--key-description-fontified (vector start) prefix))
1355 (when (not (eq start end)) 1370 (when (not (eq start end))
1356 (insert " .. " (key-description (vector end) prefix))) 1371 (insert " .. " (help--key-description-fontified (vector end) prefix)))
1357 ;; Print a description of the definition of this character. 1372 ;; Print a description of the definition of this character.
1358 ;; Called function will take care of spacing out far enough 1373 ;; Called function will take care of spacing out far enough
1359 ;; for alignment purposes. 1374 ;; for alignment purposes.
@@ -1420,7 +1435,7 @@ TRANSL, PARTIAL, SHADOW, NOMENU, MENTION-SHADOW are as in
1420;; (setq first nil)) 1435;; (setq first nil))
1421;; (when (and prefix (> (length prefix) 0)) 1436;; (when (and prefix (> (length prefix) 0))
1422;; (insert (format "%s" prefix))) 1437;; (insert (format "%s" prefix)))
1423;; (insert (key-description (vector start-idx) prefix)) 1438;; (insert (help--key-description-fontified (vector start-idx) prefix))
1424;; ;; Find all consecutive characters or rows that have the 1439;; ;; Find all consecutive characters or rows that have the
1425;; ;; same definition. 1440;; ;; same definition.
1426;; (while (equal (keymap--get-keyelt (aref vector (1+ idx)) nil) 1441;; (while (equal (keymap--get-keyelt (aref vector (1+ idx)) nil)
@@ -1433,7 +1448,7 @@ TRANSL, PARTIAL, SHADOW, NOMENU, MENTION-SHADOW are as in
1433;; (insert " .. ") 1448;; (insert " .. ")
1434;; (when (and prefix (> (length prefix) 0)) 1449;; (when (and prefix (> (length prefix) 0))
1435;; (insert (format "%s" prefix))) 1450;; (insert (format "%s" prefix)))
1436;; (insert (key-description (vector idx) prefix))) 1451;; (insert (help--key-description-fontified (vector idx) prefix)))
1437;; (if transl 1452;; (if transl
1438;; (help--describe-translation definition) 1453;; (help--describe-translation definition)
1439;; (help--describe-command definition)) 1454;; (help--describe-command definition))
@@ -1924,6 +1939,8 @@ the suggested string to use instead. See
1924(add-function :after command-error-function 1939(add-function :after command-error-function
1925 #'help-command-error-confusable-suggestions) 1940 #'help-command-error-confusable-suggestions)
1926 1941
1942(define-obsolete-function-alias 'help-for-help-internal #'help-for-help "28.1")
1943
1927 1944
1928(provide 'help) 1945(provide 'help)
1929 1946
diff --git a/lisp/isearch.el b/lisp/isearch.el
index e7926ac08ce..943e24aa563 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -460,11 +460,11 @@ and doesn't remove full-buffer highlighting after a search."
460(make-help-screen isearch-help-for-help-internal 460(make-help-screen isearch-help-for-help-internal
461 (purecopy "Type a help option: [bkm] or ?") 461 (purecopy "Type a help option: [bkm] or ?")
462 "You have typed %THIS-KEY%, the help character. Type a Help option: 462 "You have typed %THIS-KEY%, the help character. Type a Help option:
463\(Type \\<help-map>\\[help-quit] to exit the Help command.) 463\(Type \\<isearch-help-map>\\[help-quit] to exit the Help command.)
464 464
465b Display all Isearch key bindings. 465\\[isearch-describe-bindings] Display all Isearch key bindings.
466k KEYS Display full documentation of Isearch key sequence. 466\\[isearch-describe-key] KEYS Display full documentation of Isearch key sequence.
467m Display documentation of Isearch mode. 467\\[isearch-describe-mode] Display documentation of Isearch mode.
468 468
469You can't type here other help keys available in the global help map, 469You can't type here other help keys available in the global help map,
470but outside of this help window when you type them in Isearch mode, 470but outside of this help window when you type them in Isearch mode,
diff --git a/lisp/tooltip.el b/lisp/tooltip.el
index 8e00aa5c2a9..af3b86bba71 100644
--- a/lisp/tooltip.el
+++ b/lisp/tooltip.el
@@ -248,7 +248,12 @@ in echo area."
248 (setf (alist-get 'border-color params) fg)) 248 (setf (alist-get 'border-color params) fg))
249 (when (stringp bg) 249 (when (stringp bg)
250 (setf (alist-get 'background-color params) bg)) 250 (setf (alist-get 'background-color params) bg))
251 (x-show-tip (propertize text 'face 'tooltip) 251 ;; Use non-nil APPEND argument below to avoid overriding any
252 ;; faces used in our TEXT. Among other things, this allows
253 ;; tooltips to use the `help-key-binding' face used in
254 ;; `substitute-command-keys' substitutions.
255 (add-face-text-property 0 (length text) 'tooltip t text)
256 (x-show-tip text
252 (selected-frame) 257 (selected-frame)
253 params 258 params
254 tooltip-hide-delay 259 tooltip-hide-delay
diff --git a/src/keymap.c b/src/keymap.c
index 782931fadff..bb26b6389d4 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -2846,6 +2846,21 @@ DESCRIBER is the output function used; nil means use `princ'. */)
2846 return unbind_to (count, Qnil); 2846 return unbind_to (count, Qnil);
2847} 2847}
2848 2848
2849static Lisp_Object fontify_key_properties;
2850
2851static Lisp_Object
2852describe_key_maybe_fontify (Lisp_Object str, Lisp_Object prefix,
2853 bool keymap_p)
2854{
2855 Lisp_Object key_desc = Fkey_description (str, prefix);
2856 if (keymap_p)
2857 Fadd_text_properties (make_fixnum (0),
2858 make_fixnum (SCHARS (key_desc)),
2859 fontify_key_properties,
2860 key_desc);
2861 return key_desc;
2862}
2863
2849DEFUN ("help--describe-vector", Fhelp__describe_vector, Shelp__describe_vector, 7, 7, 0, 2864DEFUN ("help--describe-vector", Fhelp__describe_vector, Shelp__describe_vector, 7, 7, 0,
2850 doc: /* Insert in the current buffer a description of the contents of VECTOR. 2865 doc: /* Insert in the current buffer a description of the contents of VECTOR.
2851Call DESCRIBER to insert the description of one value found in VECTOR. 2866Call DESCRIBER to insert the description of one value found in VECTOR.
@@ -3021,7 +3036,7 @@ describe_vector (Lisp_Object vector, Lisp_Object prefix, Lisp_Object args,
3021 if (!NILP (elt_prefix)) 3036 if (!NILP (elt_prefix))
3022 insert1 (elt_prefix); 3037 insert1 (elt_prefix);
3023 3038
3024 insert1 (Fkey_description (kludge, prefix)); 3039 insert1 (describe_key_maybe_fontify (kludge, prefix, keymap_p));
3025 3040
3026 /* Find all consecutive characters or rows that have the same 3041 /* Find all consecutive characters or rows that have the same
3027 definition. But, if VECTOR is a char-table, we had better 3042 definition. But, if VECTOR is a char-table, we had better
@@ -3071,7 +3086,7 @@ describe_vector (Lisp_Object vector, Lisp_Object prefix, Lisp_Object args,
3071 if (!NILP (elt_prefix)) 3086 if (!NILP (elt_prefix))
3072 insert1 (elt_prefix); 3087 insert1 (elt_prefix);
3073 3088
3074 insert1 (Fkey_description (kludge, prefix)); 3089 insert1 (describe_key_maybe_fontify (kludge, prefix, keymap_p));
3075 } 3090 }
3076 3091
3077 /* Print a description of the definition of this character. 3092 /* Print a description of the definition of this character.
@@ -3200,6 +3215,12 @@ be preferred. */);
3200 staticpro (&where_is_cache); 3215 staticpro (&where_is_cache);
3201 staticpro (&where_is_cache_keymaps); 3216 staticpro (&where_is_cache_keymaps);
3202 3217
3218 DEFSYM (Qfont_lock_face, "font-lock-face");
3219 DEFSYM (Qhelp_key_binding, "help-key-binding");
3220 staticpro (&fontify_key_properties);
3221 fontify_key_properties = Fcons (Qfont_lock_face,
3222 Fcons (Qhelp_key_binding, Qnil));
3223
3203 defsubr (&Skeymapp); 3224 defsubr (&Skeymapp);
3204 defsubr (&Skeymap_parent); 3225 defsubr (&Skeymap_parent);
3205 defsubr (&Skeymap_prompt); 3226 defsubr (&Skeymap_prompt);
diff --git a/test/lisp/help-tests.el b/test/lisp/help-tests.el
index 8034764741c..b2fec5c1bde 100644
--- a/test/lisp/help-tests.el
+++ b/test/lisp/help-tests.el
@@ -26,6 +26,7 @@
26 26
27(require 'ert) 27(require 'ert)
28(eval-when-compile (require 'cl-lib)) 28(eval-when-compile (require 'cl-lib))
29(require 'text-property-search) ; for `text-property-search-forward'
29 30
30(ert-deftest help-split-fundoc-SECTION () 31(ert-deftest help-split-fundoc-SECTION ()
31 "Test new optional arg SECTION." 32 "Test new optional arg SECTION."
@@ -60,9 +61,8 @@
60(defmacro with-substitute-command-keys-test (&rest body) 61(defmacro with-substitute-command-keys-test (&rest body)
61 `(cl-flet* ((test 62 `(cl-flet* ((test
62 (lambda (orig result) 63 (lambda (orig result)
63 (should (equal-including-properties 64 (should (equal (substitute-command-keys orig)
64 (substitute-command-keys orig) 65 result))))
65 result))))
66 (test-re 66 (test-re
67 (lambda (orig regexp) 67 (lambda (orig regexp)
68 (should (string-match (concat "^" regexp "$") 68 (should (string-match (concat "^" regexp "$")
@@ -222,6 +222,24 @@ M-s next-matching-history-element
222(define-minor-mode help-tests-minor-mode 222(define-minor-mode help-tests-minor-mode
223 "Minor mode for testing shadowing.") 223 "Minor mode for testing shadowing.")
224 224
225(ert-deftest help-tests-substitute-command-keys/add-key-face ()
226 (should (equal (substitute-command-keys "\\[next-line]")
227 (propertize "C-n"
228 'face 'help-key-binding
229 'font-lock-face 'help-key-binding))))
230
231(ert-deftest help-tests-substitute-command-keys/add-key-face-listing ()
232 (with-temp-buffer
233 (insert (substitute-command-keys "\\{help-tests-minor-mode-map}"))
234 (goto-char (point-min))
235 (text-property-search-forward 'face 'help-key-binding)
236 (should (looking-at "C-e"))
237 ;; Don't fontify trailing whitespace.
238 (should-not (get-text-property (+ (point) 3) 'face))
239 (text-property-search-forward 'face 'help-key-binding)
240 (should (looking-at "x"))
241 (should-not (get-text-property (+ (point) 1) 'face))))
242
225(ert-deftest help-tests-substitute-command-keys/test-mode () 243(ert-deftest help-tests-substitute-command-keys/test-mode ()
226 (with-substitute-command-keys-test 244 (with-substitute-command-keys-test
227 (with-temp-buffer 245 (with-temp-buffer