emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/rust-mode 139a658 482/486: Create rust-rustfmt.el from exi


From: ELPA Syncer
Subject: [nongnu] elpa/rust-mode 139a658 482/486: Create rust-rustfmt.el from existing code
Date: Sat, 7 Aug 2021 09:26:19 -0400 (EDT)

branch: elpa/rust-mode
commit 139a6580ed85d4ec1b29b23abeb474d33e78ea39
Author: Jonas Bernoulli <jonas@bernoul.li>
Commit: brotzeit <brotzeitmacher@gmail.com>

    Create rust-rustfmt.el from existing code
---
 Makefile        |   1 +
 rust-mode.el    | 356 ------------------------------------------------------
 rust-rustfmt.el | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 369 insertions(+), 356 deletions(-)

diff --git a/Makefile b/Makefile
index 396b722..7ef02af 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ EMACS_ARGS ?=
 
 ELS   = rust-mode.el
 ELS  += rust-compile.el
+ELS  += rust-rustfmt.el
 ELCS  = $(ELS:.el=.elc)
 
 DEPS  =
diff --git a/rust-mode.el b/rust-mode.el
index f35a947..37b5cd3 100644
--- a/rust-mode.el
+++ b/rust-mode.el
@@ -70,34 +70,6 @@ When nil, `where' will be aligned with `fn' or `trait'."
   :safe #'booleanp
   :group 'rust-mode)
 
