]> git.eshelyaron.com Git - emacs.git/commitdiff
New command 'flymake-go-to-diagnostic'
authorEshel Yaron <me@eshelyaron.com>
Sat, 13 Jul 2024 14:22:12 +0000 (16:22 +0200)
committerEshel Yaron <me@eshelyaron.com>
Sat, 13 Jul 2024 14:22:12 +0000 (16:22 +0200)
lisp/progmodes/flymake.el
lisp/progmodes/project.el

index 3321ae243a0ec076c0419e83bf3bac3ce64f02a5..78c5571660962b574d3a120bbe4d85127aa3a60a 100644 (file)
@@ -902,6 +902,136 @@ Return to original margin width if ORIG-WIDTH is non-nil."
           (message "No fix available for this diagnostic")))
     (user-error "No diagnostic at this position")))
 
+(defun flymake-read-diagnostic (prompt &optional project)
+  "Prompt with PROMPT for a Flymake diagnostic in the current buffer.
+
+If optional argument PROJECT is non-nil, prompt for diagnostics across
+that project instead."
+  (let* ((buffer (current-buffer))
+         tab cands ind
+         (cands-fn
+          (lambda ()
+            (setq tab (make-hash-table) cands nil ind 0)
+            (dolist (d (if project
+                           (flymake--project-diagnostics project)
+                         (flymake-diagnostics)))
+              (let ((cand (propertize (format "%-4d:%s" (cl-incf ind) (flymake--diag-text d))
+                                      'diagnostic d)))
+                (push cand cands)
+                (puthash ind d tab)))
+            (setq cands (nreverse cands))))
+         (text
+          (minibuffer-with-setup-hook
+              (lambda ()
+                (setq minibuffer-action
+                      (cons
+                       (lambda (c)
+                         (save-selected-window
+                           (flymake-go-to-diagnostic
+                            (gethash (string-to-number c) tab))))
+                       "goto")
+                      minibuffer-alternative-action
+                      (cons
+                       (lambda (c)
+                         (let ((d (gethash (string-to-number c) tab)))
+                           (save-selected-window
+                             (flymake-go-to-diagnostic d)
+                             (if (not (flymake--fix-diagnostic d))
+                                 (user-error "No fix available for this diagnostic")
+                               (flymake-start)
+                               (flymake--update-diagnostics-listings (current-buffer))
+                               (with-current-buffer buffer (funcall cands-fn))))))
+                       "fix")))
+            (funcall cands-fn)
+            (completing-read
+             prompt
+             (completion-table-with-metadata
+              (completion-table-dynamic (lambda (_) cands))
+              `((category . flymake-diagnostic)
+                (group-function
+                 . ,(lambda (string &optional transform)
+                      (cond
+                       (transform
+                        (substring string (1+ (string-search ":" string))))
+                       (project
+                        (let ((bof (flymake--diag-locus
+                                    (gethash (string-to-number string) tab))))
+                          (if (bufferp bof) (concat "Buffer "(buffer-name bof))
+                            (concat "File " (file-relative-name
+                                             bof (project-root project)))))))))
+                (affixation-function
+                 . ,(lambda (cs)
+                      (mapcar
+                       (lambda (cand)
+                         (let ((diag (gethash (string-to-number cand) tab)))
+                           (list cand
+                                 (format "%s%s "
+                                         (if (flymake--diag-fix-function diag)
+                                             (propertize "*" 'help-echo "Fix available")
+                                           " ")
+                                         (cl-case (get (flymake--diag-type diag) 'flymake-category)
+                                           (flymake-note    (propertize "n" 'face 'flymake-note-echo))
+                                           (flymake-warning (propertize "w" 'face 'flymake-warning-echo))
+                                           (flymake-error   (propertize "e" 'face 'flymake-error-echo))
+                                           (otherwise " ")))
+                                 "")))
+                       cs)))
+                (narrow-completions-function
+                 . ((?f "fix" "Fix available"
+                        ,(lambda ()
+                           `(,(lambda (c)
+                                (flymake--diag-fix-function (get-text-property 0 'diagnostic c)))
+                             . "has fix")))
+                    (?n "note" "Notes"
+                        ,(lambda ()
+                           `(,(lambda (c)
+                                (eq (get (flymake--diag-type (get-text-property 0 'diagnostic c))
+                                         'flymake-category)
+                                    'flymake-note))
+                             . "type=note")))
+                    (?w "warning" "Warnings"
+                        ,(lambda ()
+                           `(,(lambda (c)
+                                (eq (get (flymake--diag-type (get-text-property 0 'diagnostic c))
+                                         'flymake-category)
+                                    'flymake-warning))
+                             . "type=warning")))
+                    (?e "error" "Errors"
+                        ,(lambda ()
+                           `(,(lambda (c)
+                                (eq (get (flymake--diag-type (get-text-property 0 'diagnostic c))
+                                         'flymake-category)
+                                    'flymake-error))
+                             . "type=error")))))))
+             nil t nil t))))
+    (gethash (string-to-number text) tab)))
+
+(defun flymake-go-to-diagnostic (diag)
+  "Go to Flymake diagnostic DIAG.
+
+Interactively, prompt for DIAG with completion.  By default, completion
+candidates are diagnostics for the current buffer; with prefix argument,
+completion candidates are instead all known diagnostics in the current
+project.
+
+During minibuffer input, you can use the minibuffer action to show
+candidate diagnostics without exiting the minibuffer.  The alternative
+minibuffer action lets you fix candidate diagnostics that have available
+fix suggestions."
+  (interactive
+   (list (save-excursion (flymake-read-diagnostic "Go to diagnostic: "
+                                                  (when current-prefix-arg
+                                                    (project-current))))))
+  (let* ((bof (flymake--diag-locus diag))
+         (buf (if (bufferp bof) bof (find-file-noselect bof)))
+         (beg (flymake--diag-beg diag)))
+    (push-mark)
+    (pop-to-buffer buf)
+    (goto-char
+     (if (number-or-marker-p beg) beg
+       (car (flymake-diag-region (current-buffer) (car beg) (cdr beg)))))
+    (pulse-momentary-highlight-one-line)))
+
 (cl-defun flymake--highlight-line (diagnostic &optional foreign)
   "Attempt to overlay DIAGNOSTIC in current buffer.
 
index 4dd8393021447af99ba4cd61949a0751ef29b9f7..8369f6f51202a961262312cc798c812019bbdb64 100644 (file)
@@ -880,6 +880,7 @@ DIRS must contain directory names."
     (define-key map "x" 'project-execute-extended-command)
     (define-key map "o" 'project-any-command)
     (define-key map "\C-b" 'project-list-buffers)
+    (define-key map "`" 'project-go-to-diagnostic)
     map)
   "Keymap for project commands.")
 
@@ -1615,6 +1616,19 @@ If FILES-ONLY is non-nil, only show the file-visiting buffers."
                            (member (current-buffer)
                                    (project-buffers ',project)))))))
 
+(declare-function flymake-read-diagnostic "flymake")
+(declare-function flymake-go-to-diagnostic "flymake")
+
+;;;###autoload
+(defun project-go-to-diagnostic (diag)
+  "Go to Flymake diagnostic DIAG."
+  (interactive
+   (list (save-excursion
+           (require 'flymake)
+           (flymake-read-diagnostic "Project diagnostic: " (project-current t)))))
+  (require 'flymake)
+  (flymake-go-to-diagnostic diag))
+
 (defcustom project-kill-buffer-conditions
   '(buffer-file-name    ; All file-visiting buffers are included.
     ;; Most of temp and logging buffers (aside from hidden ones):