emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

scratch/eldoc-async 78498a1 4/5: Rewrite and simplify docstring formatti


From: João Távora
Subject: scratch/eldoc-async 78498a1 4/5: Rewrite and simplify docstring formatting in Eldoc
Date: Sun, 14 Jun 2020 09:09:14 -0400 (EDT)

branch: scratch/eldoc-async
commit 78498a12040ee3515b8f76a796838578899f3ebd
Author: João Távora <joaotavora@gmail.com>
Commit: João Távora <joaotavora@gmail.com>

    Rewrite and simplify docstring formatting in Eldoc
    
    Eldoc is now more responsible for handling the format of the displayed
    docstrings, elisp-mode is less responsible.  We achieve this by having
    elisp-mode's eldoc-documentation-functions inform Eldoc of the things
    they are documenting.
    
    From here on, a natural development would be to have a new hook called
    eldoc-display-functions which would let users control the destinations
    of the gathered documention.  Third parties could write hook functions
    suitable for it, for displaying documentation in separate frames,
    external systems, etc.
    
    * lisp/cedet/semantic/grammar.el
    (semantic--docstring-format-sym-doc): Copied from
    eldoc-docstring-format-sym-doc.
    (semantic-grammar-eldoc-get-macro-docstring): Use
    semantic--docstring-format-sym-doc and correctly use
    elisp-get-fnsym-args-string.
    
    * lisp/emacs-lisp/eldoc.el (eldoc-documentation-functions):
    Describemeaning of :THING and :FACE arguments to callback.
    (eldoc--handle-docs): Rewrite.
    
    * lisp/emacs-lisp/eldoc.el (eldoc-docstring-format-sym-doc):
    Delete.
    
    * lisp/progmodes/elisp-mode.el (elisp-eldoc-funcall)
    (elisp-eldoc-var-docstring): Use a CALLBACK arg, pass it :thing
    and and :face.
    (elisp-get-var-docstring, elisp-get-fnsym-args-string): Don't do
    any truncation or special formatting..
---
 lisp/cedet/semantic/grammar.el |  45 ++++++++--
 lisp/emacs-lisp/eldoc.el       | 185 +++++++++++++++++++++--------------------
 lisp/progmodes/elisp-mode.el   |  41 +++++----
 3 files changed, 156 insertions(+), 115 deletions(-)

diff --git a/lisp/cedet/semantic/grammar.el b/lisp/cedet/semantic/grammar.el
index 2c3b24b..1ed1833 100644
--- a/lisp/cedet/semantic/grammar.el
+++ b/lisp/cedet/semantic/grammar.el
@@ -1663,6 +1663,42 @@ Select the buffer containing the tag's definition, and 
move point there."
 
 (defvar semantic-grammar-eldoc-last-data (cons nil nil))
 
