-
Notifications
You must be signed in to change notification settings - Fork 180
/
rust-cargo.el
146 lines (118 loc) · 4.71 KB
/
rust-cargo.el
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