From d45f450cff757ed520a7ee36c632ab62387c7bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Thu, 2 Mar 2023 22:55:31 +0000 Subject: [PATCH] Flymake: add new flymake-show-diagnostics-at-end-of-line option Some editors have this. Depending on your preference, this can either be wildly 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. To be safe, hide this behind a customization variable and keep it disabled. Personally, I find it less obstrusive and more helpful than expected. * lisp/progmodes/flymake.el (flymake--delete-overlay): New helper. (flymake--highlight-line): Handle flymake-show-diagnostics-at-end-of-line. (flymake--clear-foreign-diags): Use flymake--delete-overlay. (flymake--publish-diagnostics): Use flymake--delete-overlay. (flymake-mode): Use flymake--delete-overlay. (flymake-error-echo) (flymake-warning-echo, flymake-note-echo): New faces. (flymake-show-diagnostics-at-end-of-line): New option. (Version): Bump to 1.3.4 * doc/misc/flymake.texi: (Finding diagnostics): Mention flymake-show-diagnostics-at-end-of-line. (Customizable variables): Mention flymake-show-diagnostics-at-end-of-line and a few more relevant faces. * etc/NEWS (Flymake): Mention flymake-show-diagnostics-at-end-of-line. --- doc/misc/flymake.texi | 25 ++++++++++- etc/NEWS | 10 ++++- lisp/progmodes/flymake.el | 89 +++++++++++++++++++++++++++++++++------ 3 files changed, 109 insertions(+), 15 deletions(-) diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 13616f39f16..304a7d29ece 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -1,7 +1,7 @@ \input texinfo @c -*- mode: texinfo; coding: utf-8 -*- @comment %**start of header @setfilename ../../info/flymake.info -@set VERSION 1.3.3 +@set VERSION 1.3.4 @set UPDATED April 2023 @settitle GNU Flymake @value{VERSION} @include docstyle.texi @@ -142,6 +142,12 @@ highlighted regions to learn what the specific problem is. Alternatively, place point on the highlighted regions and use the commands @code{eldoc} or @code{display-local-help}. +Another easy way to get instant access to the diagnostic text is to +set @code{flymake-show-diagnostics-at-end-of-line} to a non-@code{nil} +value. This makes the diagnostic messages appear at the end of the +line where the regular annotation is located (@pxref{Customizable +variables}) + @cindex next and previous diagnostic If the diagnostics are outside the visible region of the buffer, @code{flymake-goto-next-error} and @code{flymake-goto-prev-error} are @@ -316,6 +322,23 @@ If non-@code{nil}, moving to errors with @code{flymake-goto-next-error} and @code{flymake-goto-prev-error} wraps around buffer boundaries. @end vtable +@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. + +@item flymake-error-eol +A custom face for summarizing diagnostic error messages. + +@item flymake-warning-eol +A custom face for summarizing diagnostic warning messages. + +@item flymake-note-eol +A custom face for summarizing diagnostic notes. +@end vtable + @node Extending Flymake @chapter Extending Flymake @cindex extending flymake diff --git a/etc/NEWS b/etc/NEWS index 5e1fd76e99e..611dcf3dc7d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -259,13 +259,21 @@ following to your init file: #'shortdoc-help-fns-examples-function) ** Package - --- *** New user option 'package-vc-register-as-project'. When non-nil, it will automatically register every package as a project, that you can quickly select using 'project-switch-project' ('C-x p p'). +** Flymake ++++ +*** New user option 'flymake-show-diagnostics-at-end-of-line'. +When non-nil, Flymake shows 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 the buffer or reaching for the +mouse to consult an error message. + * New Modes and Packages in Emacs 30.1 diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index c751e5bd432..f2fe97cb773 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -4,7 +4,7 @@ ;; Author: Pavel Kobyakov ;; Maintainer: João Távora -;; Version: 1.3.3 +;; Version: 1.3.4 ;; Keywords: c languages tools ;; Package-Requires: ((emacs "26.1") (eldoc "1.14.0") (project "0.7.1")) @@ -431,6 +431,26 @@ verify FILTER, a function, and sort them by COMPARE (using KEY)." "Face used for marking note regions." :version "26.1") +(defface flymake-error-echo + '((t :inherit compilation-error)) + "Face used for showing summarized descriptions of errors." + :package-version '("Flymake" . "1.3.4")) + +(defface flymake-warning-echo + '((t :inherit compilation-warning)) + "Face used for showing summarized descriptions of warnings." + :package-version '("Flymake" . "1.3.4")) + +(defface flymake-note-echo + '((t :inherit flymake-note)) + "Face used for showing summarized descriptions of notes." + :package-version '("Flymake" . "1.3.4")) + +(defcustom flymake-show-diagnostics-at-end-of-line nil + "If non-nil, add diagnostic summary messages at end-of-line." + :type 'boolean + :package-version '("Flymake" . "1.3.4")) + (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") @@ -584,22 +604,25 @@ Node `(Flymake)Flymake error types'" (put 'flymake-error 'face 'flymake-error) (put 'flymake-error 'flymake-bitmap 'flymake-error-bitmap) (put 'flymake-error 'severity (warning-numeric-level :error)) -(put 'flymake-error 'mode-line-face 'compilation-error) -(put 'flymake-error 'echo-face 'error) +(put 'flymake-error 'mode-line-face 'flymake-error-echo) +(put 'flymake-error 'echo-face 'flymake-error-echo) +(put 'flymake-error 'eol-face 'flymake-error-echo) (put 'flymake-error 'flymake-type-name "error") (put 'flymake-warning 'face 'flymake-warning) (put 'flymake-warning 'flymake-bitmap 'flymake-warning-bitmap) (put 'flymake-warning 'severity (warning-numeric-level :warning)) -(put 'flymake-warning 'mode-line-face 'compilation-warning) -(put 'flymake-warning 'echo-face 'warning) +(put 'flymake-warning 'mode-line-face 'flymake-warning-echo) +(put 'flymake-warning 'echo-face 'flymake-warning-echo) +(put 'flymake-warning 'eol-face 'flymake-warning-echo) (put 'flymake-warning 'flymake-type-name "warning") (put 'flymake-note 'face 'flymake-note) (put 'flymake-note 'flymake-bitmap 'flymake-note-bitmap) (put 'flymake-note 'severity (warning-numeric-level :debug)) -(put 'flymake-note 'mode-line-face 'compilation-info) -(put 'flymake-note 'echo-face 'compilation-info) +(put 'flymake-note 'mode-line-face 'flymake-note-echo) +(put 'flymake-note 'echo-face 'flymake-note-echo) +(put 'flymake-note 'eol-face 'flymake-note-echo) (put 'flymake-note 'flymake-type-name "note") (defun flymake--lookup-type-property (type prop &optional default) @@ -656,6 +679,12 @@ associated `flymake-category' return DEFAULT." flymake-diagnostic-text) always (equal (funcall comp a) (funcall comp b))))) +(defun flymake--delete-overlay (ov) + "Like `delete-overlay', delete OV, but do some more stuff." + (let ((eolov (overlay-get ov 'eol-ov))) + (when eolov (delete-overlay eolov)) + (delete-overlay ov))) + (cl-defun flymake--highlight-line (diagnostic &optional foreign) "Attempt to overlay DIAGNOSTIC in current buffer. @@ -695,6 +724,7 @@ Return nil or the overlay created." ;; diagnostic is already registered in the same place, which only ;; happens for clashes between domestic and foreign diagnostics (cl-loop for e in (flymake-diagnostics beg end) + for eov = (flymake--diag-overlay e) when (flymake--equal-diagnostic-p e diagnostic) ;; FIXME. This is an imperfect heuristic. Ideally, we'd ;; want to delete no overlays and keep annotating the @@ -710,7 +740,7 @@ Return nil or the overlay created." (flymake--diag-orig-beg e) (flymake--diag-end e) (flymake--diag-orig-end e)) - (delete-overlay (flymake--diag-overlay e)))) + (flymake--delete-overlay eov))) (setq ov (make-overlay end beg)) (setf (flymake--diag-beg diagnostic) (overlay-start ov) (flymake--diag-end diagnostic) (overlay-end ov)) @@ -728,6 +758,37 @@ Return nil or the overlay created." (flymake--lookup-type-property type 'flymake-overlay-control)) (alist-get type flymake-diagnostic-types-alist)) do (overlay-put ov ov-prop value)) + ;; Handle `flymake-show-diagnostics-at-end-of-line' + ;; + (when-let ((eol-face (and flymake-show-diagnostics-at-end-of-line + (flymake--lookup-type-property type 'eol-face)))) + (save-excursion + (goto-char (overlay-start ov)) + (let* ((start (line-end-position)) + (end (min (1+ start) (point-max))) + (eolov (car + (cl-remove-if-not + (lambda (o) (overlay-get o 'flymake-source-ovs)) + (overlays-at start)))) + (bs (flymake-diagnostic-oneliner diagnostic t))) + (setq bs (propertize bs 'face eol-face)) + ;; FIXME: 1. no checking if there are unexpectedly more than + ;; one eolov at point. 2. The first regular source ov to + ;; die also kills the eolov (very rare this matters, but + ;; could be improved). + (cond (eolov + (overlay-put eolov 'before-string + (concat (overlay-get eolov 'before-string) " " bs)) + (overlay-put eolov 'flymake-source-ovs + (cons ov (overlay-get eolov 'flymake-source-ovs)))) + (t + (setq eolov (make-overlay start end nil t nil)) + (setq bs (concat " " bs)) + (put-text-property 0 1 'cursor t bs) + (overlay-put eolov 'before-string bs) + (overlay-put eolov 'evaporate (not (= start end))) + (overlay-put eolov 'flymake-source-ovs (list ov)) + (overlay-put ov 'eol-ov eolov)))))) ;; Now ensure some essential defaults are set ;; (cl-flet ((default-maybe @@ -743,6 +804,8 @@ Return nil or the overlay created." 'flymake-bitmap (alist-get 'bitmap (alist-get type ; backward compat flymake-diagnostic-types-alist))))) + ;; (default-maybe 'after-string + ;; (flymake--diag-text diagnostic)) (default-maybe 'help-echo (lambda (window _ov pos) (with-selected-window window @@ -873,7 +936,7 @@ report applies to that region." (maphash (lambda (_buffer diags) (cl-loop for d in diags when (flymake--diag-overlay d) - do (delete-overlay it))) + do (flymake--delete-overlay it))) (flymake--state-foreign-diags state)) (clrhash (flymake--state-foreign-diags state))) @@ -900,7 +963,7 @@ and other buffers." (flymake--intersects-p (overlay-start ov) (overlay-end ov) (car region) (cdr region))) - do (delete-overlay ov) + do (flymake--delete-overlay ov) else collect diag into surviving finally (setf (flymake--state-diags state) surviving))) @@ -909,7 +972,7 @@ and other buffers." (not (flymake--state-reported-p state)) (cl-loop for diag in (flymake--state-diags state) for ov = (flymake--diag-overlay diag) - when ov do (delete-overlay ov)) + when ov do (flymake--delete-overlay ov)) (setf (flymake--state-diags state) nil) ;; Also clear all overlays for `foreign-diags' in all other ;; buffers. @@ -1153,7 +1216,7 @@ special *Flymake log* buffer." :group 'flymake :lighter ;; existing diagnostic overlays, lest we forget them by blindly ;; reinitializing `flymake--state' in the next line. ;; See https://github.com/joaotavora/eglot/issues/223. - (mapc #'delete-overlay (flymake--overlays)) + (mapc #'flymake--delete-overlay (flymake--overlays)) (setq flymake--state (make-hash-table)) (setq flymake--recent-changes nil) @@ -1200,7 +1263,7 @@ special *Flymake log* buffer." :group 'flymake :lighter (when flymake-timer (cancel-timer flymake-timer) (setq flymake-timer nil)) - (mapc #'delete-overlay (flymake--overlays)) + (mapc #'flymake--delete-overlay (flymake--overlays)) (when flymake--state (maphash (lambda (_backend state) (flymake--clear-foreign-diags state))