]> git.eshelyaron.com Git - emacs.git/commitdiff
Flymake: Experimental 'fancy' flymake-show-diagnostics-at-end-of-line
authorJoão Távora <joaotavora@gmail.com>
Sun, 27 Apr 2025 12:00:28 +0000 (13:00 +0100)
committerEshel Yaron <me@eshelyaron.com>
Sun, 27 Apr 2025 13:31:54 +0000 (15:31 +0200)
* doc/misc/flymake.texi (Customizable variables): Describe new
flymake-show-diagnostics-at-end-of-line.

* etc/NEWS: Mention 'flymake-show-diagnostics-at-end-of-line'.

* lisp/progmodes/flymake.el
(flymake-show-diagnostics-at-end-of-line): Update docstring.

* lisp/progmodes/flymake.el (flymake--eol-overlay-summary):
Rework.  Move to separate section.
(flymake--update-eol-overlays): Rework.  Use 'display'.  Move to
separate section.
(flymake--eol-draw-fancy-1, flymake--eol-draw-fancy): New helpers.
(flymake-end-of-line-diagnostics-face): Remove box.

(cherry picked from commit 0262e3e158e35770f864d3131baf8f1793a20e58)

doc/misc/flymake.texi
lisp/progmodes/flymake.el

index dd60e0a1bca7f42d5f2996efe5350dc569356a10..6104c38636c3e2ea1010297abe8dd966b08d330c 100644 (file)
@@ -347,9 +347,11 @@ If non-@code{nil}, moving to errors with @code{flymake-goto-next-error} and
 @item flymake-show-diagnostics-at-end-of-line
 If non-@code{nil}, show summarized descriptions of diagnostics at the
 end of the line.  Depending on your preference, this can either be
-distracting and easily confused with actual code, or a significant
-early aid that relieves you from moving around or reaching for the
-mouse to consult an error message.
+distracting and easily confused with actual code, or a significant early
+aid that relieves you from moving around or reaching for the mouse to
+consult an error message.  This value may also be set to @code{fancy},
+which will attempt to layout diagnostics below the affected line using
+unicode graphics to point to diagnostic locus.
 
 @item flymake-diagnostic-format-alist
 Control which parts of a diagnostic to show in various situations.
index 27f2199eca03b67445f1d2b934ae4b6ffa043e3c..13c488b2cabb86506aaeedcfdee965a1f0cbbccf 100644 (file)
@@ -509,7 +509,7 @@ verify FILTER, a function, and sort them by COMPARE (using KEY)."
   :package-version '(Flymake . "1.3.4"))
 
 (defface flymake-end-of-line-diagnostics-face
-  '((t :height 0.85 :box (:line-width -1)))
+  '((t :height 0.85))
   "Face used for end-of-line diagnostics.
 See variable `flymake-show-diagnostics-at-end-of-line'."
   :package-version '(Flymake . "1.3.5"))
@@ -539,8 +539,11 @@ See variable `flymake-show-diagnostics-at-end-of-line'."
 (defcustom flymake-show-diagnostics-at-end-of-line nil
   "If non-nil, add diagnostic summary messages at end-of-line.
 The value `short' means that only the most severe diagnostic
-shall be shown.  Any other non-nil value means show all
-diagnostic summaries at end-of-line."
+shall be shown.
+The value `fancy' means to layout diagnostic summary information
+below the affected line with Unicode graphics.
+Any other non-nil value means show all diagnostic summaries at
+end-of-line."
   :type '(choice (const :tag "Display most severe diagnostic" short)
                  (const :tag "Display all diagnostics" t)
                  (const :tag "Don't display diagnostics at end-of-line" nil))
@@ -795,42 +798,6 @@ Return to original margin width if ORIG-WIDTH is non-nil."
         (overlay-put eolov 'flymake-eol-source-overlays src-ovs)))
     (delete-overlay ov)))
 
