From 0e0e79c3f021dd69b1688e400bb3d8b49499ed7e Mon Sep 17 00:00:00 2001 From: "Elias G. B. Perez" Date: Sat, 6 Apr 2024 13:57:30 -0600 Subject: [PATCH] Flymake support for indicating errors in margin Add optional support for display flymake error in margin, this allow displaying error indicators in both graphical and terminal frames. * doc/misc/flymake.texi (Customizable variables) (Flymake error types): Document new margin indicator. * etc/NEWS: Announce the new Flymake user option for margin indicators. * lisp/progmodes/flymake.el (flymake-indicator-type) (flymake-margin-indicators-string, flymake-autoresize-margins) (flymake-margin-indicator-position): New user options. (flymake--original-margin-width): Add buffer-local variable for store original buffer margin width. (flymake-error, flymake-warning, flymake-note): Use new margin value. (flymake--indicator-overlay-spec): Rework and Rename from flymake--fringe-overlay-spec. (flymake--resize-margins): Add new function for resize margin width. (flymake--highlight-line, flymake-mode): Rework. (cherry picked from commit 3c4f6c78b4b2ae0b1efadf5e664fa180e663037e) --- doc/misc/flymake.texi | 26 ++++++++ etc/NEWS | 22 +++++++ lisp/progmodes/flymake.el | 134 +++++++++++++++++++++++++++++++++----- 3 files changed, 167 insertions(+), 15 deletions(-) diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 84a74a9d6ab..7019f4b2ca0 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -309,6 +309,12 @@ reported. A custom face for highlighting regions for which a note has been reported. +@item flymake-indicator-type +The indicator type which Flymake should use to indicate lines with +errors or warnings. +Depending on your preference, this can either use @code{fringes} or +@code{margins} for indicating errors. + @item flymake-error-bitmap A bitmap used in the fringe to mark lines for which an error has been reported. @@ -320,6 +326,18 @@ been reported. @item flymake-fringe-indicator-position Which fringe (if any) should show the warning/error bitmaps. +@item flymake-margin-indicators-string +Specifies the string and face to use for the margin indicators, for +each error type. + +@item flymake-margin-indicator-position +Which margin (if any) should show the warning/error strings. + +@item flymake-autoresize-margins +If non-@code{nil}, Flymake will resize the margins when +@code{flymake-mode} is turned on or off. +Only relevant if @code{flymake-indicator-type} is set to @code{margins}. + @item flymake-wrap-around If non-@code{nil}, moving to errors with @code{flymake-goto-next-error} and @code{flymake-goto-prev-error} wraps around buffer boundaries. @@ -387,6 +405,14 @@ the syntax of @code{flymake-error-bitmap} (@pxref{Customizable variables}). It is overridden by any @code{before-string} overlay property. +@item +@cindex margin of diagnostic +@code{flymake-margin-string}, a string displayed in the margin +according to @code{flymake-margin-indicator-position}. +The value actually follows the syntax of @code{flymake-margin-indicators-string} +(@pxref{Customizable variables}). It is overridden by any +@code{before-string} overlay property. + @item @code{flymake-overlay-control}, an alist ((@var{OVPROP} . @var{VALUE}) @var{...}) of further properties used to affect the appearance of diff --git a/etc/NEWS b/etc/NEWS index e8c55ff4f80..42041709595 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1340,6 +1340,28 @@ in a clean environment. ** Flymake ++++ +*** New user option 'flymake-indicator-type'. +This user option controls which error indicator type Flymake should use +in current buffer. Depending on your preference, this can either use +fringes or margins for indicating errors. + ++++ +*** New user option 'flymake-margin-indicators-string'. +It controls, for each error type, the string and its face to display as +the margin indicator. + ++++ +*** New user option 'flymake-autoresize-margins'. +If non-nil, Flymake will resize the margins when 'flymake-mode' is +turned on or off. +Only relevant if `flymake-indicator-type` is set to `margins`. + ++++ +*** New user option 'flymake-margin-indicator-position'. +It controls which margin (left or right) is used for margin +indicators. + +++ *** New user option 'flymake-show-diagnostics-at-end-of-line'. When non-nil, Flymake shows summarized descriptions of diagnostics at diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index f2750a026ce..22a139d3045 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -180,6 +180,59 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." (const right-fringe) (const :tag "No fringe indicators" nil))) +(defcustom flymake-indicator-type (if (display-graphic-p) + 'fringes + 'margins) + "Indicate which indicator type to use for display errors. + +The value can be nil (don't indicate errors but just highlight them), +fringes (use fringes) or margins (use margins) + +Difference between fringes and margin is that fringes support diplaying +bitmaps on graphical displays and margins display text in a blank area +from current buffer that works in both graphical and text displays. + +See Info node `Fringes' and Info node `(elisp)Display Margins'." + :version "30.1" + :type '(choice (const :tag "Use Fringes" fringes) + (const :tag "Use Margins "margins) + (const :tag "No indicators" nil))) + +(defcustom flymake-margin-indicators-string + '((error "!!" compilation-error) + (warning "!" compilation-warning) + (note "!" compilation-info)) + "Strings used for margins indicators. +The value of each list may be a list of 3 elements where specifies the +error type, the string to use and its face, +or a list of 2 elements specifying only the error type and +the corresponding string. + +The option `flymake-margin-indicator-position' controls how and where +this is used." + :version "30.1" + :type '(repeat :tag "Error types lists" + (list :tag "String and face for error types" + (symbol :tag "Error type") + (string :tag "String") + (face :tag "Face")))) + +(defcustom flymake-autoresize-margins t + "If non-nil, automatically resize margin-width calling flymake--resize-margins. + +Only relevant if `flymake-indicator-type' is set to margins." + :version "30.1" + :type 'boolean) + +(defcustom flymake-margin-indicator-position 'left-margin + "The position to put Flymake margin indicator. +The value can be nil (do not use indicators), `left-margin' or `right-margin'. +See `flymake-margin-indicators-string'." + :version "30.1" + :type '(choice (const left-margin) + (const right-margin) + (const :tag "No margin indicators" nil))) + (make-obsolete-variable 'flymake-start-syntax-check-on-newline "can check on newline in post-self-insert-hook" "27.1") @@ -258,6 +311,11 @@ If set to nil, don't suppress any zero counters." (defvar-local flymake-check-start-time nil "Time at which syntax check was started.") +(defvar-local flymake--original-margin-width nil + "Store original margin width. +Used by `flymake--resize-margins' for restoring original margin width +when flymake is turned off.") + (defun flymake--log-1 (level sublog msg &rest args) "Do actual work for `flymake-log'." (let (;; never popup the log buffer @@ -630,6 +688,7 @@ Node `(Flymake)Flymake error types'" (put 'flymake-error 'face 'flymake-error) (put 'flymake-error 'flymake-bitmap 'flymake-error-bitmap) +(put 'flymake-error 'flymake-margin-string (alist-get 'error flymake-margin-indicators-string)) (put 'flymake-error 'severity (warning-numeric-level :error)) (put 'flymake-error 'mode-line-face 'flymake-error-echo) (put 'flymake-error 'echo-face 'flymake-error-echo) @@ -638,6 +697,7 @@ Node `(Flymake)Flymake error types'" (put 'flymake-warning 'face 'flymake-warning) (put 'flymake-warning 'flymake-bitmap 'flymake-warning-bitmap) +(put 'flymake-warning 'flymake-margin-string (alist-get 'warning flymake-margin-indicators-string)) (put 'flymake-warning 'severity (warning-numeric-level :warning)) (put 'flymake-warning 'mode-line-face 'flymake-warning-echo) (put 'flymake-warning 'echo-face 'flymake-warning-echo) @@ -646,6 +706,7 @@ Node `(Flymake)Flymake error types'" (put 'flymake-note 'face 'flymake-note) (put 'flymake-note 'flymake-bitmap 'flymake-note-bitmap) +(put 'flymake-note 'flymake-margin-string (alist-get 'note flymake-margin-indicators-string)) (put 'flymake-note 'severity (warning-numeric-level :debug)) (put 'flymake-note 'mode-line-face 'flymake-note-echo) (put 'flymake-note 'echo-face 'flymake-note-echo) @@ -682,19 +743,53 @@ associated `flymake-category' return DEFAULT." (flymake--lookup-type-property type 'severity (warning-numeric-level :error))) -(defun flymake--fringe-overlay-spec (bitmap &optional recursed) - (if (and (symbolp bitmap) - (boundp bitmap) - (not recursed)) - (flymake--fringe-overlay-spec - (symbol-value bitmap) t) - (and flymake-fringe-indicator-position - bitmap - (propertize "!" 'display - (cons flymake-fringe-indicator-position - (if (listp bitmap) - bitmap - (list bitmap))))))) +(defun flymake--indicator-overlay-spec (indicator) + "Return INDICATOR as propertized string to use in error indicators." + (let* ((value (if (symbolp indicator) + (symbol-value indicator) + indicator)) + (indicator-car (if (listp value) + (car value) + value)) + (indicator-cdr (if (listp value) + (cdr value)))) + (cond + ((symbolp indicator-car) + (propertize "!" 'display + (cons flymake-fringe-indicator-position + (if (listp value) + value + (list value))))) + ((stringp indicator-car) + (propertize "!" + 'display + `((margin ,flymake-margin-indicator-position) + ,(propertize + indicator-car + 'face + `(:inherit (,indicator-cdr + default))))))))) + +(defun flymake--resize-margins (&optional orig-width) + "Resize current window margins according to `flymake-margin-indicator-position'. +Return to original margin width if ORIG-WIDTH is non-nil." + (when (and (eq flymake-indicator-type 'margins) + flymake-autoresize-margins) + (cond + ((and orig-width flymake--original-margin-width) + (if (eq flymake-margin-indicator-position 'left-margin) + (setq-local left-margin-width flymake--original-margin-width) + (setq-local right-margin-width flymake--original-margin-width))) + (t + (if (eq flymake-margin-indicator-position 'left-margin) + (setq-local flymake--original-margin-width left-margin-width + left-margin-width 2) + (setq-local flymake--original-margin-width right-margin-width + right-margin-width 2)))) + ;; Apply margin to all windows avalaibles + (mapc (lambda (x) + (set-window-buffer x (window-buffer x))) + (get-buffer-window-list nil nil 'visible)))) (defun flymake--equal-diagnostic-p (a b) "Tell if A and B are equivalent `flymake--diag' objects." @@ -840,10 +935,13 @@ Return nil or the overlay created." type prop value))))) (default-maybe 'face 'flymake-error) (default-maybe 'before-string - (flymake--fringe-overlay-spec + (flymake--indicator-overlay-spec (flymake--lookup-type-property type - 'flymake-bitmap + (cond ((eq flymake-indicator-type 'fringes) + 'flymake-bitmap) + ((eq flymake-indicator-type 'margins) + 'flymake-margin-string)) (alist-get 'bitmap (alist-get type ; backward compat flymake-diagnostic-types-alist))))) ;; (default-maybe 'after-string @@ -1285,6 +1383,9 @@ special *Flymake log* buffer." :group 'flymake :lighter (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) (add-hook 'eldoc-documentation-functions 'flymake-eldoc-function t t) + ;; AutoResize margins. + (flymake--resize-margins) + ;; If Flymake happened to be already ON, we must cleanup ;; existing diagnostic overlays, lest we forget them by blindly ;; reinitializing `flymake--state' in the next line. @@ -1333,6 +1434,9 @@ special *Flymake log* buffer." :group 'flymake :lighter ;;+(remove-hook 'find-file-hook (function flymake-find-file-hook) t) (remove-hook 'eldoc-documentation-functions 'flymake-eldoc-function t) + ;; return margin to original size + (flymake--resize-margins t) + (when flymake-timer (cancel-timer flymake-timer) (setq flymake-timer nil)) -- 2.39.5