+(defun semantic--docstring-format-sym-doc (prefix doc &optional face)
+  "Combine PREFIX and DOC, and shorten the result to fit in the echo area.
+
+When PREFIX is a symbol, propertize its symbol name with FACE
+before combining it with DOC.  If FACE is not provided, just
+apply the nil face.
+
+See also: `eldoc-echo-area-use-multiline-p'."
+  ;; Hoisted from old `eldoc-docstring-format-sym-doc'.
+  ;; If the entire line cannot fit in the echo area, the symbol name may be
+  ;; truncated or eliminated entirely from the output to make room for the
+  ;; description.
+  (when (symbolp prefix)
+    (setq prefix (concat (propertize (symbol-name prefix) 'face face) ": ")))
+  (let* ((ea-multi eldoc-echo-area-use-multiline-p)
+         ;; Subtract 1 from window width since emacs will not write
+         ;; any chars to the last column, or in later versions, will
+         ;; cause a wraparound and resize of the echo area.
+         (ea-width (1- (window-width (minibuffer-window))))
+         (strip (- (+ (length prefix)
+                      (length doc))
+                   ea-width)))
+    (cond ((or (<= strip 0)
+               (eq ea-multi t)
+               (and ea-multi (> (length doc) ea-width)))
+           (concat prefix doc))
+          ((> (length doc) ea-width)
+           (substring (format "%s" doc) 0 ea-width))
+          ((>= strip (string-match-p ":? *\\'" prefix))
+           doc)
+          (t
+           ;; Show the end of the partial symbol name, rather
+           ;; than the beginning, since the former is more likely
+           ;; to be unique given package namespace conventions.
+           (concat (substring prefix strip) doc)))))
+
 (defun semantic-grammar-eldoc-get-macro-docstring (macro expander)
   "Return a one-line docstring for the given grammar MACRO.
 EXPANDER is the name of the function that expands MACRO."
@@ -1681,19 +1717,18 @@ EXPANDER is the name of the function that expands 
MACRO."
         (setq doc (eldoc-function-argstring expander))))
       (when doc
         (setq doc
-             (eldoc-docstring-format-sym-doc
+             (semantic--docstring-format-sym-doc
               macro (format "==> %s %s" expander doc) 'default))
         (setq semantic-grammar-eldoc-last-data (cons expander doc)))
       doc))
    ((fboundp 'elisp-get-fnsym-args-string) ;; Emacs≥25
-    (elisp-get-fnsym-args-string
-     expander nil
-     (concat (propertize (symbol-name macro)
+    (concat (propertize (symbol-name macro)
                          'face 'font-lock-keyword-face)
              " ==> "
              (propertize (symbol-name macro)
                          'face 'font-lock-function-name-face)
-             ": ")))))
+             ": "
+             (elisp-get-fnsym-args-string expander nil )))))
 
 (define-mode-local-override semantic-idle-summary-current-symbol-info
   semantic-grammar-mode ()
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index be78b13..a3437ca 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -369,26 +369,29 @@ information.
 
 Each hook function is called with at least one argument CALLBACK
 and decides whether to display a doc short string about the
-context around point.  If that decision can be taken quickly, the
-hook function may ignore CALLBACK and immediately return either
-the doc string or nil if there's no doc appropriate for the
-context.  Otherwise, it should return a non-string, non-nil value
-and arrange for CALLBACK to be called later.
-
-In particular, if the computation of said docstring (or the
-decision as to whether there is documentation at all) is
-expensive or can't be performed directly, the hook function
-should return a non-nil, non-string value and arrange for
-CALLBACK to be called at a later time, using asynchronous
-processes or other asynchronous mechanisms.
-
-However CALLBACK is called, it expects to be passed an argument
-DOCSTRING followed by an optional list of keyword-value pairs of
-the form (:KEY VALUE :KEY2 VALUE2...).  KEY can be:
-
-* `:hint', whereby the corresponding VALUE should be a short
-  string designating the type of documentation reported on by the
-  hook function.
+context around point.
+
+- If that decision can be taken quickly, the hook function may
+  call CALLBACK immediately following the protocol described
+  berlow.  Alternatively it may ignore CALLBACK entirely and
+  return either the doc string, or nil if there's no doc
+  appropriate for the context.
+
+- If the computation of said doc string (or the decision whether
+  there is one at all) is expensive or can't be performed
+  directly, the hook function should return a non-nil, non-string
+  value and arrange for CALLBACK to be called at a later time,
+  using asynchronous processes or other asynchronous mechanisms.
+
+Regardless of the context in which CALLBACK is called, it expects
+to be passed an argument DOCSTRING followed by an optional list
+of keyword-value pairs of the form (:KEY VALUE :KEY2 VALUE2...).
+KEY can be:
+
+* `:thing', VALUE is be a short string or symbol designating what
+  is being reported on;
+
+* `:face', VALUE is a symbol designating a face;
 
 Major modes should modify this hook locally, for example:
   (add-hook \\='eldoc-documentation-functions #\\='foo-mode-eldoc nil t)
@@ -410,11 +413,12 @@ return any documentation.")
   "Display multiple DOCS in echo area.
 DOCS is a list of (STRING PLIST...).  It is already sorted.
 Honour most of `eldoc-echo-area-use-multiline-p'."
-  (with-current-buffer (eldoc-doc-buffer)
-    (let ((inhibit-read-only t))
-      (erase-buffer) (setq buffer-read-only t)
-      (local-set-key "q" 'quit-window)
-      (insert (mapconcat #'identity (mapcar #'car docs) "\n")))
+  (if (null docs) (eldoc--message nil) ; if there's nothing to report
+                                       ; clear the echo area, but
+                                       ; don't erase the last *eldoc*
+                                       ; buffer.
+    ;; If there's something to report on, first establish some
+    ;; parameterso
     (let* ((width (1- (window-width (minibuffer-window))))
            (val (if (and (symbolp eldoc-echo-area-use-multiline-p)
                          eldoc-echo-area-use-multiline-p)
@@ -423,36 +427,74 @@ Honour most of `eldoc-echo-area-use-multiline-p'."
            (available (cl-typecase val
                         (float (truncate (* (frame-height) val)))
                         (integer val)
-                        (t 1))))
-      (eldoc--message nil) ; clear the echo area
-      (when docs
-        (goto-char (point-min))
-        (cond
-         ((> available 1)
-          (cl-loop
-           initially (goto-char (line-end-position (1+ available)))
-           for truncated = nil then t
-           for needed
-           = (let ((truncate-lines message-truncate-lines))
-               (count-screen-lines (point-min) (point) t (minibuffer-window)))
-           while (> needed (if truncated (1- available) available))
-           do (goto-char (line-end-position (if truncated 0 -1)))
-           (while (bolp) (goto-char (line-end-position 0)))
-           finally
-           (unless (and truncated
-                        eldoc-prefer-doc-buffer
-                        (get-buffer-window eldoc--doc-buffer))
-             (eldoc--message
-              (concat (buffer-substring (point-min) (point))
-                      (and truncated
-                           (format
-                            "\n(Documentation truncated. Use `%s' to see rest)"
-                            (substitute-command-keys 
"\\[eldoc-doc-buffer]"))))))))
-         ((= available 1)
-          ;; truncate brutally ; FIXME: use `eldoc-prefer-doc-buffer' here, 
too?
-          (eldoc--message
-           (truncate-string-to-width
-            (buffer-substring (point-min) (line-end-position 1)) width))))))))
+                        (t 1)))
+           (things-reported-on)
+           single-sym-name)
+      ;; Then, compose the contents of the `*eldoc*' buffer
+      (with-current-buffer (eldoc-doc-buffer)
+        (let ((inhibit-read-only t))
+          (erase-buffer) (setq buffer-read-only t)
+          (local-set-key "q" 'quit-window)
+          (cl-loop for (docs . rest) on docs
+                   for (this-doc . plist) = docs
+                   for thing = (plist-get plist :thing)
+                   when thing do
+                   (cl-pushnew thing things-reported-on)
+                   (setq this-doc
+                         (concat
+                          (propertize (format "%s" thing)
+                                      'face (plist-get plist :face))
+                          ": "
+                          this-doc))
+                   do (insert this-doc)
+                   when rest do (insert "\n")))
+        ;; rename the buffer
+        (when things-reported-on
+          (rename-buffer (format "*eldoc for %s*"
+                                 (mapconcat (lambda (s) (format "%s" s))
+                                            things-reported-on
+                                            ", ")))))
+      ;; Finally, output to the echo area.  We currently do handling
+      ;; the `truncate-sym-name-if-fit' special case first, then by
+      ;; selecting a top-section of the `*eldoc' buffer.  I'm pretty
+      ;; sure nicer strategies can be used here, probably by splitting
+      ;; this pfunction into some `eldoc-display-functions' special
+      ;; hook.
+      (if (and (eq 'truncate-sym-name-if-fit eldoc-echo-area-use-multiline-p)
+               (null (cdr docs))
+               (setq single-sym-name
+                     (format "%s" (plist-get (cdar docs) :thing)))
+               (> (+ (length (caar docs)) (length single-sym-name) 2) width))
+          (eldoc--message (caar docs))
+          (with-current-buffer (eldoc-doc-buffer)
+            (goto-char (point-min))
+            (cond
+             ;; We truncate a long message
+             ((> available 1)
+              (cl-loop
+               initially (goto-char (line-end-position (1+ available)))
+               for truncated = nil then t
+               for needed
+               = (let ((truncate-lines message-truncate-lines))
+                   (count-screen-lines (point-min) (point) t 
(minibuffer-window)))
+               while (> needed (if truncated (1- available) available))
+               do (goto-char (line-end-position (if truncated 0 -1)))
+               (while (bolp) (goto-char (line-end-position 0)))
+               finally
+               (unless (and truncated
+                            eldoc-prefer-doc-buffer
+                            (get-buffer-window eldoc--doc-buffer))
+                 (eldoc--message
+                  (concat (buffer-substring (point-min) (point))
+                          (and truncated
+                               (format
+                                "\n(Documentation truncated. Use `%s' to see 
rest)"
+                                (substitute-command-keys 
"\\[eldoc-doc-buffer]"))))))))
+             ((= available 1)
+              ;; truncate brutally ; FIXME: use `eldoc-prefer-doc-buffer' 
here, too?
+              (eldoc--message
+               (truncate-string-to-width
+                (buffer-substring (point-min) (line-end-position 1)) 
width)))))))))
 
 ;; this variable should be unbound, but that confuses
 ;; `describe-symbol' for some reason.
@@ -613,39 +655,6 @@ documentation themselves."
                 (;; got something else, trust callback will be called
                  t)))))))))
 
