nix-config/homes/aarch64-darwin/marshall@canis/doomDir/flycheck-inline.el

286 lines
9.9 KiB
EmacsLisp
Raw Normal View History

2024-08-11 02:26:51 -04:00
;;; flycheck-inline.el --- Display Flycheck errors inline -*- lexical-binding: t; -*-
;; Copyright (C) 2017-2018 fmdkdd
;; Author: fmdkdd
;; URL: https://github.com/flycheck/flycheck-inline
;; Keywords: tools, convenience
;; Version: 0.1-cvs
;; Package-Requires: ((emacs "25.1") (flycheck "32"))
;; This file is not part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Provide an error display function to show Flycheck errors inline, directly
;; below their location in the buffer.
;;
;; # Setup
;;
;; Enable the local minor mode for all flycheck-mode buffers:
;;
;; (with-eval-after-load 'flycheck
;; (add-hook 'flycheck-mode-hook #'flycheck-inline-mode))
;;; Code:
(require 'flycheck)
(require 'seq)
;;; Displaying line-long overlays (phantoms)
(defun flycheck-inline-phantom-display (msg &optional pos err)
"Display MSG in a phantom directly below POS.
MSG is a string that will be put in a line-long overlay (phantom)
at the line immediately following POS. If POS is nil, current
point is used instead.
Return the displayed phantom."
(pcase-let* ((p (or pos (point)))
(`(,offset . ,pos-eol)
(save-excursion
(goto-char p)
(cons (- p (point-at-bol)) (point-at-eol))))
(ov (make-overlay pos-eol (1+ pos-eol)))
;; If the error is on the last line, and that line doesn't end
;; with a newline, the overlay will be displayed at the end of
;; the line instead of below it. Adding a newline before the
;; message fixes it.
(str (concat (when (eq pos-eol (point-max)) "\n")
(flycheck-inline-indent-message offset msg)
"\n")))
(overlay-put ov 'phantom t)
(overlay-put ov 'after-string str)
(overlay-put ov 'error err)
ov))
(defun flycheck-inline--contains-point (phantom &optional pt)
"Whether the given error overlay contains the position PT otherwise `(point)'"
(let* ((pos (or pt (point)))
(err (overlay-get phantom 'error))
(region (flycheck-error-region-for-mode err 'symbols)))
(and phantom
;; Must be one of our phantoms (probably unneeded).
(overlay-get phantom 'phantom)
;; The underlying error must currently exist.
err
(memq err flycheck-current-errors)
;; Most importantly, point must be within the error bounds.
region
(>= pos (car region))
(<= pos (cdr region)))))
(defun flycheck-inline-phantom-delete (phantom)
"Delete PHANTOM if its region doesn't contain point.
Returns the overlay removed or nil."
(if (flycheck-inline--contains-point phantom)
nil
(progn (delete-overlay phantom) t)))
(defun flycheck-inline-indent-message (offset msg)
"Indent all lines of MSG by OFFSET spaces.
MSG is trimmed beforehand."
(let* ((pad (make-string offset ?\s))
(rep (concat "\n" pad)))
(concat pad
(replace-regexp-in-string "\n" rep (string-trim msg)))))
;;; Customization
(defgroup flycheck-inline nil
"Display Flycheck errors inline."
:prefix "flycheck-inline-"
:group 'flycheck
:link '(url-link :tag "Github" "https://github.com/flycheck/flycheck-inline"))
(defface flycheck-inline-error
'((t :inherit compilation-error))
"Flycheck-inline face for errors."
:package-version '(flycheck-inline . "0.1")
:group 'flycheck-inline)
(defface flycheck-inline-warning
'((t :inherit compilation-warning))
"Flycheck-inline face for warnings."
:package-version '(flycheck-inline . "0.1")
:group 'flycheck-inline)
(defface flycheck-inline-info
'((t :inherit compilation-info))
"Flycheck-inline face for informational messages."
:package-version '(flycheck-inline . "0.1")
:group 'flycheck-inline)
(defcustom flycheck-inline-display-function #'flycheck-inline-display-phantom
"Function to display inline errors.
This function is used to display inline all errors at point, as
well as all related errors. It has the signature (MSG &optional
POS ERR), where MSG is the error message to display, POS its
buffer position, and ERR is the flycheck error in general."
:group 'flycheck-inline
:package-version '(flycheck-inline . "0.1")
:type '(function :tag "Inline error display function")
:risky t)
(defcustom flycheck-inline-clear-function #'flycheck-inline-clear-phantoms
"Function to clear all inline errors.
It takes no arguments and should remove all inline errors created
by `flycheck-inline-display-function'."
:group 'flycheck-inline
:package-version '(flycheck-inline . "0.1")
:type '(function :tag "Inline error clear function")
:risky t)
(defcustom flycheck-inline-display-error-id t
"Whether to display error IDs inline.
If non-nil, inline errors will contain the error ID. Error IDs
are optional: not all checkers suplpy this information. Error
IDs can also be seen in Flycheck's error list."
:group 'flycheck-inline
:type 'boolean
:package-version '(flycheck-inline . "0.1")
:safe #'booleanp)
;;; Displaying inline errors with phantoms
(defun flycheck-inline--displayed-p (err)
"Whether the given error is displayed with any inline overlays."
(seq-find (lambda (p) (eq err (overlay-get p 'error)))
flycheck-inline--phantoms))
(defvar-local flycheck-inline--phantoms nil
"Remember which phantoms were added to the buffer.")
(defun flycheck-inline-display-phantom (msg &optional pos err)
"Display MSG at POS representing error ERR using phantoms.
POS defaults to point."
(unless (flycheck-inline--displayed-p err)
(push (flycheck-inline-phantom-display msg pos err) flycheck-inline--phantoms)))
(defun flycheck-inline-clear-phantoms ()
"Remove all phantoms from buffer that don't contain point."
(setq flycheck-inline--phantoms
(seq-remove #'flycheck-inline-phantom-delete flycheck-inline--phantoms)))
;;; Display inline errors
(defun flycheck-inline--error-position (err)
"Return the position to insert ERR at."
(if (flycheck-relevant-error-other-file-p err)
;; Display overlays for other-file errors on the first line
(point-min)
(flycheck-error-pos err)))
(defun flycheck-inline--error-message (err)
"Return the message to display for ERR."
(let ((filename (flycheck-error-filename err))
(id (flycheck-error-id err)))
(concat (when (and filename (not (equal filename (buffer-file-name))))
(format "In \"%s\":\n" (file-relative-name filename default-directory)))
(flycheck-error-message err)
(when (and id flycheck-inline-display-error-id)
(format " [%s]" id)))))
(defun flycheck-inline--error-face (err)
"Return the face used to display ERR."
(pcase (flycheck-error-level err)
(`info 'flycheck-inline-info)
(`warning 'flycheck-inline-warning)
(`error 'flycheck-inline-error)))
(defun flycheck-inline-display-error (err)
"Display `flycheck-error' ERR inline."
(let* ((pos (flycheck-inline--error-position err))
(msg (propertize (flycheck-inline--error-message err)
'face (flycheck-inline--error-face err))))
(funcall flycheck-inline-display-function msg pos err)))
(defun flycheck-inline-hide-errors ()
"Hide all inline messages currently being shown."
(funcall flycheck-inline-clear-function))
(defun flycheck-inline-display-errors (errors)
"Display ERRORS, and all related errors, inline.
ERRORS is a list of `flycheck-error' objects."
(flycheck-inline-hide-errors)
(mapc #'flycheck-inline-display-error
(seq-uniq
(seq-mapcat #'flycheck-related-errors errors))))
;;; Global and local minor modes
;;;###autoload
(define-minor-mode flycheck-inline-mode
"A minor mode to show Flycheck error messages line.
When called interactively, toggle `flycheck-inline-mode'. With
prefix ARG, enable `flycheck-inline-mode' if ARG is positive,
otherwise disable it.
When called from Lisp, enable `flycheck-inline-mode' if ARG is
omitted, nil or positive. If ARG is `toggle', toggle
`flycheck-inline-mode'. Otherwise behave as if called
interactively.
In `flycheck-inline-mode', show Flycheck error messages inline,
directly below the error reported location."
:group 'flycheck-inline
:require 'flycheck-inline
(cond
;; Use our display function.
(flycheck-inline-mode
(setq-local flycheck-display-errors-function #'flycheck-inline-display-errors)
(add-hook 'post-command-hook #'flycheck-inline-hide-errors nil 'local))
;; Reset the display function and remove ourselves from all hooks but only
;; if the mode is still active.
((not flycheck-inline-mode)
(kill-local-variable 'flycheck-display-errors-function)
(flycheck-inline-hide-errors)
(remove-hook 'post-command-hook #'flycheck-inline-hide-errors 'local))))
(defun turn-on-flycheck-inline ()
"Turn on `flycheck-inline-mode' in Flycheck buffers."
;; Make sure to turn on flycheck-inline in this buffer, either directly if
;; flycheck is already loaded, or via a hook if flycheck hasn't been loaded
;; yet.
(if flycheck-mode
(flycheck-inline-mode)
(add-hook 'flycheck-mode-hook #'flycheck-inline-mode nil 'local)))
;;;###autoload
(define-global-minor-mode global-flycheck-inline-mode
flycheck-inline-mode turn-on-flycheck-inline
"Toggle flycheck-inline in all Flycheck buffers."
:group 'flycheck-inline
:require 'flycheck-inline)
(provide 'flycheck-inline)
;;; flycheck-inline.el ends here