1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
;;; rust-cargo.el --- Support for cargo -*-lexical-binding: t-*-
;;; Commentary:
;; This library implements support for running `cargo'.
;;; Code:
(require 'json)
;;; Options
(defcustom rust-cargo-bin "cargo"
"Path to cargo executable."
:type 'string
:group 'rust-mode)
(defcustom rust-always-locate-project-on-open nil
"Whether to run `cargo locate-project' every time `rust-mode' is activated."
:type 'boolean
:group 'rust-mode)
(defcustom rust-cargo-locate-default-arguments '("--workspace")
"Arguments for `cargo locate-project`. Remove `--workspace` if you
would prefer to use the local crate Cargo.toml instead of the
worksapce for commands like `cargo check`."
:type '(repeat string)
:group 'rust-mode)
(defcustom rust-cargo-default-arguments ""
"Default arguments when running common cargo commands."
:type 'string
:group 'rust-mode)
;;; Buffer Project
(defvar-local rust-buffer-project nil)
(defun rust-buffer-project ()
"Get project root if possible."
;; Copy environment variables into the new buffer, since
;; with-temp-buffer will re-use the variables' defaults, even if
;; they have been changed in this variable using e.g. envrc-mode.
;; See https://github.com/purcell/envrc/issues/12.
(let ((env process-environment)
(path exec-path))
(with-temp-buffer
;; Copy the entire environment just in case there's something we
;; don't know we need.
(setq-local process-environment env)
;; Set PATH so we can find cargo.
(setq-local exec-path path)
(let ((ret
(let ((args
(append
(list rust-cargo-bin nil (list (current-buffer) nil) nil
"locate-project")
rust-cargo-locate-default-arguments)))
(apply #'process-file args))))
(when (/= ret 0)
(error "`cargo locate-project' returned %s status: %s" ret (buffer-string)))
(goto-char 0)
(let ((output (let ((json-object-type 'alist))
(json-read))))
(cdr (assoc-string "root" output)))))))
(defun rust-buffer-crate ()
"Try to locate Cargo.toml using `locate-dominating-file'."
(let ((dir (locate-dominating-file default-directory "Cargo.toml")))
(if dir dir default-directory)))
(defun rust-update-buffer-project ()
(setq-local rust-buffer-project (rust-buffer-project)))
(defun rust-maybe-initialize-buffer-project ()
(setq-local rust-buffer-project nil)
(when rust-always-locate-project-on-open
(rust-update-buffer-project)))
(add-hook 'rust-mode-hook #'rust-maybe-initialize-buffer-project)
;;; Internal
(defun rust--compile (comint format-string &rest args)
(when (null rust-buffer-project)
(rust-update-buffer-project))
(let ((default-directory
(or (and rust-buffer-project
(file-name-directory rust-buffer-project))
default-directory))
;; make sure comint is a boolean value
(comint (not (not comint))))
(compile (apply #'format format-string args) comint)))
;;; Commands
(defun rust-check ()
"Compile using `cargo check`"
(interactive)
(rust--compile nil "%s check %s" rust-cargo-bin rust-cargo-default-arguments))
(defun rust-compile ()
"Compile using `cargo build`"
(interactive)
(rust--compile nil "%s build %s" rust-cargo-bin rust-cargo-default-arguments))
(defun rust-compile-release ()
"Compile using `cargo build --release`"
(interactive)
(rust--compile nil "%s build --release" rust-cargo-bin))
(defun rust-run (&optional comint)
"Run using `cargo run`
If optional arg COMINT is t or invoked with universal prefix arg,
output buffer will be in comint mode, i.e. interactive."
(interactive "P")
(rust--compile comint "%s run %s" rust-cargo-bin rust-cargo-default-arguments))
(defun rust-run-release (&optional comint)
"Run using `cargo run --release`
If optional arg COMINT is t or invoked with universal prefix arg,
output buffer will be in comint mode, i.e. interactive."
(interactive "P")
(rust--compile comint "%s run --release" rust-cargo-bin))
(defun rust-test ()
"Test using `cargo test`"
(interactive)
(rust--compile nil "%s test %s" rust-cargo-bin rust-cargo-default-arguments))
(defun rust-run-clippy ()
"Run `cargo clippy'."
(interactive)
(when (null rust-buffer-project)
(rust-update-buffer-project))
(let* ((args (list rust-cargo-bin "clippy"
(concat "--manifest-path=" rust-buffer-project)))
;; set `compile-command' temporarily so `compile' doesn't
;; clobber the existing value
(compile-command (mapconcat #'shell-quote-argument args " ")))
(rust--compile nil compile-command)))
;;; _
(provide 'rust-cargo)
;;; rust-cargo.el ends here
|