Skip to content

Commit f0d4c25

Browse files
committed
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();
1 parent 66438d4 commit f0d4c25

File tree

2 files changed

+138
-23
lines changed

2 files changed

+138
-23
lines changed

rust-mode-tests.el

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,3 +919,89 @@ list of substrings of `STR' each followed by its face."
919919
"main" font-lock-function-name-face
920920
"let" font-lock-keyword-face
921921
"'\\''" font-lock-string-face)))
922+
923+
(ert-deftest indent-method-chains-no-align ()
924+
(let ((rust-indent-method-chain nil)) (test-indent
925+
"
926+
fn main() {
927+
let x = thing.do_it()
928+
.aligned()
929+
.more_alignment();
930+
}
931+
"
932+
)))
933+
934+
(ert-deftest indent-method-chains-with-align ()
935+
(let ((rust-indent-method-chain t)) (test-indent
936+
"
937+
fn main() {
938+
let x = thing.do_it()
939+
.aligned()
940+
.more_alignment();
941+
}
942+
"
943+
)))
944+
945+
(ert-deftest indent-method-chains-with-align-and-second-stmt ()
946+
(let ((rust-indent-method-chain t)) (test-indent
947+
"
948+
fn main() {
949+
let x = thing.do_it()
950+
.aligned()
951+
.more_alignment();
952+
foo.bar();
953+
}
954+
"
955+
)))
956+
957+
(ert-deftest indent-method-chains-field ()
958+
(let ((rust-indent-method-chain t)) (test-indent
959+
"
960+
fn main() {
961+
let x = thing.do_it
962+
.aligned
963+
.more_alignment();
964+
}
965+
"
966+
)))
967+
968+
(ert-deftest indent-method-chains-double-field-on-first-line ()
969+
(let ((rust-indent-method-chain t)) (test-indent
970+
"
971+
fn main() {
972+
let x = thing.a.do_it
973+
.aligned
974+
.more_alignment();
975+
}
976+
"
977+
)))
978+
979+
(ert-deftest indent-method-chains-no-let ()
980+
(let ((rust-indent-method-chain t)) (test-indent
981+
"
982+
fn main() {
983+
thing.a.do_it
984+
.aligned
985+
.more_alignment();
986+
}
987+
"
988+
)))
989+
990+
(ert-deftest indent-method-chains-comment ()
991+
(let ((rust-indent-method-chain t)) (test-indent
992+
"
993+
fn main() {
994+
// thing.do_it()
995+
// .aligned()
996+
}
997+
"
998+
)))
999+
1000+
(ert-deftest indent-method-chains-close-block ()
1001+
(let ((rust-indent-method-chain t)) (test-indent
1002+
"
1003+
fn main() {
1004+
foo.bar()
1005+
}
1006+
"
1007+
)))

rust-mode.el

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,61 @@
8989
(backward-word 1))
9090
(current-column))))
9191

92-
(defun rust-align-to-method-chain ()
93-
(save-excursion
94-
(previous-line)
95-
(end-of-line)
96-
(backward-word 1)
97-
(backward-char)
98-
(when (looking-at "\\..+\(.*\)\n")
99-
(- (current-column) rust-indent-offset))))
100-
10192
(defun rust-rewind-to-beginning-of-current-level-expr ()
10293
(let ((current-level (rust-paren-level)))
10394
(back-to-indentation)
10495
(while (> (rust-paren-level) current-level)
10596
(backward-up-list)
10697
(back-to-indentation))))
10798

99+
(defun rust-align-to-method-chain ()
100+
(save-excursion
101+
;; for method-chain alignment to apply, we must be looking at
102+
;; another method call or field access or something like
103+
;; that. This avoids rather "eager" jumps in situations like:
104+
;;
105+
;; {
106+
;; something.foo()
107+
;; <indent>
108+
;;
109+
;; Without this check, we would wind up with the cursor under the
110+
;; `.`. In an older version, I had the inverse of the current
111+
;; check, where we checked for situations that should NOT indent,
112+
;; vs checking for the one situation where we SHOULD. It should be
113+
;; clear that this is more robust, but also I find it mildly less
114+
;; annoying to have to press tab again to align to a method chain
115+
;; than to have an over-eager indent in all other cases which must
116+
;; be undone via tab.
117+
118+
(when (looking-at (concat "\s*\." rust-re-ident))
119+
(previous-line)
120+
(end-of-line)
121+
122+
(let
123+
;; skip-dot-identifier is used to position the point at the
124+
;; `.` when looking at something like
125+
;;
126+
;; foo.bar
127+
;; ^ ^
128+
;; | |
129+
;; | position of point
130+
;; returned offset
131+
;;
132+
((skip-dot-identifier
133+
(lambda ()
134+
(when (looking-back (concat "\." rust-re-ident))
135+
(backward-word 1)
136+
(backward-char)
137+
(- (current-column) rust-indent-offset)))))
138+
(cond
139+
;; foo.bar(...)
140+
((looking-back ")")
141+
(backward-list 1)
142+
(funcall skip-dot-identifier))
143+
144+
;; foo.bar
145+
(t (funcall skip-dot-identifier)))))))
146+
108147
(defun rust-mode-indent-line ()
109148
(interactive)
110149
(let ((indent
@@ -123,10 +162,10 @@
123162
(or
124163
(when rust-indent-method-chain
125164
(rust-align-to-method-chain))
126-
(save-excursion
127-
(backward-up-list)
128-
(rust-rewind-to-beginning-of-current-level-expr)
129-
(+ (current-column) rust-indent-offset))))))
165+
(save-excursion
166+
(backward-up-list)
167+
(rust-rewind-to-beginning-of-current-level-expr)
168+
(+ (current-column) rust-indent-offset))))))
130169
(cond
131170
;; A function return type is indented to the corresponding function arguments
132171
((looking-at "->")
@@ -137,16 +176,6 @@
137176

138177
;; A closing brace is 1 level unindended
139178
((looking-at "}") (- baseline rust-indent-offset))
140-
141-
;;Line up method chains by their .'s
142-
((when (and rust-indent-method-chain
143-
(looking-at "\..+\(.*\);?\n"))
144-
(or
145-
(let ((method-indent (rust-align-to-method-chain)))
146-
(when method-indent
147-
(+ method-indent rust-indent-offset)))
148-
(+ baseline rust-indent-offset))))
149-
150179

151180
;; Doc comments in /** style with leading * indent to line up the *s
152181
((and (nth 4 (syntax-ppss)) (looking-at "*"))

0 commit comments

Comments
 (0)