@@ -198,6 +198,18 @@ to the function arguments. When nil, `->' will be indented one level."
198
198
:safe #'booleanp
199
199
:group 'rust-mode )
200
200
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
+
201
213
(defcustom rust-rustfmt-bin " rustfmt"
202
214
" Path to rustfmt executable."
203
215
:type 'string
@@ -1455,13 +1467,76 @@ This is written mainly to be used as `end-of-defun-function' for Rust."
1455
1467
(copy-to-buffer buf (point-min ) (point-max )))
1456
1468
(erase-buffer )
1457
1469
(insert-file-contents tmpf)
1470
+ (rust--format-fix-rustfmt-buffer (buffer-name buf))
1458
1471
(error " Rustfmt could not format some lines, see *rustfmt* buffer for details " ))
1459
1472
(t
1460
1473
(erase-buffer )
1461
1474
(insert-file-contents tmpf)
1475
+ (rust--format-fix-rustfmt-buffer (buffer-name buf))
1462
1476
(error " Rustfmt failed, see *rustfmt* buffer for details " ))))
1463
1477
(delete-file tmpf))))
1464
1478
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 " \n error:.+\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
+
1465
1540
(defconst rust--format-word " \
1466
1541
\\ b\\ (else\\ |enum\\ |fn\\ |for\\ |if\\ |let\\ |loop\\ |\
1467
1542
match\\ |struct\\ |union\\ |unsafe\\ |while\\ )\\ b" )
@@ -1606,28 +1681,31 @@ Return the created process."
1606
1681
(rust--format-get-loc buffer start)
1607
1682
(rust--format-get-loc buffer point))
1608
1683
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)))))))
1631
1709
1632
1710
(defun rust-enable-format-on-save ()
1633
1711
" Enable formatting using rustfmt when saving buffer."
@@ -1658,6 +1736,7 @@ Return the created process."
1658
1736
(let ((map (make-sparse-keymap )))
1659
1737
(define-key map (kbd " C-c C-f" ) 'rust-format-buffer )
1660
1738
(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 )
1661
1740
map)
1662
1741
" Keymap for Rust major mode." )
1663
1742
@@ -1736,8 +1815,13 @@ Return the created process."
1736
1815
1737
1816
(defun rust-after-save-hook ()
1738
1817
(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. " ))))))
1741
1825
1742
1826
(defvar rustc-compilation-regexps
1743
1827
(let ((file " \\ ([^\n ]+\\ )" )
0 commit comments