From: João Távora Date: Sun, 27 Apr 2025 12:00:28 +0000 (+0100) Subject: Flymake: Experimental 'fancy' flymake-show-diagnostics-at-end-of-line X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=3065a49c2fd9ebd3d5893eec9a234e53954bd0fc;p=emacs.git Flymake: Experimental 'fancy' flymake-show-diagnostics-at-end-of-line * 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) --- diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index dd60e0a1bca..6104c38636c 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -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. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 27f2199eca0..13c488b2cab 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -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)))))) + +;;; 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