|
| 1 | +;;; lsp-dired.el --- `lsp-mode' diagnostics integrated into `dired' -*- lexical-binding: t -*- |
| 2 | + |
| 3 | +;; Copyright (C) 2021 |
| 4 | + |
| 5 | +;; Author: Alexander Miller <[email protected]> |
| 6 | +;; Author: Ivan Yonchovski <[email protected]> |
| 7 | + |
| 8 | +;; This program is free software; you can redistribute it and/or modify |
| 9 | +;; it under the terms of the GNU General Public License as published by |
| 10 | +;; the Free Software Foundation, either version 3 of the License, or |
| 11 | +;; (at your option) any later version. |
| 12 | + |
| 13 | +;; This program is distributed in the hope that it will be useful, |
| 14 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | +;; GNU General Public License for more details. |
| 17 | + |
| 18 | +;; You should have received a copy of the GNU General Public License |
| 19 | +;; along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 20 | + |
| 21 | +;;; Commentary: |
| 22 | +;;; `lsp-mode' diagnostics integrated into `dired' |
| 23 | + |
| 24 | +;;; Code: |
| 25 | + |
| 26 | +(require 'dired) |
| 27 | +(require 'pcase) |
| 28 | +(require 'lsp-mode) |
| 29 | + |
| 30 | +(defvar lsp-dired--ranger-adjust nil) |
| 31 | +(with-eval-after-load 'ranger (setf lsp-dired--ranger-adjust t)) |
| 32 | + |
| 33 | +(defvar-local lsp-dired-displayed nil |
| 34 | + "Flags whether icons have been added.") |
| 35 | + |
| 36 | +(defvar-local lsp-dired--covered-subdirs nil |
| 37 | + "List of subdirs icons were already added for.") |
| 38 | + |
| 39 | +(defun lsp-dired--display () |
| 40 | + "Display the icons of files in a dired buffer." |
| 41 | + (when (and (display-graphic-p) |
| 42 | + (not lsp-dired-displayed) |
| 43 | + dired-subdir-alist) |
| 44 | + (setq-local lsp-dired-displayed t) |
| 45 | + (pcase-dolist (`(,path . ,pos) dired-subdir-alist) |
| 46 | + (lsp-dired--insert-for-subdir path pos)))) |
| 47 | + |
| 48 | +(defun lsp-dired--insert-for-subdir (path pos) |
| 49 | + "Display icons for subdir PATH at given POS." |
| 50 | + (let ((buf (current-buffer))) |
| 51 | + ;; run the function after current to make sure that we are creating the |
| 52 | + ;; overlays after `treemacs-icons-dired' has run. |
| 53 | + (run-with-idle-timer |
| 54 | + 0.0 nil |
| 55 | + (lambda () |
| 56 | + (unless (and (member path lsp-dired--covered-subdirs) |
| 57 | + (not (buffer-live-p buf))) |
| 58 | + (with-current-buffer buf |
| 59 | + (add-to-list 'lsp-dired--covered-subdirs path) |
| 60 | + (let (buffer-read-only) |
| 61 | + (save-excursion |
| 62 | + (goto-char pos) |
| 63 | + (forward-line (if lsp-dired--ranger-adjust 1 2)) |
| 64 | + (cl-block :file |
| 65 | + (while (not (eobp)) |
| 66 | + (if (dired-move-to-filename nil) |
| 67 | + (let* ((file (dired-get-filename nil t)) |
| 68 | + (bol (progn |
| 69 | + (point-at-bol) |
| 70 | + (search-forward-regexp "^[[:space:]]*" (line-end-position) t) |
| 71 | + (point))) |
| 72 | + (face (lsp-dired--face-for-path file))) |
| 73 | + (when face |
| 74 | + (-doto (make-overlay bol (point-at-eol)) |
| 75 | + (overlay-put 'evaporate t) |
| 76 | + (overlay-put 'face face)))) |
| 77 | + (cl-return-from :file nil)) |
| 78 | + (forward-line 1))))))))))) |
| 79 | + |
| 80 | +(defface lsp-dired-path-face '((t :inherit font-lock-string-face)) |
| 81 | + "Face used for breadcrumb paths on headerline." |
| 82 | + :group 'lsp-faces) |
| 83 | + |
| 84 | +(defface lsp-dired-path-error-face |
| 85 | + '((t :underline (:style wave :color "Red1"))) |
| 86 | + "Face used for breadcrumb paths on headerline when there is an error under that path" |
| 87 | + :group 'lsp-faces) |
| 88 | + |
| 89 | +(defface lsp-dired-path-warning-face |
| 90 | + '((t :underline (:style wave :color "Yellow"))) |
| 91 | + "Face used for breadcrumb paths on headerline when there is an warning under that path" |
| 92 | + :group 'lsp-faces) |
| 93 | + |
| 94 | +(defface lsp-dired-path-info-face |
| 95 | + '((t :underline (:style wave :color "Green"))) |
| 96 | + "Face used for breadcrumb paths on headerline when there is an info under that path" |
| 97 | + :group 'lsp-faces) |
| 98 | + |
| 99 | +(defface lsp-dired-path-hint-face |
| 100 | + '((t :underline (:style wave :color "Green"))) |
| 101 | + "Face used for breadcrumb paths on headerline when there is an hint under that path" |
| 102 | + :group 'lsp-faces) |
| 103 | + |
| 104 | +(defun lsp-dired--face-for-path (dir) |
| 105 | + "Calculate the face for DIR." |
| 106 | + (when-let ((diags (lsp-diagnostics-stats-for (directory-file-name dir)))) |
| 107 | + (cl-labels ((check-severity |
| 108 | + (severity) |
| 109 | + (not (zerop (aref diags severity))))) |
| 110 | + (cond |
| 111 | + ((check-severity lsp/diagnostic-severity-error) |
| 112 | + 'lsp-dired-path-error-face) |
| 113 | + ((check-severity lsp/diagnostic-severity-warning) |
| 114 | + 'lsp-dired-path-warning-face) |
| 115 | + ((check-severity lsp/diagnostic-severity-information) |
| 116 | + 'lsp-dired-path-info-face) |
| 117 | + ((check-severity lsp/diagnostic-severity-hint) |
| 118 | + 'lsp-dired-path-hint-face))))) |
| 119 | + |
| 120 | +(defun lsp-dired--insert-subdir-advice (&rest args) |
| 121 | + "Advice to dired & dired+ insert-subdir commands. |
| 122 | +Will add icons for the subdir in the `car' of ARGS." |
| 123 | + (let* ((path (car args)) |
| 124 | + (pos (cdr (assoc path dired-subdir-alist)))) |
| 125 | + (when pos |
| 126 | + (lsp-dired--insert-for-subdir path pos)))) |
| 127 | + |
| 128 | +(defun lsp-dired--kill-subdir-advice (&rest _args) |
| 129 | + "Advice to dired kill-subdir commands. |
| 130 | +Will remove the killed subdir from `lsp-dired--covered-subdirs'." |
| 131 | + (setf lsp-dired--covered-subdirs (delete (dired-current-directory) |
| 132 | + lsp-dired--covered-subdirs))) |
| 133 | + |
| 134 | +(defun lsp-dired--reset (&rest _args) |
| 135 | + "Reset metadata on revert." |
| 136 | + (setq-local lsp-dired--covered-subdirs nil) |
| 137 | + (setq-local lsp-dired-displayed nil)) |
| 138 | + |
| 139 | +;;;###autoload |
| 140 | +(define-minor-mode lsp-dired-mode |
| 141 | + "Display `lsp-mode' icons for each file in a dired buffer." |
| 142 | + :require 'lsp-dired |
| 143 | + :init-value nil |
| 144 | + :global t |
| 145 | + :group 'lsp-mode |
| 146 | + (cond |
| 147 | + (lsp-dired-mode |
| 148 | + (add-hook 'dired-after-readin-hook #'lsp-dired--display) |
| 149 | + (advice-add 'dired-kill-subdir :before #'lsp-dired--kill-subdir-advice) |
| 150 | + (advice-add 'dired-insert-subdir :after #'lsp-dired--insert-subdir-advice) |
| 151 | + (advice-add 'diredp-insert-subdirs :after #'lsp-dired--insert-subdir-advice) |
| 152 | + (advice-add 'dired-revert :before #'lsp-dired--reset) |
| 153 | + (dolist (buffer (buffer-list)) |
| 154 | + (with-current-buffer buffer |
| 155 | + (when (derived-mode-p 'dired-mode) |
| 156 | + (lsp-dired--display))))) |
| 157 | + (t |
| 158 | + (advice-remove 'dired-kill-subdir #'lsp-dired--kill-subdir-advice) |
| 159 | + (advice-remove 'dired-insert-subdir #'lsp-dired--insert-subdir-advice) |
| 160 | + (advice-remove 'diredp-insert-subdirs #'lsp-dired--insert-subdir-advice) |
| 161 | + (advice-remove 'dired-revert #'lsp-dired--reset) |
| 162 | + (remove-hook 'dired-after-readin-hook #'lsp-dired--display) |
| 163 | + (dolist (buffer (buffer-list)) |
| 164 | + (with-current-buffer buffer |
| 165 | + (when (derived-mode-p 'dired-mode) |
| 166 | + (dired-revert))))))) |
| 167 | + |
| 168 | +(provide 'lsp-dired) |
| 169 | + |
| 170 | +;;; lsp-dired.el ends here |
0 commit comments