-;; If the entire line cannot fit in the echo area, the symbol name may be
-;; truncated or eliminated entirely from the output to make room for the
-;; description.
-(defun eldoc-docstring-format-sym-doc (prefix doc &optional face)
-  "Combine PREFIX and DOC, and shorten the result to fit in the echo area.
-
-When PREFIX is a symbol, propertize its symbol name with FACE
-before combining it with DOC.  If FACE is not provided, just
-apply the nil face.
-
-See also: `eldoc-echo-area-use-multiline-p'."
-  (when (symbolp prefix)
-    (setq prefix (concat (propertize (symbol-name prefix) 'face face) ": ")))
-  (let* ((ea-multi eldoc-echo-area-use-multiline-p)
-         ;; Subtract 1 from window width since emacs will not write
-         ;; any chars to the last column, or in later versions, will
-         ;; cause a wraparound and resize of the echo area.
-         (ea-width (1- (window-width (minibuffer-window))))
-         (strip (- (+ (length prefix) (length doc)) ea-width)))
-    (cond ((or (<= strip 0)
-               (eq ea-multi t)
-               (and ea-multi (> (length doc) ea-width)))
-           (concat prefix doc))
-          ((> (length doc) ea-width)
-           (substring (format "%s" doc) 0 ea-width))
-          ((>= strip (string-match-p ":? *\\'" prefix))
-           doc)
-          (t
-           ;; Show the end of the partial symbol name, rather
-           ;; than the beginning, since the former is more likely
-           ;; to be unique given package namespace conventions.
-           (concat (substring prefix strip) doc)))))
-
 ;; When point is in a sexp, the function args are not reprinted in the echo
 ;; area after every possible interactive command because some of them print
 ;; their own messages in the echo area; the eldoc functions would instantly
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index a003f0f..6df5411 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -1405,20 +1405,27 @@ which see."
       or argument string for functions.
   2 - `function' if function args, `variable' if variable documentation.")
 
-(defun elisp-eldoc-funcall (&rest _ignored)
+(defun elisp-eldoc-funcall (callback &rest _ignored)
   "Document function call at point.
 Intended for `eldoc-documentation-functions' (which see)."
-  (let ((fn-sym (elisp--fnsym-in-current-sexp)))
+  (let* ((sym-info (elisp--fnsym-in-current-sexp))
+         (fn-sym (car sym-info)))
     (when fn-sym
-      (apply #'elisp-get-fnsym-args-string fn-sym))))
+      (funcall callback (apply #'elisp-get-fnsym-args-string sym-info)
+               :thing fn-sym
+               :face (if (functionp fn-sym)
+                         'font-lock-function-name-face
+                       'font-lock-keyword-face)))))
 
-(defun elisp-eldoc-var-docstring (&rest _ignored)
+(defun elisp-eldoc-var-docstring (callback &rest _ignored)
   "Document variable at point.
 Intended for `eldoc-documentation-functions' (which see)."
   (let ((sym (elisp--current-symbol)))
-    (when sym (elisp-get-var-docstring sym))))
+    (when sym (funcall callback (elisp-get-var-docstring sym)
+                       :thing sym
+                       :face 'font-lock-variable-name-face))))
 
-(defun elisp-get-fnsym-args-string (sym &optional index prefix)
+(defun elisp-get-fnsym-args-string (sym &optional index)
   "Return a string containing the parameter list of the function SYM.
 If SYM is a subr and no arglist is obtainable from the docstring
 or elsewhere, return a 1-line docstring."
@@ -1444,20 +1451,13 @@ or elsewhere, return a 1-line docstring."
              ;; Stringify, and store before highlighting, downcasing, etc.
             (elisp--last-data-store sym (elisp-function-argstring args)
                                      'function))))))
-    ;; Highlight, truncate.
+    ;; Highlight
     (if argstring
        (elisp--highlight-function-argument
-         sym argstring index
-         (or prefix
-             (concat (propertize (symbol-name sym) 'face
-                                 (if (functionp sym)
-                                     'font-lock-function-name-face
-                                   'font-lock-keyword-face))
-                     ": "))))))
-
-(defun elisp--highlight-function-argument (sym args index prefix)
-  "Highlight argument INDEX in ARGS list for function SYM.
-In the absence of INDEX, just call `eldoc-docstring-format-sym-doc'."
+         sym argstring index))))
+
+(defun elisp--highlight-function-argument (sym args index)
+  "Highlight argument INDEX in ARGS list for function SYM."
   ;; FIXME: This should probably work on the list representation of `args'
   ;; rather than its string representation.
   ;; FIXME: This function is much too long, we need to split it up!
@@ -1560,7 +1560,6 @@ In the absence of INDEX, just call 
`eldoc-docstring-format-sym-doc'."
       (when start
        (setq doc (copy-sequence args))
        (add-text-properties start end (list 'face argument-face) doc))
-      (setq doc (eldoc-docstring-format-sym-doc prefix doc))
       doc)))
 
 ;; Return a string containing a brief (one-line) documentation string for
@@ -1573,9 +1572,7 @@ In the absence of INDEX, just call 
`eldoc-docstring-format-sym-doc'."
         (t
          (let ((doc (documentation-property sym 'variable-documentation t)))
            (when doc
-             (let ((doc (eldoc-docstring-format-sym-doc
-                         sym (elisp--docstring-first-line doc)
-                         'font-lock-variable-name-face)))
+             (let ((doc (elisp--docstring-first-line doc)))
                (elisp--last-data-store sym doc 'variable)))))))
 
 (defun elisp--last-data-store (symbol doc type)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]