aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--flymake-rust.el120
1 files changed, 70 insertions, 50 deletions
diff --git a/flymake-rust.el b/flymake-rust.el
index 8092061..9bbb0e3 100644
--- a/flymake-rust.el
+++ b/flymake-rust.el
@@ -1,10 +1,10 @@
-;;; flymake-rust -- Flymake integration for the Rust programming language -*- lexical-binding: t; -*-
+;;; flymake-rust.el -- Flymake integration for the Rust programming language -*- lexical-binding: t; -*-
;; Copyright © 2021 Brian Cully <bjc@kublai.com>
;; Author: Brian Cully <bjc@kublai.com>
-;; URL: https://github.com/bjc/nspawn-tramp
-;; Keywords: flymake, rust
+;; URL: https://github.com/bjc/flymake-rust
+;; Keywords: tools, flymake, rust
;; Maintainer: Brian Cully <bjc@kublai.com>
;; Version: 0.1
;; Package-Requires: ((emacs "27.1"))
@@ -38,6 +38,9 @@
;;; Code:
+(eval-when-compile
+ (require 'subr-x))
+
(defgroup flymake-rust nil
"Flymake integration for the Rust programming language."
:prefix "flymake-rust-"
@@ -47,11 +50,13 @@
(defcustom flymake-rust-cargo-path "cargo"
"Path to cargo executable."
+ :package-version '(flymake-rust . "0.1")
:type 'string
:group 'flymake-rust)
(defcustom flymake-rust-rustc-path "rustc"
"Path to rustc executable."
+ :package-version '(flymake-rust . "0.1")
:type 'string
:group 'flymake-rust)
@@ -63,6 +68,7 @@ for all projects.
* rustc can check between saves, but isn’t project aware, and
will complain about things like missing main functions."
+ :package-version '(flymake-rust . "0.1")
:type '(choice (const cargo)
(const rustc))
:options '(cargo rustc)
@@ -74,27 +80,18 @@ will complain about things like missing main functions."
(defvar flymake-rust--cargo-flags '("check" "--quiet" "--message-format=json")
"Additional flags to pass to cargo.")
-(defvar flymake-rust--rustc-flags '("--error-format=json" "-")
+(defvar flymake-rust--rustc-flags '("--error-format=json" "-o" "/dev/null" "-")
"Additional flags to pass to rustc.")
(defun flymake-rust--buffer-name (suffix)
"What to name the flymake buffer for ‘SUFFIX’."
(format "*flymake-rust[%s] %s*" suffix buffer-file-name))
-(defmacro flymake-rust--with-proc-buf (&rest body)
- "Run ‘BODY’ in the buffer for the current buffer’s Flymake process."
- (declare (indent defun))
- (let ((buf (gensym)))
- `(when-let ((,buf (and flymake-rust--process
- (process-buffer flymake-rust--process))))
- (with-current-buffer ,buf
- ,@body))))
-
(defun flymake-rust--extract-error-location (hash)
"Extract file location data from ‘HASH’.
Returns a sequence of data in the form of (FILE-NAME (BYTE-START
-. BYTE-END)) If any data are not available, nil will be used in
+. BYTE-END)). If any datum is not available, nil will be used in
its place."
(let* ((spans (or (gethash "spans" hash) [])))
(mapcar (lambda (span)
@@ -106,43 +103,49 @@ its place."
(defun flymake-rust--level-to-action (level)
"Convert ‘LEVEL’ into a Flymake action."
- (cond ((string= level "error") :error)
- ((string= level "warning") :warning)
- (t :note)))
+ (pcase level
+ ("error" :error)
+ ("warning" :warning)
+ (_ :note)))
(defun flymake-rust--extract-msghash (hash)
"Return the message hash from ‘HASH’.
-Cargo and rustc have slightly different formats for this."
- (cond ((eq flymake-rust-checker 'cargo)
- (when (string= (gethash "reason" hash) "compiler-message")
- (gethash "message" hash)))
- ((eq flymake-rust-checker 'rustc) hash)))
+Cargo and rustc have slightly different formats for this, which
+this function attempts to account for."
+ (pcase flymake-rust-checker
+ ('cargo (when (string= (gethash "reason" hash) "compiler-message")
+ (gethash "message" hash)))
+ ('rustc hash)))
(defun flymake-rust--normalize-path (hash file-name)
"Return full path to ‘FILE-NAME’.
Cargo only puts the relative path in there, so we need to add the
path to the workspace from ‘HASH’ to get the full path."
- (cond ((eq flymake-rust-checker 'cargo)
- (gethash "src_path" (gethash "target" hash)))
- ((eq flymake-rust-checker 'rustc) file-name)))
+ (pcase flymake-rust-checker
+ ('cargo (thread-last hash
+ (gethash "target")
+ (gethash "src_path")))
+ ('rustc file-name)))
;; See https://doc.rust-lang.org/rustc/json.html for a description of
;; the JSON format.
(defun flymake-rust--make-diagnostics (source-buffer hash)
- "Pull interesting things out of ‘HASH’ for ‘SOURCE-BUFFER ’and forward them to ‘REPORT-FN’."
+ "Extract diagnostic messages for Flymake from ‘HASH’.
+
+‘HASH’ is the hash-table representation of the JSON output by the
+checker for ‘SOURCE-BUFFER’"
(when-let ((msghash (flymake-rust--extract-msghash hash)))
(let ((message (gethash "message" msghash))
(level (gethash "level" msghash))
(errlocs (flymake-rust--extract-error-location msghash)))
(mapcar (lambda (errloc)
- (when-let ((start (caadr errloc))
- (end (cdadr errloc)))
+ (pcase-let ((`(,file-name (,start . ,end)) errloc))
;; Filter out errors that don’t relate to
;; ‘source-buffer’.
- (when (or (string= (car errloc) "<anon>")
- (string= (flymake-rust--normalize-path hash (car errloc))
+ (when (or (string= file-name "<anon>")
+ (string= (flymake-rust--normalize-path hash file-name)
(file-local-name (buffer-file-name source-buffer))))
(flymake-make-diagnostic source-buffer
start end
@@ -150,9 +153,13 @@ path to the workspace from ‘HASH’ to get the full path."
message))))
errlocs))))
-(defun flymake-rust--parse-buffer (source-buffer report-fn)
- "Parse the diagnostics for ‘SOURCE-BUFFER’ and send to ‘REPORT-FN’."
- (flymake-rust--with-proc-buf
+(defun flymake-rust--parse-buffer (proc-buffer source-buffer report-fn)
+ "Parse ‘PROC-BUFFER’ as JSON compiler output for ‘REPORT-FN’.
+
+‘PROC-BUFFER’ is the process buffer associated with the checker
+process for ‘SOURCE-BUFFER’, and is expected to contain JSON
+output from the Rust compiler."
+ (with-current-buffer proc-buffer
(let ((continue t)
(diags nil))
(goto-char (point-min))
@@ -161,9 +168,9 @@ path to the workspace from ‘HASH’ to get the full path."
(json-parse-buffer)
(json-end-of-file nil)
(t (error err)))))
- (progn
- (push (flymake-rust--make-diagnostics source-buffer diag) diags))
+ (push (flymake-rust--make-diagnostics source-buffer diag) diags)
(setq continue nil)))
+
(let ((diags (flatten-list diags)))
(if diags
(funcall report-fn (flatten-list diags))
@@ -173,15 +180,17 @@ path to the workspace from ‘HASH’ to get the full path."
;; are, in fact, an improvement over just calling stuff in the
;; sentinel directly.
(defun flymake-rust--sentinel (source-buffer report-fn proc _event)
- "Call ‘REPORT-FN’ for ‘SOURCE-BUFFER’ with diagnostics when ‘PROC’ finishes.
+ "Handle events for ‘PROC‘.
-This function does not directly call ‘REPORT-FN’, but instead
-sets up a short timer to do so. This is done because sentinel
-processes inhibits Emacs handling of events like quit."
+The only event currently being handled is the process finishing,
+at which point a short-delay idle timer is set up to handle the
+processing of compiler output for ‘SOURCE-BUFFER’, which will be
+reported to ‘REPORT-FN’."
(when (eq 'exit (process-status proc))
(process-put proc 'timer
(run-with-idle-timer 0.1 nil
- 'flymake-rust--parse-buffer source-buffer report-fn))))
+ 'flymake-rust--parse-buffer
+ (process-buffer flymake-rust--process) source-buffer report-fn))))
(defun flymake-rust--make-sentinel (source-buffer report-fn)
"Create a sentinel for ‘SOURCE-BUFFER’ reporting to ‘REPORT-FN’."
@@ -189,20 +198,21 @@ processes inhibits Emacs handling of events like quit."
(flymake-rust--sentinel source-buffer report-fn proc event)))
(defun flymake-rust--cleanup ()
- "Clean up and leftover processes and buffers for the current buffer."
+ "Clean up processes and buffers associated with the current buffer."
(when-let ((timer (and flymake-rust--process
(process-get flymake-rust--process 'timer))))
(cancel-timer timer))
+
(when (process-live-p flymake-rust--process)
(flymake-log :warning "Killing out-of-date checker process.")
(delete-process flymake-rust--process))
- (mapc (lambda (suffix)
- (when-let ((buf (get-buffer (flymake-rust--buffer-name suffix))))
- (kill-buffer buf)))
- '("stdout" "stderr")))
+
+ (dolist (suffix '("stdout" "stderr"))
+ (when-let ((buf (get-buffer (flymake-rust--buffer-name suffix))))
+ (kill-buffer buf))))
(defun flymake-rust--cargo-command ()
- "Return an appropriate cargo check command line."
+ "Return a command line for cargo check."
(cons (executable-find flymake-rust-cargo-path t)
flymake-rust--cargo-flags))
@@ -220,17 +230,26 @@ shunted elsewhere or it breaks parsing."
(defun flymake-rust--checker-command ()
"Return a command line to check ‘PATH’.
-This will use the value configured in ‘flymake-rust-checker’ to what to run."
- (cond ((eq flymake-rust-checker 'cargo) (flymake-rust--cargo-command))
- ((eq flymake-rust-checker 'rustc) (flymake-rust--rustc-command))))
+This uses the value of ‘flymake-rust-checker’ to determine the
+specific command line."
+ (pcase flymake-rust-checker
+ ('cargo (flymake-rust--cargo-command))
+ ('rustc (flymake-rust--rustc-command))))
(defun flymake-rust--call (source-buffer report-fn)
- "Begin checking ‘SOURCE-BUFFER’ and report to ‘REPORT-FN’."
+ "Check ‘SOURCE-BUFFER’ for errors and report them to ‘REPORT-FN’.
+
+‘REPORT-FN’ is a function normally created by Flymake which
+expects a list of diagnostics created by
+‘flymake-make-diagnostic’. For further information, see the Info
+node ‘(flymake)Backend functions’."
(with-current-buffer source-buffer
(flymake-rust--cleanup)
+
(let ((buf (get-buffer-create (flymake-rust--buffer-name "stdout"))))
(with-current-buffer buf
(js-mode))
+
(setq flymake-rust--process
(make-process :name "flymake-rust"
:buffer buf
@@ -256,6 +275,7 @@ For the value of ‘ARGS’, see the documentation for
(when (or (not rc) (cadr rc))
(flymake-rust--call (current-buffer) report-fn))))
+;;;###autoload
(defun flymake-rust-setup ()
"Provide Flymake support for the Rust programming language."
(add-hook 'flymake-diagnostic-functions 'flymake-rust--backend nil t))