aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilfred Hughes <me@wilfred.me.uk>2017-12-30 01:23:06 +0000
committerWilfred Hughes <me@wilfred.me.uk>2017-12-30 01:23:06 +0000
commit59fb24dc969658f37e9464fc87200ed06a8b1ecc (patch)
treed28f180645575767a4c81a30bf8b59c3a5d15bd8
parented23fa740151cd15c27ad56d8cef5fc84b63d003 (diff)
Handle command key substitution correctly
Emacs allows you to write \[foo] to show the keybinding for command foo. It provides \= as a way of escaping. Ensure we substitute \= properly in docstrings, using a similar logic to substitute-command-keys. We still provide additional buttons, so \[foo] will be converted to a button pointing to foo in Helpful buffers. Fixes #80.
-rw-r--r--helpful.el72
-rw-r--r--test/unit-test.el7
2 files changed, 57 insertions, 22 deletions
diff --git a/helpful.el b/helpful.el
index b54f9e2..b30030c 100644
--- a/helpful.el
+++ b/helpful.el
@@ -569,38 +569,64 @@ blank line afterwards."
docstring
t t))
-(defun helpful--propertize-command-keys (docstring)
- "Convert command key references in docstrings to buttons."
- (replace-regexp-in-string
- ;; Replace all text of the form \\[foo] with a button that links to
- ;; foo but shows the keys necessary to call foo.
- (rx "\\["
- (group (+ (not (in "]"))))
- "]")
- (lambda (it)
- (let* ((symbol-with-parens (match-string 0 it))
- (symbol-name (match-string 1 it))
- (symbol (intern symbol-name)))
- (helpful--button
- ;; The button text should just be the keys required.
- (substitute-command-keys symbol-with-parens)
- 'helpful-describe-exactly-button
- 'symbol symbol
- 'callable-p t)))
- docstring
- t t))
+;; TODO: handle keymaps of the form \\{foo}.
+(defun helpful--format-command-keys (docstring)
+ "Convert command key references in docstrings to buttons.
+Emacs uses \\= to escape \\[ references, so replace that
+unescaping too."
+ ;; Based on `substitute-command-keys', but converts command
+ ;; references to buttons.
+ (with-temp-buffer
+ (insert docstring)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (cond
+ ((looking-at
+ ;; Text of the form \=X
+ (rx "\\="))
+ ;; Remove the escaping, then step over the escaped char.
+ ;; Step over the escaped character.
+ (delete-region (point) (+ (point) 2))
+ (forward-char 1))
+ ((looking-at
+ ;; Text of the form \\[foo]
+ (rx "\\[" (group (+ (not (in "]")))) "]"))
+ (let* ((symbol-with-parens (match-string 0))
+ (symbol-name (match-string 1)))
+ ;; Remove the original string.
+ (delete-region (point)
+ (+ (point) (length symbol-with-parens)))
+ ;; Add a button.
+ (let* ((symbol (intern symbol-name))
+ (key (where-is-internal symbol nil t))
+ (key-description
+ (if key
+ (key-description key)
+ (format "M-x %s" symbol-name)))))
+ (insert
+ (helpful--button
+ key-description
+ 'helpful-describe-exactly-button
+ 'symbol symbol
+ 'callable-p t))))
+ ;; Don't modify other characters.
+ (t
+ (forward-char 1))))
+ (buffer-string)))
;; TODO: fix upstream Emacs bug that means `-map' is not highlighted
;; in the docstring for `--map'.
(defun helpful--format-docstring (docstring)
"Replace cross-references with links in DOCSTRING."
(-> docstring
+ (helpful--format-command-keys)
(helpful--split-first-line)
- (helpful--propertize-command-keys)
(helpful--propertize-info)
(helpful--propertize-symbols)
(s-trim)))
+(helpful--format-docstring "(apply '+ 1 '(1 2))")
+
(defconst helpful--highlighting-funcs
'(ert--activate-font-lock-keywords
highlight-quoted-mode
@@ -1205,7 +1231,9 @@ For example, \"(some-func FOO &optional BAR)\"."
(or docstring-sig source-sig)))
(defun helpful--docstring (sym callable-p)
- "Get the docstring for SYM."
+ "Get the docstring for SYM.
+Note that this returns the raw docstring, including \\=\\=
+escapes that are used by `substitute-command-keys'."
(let ((text-quoting-style 'grave)
docstring)
(if callable-p
diff --git a/test/unit-test.el b/test/unit-test.el
index d0e3893..51029da 100644
--- a/test/unit-test.el
+++ b/test/unit-test.el
@@ -27,6 +27,13 @@
(helpful--docstring #'test-foo t)
"Docstring here.")))
+(ert-deftest helpful--docstring-unescape ()
+ "Discard \\=\\= in docstrings."
+ (let* ((docstring (helpful--docstring #'apply t))
+ (formatted-docstring (helpful--format-docstring docstring)))
+ (should
+ (not (s-contains-p "\\=" formatted-docstring)))))
+
(ert-deftest helpful--docstring-advice ()
"Get the docstring on advised functions."
(should