From a2ffb127ad549b457962c1dc0e1a5bc7d84f13e5 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Tue, 9 Apr 2024 22:39:32 +0300 Subject: [PATCH] Add Flymake backend to rust-ts-mode using Clippy * lisp/progmodes/rust-ts-mode.el (rust-ts-flymake-command): New option (bug#70260). (rust-ts--flymake-proc): New variable. (rust-ts-flymake--helper): New function. (rust-ts-flymake): New function. (rust-ts-mode): Add it to flymake-diagnostic-functions. (cherry picked from commit ccced8c3e4323b9c3d24b2480fc7b8df946fce88) --- lisp/progmodes/rust-ts-mode.el | 80 +++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el index c67ac43e4d0..7112ceced57 100644 --- a/lisp/progmodes/rust-ts-mode.el +++ b/lisp/progmodes/rust-ts-mode.el @@ -48,6 +48,20 @@ :safe 'integerp :group 'rust) +(defcustom rust-ts-flymake-command '("clippy-driver" "-") + "The external tool that will be used to perform the check. +This is a non-empty list of strings: the checker tool possibly followed +by required arguments. Once launched it will receive the Rust source +to be checked as its standard input." + :version "30.1" + :type '(choice (const :tag "Clippy standalone" ("clippy-driver" "-")) + ;; TODO: Maybe add diagnostics filtering by file name, + ;; to limit non-project list to the current buffer. + ;; Or annotate them with file names, at least. + (const :tag "Clippy cargo" ("cargo" "clippy")) + (repeat :tag "Custom command" string)) + :group 'rust) + (defvar rust-ts-mode-prettify-symbols-alist '(("&&" . ?∧) ("||" . ?∨) ("<=" . ?≤) (">=" . ?≥) ("!=" . ?≠) @@ -417,6 +431,67 @@ See `prettify-symbols-compose-predicate'." "operator")) (_ t)))) +(defvar rust-ts--flymake-proc nil) + +(defun rust-ts-flymake--helper (process-name command parser-fn) + (when (process-live-p rust-ts--flymake-proc) + (kill-process rust-ts--flymake-proc)) + + (let ((source (current-buffer))) + (save-restriction + (widen) + (setq + rust-ts--flymake-proc + (make-process + :name process-name :noquery t :connection-type 'pipe + :buffer (generate-new-buffer (format " *%s*" process-name)) + :command command + :sentinel + (lambda (proc _event) + (when (and (eq 'exit (process-status proc)) (buffer-live-p source)) + (unwind-protect + (if (with-current-buffer source (eq proc rust-ts--flymake-proc)) + (with-current-buffer (process-buffer proc) + (funcall parser-fn proc source)) + (flymake-log :debug "Canceling obsolete check %s" + proc)) + (kill-buffer (process-buffer proc))))))) + (process-send-region rust-ts--flymake-proc (point-min) (point-max)) + (process-send-eof rust-ts--flymake-proc)))) + +(defun rust-ts-flymake (report-fn &rest _args) + "Rust backend for Flymake." + (unless (executable-find (car rust-ts-flymake-command)) + (error "Cannot find the rust flymake program: %s" (car rust-ts-flymake-command))) + + (rust-ts-flymake--helper + "rust-ts-flymake" + rust-ts-flymake-command + (lambda (_proc source) + (goto-char (point-min)) + (cl-loop + while (search-forward-regexp + (concat + "^\\(\\(?:warning\\|error\\|help\\).*\\)\n +--> [^:]+:" + "\\([0-9]+\\):\\([0-9]+\\)\\(\\(?:\n[^\n]+\\)*\\)\n\n") + nil t) + for msg1 = (match-string 1) + for msg2 = (match-string 4) + for (beg . end) = (flymake-diag-region + source + (string-to-number (match-string 2)) + (string-to-number (match-string 3))) + for type = (if (string-match "^warning" msg1) + :warning + :error) + collect (flymake-make-diagnostic source + beg + end + type + (concat msg1 msg2)) + into diags + finally (funcall report-fn diags))))) + ;;;###autoload (define-derived-mode rust-ts-mode prog-mode "Rust" "Major mode for editing Rust, powered by tree-sitter." @@ -460,10 +535,13 @@ See `prettify-symbols-compose-predicate'." (setq-local indent-tabs-mode nil treesit-simple-indent-rules rust-ts-mode--indent-rules) - ;; Electric + ;; Electric. (setq-local electric-indent-chars (append "{}():;,#" electric-indent-chars)) + ;; Flymake. + (add-hook 'flymake-diagnostic-functions #'rust-ts-flymake nil 'local) + ;; Navigation. (setq-local treesit-defun-type-regexp (regexp-opt '("enum_item" -- 2.39.5