branch: elpa/rust-mode
commit f0d4c25b9f13702d6c4b6ca7fe7fca9df4e7f4e3
Author: Niko Matsakis <[email protected]>
Commit: Niko Matsakis <[email protected]>

    Fix aligning of method chains (more-or-less) and add various unit tests.
    The regular expressions are probably kind of wrong for things like
    
        self.foo.something::<X>()
                .more();
---
 rust-mode-tests.el | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 rust-mode.el       | 75 ++++++++++++++++++++++++++++++++---------------
 2 files changed, 138 insertions(+), 23 deletions(-)

diff --git a/rust-mode-tests.el b/rust-mode-tests.el
index 2b18728..2e6f77f 100644
--- a/rust-mode-tests.el
+++ b/rust-mode-tests.el
@@ -919,3 +919,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..8da04bd 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 "*"))

Reply via email to