Skip to content

Commit a36c96a

Browse files
nikodemusmookid
authored andcommitted
integrate rustfmt error messages
If rustfmt reports an error, by default display it in the message area and jump to the location. (custom rust-format-goto-problem) Optionally display the *rustfmt* buffer when rustfmt reports an error. (custom rust-format-show-buffer)
1 parent 63ec74c commit a36c96a

File tree

1 file changed

+108
-24
lines changed

1 file changed

+108
-24
lines changed

rust-mode.el

Lines changed: 108 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,18 @@ to the function arguments. When nil, `->' will be indented one level."
198198
:safe #'booleanp
199199
:group 'rust-mode)
200200

201+
(defcustom rust-format-show-buffer nil
202+
"Show *rustfmt* buffer if formatting detected problems."
203+
:type 'boolean
204+
:safe #'booleanp
205+
:group 'rust-mode)
206+
207+
(defcustom rust-format-goto-problem t
208+
"Jump to location of first detected probem when formatting buffer."
209+
:type 'boolean
210+
:safe #'booleanp
211+
:group 'rust-mode)
212+
201213
(defcustom rust-rustfmt-bin "rustfmt"
202214
"Path to rustfmt executable."
203215
:type 'string
@@ -1455,13 +1467,76 @@ This is written mainly to be used as `end-of-defun-function' for Rust."
14551467
(copy-to-buffer buf (point-min) (point-max)))
14561468
(erase-buffer)
14571469
(insert-file-contents tmpf)
1470+
(rust--format-fix-rustfmt-buffer (buffer-name buf))
14581471
(error "Rustfmt could not format some lines, see *rustfmt* buffer for details"))
14591472
(t
14601473
(erase-buffer)
14611474
(insert-file-contents tmpf)
1475+
(rust--format-fix-rustfmt-buffer (buffer-name buf))
14621476
(error "Rustfmt failed, see *rustfmt* buffer for details"))))
14631477
(delete-file tmpf))))
14641478

