diff --git a/src/etc/emacs/rust-mode-tests.el b/src/etc/emacs/rust-mode-tests.el index 524d474d8350d..63c1a077c8c3d 100644 --- a/src/etc/emacs/rust-mode-tests.el +++ b/src/etc/emacs/rust-mode-tests.el @@ -425,6 +425,108 @@ fn foo() " )) +(ert-deftest indent-match () + (test-indent + " +fn foo() { + match blah { + Pattern => stuff(), + _ => whatever + } +} +" + )) + +(ert-deftest indent-match-multiline-pattern () + (test-indent + " +fn foo() { + match blah { + Pattern | + Pattern2 => { + hello() + }, + _ => whatever + } +} +" + )) + +(ert-deftest indent-indented-match () + (test-indent + " +fn foo() { + let x = + match blah { + Pattern | + Pattern2 => { + hello() + }, + _ => whatever + }; + y(); +} +" + )) + +(ert-deftest indent-curly-braces-within-parens () + (test-indent + " +fn foo() { + let x = + foo(bar(|x| { + only_one_indent_here(); + })); + y(); +} +" + )) + +(ert-deftest indent-weirdly-indented-block () + (rust-test-manip-code + " +fn foo() { + { +this_block_is_over_to_the_left_for_some_reason(); + } + +} +" + 16 + #'indent-for-tab-command + " +fn foo() { + { + this_block_is_over_to_the_left_for_some_reason(); + } + +} +" + )) + +(ert-deftest indent-multi-line-attrib () + (test-indent + " +#[attrib( + this, + that, + theotherthing)] +fn function_with_multiline_attribute() {} +" + )) + + +;; Make sure that in effort to cover match patterns we don't mistreat || or expressions +(ert-deftest indent-nonmatch-or-expression () + (test-indent + " +fn foo() { + let x = foo() || + bar(); +} +" + )) + (setq rust-test-motion-string " fn fn1(arg: int) -> bool { @@ -450,6 +552,26 @@ struct Foo { } " rust-test-region-string rust-test-motion-string + rust-test-indent-motion-string + " +fn blank_line(arg:int) -> bool { + +} + +fn indenting_closing_brace() { + if(true) { +} +} + +fn indenting_middle_of_line() { + if(true) { + push_me_out(); +} else { + pull_me_back_in(); +} +} +" + ;; Symbol -> (line column) rust-test-positions-alist '((start-of-fn1 (2 0)) (start-of-fn1-middle-of-line (2 15)) @@ -464,7 +586,17 @@ struct Foo { (middle-of-fn3 (16 4)) (middle-of-struct (21 10)) (before-start-of-struct (19 0)) - (after-end-of-struct (23 0)))) + (after-end-of-struct (23 0)) + (blank-line-indent-start (3 0)) + (blank-line-indent-target (3 4)) + (closing-brace-indent-start (8 1)) + (closing-brace-indent-target (8 5)) + (middle-push-indent-start (13 2)) + (middle-push-indent-target (13 9)) + (after-whitespace-indent-start (13 1)) + (after-whitespace-indent-target (13 8)) + (middle-pull-indent-start (15 19)) + (middle-pull-indent-target (15 12)))) (defun rust-get-buffer-pos (pos-symbol) "Get buffer position from POS-SYMBOL. @@ -626,3 +758,38 @@ All positions are position symbols found in `rust-test-positions-alist'." 'middle-of-struct 'before-start-of-struct 'after-end-of-struct #'mark-defun)) + +(ert-deftest indent-line-blank-line-motion () + (rust-test-motion + rust-test-indent-motion-string + 'blank-line-indent-start + 'blank-line-indent-target + #'indent-for-tab-command)) + +(ert-deftest indent-line-closing-brace-motion () + (rust-test-motion + rust-test-indent-motion-string + 'closing-brace-indent-start + 'closing-brace-indent-target + #'indent-for-tab-command)) + +(ert-deftest indent-line-middle-push-motion () + (rust-test-motion + rust-test-indent-motion-string + 'middle-push-indent-start + 'middle-push-indent-target + #'indent-for-tab-command)) + +(ert-deftest indent-line-after-whitespace-motion () + (rust-test-motion + rust-test-indent-motion-string + 'after-whitespace-indent-start + 'after-whitespace-indent-target + #'indent-for-tab-command)) + +(ert-deftest indent-line-middle-pull-motion () + (rust-test-motion + rust-test-indent-motion-string + 'middle-pull-indent-start + 'middle-pull-indent-target + #'indent-for-tab-command)) diff --git a/src/etc/emacs/rust-mode.el b/src/etc/emacs/rust-mode.el index 4e1c74c31df91..b304df8f14c16 100644 --- a/src/etc/emacs/rust-mode.el +++ b/src/etc/emacs/rust-mode.el @@ -59,70 +59,95 @@ (backward-word 1)) (current-column)))) +(defun rust-rewind-to-beginning-of-current-level-expr () + (let ((current-level (rust-paren-level))) + (back-to-indentation) + (while (> (rust-paren-level) current-level) + (backward-up-list) + (back-to-indentation)))) + (defun rust-mode-indent-line () (interactive) (let ((indent (save-excursion (back-to-indentation) - (let ((level (rust-paren-level))) + ;; Point is now at beginning of current line + (let* ((level (rust-paren-level)) + (baseline + ;; Our "baseline" is one level out from the indentation of the expression + ;; containing the innermost enclosing opening bracket. That + ;; way if we are within a block that has a different + ;; indentation than this mode would give it, we still indent + ;; the inside of it correctly relative to the outside. + (if (= 0 level) + 0 + (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 "->") (save-excursion (backward-list) (or (rust-align-to-expr-after-brace) - (* rust-indent-offset (+ 1 level))))) + (+ baseline rust-indent-offset)))) ;; A closing brace is 1 level unindended - ((looking-at "}") (* rust-indent-offset (- level 1))) + ((looking-at "}") (- baseline rust-indent-offset)) ;; Doc comments in /** style with leading * indent to line up the *s ((and (nth 4 (syntax-ppss)) (looking-at "*")) - (+ 1 (* rust-indent-offset level))) + (+ 1 baseline)) ;; If we're in any other token-tree / sexp, then: - ;; - [ or ( means line up with the opening token - ;; - { means indent to either nesting-level * rust-indent-offset, - ;; or one further indent from that if either current line - ;; begins with 'else', or previous line didn't end in - ;; semi, comma or brace (other than whitespace and line - ;; comments) , and wasn't an attribute. But if we have - ;; something after the open brace and ending with a comma, - ;; treat it as fields and align them. PHEW. - ((> level 0) - (let ((pt (point))) - (rust-rewind-irrelevant) - (backward-up-list) - (or (and (looking-at "[[({]") - (rust-align-to-expr-after-brace)) - (progn - (goto-char pt) - (back-to-indentation) - (if (looking-at "\\") - (* rust-indent-offset (+ 1 level)) - (progn - (goto-char pt) - (beginning-of-line) - (rust-rewind-irrelevant) - (end-of-line) - (if (looking-back - "[[,;{}(][[:space:]]*\\(?://.*\\)?") - (* rust-indent-offset level) - (back-to-indentation) - (if (looking-at "#") - (* rust-indent-offset level) - (* rust-indent-offset (+ 1 level)))))))))) - - ;; Otherwise we're in a column-zero definition - (t 0)))))) - (cond - ;; If we're to the left of the indentation, reindent and jump to it. - ((<= (current-column) indent) - (indent-line-to indent)) - - ;; We're to the right; if it needs indent, do so but save excursion. - ((not (eq (current-indentation) indent)) - (save-excursion (indent-line-to indent)))))) + (t + (or + ;; If we are inside a pair of braces, with something after the + ;; open brace on the same line and ending with a comma, treat + ;; it as fields and align them. + (when (> level 0) + (save-excursion + (rust-rewind-irrelevant) + (backward-up-list) + ;; Point is now at the beginning of the containing set of braces + (rust-align-to-expr-after-brace))) + + (progn + (back-to-indentation) + ;; Point is now at the beginning of the current line + (if (or + ;; If this line begins with "else" or "{", stay on the + ;; baseline as well (we are continuing an expression, + ;; but the "else" or "{" should align with the beginning + ;; of the expression it's in.) + (looking-at "\\\\|{") + + (save-excursion + (rust-rewind-irrelevant) + ;; Point is now at the end of the previous ine + (or + ;; If we are at the first line, no indentation is needed, so stay at baseline... + (= 1 (line-number-at-pos (point))) + ;; ..or if the previous line ends with any of these: + ;; { ? : ( , ; [ } + ;; then we are at the beginning of an expression, so stay on the baseline... + (looking-back "[(,:;?[{}]\\|[^|]|") + ;; or if the previous line is the end of an attribute, stay at the baseline... + (progn (rust-rewind-to-beginning-of-current-level-expr) (looking-at "#"))))) + baseline + + ;; Otherwise, we are continuing the same expression from the previous line, + ;; so add one additional indent level + (+ baseline rust-indent-offset)))))))))) + (when (not (eq (current-indentation) indent)) + ;; If we're at the beginning of the line (before or at the current + ;; indentation), jump with the indentation change. Otherwise, save the + ;; excursion so that adding the indentations will leave us at the + ;; equivalent position within the line to where we were before. + (if (<= (current-column) (current-indentation)) + (indent-line-to indent) + (save-excursion (indent-line-to indent)))))) ;; Font-locking definitions and helpers