-(defcustom rust-format-on-save nil
-  "Format future rust buffers before saving using rustfmt."
-  :type 'boolean
-  :safe #'booleanp
-  :group 'rust-mode)
-
-(defcustom rust-format-show-buffer t
-  "Show *rustfmt* buffer if formatting detected problems."
-  :type 'boolean
-  :safe #'booleanp
-  :group 'rust-mode)
-
-(defcustom rust-format-goto-problem t
-  "Jump to location of first detected problem when formatting buffer."
-  :type 'boolean
-  :safe #'booleanp
-  :group 'rust-mode)
-
-(defcustom rust-rustfmt-bin "rustfmt"
-  "Path to rustfmt executable."
-  :type 'string
-  :group 'rust-mode)
-
-(defcustom rust-rustfmt-switches '("--edition" "2018")
-  "Arguments to pass when invoking the `rustfmt' executable."
-  :type '(repeat string)
-  :group 'rust-mode)
-
 (defcustom rust-cargo-bin "cargo"
   "Path to cargo executable."
   :type 'string
@@ -1608,314 +1580,6 @@ This is written mainly to be used as 
`end-of-defun-function' for Rust."
     ;; There is no opening brace, so consider the whole buffer to be one 
"defun"
     (goto-char (point-max))))
 
-;;; Formatting using rustfmt
-
-(defconst rust-rustfmt-buffername "*rustfmt*")
-
-(defun rust--format-call (buf)
-  "Format BUF using rustfmt."
-  (with-current-buffer (get-buffer-create rust-rustfmt-buffername)
-    (view-mode +1)
-    (let ((inhibit-read-only t))
-      (erase-buffer)
-      (insert-buffer-substring buf)
-      (let* ((tmpf (make-temp-file "rustfmt"))
-             (ret (apply 'call-process-region
-                         (point-min)
-                         (point-max)
-                         rust-rustfmt-bin
-                         t
-                         `(t ,tmpf)
-                         nil
-                         rust-rustfmt-switches)))
-        (unwind-protect
-            (cond
-             ((zerop ret)
-              (if (not (string= (buffer-string)
-                                (with-current-buffer buf (buffer-string))))
-                  ;; replace-buffer-contents was in emacs 26.1, but it
-                  ;; was broken for non-ASCII strings, so we need 26.2.
-                  (if (and (fboundp 'replace-buffer-contents)
-                           (version<= "26.2" emacs-version))
-                      (with-current-buffer buf
-                        (replace-buffer-contents rust-rustfmt-buffername))
-                    (copy-to-buffer buf (point-min) (point-max))))
-              (kill-buffer))
-             ((= ret 3)
-              (if (not (string= (buffer-string)
-                                (with-current-buffer buf (buffer-string))))
-                  (copy-to-buffer buf (point-min) (point-max)))
-              (erase-buffer)
-              (insert-file-contents tmpf)
-              (rust--format-fix-rustfmt-buffer (buffer-name buf))
-              (error "Rustfmt could not format some lines, see *rustfmt* 
buffer for details"))
-             (t
-              (erase-buffer)
-              (insert-file-contents tmpf)
-              (rust--format-fix-rustfmt-buffer (buffer-name buf))
-              (error "Rustfmt failed, see *rustfmt* buffer for details"))))
-        (delete-file tmpf)))))
-
-;; Since we run rustfmt through stdin we get <stdin> markers in the
-;; output. This replaces them with the buffer name instead.
-(defun rust--format-fix-rustfmt-buffer (buffer-name)
-  (with-current-buffer (get-buffer rust-rustfmt-buffername)
-    (let ((inhibit-read-only t))
-      (goto-char (point-min))
-      (while (re-search-forward "--> <stdin>:" nil t)
-        (replace-match (format "--> %s:" buffer-name)))
-      (while (re-search-forward "--> stdin:" nil t)
-        (replace-match (format "--> %s:" buffer-name))))))
-
-;; If rust-mode has been configured to navigate to source of the error
-;; or display it, do so -- and return true. Otherwise return nil to
-;; indicate nothing was done.
-(defun rust--format-error-handler ()
-  (let ((ok nil))
-    (when rust-format-show-buffer
-      (display-buffer (get-buffer rust-rustfmt-buffername))
-      (setq ok t))
-    (when rust-format-goto-problem
-      (rust-goto-format-problem)
-      (setq ok t))
-    ok))
-
-(defun rust-goto-format-problem ()
-  "Jumps to problem reported by rustfmt, if any.
-
-In case of multiple problems cycles through them. Displays the
-rustfmt complain in the echo area."
-  (interactive)
-  ;; This uses position in *rustfmt* buffer to know which is the next
-  ;; error to jump to, and source: line in the buffer to figure which
-  ;; buffer it is from.
-  (let ((rustfmt (get-buffer rust-rustfmt-buffername)))
-    (if (not rustfmt)
-        (message "No *rustfmt*, no problems.")
-      (let ((target-buffer (with-current-buffer rustfmt
-                             (save-excursion
-                               (goto-char (point-min))
-                               (when (re-search-forward "--> \\([^:]+\\):" nil 
t)
-                                 (match-string 1)))))
-            (target-point (with-current-buffer rustfmt
-                            ;; No save-excursion, this is how we cycle through!
-                            (let ((regex "--> 
[^:]+:\\([0-9]+\\):\\([0-9]+\\)"))
-                              (when (or (re-search-forward regex nil t)
-                                        (progn (goto-char (point-min))
-                                               (re-search-forward regex nil 
t)))
-                                (cons (string-to-number (match-string 1))
-                                      (string-to-number (match-string 2)))))))
-            (target-problem (with-current-buffer rustfmt
-                              (save-excursion
-                                (when (re-search-backward "^error:.+\n" nil t)
-                                  (forward-char (length "error: "))
-                                  (let ((p0 (point)))
-                                    (if (re-search-forward "\nerror:.+\n" nil 
t)
-                                        (buffer-substring p0 (point))
-                                      (buffer-substring p0 (point-max)))))))))
-        (when (and target-buffer (get-buffer target-buffer) target-point)
-          (switch-to-buffer target-buffer)
-          (goto-char (point-min))
-          (forward-line (1- (car target-point)))
-          (forward-char (1- (cdr target-point))))
-        (message target-problem)))))
-
-(defconst rust--format-word "\
-\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\
-match\\|struct\\|union\\|unsafe\\|while\\)\\b")
-(defconst rust--format-line "\\([\n]\\)")
-
-;; Counts number of matches of regex beginning up to max-beginning,
-;; leaving the point at the beginning of the last match.
-(defun rust--format-count (regex max-beginning)
-  (let ((count 0)
-        save-point
-        beginning)
-    (while (and (< (point) max-beginning)
-                (re-search-forward regex max-beginning t))
-      (setq count (1+ count))
-      (setq beginning (match-beginning 1)))
-    ;; try one more in case max-beginning lies in the middle of a match
-    (setq save-point (point))
-    (when (re-search-forward regex nil t)
-      (let ((try-beginning (match-beginning 1)))
-        (if (> try-beginning max-beginning)
-            (goto-char save-point)
-          (setq count (1+ count))
-          (setq beginning try-beginning))))
-    (when beginning (goto-char beginning))
-    count))
-
-;; Gets list describing pos or (point).
-;; The list contains:
-;; 1. the number of matches of rust--format-word,
-;; 2. the number of matches of rust--format-line after that,
-;; 3. the number of columns after that.
-(defun rust--format-get-loc (buffer &optional pos)
-  (with-current-buffer buffer
-    (save-excursion
-      (let ((pos (or pos (point)))
-            words lines columns)
-        (goto-char (point-min))
-        (setq words (rust--format-count rust--format-word pos))
-        (setq lines (rust--format-count rust--format-line pos))
-        (if (> lines 0)
-            (if (= (point) pos)
-                (setq columns -1)
-              (forward-char 1)
-              (goto-char pos)
-              (setq columns (current-column)))
-          (let ((initial-column (current-column)))
-            (goto-char pos)
-            (setq columns (- (current-column) initial-column))))
-        (list words lines columns)))))
-
-;; Moves the point forward by count matches of regex up to max-pos,
-;; and returns new max-pos making sure final position does not include another 
match.
-(defun rust--format-forward (regex count max-pos)
-  (when (< (point) max-pos)
-    (let ((beginning (point)))
-      (while (> count 0)
-        (setq count (1- count))
-        (re-search-forward regex nil t)
-        (setq beginning (match-beginning 1)))
-      (when (re-search-forward regex nil t)
-        (setq max-pos (min max-pos (match-beginning 1))))
-      (goto-char beginning)))
-  max-pos)
-
-;; Gets the position from a location list obtained using rust--format-get-loc.
-(defun rust--format-get-pos (buffer loc)
-  (with-current-buffer buffer
-    (save-excursion
-      (goto-char (point-min))
-      (let ((max-pos (point-max))
-            (words (pop loc))
-            (lines (pop loc))
-            (columns (pop loc)))
-        (setq max-pos (rust--format-forward rust--format-word words max-pos))
-        (setq max-pos (rust--format-forward rust--format-line lines max-pos))
-        (when (> lines 0) (forward-char))
-        (let ((initial-column (current-column))
-              (save-point (point)))
-          (move-end-of-line nil)
-          (when (> (current-column) (+ initial-column columns))
-            (goto-char save-point)
-            (forward-char columns)))
-        (min (point) max-pos)))))
-
-(defun rust-format-diff-buffer ()
-  "Show diff to current buffer from rustfmt.
-
-Return the created process."
-  (interactive)
-  (unless (executable-find rust-rustfmt-bin)
-    (error "Could not locate executable \%s\"" rust-rustfmt-bin))
-  (let* ((buffer
-          (with-current-buffer
-              (get-buffer-create "*rustfmt-diff*")
-            (let ((inhibit-read-only t))
-              (erase-buffer))
-            (current-buffer)))
-         (proc
-          (apply 'start-process
-                 "rustfmt-diff"
-                 buffer
-                 rust-rustfmt-bin
-                 "--check"
-                 (cons (buffer-file-name)
-                       rust-rustfmt-switches))))
-    (set-process-sentinel proc 'rust-format-diff-buffer-sentinel)
-    proc))
-
-(defun rust-format-diff-buffer-sentinel (process _e)
-  (when (eq 'exit (process-status process))
-    (if (> (process-exit-status process) 0)
-        (with-current-buffer "*rustfmt-diff*"
-          (let ((inhibit-read-only t))
-            (diff-mode))
-          (pop-to-buffer (current-buffer)))
-      (message "rustfmt check passed."))))
-
-(defun rust--format-buffer-using-replace-buffer-contents ()
-  (condition-case err
-      (progn
-        (rust--format-call (current-buffer))
-        (message "Formatted buffer with rustfmt."))
-    (error
-     (or (rust--format-error-handler)
-         (signal (car err) (cdr err))))))
-
-(defun rust--format-buffer-saving-position-manually ()
-  (let* ((current (current-buffer))
-         (base (or (buffer-base-buffer current) current))
-         buffer-loc
-         window-loc)
-    (dolist (buffer (buffer-list))
-      (when (or (eq buffer base)
-                (eq (buffer-base-buffer buffer) base))
-        (push (list buffer
-                    (rust--format-get-loc buffer nil))
-              buffer-loc)))
-    (dolist (frame (frame-list))
-      (dolist (window (window-list frame))
-        (let ((buffer (window-buffer window)))
-          (when (or (eq buffer base)
-                    (eq (buffer-base-buffer buffer) base))
-            (let ((start (window-start window))
-                  (point (window-point window)))
-              (push (list window
-                          (rust--format-get-loc buffer start)
-                          (rust--format-get-loc buffer point))
-                    window-loc))))))
-    (condition-case err
-        (unwind-protect
-            ;; save and restore window start position
-            ;; after reformatting
-            ;; to avoid the disturbing scrolling
-            (let ((w-start (window-start)))
-              (rust--format-call (current-buffer))
-              (set-window-start (selected-window) w-start)
-              (message "Formatted buffer with rustfmt."))
-          (dolist (loc buffer-loc)
-            (let* ((buffer (pop loc))
-                   (pos (rust--format-get-pos buffer (pop loc))))
-              (with-current-buffer buffer
-                (goto-char pos))))
-          (dolist (loc window-loc)
-            (let* ((window (pop loc))
-                   (buffer (window-buffer window))
-                   (start (rust--format-get-pos buffer (pop loc)))
-                   (pos (rust--format-get-pos buffer (pop loc))))
-              (unless (eq buffer current)
-                (set-window-start window start))
-              (set-window-point window pos))))
-      (error
-       (or (rust--format-error-handler)
-           (signal (car err) (cdr err)))))))
-
-(defun rust-format-buffer ()
-  "Format the current buffer using rustfmt."
-  (interactive)
-  (unless (executable-find rust-rustfmt-bin)
-    (error "Could not locate executable \"%s\"" rust-rustfmt-bin))
-  ;; If emacs version >= 26.2, we can use replace-buffer-contents to
-  ;; preserve location and markers in buffer, otherwise we can try to
-  ;; save locations as best we can, though we still lose markers.
-  (if (version<= "26.2" emacs-version)
-      (rust--format-buffer-using-replace-buffer-contents)
-    (rust--format-buffer-saving-position-manually)))
-
-(defun rust-enable-format-on-save ()
-  "Enable formatting using rustfmt when saving buffer."
-  (interactive)
-  (setq-local rust-format-on-save t))
-
-(defun rust-disable-format-on-save ()
-  "Disable formatting using rustfmt when saving buffer."
-  (interactive)
-  (setq-local rust-format-on-save nil))
-
 (defun rust--compile (format-string &rest args)
   (when (null rust-buffer-project)
     (rust-update-buffer-project))
@@ -1955,26 +1619,6 @@ Return the created process."
   (interactive)
   (rust--compile "%s test" rust-cargo-bin))
 
-;;; Hooks
-
-(defun rust-before-save-hook ()
-  (when rust-format-on-save
-    (condition-case e
-        (rust-format-buffer)
-      (error (format "rust-before-save-hook: %S %S"
-                     (car e)
-                     (cdr e))))))
-
-(defun rust-after-save-hook ()
-  (when rust-format-on-save
-    (if (not (executable-find rust-rustfmt-bin))
-        (error "Could not locate executable \"%s\"" rust-rustfmt-bin)
-      (when (get-buffer rust-rustfmt-buffername)
-        ;; KLDUGE: re-run the error handlers -- otherwise message area
-        ;; would show "Wrote ..." instead of the error description.
-        (or (rust--format-error-handler)
-            (message "rustfmt detected problems, see *rustfmt* for more."))))))
-
 ;;; Secondary Commands
 
 (defun rust-playpen-region (begin end)
diff --git a/rust-rustfmt.el b/rust-rustfmt.el
new file mode 100644
index 0000000..d3b7509
--- /dev/null
+++ b/rust-rustfmt.el
@@ -0,0 +1,368 @@
+;;; rust-rustfmt.el --- Support for rustfmt         -*- lexical-binding:t -*-
+;;; Commentary:
+
+;; This library implements support for "rustfmt", a tool for
+;; formatting Rust code according to style guidelines.
+
+;;; Code:
+;;; Options
+
+(defcustom rust-format-on-save nil
+  "Format future rust buffers before saving using rustfmt."
+  :type 'boolean
+  :safe #'booleanp
+  :group 'rust-mode)
+
+(defcustom rust-format-show-buffer t
+  "Show *rustfmt* buffer if formatting detected problems."
+  :type 'boolean
+  :safe #'booleanp
+  :group 'rust-mode)
+
+(defcustom rust-format-goto-problem t
+  "Jump to location of first detected problem when formatting buffer."
+  :type 'boolean
+  :safe #'booleanp
+  :group 'rust-mode)
+
+(defcustom rust-rustfmt-bin "rustfmt"
+  "Path to rustfmt executable."
+  :type 'string
+  :group 'rust-mode)
+
+(defcustom rust-rustfmt-switches '("--edition" "2018")
+  "Arguments to pass when invoking the `rustfmt' executable."
+  :type '(repeat string)
+  :group 'rust-mode)
+
+;;; _
+
+(defconst rust-rustfmt-buffername "*rustfmt*")
+
+(defun rust--format-call (buf)
+  "Format BUF using rustfmt."
+  (with-current-buffer (get-buffer-create rust-rustfmt-buffername)
+    (view-mode +1)
+    (let ((inhibit-read-only t))
+      (erase-buffer)
+      (insert-buffer-substring buf)
+      (let* ((tmpf (make-temp-file "rustfmt"))
+             (ret (apply 'call-process-region
+                         (point-min)
+                         (point-max)
+                         rust-rustfmt-bin
+                         t
+                         `(t ,tmpf)
+                         nil
+                         rust-rustfmt-switches)))
+        (unwind-protect
+            (cond
+             ((zerop ret)
+              (if (not (string= (buffer-string)
+                                (with-current-buffer buf (buffer-string))))
+                  ;; replace-buffer-contents was in emacs 26.1, but it
+                  ;; was broken for non-ASCII strings, so we need 26.2.
+                  (if (and (fboundp 'replace-buffer-contents)
+                           (version<= "26.2" emacs-version))
+                      (with-current-buffer buf
+                        (replace-buffer-contents rust-rustfmt-buffername))
+                    (copy-to-buffer buf (point-min) (point-max))))
+              (kill-buffer))
+             ((= ret 3)
+              (if (not (string= (buffer-string)
+                                (with-current-buffer buf (buffer-string))))
+                  (copy-to-buffer buf (point-min) (point-max)))
+              (erase-buffer)
+              (insert-file-contents tmpf)
+              (rust--format-fix-rustfmt-buffer (buffer-name buf))
+              (error "Rustfmt could not format some lines, see *rustfmt* 
buffer for details"))
+             (t
+              (erase-buffer)
+              (insert-file-contents tmpf)
+              (rust--format-fix-rustfmt-buffer (buffer-name buf))
+              (error "Rustfmt failed, see *rustfmt* buffer for details"))))
+        (delete-file tmpf)))))
+
+;; Since we run rustfmt through stdin we get <stdin> markers in the
+;; output. This replaces them with the buffer name instead.
+(defun rust--format-fix-rustfmt-buffer (buffer-name)
+  (with-current-buffer (get-buffer rust-rustfmt-buffername)
+    (let ((inhibit-read-only t))
+      (goto-char (point-min))
+      (while (re-search-forward "--> <stdin>:" nil t)
+        (replace-match (format "--> %s:" buffer-name)))
+      (while (re-search-forward "--> stdin:" nil t)
+        (replace-match (format "--> %s:" buffer-name))))))
+
+;; If rust-mode has been configured to navigate to source of the error
+;; or display it, do so -- and return true. Otherwise return nil to
+;; indicate nothing was done.
+(defun rust--format-error-handler ()
+  (let ((ok nil))
+    (when rust-format-show-buffer
+      (display-buffer (get-buffer rust-rustfmt-buffername))
+      (setq ok t))
+    (when rust-format-goto-problem
+      (rust-goto-format-problem)
+      (setq ok t))
+    ok))
+
+(defun rust-goto-format-problem ()
+  "Jumps to problem reported by rustfmt, if any.
+
+In case of multiple problems cycles through them. Displays the
+rustfmt complain in the echo area."
+  (interactive)
+  ;; This uses position in *rustfmt* buffer to know which is the next
+  ;; error to jump to, and source: line in the buffer to figure which
+  ;; buffer it is from.
+  (let ((rustfmt (get-buffer rust-rustfmt-buffername)))
+    (if (not rustfmt)
+        (message "No *rustfmt*, no problems.")
+      (let ((target-buffer (with-current-buffer rustfmt
+                             (save-excursion
+                               (goto-char (point-min))
+                               (when (re-search-forward "--> \\([^:]+\\):" nil 
t)
+                                 (match-string 1)))))
+            (target-point (with-current-buffer rustfmt
+                            ;; No save-excursion, this is how we cycle through!
+                            (let ((regex "--> 
[^:]+:\\([0-9]+\\):\\([0-9]+\\)"))
+                              (when (or (re-search-forward regex nil t)
+                                        (progn (goto-char (point-min))
+                                               (re-search-forward regex nil 
t)))
+                                (cons (string-to-number (match-string 1))
+                                      (string-to-number (match-string 2)))))))
+            (target-problem (with-current-buffer rustfmt
+                              (save-excursion
+                                (when (re-search-backward "^error:.+\n" nil t)
+                                  (forward-char (length "error: "))
+                                  (let ((p0 (point)))
+                                    (if (re-search-forward "\nerror:.+\n" nil 
t)
+                                        (buffer-substring p0 (point))
+                                      (buffer-substring p0 (point-max)))))))))
+        (when (and target-buffer (get-buffer target-buffer) target-point)
+          (switch-to-buffer target-buffer)
+          (goto-char (point-min))
+          (forward-line (1- (car target-point)))
+          (forward-char (1- (cdr target-point))))
+        (message target-problem)))))
+
+(defconst rust--format-word "\
+\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\
+match\\|struct\\|union\\|unsafe\\|while\\)\\b")
+(defconst rust--format-line "\\([\n]\\)")
+
+;; Counts number of matches of regex beginning up to max-beginning,
+;; leaving the point at the beginning of the last match.
+(defun rust--format-count (regex max-beginning)
+  (let ((count 0)
+        save-point
+        beginning)
+    (while (and (< (point) max-beginning)
+                (re-search-forward regex max-beginning t))
+      (setq count (1+ count))
+      (setq beginning (match-beginning 1)))
+    ;; try one more in case max-beginning lies in the middle of a match
+    (setq save-point (point))
+    (when (re-search-forward regex nil t)
+      (let ((try-beginning (match-beginning 1)))
+        (if (> try-beginning max-beginning)
+            (goto-char save-point)
+          (setq count (1+ count))
+          (setq beginning try-beginning))))
+    (when beginning (goto-char beginning))
+    count))
+
+;; Gets list describing pos or (point).
+;; The list contains:
+;; 1. the number of matches of rust--format-word,
+;; 2. the number of matches of rust--format-line after that,
+;; 3. the number of columns after that.
+(defun rust--format-get-loc (buffer &optional pos)
+  (with-current-buffer buffer
+    (save-excursion
+      (let ((pos (or pos (point)))
+            words lines columns)
+        (goto-char (point-min))
+        (setq words (rust--format-count rust--format-word pos))
+        (setq lines (rust--format-count rust--format-line pos))
+        (if (> lines 0)
+            (if (= (point) pos)
+                (setq columns -1)
+              (forward-char 1)
+              (goto-char pos)
+              (setq columns (current-column)))
+          (let ((initial-column (current-column)))
+            (goto-char pos)
+            (setq columns (- (current-column) initial-column))))
+        (list words lines columns)))))
+
+;; Moves the point forward by count matches of regex up to max-pos,
+;; and returns new max-pos making sure final position does not include another 
match.
+(defun rust--format-forward (regex count max-pos)
+  (when (< (point) max-pos)
+    (let ((beginning (point)))
+      (while (> count 0)
+        (setq count (1- count))
+        (re-search-forward regex nil t)
+        (setq beginning (match-beginning 1)))
+      (when (re-search-forward regex nil t)
+        (setq max-pos (min max-pos (match-beginning 1))))
+      (goto-char beginning)))
+  max-pos)
+
+;; Gets the position from a location list obtained using rust--format-get-loc.
+(defun rust--format-get-pos (buffer loc)
+  (with-current-buffer buffer
+    (save-excursion
+      (goto-char (point-min))
+      (let ((max-pos (point-max))
+            (words (pop loc))
+            (lines (pop loc))
+            (columns (pop loc)))
+        (setq max-pos (rust--format-forward rust--format-word words max-pos))
+        (setq max-pos (rust--format-forward rust--format-line lines max-pos))
+        (when (> lines 0) (forward-char))
+        (let ((initial-column (current-column))
+              (save-point (point)))
+          (move-end-of-line nil)
+          (when (> (current-column) (+ initial-column columns))
+            (goto-char save-point)
+            (forward-char columns)))
+        (min (point) max-pos)))))
+
+(defun rust-format-diff-buffer ()
+  "Show diff to current buffer from rustfmt.
+
+Return the created process."
+  (interactive)
+  (unless (executable-find rust-rustfmt-bin)
+    (error "Could not locate executable \%s\"" rust-rustfmt-bin))
+  (let* ((buffer
+          (with-current-buffer
+              (get-buffer-create "*rustfmt-diff*")
+            (let ((inhibit-read-only t))
+              (erase-buffer))
+            (current-buffer)))
+         (proc
+          (apply 'start-process
+                 "rustfmt-diff"
+                 buffer
+                 rust-rustfmt-bin
+                 "--check"
+                 (cons (buffer-file-name)
+                       rust-rustfmt-switches))))
+    (set-process-sentinel proc 'rust-format-diff-buffer-sentinel)
+    proc))
+
+(defun rust-format-diff-buffer-sentinel (process _e)
+  (when (eq 'exit (process-status process))
+    (if (> (process-exit-status process) 0)
+        (with-current-buffer "*rustfmt-diff*"
+          (let ((inhibit-read-only t))
+            (diff-mode))
+          (pop-to-buffer (current-buffer)))
+      (message "rustfmt check passed."))))
+
+(defun rust--format-buffer-using-replace-buffer-contents ()
+  (condition-case err
+      (progn
+        (rust--format-call (current-buffer))
+        (message "Formatted buffer with rustfmt."))
+    (error
+     (or (rust--format-error-handler)
+         (signal (car err) (cdr err))))))
+
+(defun rust--format-buffer-saving-position-manually ()
+  (let* ((current (current-buffer))
+         (base (or (buffer-base-buffer current) current))
+         buffer-loc
+         window-loc)
+    (dolist (buffer (buffer-list))
+      (when (or (eq buffer base)
+                (eq (buffer-base-buffer buffer) base))
+        (push (list buffer
+                    (rust--format-get-loc buffer nil))
+              buffer-loc)))
+    (dolist (frame (frame-list))
+      (dolist (window (window-list frame))
+        (let ((buffer (window-buffer window)))
+          (when (or (eq buffer base)
+                    (eq (buffer-base-buffer buffer) base))
+            (let ((start (window-start window))
+                  (point (window-point window)))
+              (push (list window
+                          (rust--format-get-loc buffer start)
+                          (rust--format-get-loc buffer point))
+                    window-loc))))))
+    (condition-case err
+        (unwind-protect
+            ;; save and restore window start position
+            ;; after reformatting
+            ;; to avoid the disturbing scrolling
+            (let ((w-start (window-start)))
+              (rust--format-call (current-buffer))
+              (set-window-start (selected-window) w-start)
+              (message "Formatted buffer with rustfmt."))
+          (dolist (loc buffer-loc)
+            (let* ((buffer (pop loc))
+                   (pos (rust--format-get-pos buffer (pop loc))))
+              (with-current-buffer buffer
+                (goto-char pos))))
+          (dolist (loc window-loc)
+            (let* ((window (pop loc))
+                   (buffer (window-buffer window))
+                   (start (rust--format-get-pos buffer (pop loc)))
+                   (pos (rust--format-get-pos buffer (pop loc))))
+              (unless (eq buffer current)
+                (set-window-start window start))
+              (set-window-point window pos))))
+      (error
+       (or (rust--format-error-handler)
+           (signal (car err) (cdr err)))))))
+
+(defun rust-format-buffer ()
+  "Format the current buffer using rustfmt."
+  (interactive)
+  (unless (executable-find rust-rustfmt-bin)
+    (error "Could not locate executable \"%s\"" rust-rustfmt-bin))
+  ;; If emacs version >= 26.2, we can use replace-buffer-contents to
+  ;; preserve location and markers in buffer, otherwise we can try to
+  ;; save locations as best we can, though we still lose markers.
+  (if (version<= "26.2" emacs-version)
+      (rust--format-buffer-using-replace-buffer-contents)
+    (rust--format-buffer-saving-position-manually)))
+
+(defun rust-enable-format-on-save ()
+  "Enable formatting using rustfmt when saving buffer."
+  (interactive)
+  (setq-local rust-format-on-save t))
+
+(defun rust-disable-format-on-save ()
+  "Disable formatting using rustfmt when saving buffer."
+  (interactive)
+  (setq-local rust-format-on-save nil))
+
+;;; Hooks
+
+(defun rust-before-save-hook ()
+  (when rust-format-on-save
+    (condition-case e
+        (rust-format-buffer)
+      (error (format "rust-before-save-hook: %S %S"
+                     (car e)
+                     (cdr e))))))
+
+(defun rust-after-save-hook ()
+  (when rust-format-on-save
+    (if (not (executable-find rust-rustfmt-bin))
+        (error "Could not locate executable \"%s\"" rust-rustfmt-bin)
+      (when (get-buffer rust-rustfmt-buffername)
+        ;; KLDUGE: re-run the error handlers -- otherwise message area
+        ;; would show "Wrote ..." instead of the error description.
+        (or (rust--format-error-handler)
+            (message "rustfmt detected problems, see *rustfmt* for more."))))))
+
+;;; _
+(provide 'rust-rustfmt)
+;;; rust-rustfmt.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]