1479+
;; Since we run rustfmt through stdin we get <stdin> markers in the
1480+
;; output. This replaces them with the buffer name instead.
1481+
(defun rust--format-fix-rustfmt-buffer (buffer-name)
1482+
(with-current-buffer (get-buffer "*rustfmt*")
1483+
(goto-char (point-min))
1484+
(while (re-search-forward "--> <stdin>:")
1485+
(replace-match (format "--> %s:" buffer-name)))))
1486+
1487+
;; If rust-mode has been configured to navigate to source of the error
1488+
;; or display it, do so -- and return true. Otherwise return nil to
1489+
;; indicate nothing was done.
1490+
(defun rust--format-error-handler ()
1491+
(let ((ok nil))
1492+
(when rust-format-show-buffer
1493+
(display-buffer (get-buffer "*rustfmt*"))
1494+
(setq ok t))
1495+
(when rust-format-goto-problem
1496+
(rust-goto-format-problem)
1497+
(setq ok t))
1498+
ok))
1499+
1500+
(defun rust-goto-format-problem ()
1501+
"Jumps to problem reported by rustfmt, if any.
1502+
1503+
In case of multiple problems cycles through them. Displays the
1504+
rustfmt complain in the echo area."
1505+
(interactive)
1506+
;; This uses position in *rustfmt* buffer to know which is the next
1507+
;; error to jump to, and source: line in the buffer to figure which
1508+
;; buffer it is from.
1509+
(let ((rustfmt (get-buffer "*rustfmt*")))
1510+
(if (not rustfmt)
1511+
(message "No *rustfmt*, no problems.")
1512+
(let ((target-buffer (with-current-buffer rustfmt
1513+
(save-excursion
1514+
(goto-char (point-min))
1515+
(when (re-search-forward "--> \\([^:]+\\):")
1516+
(match-string 1)))))
1517+
(target-point (with-current-buffer rustfmt
1518+
;; No save-excursion, this is how we cycle through!
1519+
(let ((regex "--> [^:]+:\\([0-9]+\\):\\([0-9]+\\)"))
1520+
(when (or (re-search-forward regex nil t)
1521+
(progn (goto-char (point-min))
1522+
(re-search-forward regex nil t)))
1523+
(cons (string-to-number (match-string 1))
1524+
(string-to-number (match-string 2)))))))
1525+
(target-problem (with-current-buffer rustfmt
1526+
(save-excursion
1527+
(when (re-search-backward "^error:.+\n" nil t)
1528+
(forward-char (length "error: "))
1529+
(let ((p0 (point)))
1530+
(if (re-search-forward "\nerror:.+\n" nil t)
1531+
(buffer-substring p0 (point))
1532+
(buffer-substring p0 (point-max)))))))))
1533+
(when (and target-buffer target-point)
1534+
(switch-to-buffer target-buffer)
1535+
(goto-char (point-min))
1536+
(forward-line (1- (car target-point)))
1537+
(forward-char (1- (cdr target-point))))
1538+
(message target-problem)))))
1539+
14651540
(defconst rust--format-word "\
14661541
\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\
14671542
match\\|struct\\|union\\|unsafe\\|while\\)\\b")
@@ -1606,28 +1681,31 @@ Return the created process."
16061681
(rust--format-get-loc buffer start)
16071682
(rust--format-get-loc buffer point))
16081683
window-loc))))))
1609-
(unwind-protect
1610-
;; save and restore window start position
1611-
;; after reformatting
1612-
;; to avoid the disturbing scrolling
1613-
(let ((w-start (window-start)))
1614-
(rust--format-call (current-buffer))
1615-
(set-window-start (selected-window) w-start))
1616-
(dolist (loc buffer-loc)
1617-
(let* ((buffer (pop loc))
1618-
(pos (rust--format-get-pos buffer (pop loc))))
1619-
(with-current-buffer buffer
1620-
(goto-char pos))))
1621-
(dolist (loc window-loc)
1622-
(let* ((window (pop loc))
1623-
(buffer (window-buffer window))
1624-
(start (rust--format-get-pos buffer (pop loc)))
1625-
(pos (rust--format-get-pos buffer (pop loc))))
1626-
(unless (eq buffer current)
1627-
(set-window-start window start))
1628-
(set-window-point window pos)))))
1629-
1630-
(message "Formatted buffer with rustfmt."))
1684+
(condition-case err
1685+
(unwind-protect
1686+
;; save and restore window start position
1687+
;; after reformatting
1688+
;; to avoid the disturbing scrolling
1689+
(let ((w-start (window-start)))
1690+
(rust--format-call (current-buffer))
1691+
(set-window-start (selected-window) w-start)
1692+
(message "Formatted buffer with rustfmt."))
1693+
(dolist (loc buffer-loc)
1694+
(let* ((buffer (pop loc))
1695+
(pos (rust--format-get-pos buffer (pop loc))))
1696+
(with-current-buffer buffer
1697+
(goto-char pos))))
1698+
(dolist (loc window-loc)
1699+
(let* ((window (pop loc))
1700+
(buffer (window-buffer window))
1701+
(start (rust--format-get-pos buffer (pop loc)))
1702+
(pos (rust--format-get-pos buffer (pop loc))))
1703+
(unless (eq buffer current)
1704+
(set-window-start window start))
1705+
(set-window-point window pos))))
1706+
(error
1707+
(or (rust--format-error-handler)
1708+
(signal (car err) (cdr err)))))))
16311709

16321710
(defun rust-enable-format-on-save ()
16331711
"Enable formatting using rustfmt when saving buffer."
@@ -1658,6 +1736,7 @@ Return the created process."
16581736
(let ((map (make-sparse-keymap)))
16591737
(define-key map (kbd "C-c C-f") 'rust-format-buffer)
16601738
(define-key map (kbd "C-c C-d") 'rust-dbg-wrap-or-unwrap)
1739+
(define-key map (kbd "C-c C-n") 'rust-goto-format-problem)
16611740
map)
16621741
"Keymap for Rust major mode.")
16631742

@@ -1736,8 +1815,13 @@ Return the created process."
17361815

17371816
(defun rust-after-save-hook ()
17381817
(when rust-format-on-save
1739-
(unless (executable-find rust-rustfmt-bin)
1740-
(error "Could not locate executable \"%s\"" rust-rustfmt-bin))))
1818+
(if (not (executable-find rust-rustfmt-bin))
1819+
(error "Could not locate executable \"%s\"" rust-rustfmt-bin)
1820+
(when (get-buffer "*rustfmt*")
1821+
;; KLDUGE: re-run the error handlers -- otherwise message area
1822+
;; would show "Wrote ..." instead of the error description.
1823+
(or (rust--format-error-handler)
1824+
(message "rustfmt detected problems, see *rustfmt* for more."))))))
17411825

17421826
(defvar rustc-compilation-regexps
17431827
(let ((file "\\([^\n]+\\)")

0 commit comments

Comments
 (0)