Function: substitute-command-keys

substitute-command-keys is a byte-compiled function defined in help.el.gz.

Signature

(substitute-command-keys STRING &optional NO-FACE INCLUDE-MENUS)

Documentation

Substitute key descriptions for command names in STRING.

Each substring of the form \[COMMAND] is replaced by either a keystroke sequence that invokes COMMAND, or "M-x COMMAND" if COMMAND is not on any keys. Keybindings will use the face help-key-binding, unless the optional argument NO-FACE is non-nil.

Each substring of the form \`KEYBINDING' will be replaced by KEYBINDING and use the help-key-binding face.

Each substring of the form \{MAPVAR} is replaced by a summary of the value of MAPVAR as a keymap. This summary is similar to the one produced by describe-bindings. This will normally exclude menu bindings, but if the optional INCLUDE-MENUS argument is non-nil, also include menu bindings. The summary ends in two newlines (used by the helper function help-make-xrefs to find the end of the summary).

Each substring of the form \<MAPVAR> specifies the use of MAPVAR as the keymap for future \[COMMAND] substrings.

Each grave accent ` is replaced by left quote, and each apostrophe ' is replaced by right quote. Left and right quote characters are specified by text-quoting-style(var)/text-quoting-style(fun).

\= quotes the following character and is discarded; thus, \=\= puts \=
into the output, \=\[ puts \[ into the output, and \=` puts ` into the output.

Return the original STRING if no substitutions are made. Otherwise, return a new string.

View in manual

Probably introduced at or before Emacs version 16.

Source Code

;; Defined in /usr/src/emacs/lisp/help.el.gz
(defun substitute-command-keys (string &optional no-face include-menus)
  "Substitute key descriptions for command names in STRING.
Each substring of the form \\\\=[COMMAND] is replaced by either a
keystroke sequence that invokes COMMAND, or \"M-x COMMAND\" if COMMAND
is not on any keys.  Keybindings will use the face `help-key-binding',
unless the optional argument NO-FACE is non-nil.

Each substring of the form \\\\=`KEYBINDING' will be replaced by
KEYBINDING and use the `help-key-binding' face.

Each substring of the form \\\\={MAPVAR} is replaced by a summary
of the value of MAPVAR as a keymap.  This summary is similar to
the one produced by `describe-bindings'.  This will normally
exclude menu bindings, but if the optional INCLUDE-MENUS argument
is non-nil, also include menu bindings.  The summary ends in two
newlines (used by the helper function `help-make-xrefs' to find
the end of the summary).

Each substring of the form \\\\=<MAPVAR> specifies the use of MAPVAR
as the keymap for future \\\\=[COMMAND] substrings.

Each grave accent \\=` is replaced by left quote, and each apostrophe \\='
is replaced by right quote.  Left and right quote characters are
specified by `text-quoting-style'.

\\\\== quotes the following character and is discarded; thus, \\\\==\\\\== puts \\\\==
into the output, \\\\==\\[ puts \\[ into the output, and \\\\==\\=` puts \\=` into the
output.

Return the original STRING if no substitutions are made.
Otherwise, return a new string."
  (when (not (null string))
    ;; KEYMAP is either nil (which means search all the active
    ;; keymaps) or a specified local map (which means search just that
    ;; and the global map).  If non-nil, it might come from
    ;; overriding-local-map, or from a \\<mapname> construct in STRING
    ;; itself.
    (let ((keymap overriding-local-map)
          (inhibit-read-only t)
          (orig-buf (current-buffer)))
      (with-temp-buffer
        (setq-local inhibit-modification-hooks t) ;; For speed.
        (insert string)
        (goto-char (point-min))
        (while (< (point) (point-max))
          (let ((orig-point (point))
                end-point active-maps
                close generate-summary)
            (cond
             ;; 1. Handle all sequences starting with "\"
             ((= (following-char) ?\\)
              (ignore-errors
                (forward-char 1))
              (cond
               ;; 1A. Ignore \= at end of string.
               ((and (= (+ (point) 1) (point-max))
                     (= (following-char) ?=))
                (forward-char 1))
               ;; 1B. \= quotes the next character; thus, to put in \[
               ;;     without its special meaning, use \=\[.
               ((= (following-char) ?=)
                (goto-char orig-point)
                (delete-char 2)
                (ignore-errors
                  (forward-char 1)))
               ;; 1C. \`f' is replaced with a fontified f.
               ((and (= (following-char) ?`)
                     (save-excursion
                       (prog1 (search-forward "'" nil t)
                         (setq end-point (1- (point))))))
                (let ((k (buffer-substring-no-properties (+ orig-point 2)
                                                         end-point)))
                  (when (or (key-valid-p k)
                            (string-match-p "\\`mouse-[1-9]" k)
                            (string-match-p "\\`M-x " k))
                    (goto-char orig-point)
                    (delete-char 2)
                    (goto-char (- end-point 2)) ; nb. take deletion into account
                    (delete-char 1)
                    (unless no-face
                      (add-text-properties orig-point (point)
                                           '( face help-key-binding
                                              font-lock-face help-key-binding))))))
               ;; 1D. \[foo] is replaced with the keybinding.
               ((and (= (following-char) ?\[)
                     (save-excursion
                       (prog1 (search-forward "]" nil t)
                         (setq end-point (- (point) 2)))))
                (goto-char orig-point)
                (delete-char 2)
                (let* ((fun (intern (buffer-substring (point) (1- end-point))))
                       (key (with-current-buffer orig-buf
                              (where-is-internal fun
                                                 (and keymap
                                                      (list keymap))
                                                 t))))
                  ;; If we're looking in a particular keymap which has
                  ;; no binding, then we need to redo the lookup, with
                  ;; the global map as well this time.
                  (when (and (not key) keymap)
                    (setq key (with-current-buffer orig-buf
                                (where-is-internal fun keymap t))))
                  (if (not key)
                      ;; Function is not on any key.
                      (let ((op (point)))
                        (insert "M-x ")
                        (goto-char (+ end-point 3))
                        (or no-face
                            (add-text-properties
                             op (point)
                             '( face help-key-binding
                                font-lock-face help-key-binding)))
                        (delete-char 1))
                    ;; Function is on a key.
                    (delete-char (- end-point (point)))

                    (insert
                     (if no-face
                         (key-description key)
                       (let ((key (help--key-description-fontified key)))
                         (if (and help-link-key-to-documentation
                                  help-buffer-under-preparation
                                  (functionp fun))
                             ;; The `fboundp' fixes bootstrap.
                             (if (fboundp 'help-mode--add-function-link)
                                 (help-mode--add-function-link key fun)
                               key)
                           key)))))))
               ;; 1E. \{foo} is replaced with a summary of the keymap
               ;;            (symbol-value foo).
               ;;     \<foo> just sets the keymap used for \[cmd].
               ((and (or (and (= (following-char) ?{)
                              (setq close "}")
                              (setq generate-summary t))
                         (and (= (following-char) ?<)
                              (setq close ">")))
                     (or (save-excursion
                           (prog1 (search-forward close nil t)
                             (setq end-point (- (point) 2))))))
                (goto-char orig-point)
                (delete-char 2)
                (let* ((name (intern (buffer-substring (point) (1- end-point))))
                       this-keymap)
                  (delete-char (- end-point (point)))
                  ;; Get the value of the keymap in TEM, or nil if
                  ;; undefined. Do this in the user's current buffer
                  ;; in case it is a local variable.
                  (with-current-buffer orig-buf
                    ;; This is for computing the SHADOWS arg for
                    ;; help--describe-map-tree.
                    (setq active-maps (current-active-maps))
                    (when (boundp name)
                      (setq this-keymap (and (keymapp (symbol-value name))
                                             (symbol-value name)))))
                  (cond
                   ((null this-keymap)
                    (insert "\nUses keymap "
                            (substitute-quotes "`")
                            (symbol-name name)
                            (substitute-quotes "'")
                            ", which is not currently defined.\n")
                    (unless generate-summary
                      (setq keymap nil)))
                   ((not generate-summary)
                    (setq keymap this-keymap))
                   (t
                    ;; Get the list of active keymaps that precede this one.
                    ;; If this one's not active, get nil.
                    (let ((earlier-maps
                           (cdr (memq this-keymap (reverse active-maps)))))
                      (help--describe-map-tree this-keymap t
                                               (nreverse earlier-maps)
                                               nil nil (not include-menus)
                                               nil nil t))))))))
             ;; 2. Handle quotes.
             ((and (eq (text-quoting-style) 'curve)
                   (or (and (= (following-char) ?\`)
                            (prog1 t (insert "‘")))
                       (and (= (following-char) ?')
                            (prog1 t (insert "’")))))
              (delete-char 1))
             ((and (eq (text-quoting-style) 'straight)
                   (= (following-char) ?\`))
              (insert "'")
              (delete-char 1))
             ;; 3. Nothing to do -- next character.
             (t (forward-char 1)))))
        (buffer-string)))))