-(defun flymake--eol-overlay-summary (src-ovs)
-  "Helper function for `flymake--update-eol-overlays'."
-  (cl-flet ((summarize (d)
-              (flymake--format-diagnostic d :eol 'eol-face)))
-    (let* ((diags
-            (cl-sort
-             (mapcar (lambda (o) (overlay-get o 'flymake-diagnostic)) src-ovs)
-             #'>
-             :key (compf flymake--severity flymake-diagnostic-type)))
-           (summary
-            (concat
-             "  "
-             (cond ((eq flymake-show-diagnostics-at-end-of-line 'short)
-                    (concat
-                     (summarize (car diags))
-                     (and (cdr diags)
-                          (concat
-                           " "
-                           (propertize (format "and %s more"
-                                               (1- (length diags)))
-                                       'face 'flymake-eol-information-face)))))
-                   (t
-                    (mapconcat #'summarize diags " "))))))
-      (put-text-property 0 1 'cursor t summary)
-      summary)))
-
-(defun flymake--update-eol-overlays ()
-  "Update the `before-string' property of end-of-line overlays."
-  (save-restriction
-    (widen)
-    (dolist (o (overlays-in (point-min) (point-max)))
-      (when (overlay-get o 'flymake--eol-overlay)
-        (if-let ((src-ovs (overlay-get o 'flymake-eol-source-overlays)))
-            (overlay-put o 'before-string (flymake--eol-overlay-summary src-ovs))
-          (delete-overlay o))))))
-
 (defun flymake-diagnostic-context-menu (menu click)
   "Extend MENU with fix suggestions for diagnostic at CLICK."
   (when-let ((diag (seq-find #'flymake--diag-fix-function
@@ -2174,5 +2141,129 @@ defaults to all project diagnostics."
                      (eq flymake--diagnostics-buffer-source buffer)))
         (unless flymake--diagnostics-buffer-diags (revert-buffer))))))
 
+\f
+;;; Eol overlay helpers
+;;;
+(defun flymake--update-eol-overlays ()
+  "Update the `display' property of end-of-line overlays."
+  (save-restriction
+    (widen)
+    (dolist (o (overlays-in (point-min) (point-max)))
+      (when (overlay-get o 'flymake--eol-overlay)
+        (if-let* ((src-ovs (overlay-get o 'flymake-eol-source-overlays)))
+            (overlay-put o 'display (flymake--eol-overlay-summary src-ovs))
+          (delete-overlay o))))))
+
+(defun flymake--eol-overlay-summary (src-ovs)
+  "Helper function for `flymake--update-eol-overlays'."
+  (cl-flet ((summarize (d)
+              (flymake--format-diagnostic d :eol 'eol-face)))
+    (let* ((diags
+            (cl-sort
+             (mapcar (lambda (o) (overlay-get o 'flymake-diagnostic)) src-ovs)
+             #'>
+             :key (lambda (d) (flymake--severity (flymake-diagnostic-type d)))))
+           (summary
+            (concat
+             "  "
+             (cond ((eq flymake-show-diagnostics-at-end-of-line 'short)
+                    (concat
+                     (summarize (car diags))
+                     (and (cdr diags)
+                          (concat
+                           " "
+                           (propertize (format "and %s more"
+                                               (1- (length diags)))
+                                       'face 'flymake-eol-information-face)))))
+                   ((eq flymake-show-diagnostics-at-end-of-line 'fancy)
+                    (flymake--eol-draw-fancy diags #'summarize))
+                   (t
+                    (mapconcat #'summarize diags " ")))
+             "\n")))
+      (put-text-property 0 1 'cursor t summary)
+      summary)))
+
+(defun flymake--eol-draw-fancy-1 (text boxdraw-face line-beg-col
+                                       height-to-clear
+                                       text-beg-col
+                                       text-end-col)
+  (cl-flet ((move (cl)
+              (let* ((lep (line-end-position))
+                     (target (+ (point) cl))
+                     (diff (- target lep)))
+                (cond ((> diff 0)
+                       (goto-char lep)
+                       (insert (make-string diff ? )))
+                      (t
+                       (goto-char target)))))
+            (onward ()
+              (let ((rem (forward-line 1)))
+                (unless (and (not (eobp)) (zerop rem))
+                  (goto-char (point-max))
+                  (insert "\n")))))
+    (goto-char (point-min))
+    (cl-loop
+     with fork = (propertize "├" 'face boxdraw-face)
+     with pipe = (propertize "│" 'face boxdraw-face)
+     with inhibit-field-text-motion = t
+     for i from 0
+     repeat height-to-clear
+     do (move line-beg-col)
+     (let ((c (char-before)))
+       (delete-char -1)
+       (insert
+        (propertize
+         (cond ;; ((zerop i) "┬")
+          ((memq c '(?└ ?├)) fork)
+          (t         pipe))
+         'face boxdraw-face)))
+     (onward))
+    (move line-beg-col)
+    (delete-char -1)
+    (insert (propertize "└" 'face boxdraw-face))
+    (insert (propertize (make-string (- text-beg-col line-beg-col 1)
+                                     ?─)
+                        'face boxdraw-face))
+    (insert " ")
+    (let ((rect (with-temp-buffer
+                  (insert text)
+                  (let ((fill-column (- text-end-col text-beg-col)))
+                    (fill-paragraph)
+                    (forward-line 0)
+                    (move fill-column)
+                    (extract-rectangle (point-min) (point-max))))))
+      (insert-rectangle rect)
+      (+ height-to-clear (length rect)))))
+
+(defun flymake--eol-draw-fancy (diags summarize-fn)
+  (with-temp-buffer
+    (cl-loop
+     with sorted = (cl-sort diags #'> :key #'flymake-diagnostic-beg)
+     for diag in sorted
+     for text = (funcall summarize-fn diag)
+     for line-beg-col =
+     (with-current-buffer (flymake-diagnostic-buffer diag)
+       (save-excursion
+         (goto-char (flymake-diagnostic-beg diag))
+         (1+ (current-column))))
+     for height-to-clear = 0 then ret
+     for i from 0
+     for adjust = (* i 2)
+     for face = `(:inherit default
+                           :foreground
+                           ,(face-attribute
+                             (get-text-property 0 'face text)
+                             :foreground nil t))
+     for text-beg-col = (max (- (max 30 (+ line-beg-col 5)) adjust) (+ line-beg-col 1))
+     for text-end-col = (max 100 (+ text-beg-col 40))
+     for ret = (flymake--eol-draw-fancy-1
+                text
+                face
+                line-beg-col
+                height-to-clear
+                text-beg-col
+                text-end-col))
+    (concat " \n" (buffer-string))))
+
 (provide 'flymake)
 ;;; flymake.el ends here