(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.
(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.")
(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):