diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2017-01-07 07:50:49 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-01-07 07:50:49 -0500 |
| commit | 8f1297a5f6da4a587abf604756d7a99b046616d5 (patch) | |
| tree | 2dbb47de97096d17b63a4134c2112870fc436072 | |
| parent | cd09a54f94be0c532c1f2b0b8fff34ee8cd841a7 (diff) | |
| parent | 664c7b037c2fe2e2b2d1c0629835715194b844bb (diff) | |
| download | rust-mode-8f1297a5f6da4a587abf604756d7a99b046616d5.tar.gz | |
Merge pull request #187 from tspiteri/format-other-buffers
handle indirect buffers and multiple windows in rust-format-buffer
| -rw-r--r-- | rust-mode.el | 124 |
1 files changed, 114 insertions, 10 deletions
diff --git a/rust-mode.el b/rust-mode.el index 1877234..b8f26d3 100644 --- a/rust-mode.el +++ b/rust-mode.el @@ -1283,23 +1283,127 @@ This is written mainly to be used as `end-of-defun-function' for Rust." (kill-buffer)) (error "Rustfmt failed, see *rustfmt* buffer for details")))) +(defconst rust--format-word "\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|match\\|struct\\|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-buffer () "Format the current buffer using rustfmt." (interactive) (unless (executable-find rust-rustfmt-bin) (error "Could not locate executable \"%s\"" rust-rustfmt-bin)) - (let ((cur-line (line-number-at-pos)) - (cur-column (current-column)) - (cur-win-start (window-start))) + (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 (window (window-list)) + (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))))) (rust--format-call (current-buffer)) - ;; Move to the same line and column as before. This is best - ;; effort: if rustfmt inserted lines before point, we end up in - ;; the wrong place. See issue #162. - (goto-char (point-min)) - (forward-line (1- cur-line)) - (forward-char cur-column) - (set-window-start (selected-window) cur-win-start)) + (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)))) + (set-window-start window start) + (set-window-point window pos)))) ;; Issue #127: Running this on a buffer acts like a revert, and could cause ;; the fontification to get out of sync. Call the same hook to ensure it is |
