Skip to content

Commit 139a658

Browse files
tarsiusbrotzeit
authored andcommitted
Create rust-rustfmt.el from existing code
1 parent 41642f0 commit 139a658

File tree

3 files changed

+369
-356
lines changed

3 files changed

+369
-356
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ EMACS_ARGS ?=
1111

1212
ELS = rust-mode.el
1313
ELS += rust-compile.el
14+
ELS += rust-rustfmt.el
1415
ELCS = $(ELS:.el=.elc)
1516

1617
DEPS =

rust-mode.el

Lines changed: 0 additions & 356 deletions
Original file line numberDiff line numberDiff line change
@@ -70,34 +70,6 @@ When nil, `where' will be aligned with `fn' or `trait'."
7070
:safe #'booleanp
7171
:group 'rust-mode)
7272

73-
(defcustom rust-format-on-save nil
74-
"Format future rust buffers before saving using rustfmt."
75-
:type 'boolean
76-
:safe #'booleanp
77-
:group 'rust-mode)
78-
79-
(defcustom rust-format-show-buffer t
80-
"Show *rustfmt* buffer if formatting detected problems."
81-
:type 'boolean
82-
:safe #'booleanp
83-
:group 'rust-mode)
84-
85-
(defcustom rust-format-goto-problem t
86-
"Jump to location of first detected problem when formatting buffer."
87-
:type 'boolean
88-
:safe #'booleanp
89-
:group 'rust-mode)
90-
91-
(defcustom rust-rustfmt-bin "rustfmt"
92-
"Path to rustfmt executable."
93-
:type 'string
94-
:group 'rust-mode)
95-
96-
(defcustom rust-rustfmt-switches '("--edition" "2018")
97-
"Arguments to pass when invoking the `rustfmt' executable."
98-
:type '(repeat string)
99-
:group 'rust-mode)
100-
10173
(defcustom rust-cargo-bin "cargo"
10274
"Path to cargo executable."
10375
:type 'string
@@ -1608,314 +1580,6 @@ This is written mainly to be used as `end-of-defun-function' for Rust."
16081580
;; There is no opening brace, so consider the whole buffer to be one "defun"
16091581
(goto-char (point-max))))
16101582

1611-
;;; Formatting using rustfmt
1612-
1613-
(defconst rust-rustfmt-buffername "*rustfmt*")
1614-
1615-
(defun rust--format-call (buf)
1616-
"Format BUF using rustfmt."
1617-
(with-current-buffer (get-buffer-create rust-rustfmt-buffername)
1618-
(view-mode +1)
1619-
(let ((inhibit-read-only t))
1620-
(erase-buffer)
1621-
(insert-buffer-substring buf)
1622-
(let* ((tmpf (make-temp-file "rustfmt"))
1623-
(ret (apply 'call-process-region
1624-
(point-min)
1625-
(point-max)
1626-
rust-rustfmt-bin
1627-
t
1628-
`(t ,tmpf)
1629-
nil
1630-
rust-rustfmt-switches)))
1631-
(unwind-protect
1632-
(cond
1633-
((zerop ret)
1634-
(if (not (string= (buffer-string)
1635-
(with-current-buffer buf (buffer-string))))
1636-
;; replace-buffer-contents was in emacs 26.1, but it
1637-
;; was broken for non-ASCII strings, so we need 26.2.
1638-
(if (and (fboundp 'replace-buffer-contents)
1639-
(version<= "26.2" emacs-version))
1640-
(with-current-buffer buf
1641-
(replace-buffer-contents rust-rustfmt-buffername))
1642-
(copy-to-buffer buf (point-min) (point-max))))
1643-
(kill-buffer))
1644-
((= ret 3)
1645-
(if (not (string= (buffer-string)
1646-
(with-current-buffer buf (buffer-string))))
1647-
(copy-to-buffer buf (point-min) (point-max)))
1648-
(erase-buffer)
1649-
(insert-file-contents tmpf)
1650-
(rust--format-fix-rustfmt-buffer (buffer-name buf))
1651-
(error "Rustfmt could not format some lines, see *rustfmt* buffer for details"))
1652-
(t
1653-
(erase-buffer)
1654-
(insert-file-contents tmpf)
1655-
(rust--format-fix-rustfmt-buffer (buffer-name buf))
1656-
(error "Rustfmt failed, see *rustfmt* buffer for details"))))
1657-
(delete-file tmpf)))))
1658-
1659-
;; Since we run rustfmt through stdin we get <stdin> markers in the
1660-
;; output. This replaces them with the buffer name instead.
1661-
(defun rust--format-fix-rustfmt-buffer (buffer-name)
1662-
(with-current-buffer (get-buffer rust-rustfmt-buffername)
1663-
(let ((inhibit-read-only t))
1664-
(goto-char (point-min))
1665-
(while (re-search-forward "--> <stdin>:" nil t)
1666-
(replace-match (format "--> %s:" buffer-name)))
1667-
(while (re-search-forward "--> stdin:" nil t)
1668-
(replace-match (format "--> %s:" buffer-name))))))
1669-
1670-
;; If rust-mode has been configured to navigate to source of the error
1671-
;; or display it, do so -- and return true. Otherwise return nil to
1672-
;; indicate nothing was done.
1673-
(defun rust--format-error-handler ()
1674-
(let ((ok nil))
1675-
(when rust-format-show-buffer
1676-
(display-buffer (get-buffer rust-rustfmt-buffername))
1677-
(setq ok t))
1678-
(when rust-format-goto-problem
1679-
(rust-goto-format-problem)
1680-
(setq ok t))
1681-
ok))
1682-
1683-
(defun rust-goto-format-problem ()
1684-
"Jumps to problem reported by rustfmt, if any.
1685-
1686-
In case of multiple problems cycles through them. Displays the
1687-
rustfmt complain in the echo area."
1688-
(interactive)
1689-
;; This uses position in *rustfmt* buffer to know which is the next
1690-
;; error to jump to, and source: line in the buffer to figure which
1691-
;; buffer it is from.
1692-
(let ((rustfmt (get-buffer rust-rustfmt-buffername)))
1693-
(if (not rustfmt)
1694-
(message "No *rustfmt*, no problems.")
1695-
(let ((target-buffer (with-current-buffer rustfmt
1696-
(save-excursion
1697-
(goto-char (point-min))
1698-
(when (re-search-forward "--> \\([^:]+\\):" nil t)
1699-
(match-string 1)))))
1700-
(target-point (with-current-buffer rustfmt
1701-
;; No save-excursion, this is how we cycle through!
1702-
(let ((regex "--> [^:]+:\\([0-9]+\\):\\([0-9]+\\)"))
1703-
(when (or (re-search-forward regex nil t)
1704-
(progn (goto-char (point-min))
1705-
(re-search-forward regex nil t)))
1706-
(cons (string-to-number (match-string 1))
1707-
(string-to-number (match-string 2)))))))
1708-
(target-problem (with-current-buffer rustfmt
1709-
(save-excursion
1710-
(when (re-search-backward "^error:.+\n" nil t)
1711-
(forward-char (length "error: "))
1712-
(let ((p0 (point)))
1713-
(if (re-search-forward "\nerror:.+\n" nil t)
1714-
(buffer-substring p0 (point))
1715-
(buffer-substring p0 (point-max)))))))))
1716-
(when (and target-buffer (get-buffer target-buffer) target-point)
1717-
(switch-to-buffer target-buffer)
1718-
(goto-char (point-min))
1719-
(forward-line (1- (car target-point)))
1720-
(forward-char (1- (cdr target-point))))
1721-
(message target-problem)))))
1722-
1723-
(defconst rust--format-word "\
1724-
\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\
1725-
match\\|struct\\|union\\|unsafe\\|while\\)\\b")
1726-
(defconst rust--format-line "\\([\n]\\)")
1727-
1728-
;; Counts number of matches of regex beginning up to max-beginning,
1729-
;; leaving the point at the beginning of the last match.
1730-
(defun rust--format-count (regex max-beginning)
1731-
(let ((count 0)
1732-
save-point
1733-
beginning)
1734-
(while (and (< (point) max-beginning)
1735-
(re-search-forward regex max-beginning t))
1736-
(setq count (1+ count))
1737-
(setq beginning (match-beginning 1)))
1738-
;; try one more in case max-beginning lies in the middle of a match
1739-
(setq save-point (point))
1740-
(when (re-search-forward regex nil t)
1741-
(let ((try-beginning (match-beginning 1)))
1742-
(if (> try-beginning max-beginning)
1743-
(goto-char save-point)
1744-
(setq count (1+ count))
1745-
(setq beginning try-beginning))))
1746-
(when beginning (goto-char beginning))
1747-
count))
1748-
1749-
;; Gets list describing pos or (point).
1750-
;; The list contains:
1751-
;; 1. the number of matches of rust--format-word,
1752-
;; 2. the number of matches of rust--format-line after that,
1753-
;; 3. the number of columns after that.
1754-
(defun rust--format-get-loc (buffer &optional pos)
1755-
(with-current-buffer buffer
1756-
(save-excursion
1757-
(let ((pos (or pos (point)))
1758-
words lines columns)
1759-
(goto-char (point-min))
1760-
(setq words (rust--format-count rust--format-word pos))
1761-
(setq lines (rust--format-count rust--format-line pos))
1762-
(if (> lines 0)
1763-
(if (= (point) pos)
1764-
(setq columns -1)
1765-
(forward-char 1)
1766-
(goto-char pos)
1767-
(setq columns (current-column)))
1768-
(let ((initial-column (current-column)))
1769-
(goto-char pos)
1770-
(setq columns (- (current-column) initial-column))))
1771-
(list words lines columns)))))
1772-
1773-
;; Moves the point forward by count matches of regex up to max-pos,
1774-
;; and returns new max-pos making sure final position does not include another match.
1775-
(defun rust--format-forward (regex count max-pos)
1776-
(when (< (point) max-pos)
1777-
(let ((beginning (point)))
1778-
(while (> count 0)
1779-
(setq count (1- count))
1780-
(re-search-forward regex nil t)
1781-
(setq beginning (match-beginning 1)))
1782-
(when (re-search-forward regex nil t)
1783-
(setq max-pos (min max-pos (match-beginning 1))))
1784-
(goto-char beginning)))
1785-
max-pos)
1786-
1787-
;; Gets the position from a location list obtained using rust--format-get-loc.
1788-
(defun rust--format-get-pos (buffer loc)
1789-
(with-current-buffer buffer
1790-
(save-excursion
1791-
(goto-char (point-min))
1792-
(let ((max-pos (point-max))
1793-
(words (pop loc))
1794-
(lines (pop loc))
1795-
(columns (pop loc)))
1796-
(setq max-pos (rust--format-forward rust--format-word words max-pos))
1797-
(setq max-pos (rust--format-forward rust--format-line lines max-pos))
1798-
(when (> lines 0) (forward-char))
1799-
(let ((initial-column (current-column))
1800-
(save-point (point)))
1801-
(move-end-of-line nil)
1802-
(when (> (current-column) (+ initial-column columns))
1803-
(goto-char save-point)
1804-
(forward-char columns)))
1805-
(min (point) max-pos)))))
1806-
1807-
(defun rust-format-diff-buffer ()
1808-
"Show diff to current buffer from rustfmt.
1809-
1810-
Return the created process."
1811-
(interactive)
1812-
(unless (executable-find rust-rustfmt-bin)
1813-
(error "Could not locate executable \%s\"" rust-rustfmt-bin))
1814-
(let* ((buffer
1815-
(with-current-buffer
1816-
(get-buffer-create "*rustfmt-diff*")
1817-
(let ((inhibit-read-only t))
1818-
(erase-buffer))
1819-
(current-buffer)))
1820-
(proc
1821-
(apply 'start-process
1822-
"rustfmt-diff"
1823-
buffer
1824-
rust-rustfmt-bin
1825-
"--check"
1826-
(cons (buffer-file-name)
1827-
rust-rustfmt-switches))))
1828-
(set-process-sentinel proc 'rust-format-diff-buffer-sentinel)
1829-
proc))
1830-
1831-
(defun rust-format-diff-buffer-sentinel (process _e)
1832-
(when (eq 'exit (process-status process))
1833-
(if (> (process-exit-status process) 0)
1834-
(with-current-buffer "*rustfmt-diff*"
1835-
(let ((inhibit-read-only t))
1836-
(diff-mode))
1837-
(pop-to-buffer (current-buffer)))
1838-
(message "rustfmt check passed."))))
1839-
1840-
(defun rust--format-buffer-using-replace-buffer-contents ()
1841-
(condition-case err
1842-
(progn
1843-
(rust--format-call (current-buffer))
1844-
(message "Formatted buffer with rustfmt."))
1845-
(error
1846-
(or (rust--format-error-handler)
1847-
(signal (car err) (cdr err))))))
1848-
1849-
(defun rust--format-buffer-saving-position-manually ()
1850-
(let* ((current (current-buffer))
1851-
(base (or (buffer-base-buffer current) current))
1852-
buffer-loc
1853-
window-loc)
1854-
(dolist (buffer (buffer-list))
1855-
(when (or (eq buffer base)
1856-
(eq (buffer-base-buffer buffer) base))
1857-
(push (list buffer
1858-
(rust--format-get-loc buffer nil))
1859-
buffer-loc)))
1860-
(dolist (frame (frame-list))
1861-
(dolist (window (window-list frame))
1862-
(let ((buffer (window-buffer window)))
1863-
(when (or (eq buffer base)
1864-
(eq (buffer-base-buffer buffer) base))
1865-
(let ((start (window-start window))
1866-
(point (window-point window)))
1867-
(push (list window
1868-
(rust--format-get-loc buffer start)
1869-
(rust--format-get-loc buffer point))
1870-
window-loc))))))
1871-
(condition-case err
1872-
(unwind-protect
1873-
;; save and restore window start position
1874-
;; after reformatting
1875-
;; to avoid the disturbing scrolling
1876-
(let ((w-start (window-start)))
1877-
(rust--format-call (current-buffer))
1878-
(set-window-start (selected-window) w-start)
1879-
(message "Formatted buffer with rustfmt."))
1880-
(dolist (loc buffer-loc)
1881-
(let* ((buffer (pop loc))
1882-
(pos (rust--format-get-pos buffer (pop loc))))
1883-
(with-current-buffer buffer
1884-
(goto-char pos))))
1885-
(dolist (loc window-loc)
1886-
(let* ((window (pop loc))
1887-
(buffer (window-buffer window))
1888-
(start (rust--format-get-pos buffer (pop loc)))
1889-
(pos (rust--format-get-pos buffer (pop loc))))
1890-
(unless (eq buffer current)
1891-
(set-window-start window start))
1892-
(set-window-point window pos))))
1893-
(error
1894-
(or (rust--format-error-handler)
1895-
(signal (car err) (cdr err)))))))
1896-
1897-
(defun rust-format-buffer ()
1898-
"Format the current buffer using rustfmt."
1899-
(interactive)
1900-
(unless (executable-find rust-rustfmt-bin)
1901-
(error "Could not locate executable \"%s\"" rust-rustfmt-bin))
1902-
;; If emacs version >= 26.2, we can use replace-buffer-contents to
1903-
;; preserve location and markers in buffer, otherwise we can try to
1904-
;; save locations as best we can, though we still lose markers.
1905-
(if (version<= "26.2" emacs-version)
1906-
(rust--format-buffer-using-replace-buffer-contents)
1907-
(rust--format-buffer-saving-position-manually)))
1908-
1909-
(defun rust-enable-format-on-save ()
1910-
"Enable formatting using rustfmt when saving buffer."
1911-
(interactive)
1912-
(setq-local rust-format-on-save t))
1913-
1914-
(defun rust-disable-format-on-save ()
1915-
"Disable formatting using rustfmt when saving buffer."
1916-
(interactive)
1917-
(setq-local rust-format-on-save nil))
1918-
19191583
(defun rust--compile (format-string &rest args)
19201584
(when (null rust-buffer-project)
19211585
(rust-update-buffer-project))
@@ -1955,26 +1619,6 @@ Return the created process."
19551619
(interactive)
19561620
(rust--compile "%s test" rust-cargo-bin))
19571621

1958-
;;; Hooks
1959-
1960-
(defun rust-before-save-hook ()
1961-
(when rust-format-on-save
1962-
(condition-case e
1963-
(rust-format-buffer)
1964-
(error (format "rust-before-save-hook: %S %S"
1965-
(car e)
1966-
(cdr e))))))
1967-
1968-
(defun rust-after-save-hook ()
1969-
(when rust-format-on-save
1970-
(if (not (executable-find rust-rustfmt-bin))
1971-
(error "Could not locate executable \"%s\"" rust-rustfmt-bin)
1972-
(when (get-buffer rust-rustfmt-buffername)
1973-
;; KLDUGE: re-run the error handlers -- otherwise message area
1974-
;; would show "Wrote ..." instead of the error description.
1975-
(or (rust--format-error-handler)
1976-
(message "rustfmt detected problems, see *rustfmt* for more."))))))
1977-
19781622
;;; Secondary Commands
19791623

19801624
(defun rust-playpen-region (begin end)

0 commit comments

Comments
 (0)