diff options
Diffstat (limited to 'elpa/lsp-mode-20220505.630/lsp-diagnostics.el')
-rw-r--r-- | elpa/lsp-mode-20220505.630/lsp-diagnostics.el | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/elpa/lsp-mode-20220505.630/lsp-diagnostics.el b/elpa/lsp-mode-20220505.630/lsp-diagnostics.el new file mode 100644 index 0000000..f82ef56 --- /dev/null +++ b/elpa/lsp-mode-20220505.630/lsp-diagnostics.el @@ -0,0 +1,370 @@ +;;; lsp-diagnostics.el --- LSP diagnostics integration -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2020 emacs-lsp maintainers +;; +;; 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 <https://www.gnu.org/licenses/>. +;; +;;; Commentary: +;; +;; LSP diagnostics integration +;; +;;; Code: + +(require 'lsp-mode) + +(defgroup lsp-diagnostics nil + "LSP support for diagnostics" + :prefix "lsp-disagnostics-" + :group 'lsp-mode + :tag "LSP Diagnostics") + +;;;###autoload +(define-obsolete-variable-alias 'lsp-diagnostic-package + 'lsp-diagnostics-provider "lsp-mode 7.0.1") + +(defcustom lsp-diagnostics-provider :auto + "The checker backend provider." + :type + '(choice + (const :tag "Pick flycheck if present and fallback to flymake" :auto) + (const :tag "Pick flycheck" :flycheck) + (const :tag "Pick flymake" :flymake) + (const :tag "Use neither flymake nor lsp" :none) + (const :tag "Prefer flymake" t) + (const :tag "Prefer flycheck" nil)) + :group 'lsp-diagnostics + :package-version '(lsp-mode . "6.3")) + +;;;###autoload +(define-obsolete-variable-alias 'lsp-flycheck-default-level + 'lsp-diagnostics-flycheck-default-level "lsp-mode 7.0.1") + +(defcustom lsp-diagnostics-flycheck-default-level 'error + "Error level to use when the server does not report back a diagnostic level." + :type '(choice + (const error) + (const warning) + (const info)) + :group 'lsp-diagnostics) + +(defcustom lsp-diagnostics-attributes + `((unnecessary :foreground "gray") + (deprecated :strike-through t)) + "The Attributes used on the diagnostics. +List containing (tag attributes) where tag is the LSP diagnostic tag and +attributes is a `plist' containing face attributes which will be applied +on top the flycheck face for that error level." + :type '(repeat list) + :group 'lsp-diagnostics) + +(defcustom lsp-diagnostics-disabled-modes nil + "A list of major models for which `lsp-diagnostics-mode' should be disabled." + :type '(repeat symbol) + :group 'lsp-diagnostics + :package-version '(lsp-mode . "8.0.0")) + +;; Flycheck integration + +(declare-function flycheck-mode "ext:flycheck") +(declare-function flycheck-define-generic-checker + "ext:flycheck" (symbol docstring &rest properties)) +(declare-function flycheck-error-new "ext:flycheck" t t) +(declare-function flycheck-error-message "ext:flycheck" (err) t) +(declare-function flycheck-define-error-level "ext:flycheck" (level &rest properties)) +(declare-function flycheck-buffer "ext:flycheck") +(declare-function flycheck-valid-checker-p "ext:flycheck") +(declare-function flycheck-stop "ext:flycheck") + +(defvar flycheck-mode) +(defvar flycheck-check-syntax-automatically) +(defvar flycheck-checker) +(defvar flycheck-checkers) + + +(defvar-local lsp-diagnostics--flycheck-enabled nil + "True when lsp diagnostics flycheck integration has been enabled in this buffer.") + +(defvar-local lsp-diagnostics--flycheck-checker nil + "The value of flycheck-checker before lsp diagnostics was activated.") + +(defun lsp-diagnostics--flycheck-level (flycheck-level tags) + "Generate flycheck level from the original FLYCHECK-LEVEL (e. +g. `error', `warning') and list of LSP TAGS." + (let ((name (format "lsp-flycheck-%s-%s" + flycheck-level + (mapconcat #'symbol-name tags "-")))) + (or (intern-soft name) + (let* ((face (--doto (intern (format "lsp-%s-face" name)) + (copy-face (-> flycheck-level + (get 'flycheck-overlay-category) + (get 'face)) + it) + (mapc (lambda (tag) + (apply #'set-face-attribute it nil + (cl-rest (assoc tag lsp-diagnostics-attributes)))) + tags))) + (category (--doto (intern (format "lsp-%s-category" name)) + (setf (get it 'face) face + (get it 'priority) 100))) + (new-level (intern name)) + (bitmap (or (get flycheck-level 'flycheck-fringe-bitmaps) + (get flycheck-level 'flycheck-fringe-bitmap-double-arrow)))) + (flycheck-define-error-level new-level + :severity (get flycheck-level 'flycheck-error-severity) + :compilation-level (get flycheck-level 'flycheck-compilation-level) + :overlay-category category + :fringe-bitmap bitmap + :fringe-face (get flycheck-level 'flycheck-fringe-face) + :error-list-face face) + new-level)))) + +(defun lsp-diagnostics--flycheck-calculate-level (severity tags) + "Calculate flycheck level by SEVERITY and TAGS." + (let ((level (pcase severity + (1 'error) + (2 'warning) + (3 'info) + (4 'info) + (_ lsp-flycheck-default-level))) + ;; materialize only first tag. + (tags (seq-map (lambda (tag) + (cond + ((= tag lsp/diagnostic-tag-unnecessary) 'unnecessary) + ((= tag lsp/diagnostic-tag-deprecated) 'deprecated))) + tags))) + (if tags + (lsp-diagnostics--flycheck-level level tags) + level))) + +(defun lsp-diagnostics--flycheck-start (checker callback) + "Start an LSP syntax check with CHECKER. + +CALLBACK is the status callback passed by Flycheck." + + (remove-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer t) + + (->> (lsp--get-buffer-diagnostics) + (-map (-lambda ((&Diagnostic :message :severity? :tags? :code? :source? + :range (&Range :start (&Position :line start-line + :character start-character) + :end (&Position :line end-line + :character end-character)))) + (flycheck-error-new + :buffer (current-buffer) + :checker checker + :filename buffer-file-name + :message message + :level (lsp-diagnostics--flycheck-calculate-level severity? tags?) + :id code? + :group source? + :line (lsp-translate-line (1+ start-line)) + :column (1+ (lsp-translate-column start-character)) + :end-line (lsp-translate-line (1+ end-line)) + :end-column (1+ (lsp-translate-column end-character))))) + (funcall callback 'finished))) + +(defun lsp-diagnostics--flycheck-buffer () + "Trigger flyckeck on buffer." + (remove-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer t) + (when (bound-and-true-p flycheck-mode) + (flycheck-buffer))) + +(defun lsp-diagnostics--flycheck-report () + "Report flycheck. +This callback is invoked when new diagnostics are received +from the language server." + (when (and (or (memq 'idle-change flycheck-check-syntax-automatically) + (and (memq 'save flycheck-check-syntax-automatically) + (not (buffer-modified-p)))) + lsp--cur-workspace) + ;; make sure diagnostics are published even if the diagnostics + ;; have been received after idle-change has been triggered + (->> lsp--cur-workspace + (lsp--workspace-buffers) + (mapc (lambda (buffer) + (when (and (lsp-buffer-live-p buffer) + (or + (not (bufferp buffer)) + (and (get-buffer-window buffer) + (not (-contains? (buffer-local-value 'lsp-on-idle-hook buffer) + 'lsp-diagnostics--flycheck-buffer))))) + (lsp-with-current-buffer buffer + (add-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer nil t) + (lsp--idle-reschedule (current-buffer))))))))) + +(cl-defgeneric lsp-diagnostics-flycheck-error-explainer (e _server-id) + "Explain a `flycheck-error' E in a generic way depending on the SERVER-ID." + (flycheck-error-message e)) + +(defvar lsp-diagnostics-mode) ;; properly defined by define-minor-mode below + +;;;###autoload +(defun lsp-diagnostics-lsp-checker-if-needed () + (unless (flycheck-valid-checker-p 'lsp) + (flycheck-define-generic-checker 'lsp + "A syntax checker using the Language Server Protocol (LSP) +provided by lsp-mode. +See https://github.com/emacs-lsp/lsp-mode." + :start #'lsp-diagnostics--flycheck-start + :modes '(lsp-placeholder-mode) ;; placeholder + :predicate (lambda () lsp-diagnostics-mode) + :error-explainer (lambda (e) + (lsp-diagnostics-flycheck-error-explainer + e (lsp--workspace-server-id (car-safe (lsp-workspaces)))))))) + +(defun lsp-diagnostics-flycheck-enable (&rest _) + "Enable flycheck integration for the current buffer." + (require 'flycheck) + (lsp-diagnostics-lsp-checker-if-needed) + (and (not lsp-diagnostics--flycheck-enabled) + (not (eq flycheck-checker 'lsp)) + (setq lsp-diagnostics--flycheck-checker flycheck-checker)) + (setq-local lsp-diagnostics--flycheck-enabled t) + (flycheck-mode 1) + (flycheck-stop) + (setq-local flycheck-checker 'lsp) + (lsp-flycheck-add-mode major-mode) + (add-to-list 'flycheck-checkers 'lsp) + (add-hook 'lsp-diagnostics-updated-hook #'lsp-diagnostics--flycheck-report nil t) + (add-hook 'lsp-managed-mode-hook #'lsp-diagnostics--flycheck-report nil t)) + +(defun lsp-diagnostics-flycheck-disable () + "Disable flycheck integration for the current buffer is it was enabled." + (when lsp-diagnostics--flycheck-enabled + (flycheck-stop) + (when (eq flycheck-checker 'lsp) + (setq-local flycheck-checker lsp-diagnostics--flycheck-checker)) + (setq lsp-diagnostics--flycheck-checker nil) + (setq-local lsp-diagnostics--flycheck-enabled nil) + (when flycheck-mode + (flycheck-mode 1)))) + +;; Flymake integration + +(declare-function flymake-mode "ext:flymake") +(declare-function flymake-make-diagnostic "ext:flymake") +(declare-function flymake-diag-region "ext:flymake") + +(defvar flymake-diagnostic-functions) +(defvar flymake-mode) +(defvar-local lsp-diagnostics--flymake-report-fn nil) + +(defun lsp-diagnostics--flymake-setup () + "Setup flymake." + (setq lsp-diagnostics--flymake-report-fn nil) + (add-hook 'flymake-diagnostic-functions 'lsp-diagnostics--flymake-backend nil t) + (add-hook 'lsp-diagnostics-updated-hook 'lsp-diagnostics--flymake-after-diagnostics nil t) + (flymake-mode 1)) + +(defun lsp-diagnostics--flymake-after-diagnostics () + "Handler for `lsp-diagnostics-updated-hook'." + (cond + ((and lsp-diagnostics--flymake-report-fn flymake-mode) + (lsp-diagnostics--flymake-update-diagnostics)) + ((not flymake-mode) + (setq lsp-diagnostics--flymake-report-fn nil)))) + +(defun lsp-diagnostics--flymake-backend (report-fn &rest _args) + "Flymake backend using REPORT-FN." + (let ((first-run (null lsp-diagnostics--flymake-report-fn))) + (setq lsp-diagnostics--flymake-report-fn report-fn) + (when first-run + (lsp-diagnostics--flymake-update-diagnostics)))) + +(defun lsp-diagnostics--flymake-update-diagnostics () + "Report new diagnostics to flymake." + (funcall lsp-diagnostics--flymake-report-fn + (-some->> (lsp-diagnostics t) + (gethash (lsp--fix-path-casing buffer-file-name)) + (--map (-let* (((&Diagnostic :message :severity? + :range (range &as &Range + :start (&Position :line start-line :character) + :end (&Position :line end-line))) it) + ((start . end) (lsp--range-to-region range))) + (when (= start end) + (if-let ((region (flymake-diag-region (current-buffer) + (1+ start-line) + character))) + (setq start (car region) + end (cdr region)) + (lsp-save-restriction-and-excursion + (goto-char (point-min)) + (setq start (point-at-bol (1+ start-line)) + end (point-at-eol (1+ end-line)))))) + (flymake-make-diagnostic (current-buffer) + start + end + (cl-case severity? + (1 :error) + (2 :warning) + (t :note)) + message)))) + ;; This :region keyword forces flymake to delete old diagnostics in + ;; case the buffer hasn't changed since the last call to the report + ;; function. See https://github.com/joaotavora/eglot/issues/159 + :region (cons (point-min) (point-max)))) + + + +;;;###autoload +(defun lsp-diagnostics--enable () + "Enable LSP checker support." + (when (and (member lsp-diagnostics-provider '(:auto :none :flycheck :flymake t nil)) + (not (member major-mode lsp-diagnostics-disabled-modes))) + (lsp-diagnostics-mode 1))) + +(defun lsp-diagnostics--disable () + "Disable LSP checker support." + (lsp-diagnostics-mode -1)) + +;;;###autoload +(define-minor-mode lsp-diagnostics-mode + "Toggle LSP diagnostics integration." + :group 'lsp-diagnostics + :global nil + :lighter "" + (cond + (lsp-diagnostics-mode + (cond + ((and (or + (and (eq lsp-diagnostics-provider :auto) + (functionp 'flycheck-mode)) + (and (eq lsp-diagnostics-provider :flycheck) + (or (functionp 'flycheck-mode) + (user-error "The lsp-diagnostics-provider is set to :flycheck but flycheck is not installed?"))) + ;; legacy + (null lsp-diagnostics-provider)) + (require 'flycheck nil t)) + (lsp-diagnostics-flycheck-enable)) + ((or (eq lsp-diagnostics-provider :auto) + (eq lsp-diagnostics-provider :flymake) + (eq lsp-diagnostics-provider t)) + (require 'flymake) + (lsp-diagnostics--flymake-setup)) + ((not (eq lsp-diagnostics-provider :none)) + (lsp--warn "Unable to autoconfigure flycheck/flymake. The diagnostics won't be rendered."))) + + (add-hook 'lsp-unconfigure-hook #'lsp-diagnostics--disable nil t)) + (t (lsp-diagnostics-flycheck-disable) + (remove-hook 'lsp-unconfigure-hook #'lsp-diagnostics--disable t)))) + +;;;###autoload +(add-hook 'lsp-configure-hook (lambda () + (when lsp-auto-configure + (lsp-diagnostics--enable)))) + +(lsp-consistency-check lsp-diagnostics) + +(provide 'lsp-diagnostics) +;;; lsp-diagnostics.el ends here |