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

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

[nongnu] elpa/rust-mode ec81a95 114/486: Merge pull request #22 from nik


From: ELPA Syncer
Subject: [nongnu] elpa/rust-mode ec81a95 114/486: Merge pull request #22 from nikomatsakis/align-method-chain
Date: Sat, 7 Aug 2021 09:25:00 -0400 (EDT)

branch: elpa/rust-mode
commit ec81a95287a69bb36d1535a972e5afb46948247d
Merge: 351cc91 55e7483
Author: Felix S Klock II <pnkfelix@pnkfx.org>
Commit: Felix S Klock II <pnkfelix@pnkfx.org>

    Merge pull request #22 from nikomatsakis/align-method-chain
    
    Fix aligning of method chains (more-or-less), add various unit tests, and 
add matching angle brackets.
---
 rust-mode-tests.el |  86 ++++++++++++++++++++++++++++++
 rust-mode.el       | 151 ++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 213 insertions(+), 24 deletions(-)

diff --git a/rust-mode-tests.el b/rust-mode-tests.el
index 3ef4cb6..a0e27b0 100644
--- a/rust-mode-tests.el
+++ b/rust-mode-tests.el
@@ -922,3 +922,89 @@ list of substrings of `STR' each followed by its face."
      "main" font-lock-function-name-face
      "let" font-lock-keyword-face
      "'\\''" font-lock-string-face)))
+
+(ert-deftest indent-method-chains-no-align ()
+  (let ((rust-indent-method-chain nil)) (test-indent
+   "
+fn main() {
+    let x = thing.do_it()
+        .aligned()
+        .more_alignment();
+}
+"
+   )))
+
+(ert-deftest indent-method-chains-with-align ()
+  (let ((rust-indent-method-chain t)) (test-indent
+   "
+fn main() {
+    let x = thing.do_it()
+                 .aligned()
+                 .more_alignment();
+}
+"
+   )))
+
+(ert-deftest indent-method-chains-with-align-and-second-stmt ()
+  (let ((rust-indent-method-chain t)) (test-indent
+   "
+fn main() {
+    let x = thing.do_it()
+                 .aligned()
+                 .more_alignment();
+    foo.bar();
+}
+"
+   )))
+
+(ert-deftest indent-method-chains-field ()
+  (let ((rust-indent-method-chain t)) (test-indent
+   "
+fn main() {
+    let x = thing.do_it
+                 .aligned
+                 .more_alignment();
+}
+"
+   )))
+
+(ert-deftest indent-method-chains-double-field-on-first-line ()
+  (let ((rust-indent-method-chain t)) (test-indent
+   "
+fn main() {
+    let x = thing.a.do_it
+                   .aligned
+                   .more_alignment();
+}
+"
+   )))
+
+(ert-deftest indent-method-chains-no-let ()
+  (let ((rust-indent-method-chain t)) (test-indent
+   "
+fn main() {
+    thing.a.do_it
+           .aligned
+           .more_alignment();
+}
+"
+   )))
+
+(ert-deftest indent-method-chains-comment ()
+  (let ((rust-indent-method-chain t)) (test-indent
+   "
+fn main() {
+    // thing.do_it()
+    // .aligned()
+}
+"
+   )))
+
+(ert-deftest indent-method-chains-close-block ()
+  (let ((rust-indent-method-chain t)) (test-indent
+   "
+fn main() {
+    foo.bar()
+}
+"
+   )))
diff --git a/rust-mode.el b/rust-mode.el
index 70b49fe..b2e0d5d 100644
--- a/rust-mode.el
+++ b/rust-mode.el
@@ -89,15 +89,6 @@
     (backward-word 1))
       (current-column))))
 
-(defun rust-align-to-method-chain ()
-  (save-excursion
-    (previous-line)
-    (end-of-line)
-    (backward-word 1)
-    (backward-char)
-    (when (looking-at "\\..+\(.*\)\n")
-      (- (current-column) rust-indent-offset))))
-
 (defun rust-rewind-to-beginning-of-current-level-expr ()
   (let ((current-level (rust-paren-level)))
     (back-to-indentation)
@@ -105,6 +96,54 @@
       (backward-up-list)
       (back-to-indentation))))
 
+(defun rust-align-to-method-chain ()
+  (save-excursion
+    ;; for method-chain alignment to apply, we must be looking at
+    ;; another method call or field access or something like
+    ;; that. This avoids rather "eager" jumps in situations like:
+    ;;
+    ;; {
+    ;;     something.foo()
+    ;; <indent>
+    ;;
+    ;; Without this check, we would wind up with the cursor under the
+    ;; `.`. In an older version, I had the inverse of the current
+    ;; check, where we checked for situations that should NOT indent,
+    ;; vs checking for the one situation where we SHOULD. It should be
+    ;; clear that this is more robust, but also I find it mildly less
+    ;; annoying to have to press tab again to align to a method chain
+    ;; than to have an over-eager indent in all other cases which must
+    ;; be undone via tab.
+    
+    (when (looking-at (concat "\s*\." rust-re-ident))
+      (previous-line)
+      (end-of-line)
+
+      (let
+          ;; skip-dot-identifier is used to position the point at the
+          ;; `.` when looking at something like
+          ;;
+          ;;      foo.bar
+          ;;         ^   ^
+          ;;         |   |
+          ;;         |  position of point
+          ;;       returned offset      
+          ;;
+          ((skip-dot-identifier
+            (lambda ()
+              (when (looking-back (concat "\." rust-re-ident))
+                (backward-word 1)
+                (backward-char)
+                (- (current-column) rust-indent-offset)))))
+        (cond
+         ;; foo.bar(...)
+         ((looking-back ")")
+          (backward-list 1)
+          (funcall skip-dot-identifier))
+
+         ;; foo.bar
+         (t (funcall skip-dot-identifier)))))))
+
 (defun rust-mode-indent-line ()
   (interactive)
   (let ((indent
@@ -123,10 +162,10 @@
                      (or
                       (when rust-indent-method-chain
                         (rust-align-to-method-chain))
-                     (save-excursion
-                       (backward-up-list)
-                       (rust-rewind-to-beginning-of-current-level-expr)
-                       (+ (current-column) rust-indent-offset))))))
+                      (save-excursion
+                        (backward-up-list)
+                        (rust-rewind-to-beginning-of-current-level-expr)
+                        (+ (current-column) rust-indent-offset))))))
              (cond
               ;; A function return type is indented to the corresponding 
function arguments
               ((looking-at "->")
@@ -137,16 +176,6 @@
 
               ;; A closing brace is 1 level unindended
               ((looking-at "}") (- baseline rust-indent-offset))
-
-              ;;Line up method chains by their .'s
-              ((when (and rust-indent-method-chain
-                          (looking-at "\..+\(.*\);?\n"))
-                 (or
-                  (let ((method-indent (rust-align-to-method-chain)))
-                    (when method-indent
-                      (+ method-indent rust-indent-offset)))
-                  (+ baseline rust-indent-offset))))
-
               
               ;; Doc comments in /** style with leading * indent to line up 
the *s
               ((and (nth 4 (syntax-ppss)) (looking-at "*"))
@@ -452,11 +481,84 @@ 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))))
 
+;; Angle-bracket matching. This is kind of a hack designed to deal
+;; with the fact that we can't add angle-brackets to the list of
+;; matching characters unconditionally. Basically we just have some
+;; special-case code such that whenever `>` is typed, we look
+;; backwards to find a matching `<` and highlight it, whether or not
+;; this is *actually* appropriate. This could be annoying so it is
+;; configurable (but on by default because it's awesome).
+
+(defcustom rust-blink-matching-angle-brackets t
+  "Blink matching `<` (if any) when `>` is typed"
+  :type 'boolean
+  :group 'rust-mode)
+
+(defvar rust-point-before-matching-angle-bracket 0)
+
+(defvar rust-matching-angle-bracker-timer nil)
+
+(defun rust-find-matching-angle-bracket ()
+  (save-excursion
+    (let ((angle-brackets 1)
+          (start-point (point))
+          (invalid nil))
+      (while (and
+              ;; didn't find a match
+              (> angle-brackets 0)
+              ;; we have no guarantee of a match, so give up eventually
+              (< (- start-point (point)) blink-matching-paren-distance)
+              ;; didn't hit the top of the buffer
+              (> (point) (point-min))
+              ;; didn't hit something else weird like a `;`
+              (not invalid))
+        (backward-char 1)
+        (cond
+         ((looking-at ">")
+           (setq angle-brackets (+ angle-brackets 1)))
+          ((looking-at "<")
+           (setq angle-brackets (- angle-brackets 1)))
+          ((looking-at "[;{]")
+           (setq invalid t))))
+      (cond
+       ((= angle-brackets 0) (point))
+       (t nil)))))
+
+(defun rust-restore-point-after-angle-bracket ()
+  (goto-char rust-point-before-matching-angle-bracket)
+  (when rust-matching-angle-bracker-timer
+    (cancel-timer rust-matching-angle-bracker-timer))
+  (setq rust-matching-angle-bracker-timer nil)
+  (remove-hook 'pre-command-hook 'rust-restore-point-after-angle-bracket))
+
+(defun rust-match-angle-bracket-hook ()
+  "If the most recently inserted character is a `>`, briefly moves point to 
matching `<` (if any)."
+  (interactive)
+  (when (and rust-blink-matching-angle-brackets
+             (looking-back ">"))
+    (let ((matching-angle-bracket-point (save-excursion
+                                          (backward-char 1)
+                                          (rust-find-matching-angle-bracket))))
+      (when matching-angle-bracket-point
+        (progn
+          (setq rust-point-before-matching-angle-bracket (point))
+          (goto-char matching-angle-bracket-point)
+          (add-hook 'pre-command-hook 'rust-restore-point-after-angle-bracket)
+          (setq rust-matching-angle-bracker-timer
+                (run-at-time blink-matching-delay nil 
'rust-restore-point-after-angle-bracket)))))))
+
+(defun rust-match-angle-bracket ()
+  "The point should be placed on a `>`. Finds the matching `<` and moves point 
there."
+  (interactive)
+  (let ((matching-angle-bracket-point (rust-find-matching-angle-bracket)))
+    (if matching-angle-bracket-point
+        (goto-char matching-angle-bracket-point)
+      (message "no matching angle bracket found"))))
+
 ;; For compatibility with Emacs < 24, derive conditionally
 (defalias 'rust-parent-mode
   (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
 
-
 ;;;###autoload
 (define-derived-mode rust-mode rust-parent-mode "Rust"
   "Major mode for Rust code."
@@ -490,6 +592,7 @@ This is written mainly to be used as 
`end-of-defun-function' for Rust."
   (setq-local end-of-defun-function 'rust-end-of-defun)
   (setq-local parse-sexp-lookup-properties t)
   (add-hook 'syntax-propertize-extend-region-functions 
'rust-syntax-propertize-extend-region)
+  (add-hook 'post-self-insert-hook 'rust-match-angle-bracket-hook)
   (setq-local syntax-propertize-function 'rust-syntax-propertize))
 
 (defun rust-syntax-propertize-extend-region (start end)



reply via email to

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