branch: elpa/swift-mode commit 0a75736666dc2ea548d976bdb05576d0d89f672f Author: taku0 <mxxouy6x3m_git...@tatapa.org> Commit: taku0 <mxxouy6x3m_git...@tatapa.org>
Fix indentation --- swift-mode-indent.el | 194 +++++++++++++++++++++++++----------- swift-mode-lexer.el | 45 +++++---- test/swift-files/declarations.swift | 4 +- test/swift-files/statements.swift | 31 ++++-- test/swift-files/types.swift | 10 +- 5 files changed, 192 insertions(+), 92 deletions(-) diff --git a/swift-mode-indent.el b/swift-mode-indent.el index 87462cc..b1bca83 100644 --- a/swift-mode-indent.el +++ b/swift-mode-indent.el @@ -89,7 +89,7 @@ (defconst swift-mode:expression-parent-tokens (append swift-mode:statement-parent-tokens - '(\, < "where" "if" "guard" "while")) + '(\, < supertype-: "where" "if" "guard" "while")) "Parent tokens for expressions.") (defun swift-mode:indent-line () @@ -158,23 +158,7 @@ ((and next-is-on-same-line (memq next-type '(\) \]))) (goto-char (swift-mode:token:end next-token)) (backward-list) - (swift-mode:calculate-indent-of-expression - swift-mode:expression-parent-tokens - 0 - ;; Stops scanning at BOL: - ;; - ;; foo - ;; .bar( - ;; 1 - ;; ) - ;; - ;; rather than - ;; - ;; foo - ;; .bar( - ;; 1 - ;; ) - 'any)) + (swift-mode:calculate-indent-of-expression 0)) ;; Before , on the same line ((and next-is-on-same-line (eq next-type '\,)) @@ -223,7 +207,7 @@ ;; in ;; a ;; } - (swift-mode:calculate-indent-of-expression '("for" {))) + (swift-mode:find-and-align-with-parents '("for" {))) ;; Before "case" or "default" on the same line, for switch statement ((and @@ -282,7 +266,7 @@ '("switch") nil '("case" "default")))) (if (equal (swift-mode:token:text parent) "switch") ;; Inside a switch-statement. Aligns with the "switch" - (swift-mode:calculate-indent-of-expression + (swift-mode:find-and-align-with-parents swift-mode:statement-parent-tokens swift-mode:switch-case-offset) ;; Other cases. Aligns with the previous case. @@ -327,7 +311,7 @@ (let ((parent (save-excursion (swift-mode:backward-sexps-until (append swift-mode:statement-parent-tokens '("case")))))) - (swift-mode:calculate-indent-of-expression + (swift-mode:find-and-align-with-parents (append swift-mode:statement-parent-tokens '(< "case" "catch" "for") (if (equal (swift-mode:token:text parent) "case") '(\,) '())) @@ -343,23 +327,7 @@ ((memq previous-type '(\( \[)) (goto-char (swift-mode:token:start previous-token)) (swift-mode:calculate-indent-of-expression - swift-mode:expression-parent-tokens swift-mode:parenthesized-expression-offset - ;; Stops scanning at BOL: - ;; - ;; foo - ;; .bar( - ;; 1 - ;; ) - ;; - ;; rather than - ;; - ;; foo - ;; .bar( - ;; 1 - ;; ) - 'any - nil swift-mode:parenthesized-expression-offset)) ;; After "where" @@ -435,7 +403,7 @@ (swift-mode:backward-sexps-until (append swift-mode:statement-parent-tokens '("case")))))) - (swift-mode:calculate-indent-of-expression + (swift-mode:find-and-align-with-parents (append swift-mode:statement-parent-tokens '(< "case" "catch" "for") (if (equal (swift-mode:token:text parent) "case") '(\,) '())) @@ -444,7 +412,7 @@ ;; After implicit-\; or ; ((memq previous-type '(implicit-\; \;)) (goto-char (swift-mode:token:start previous-token)) - (swift-mode:calculate-indent-of-expression + (swift-mode:find-and-align-with-parents (remove '\; (remove 'implicit-\; swift-mode:statement-parent-tokens)) 0 '(implicit-\; \;))) @@ -486,20 +454,20 @@ ;; After case ... : or default: ((eq previous-type 'case-:) (goto-char (swift-mode:token:start previous-token)) - (swift-mode:calculate-indent-of-expression + (swift-mode:find-and-align-with-parents swift-mode:statement-parent-tokens swift-mode:basic-offset)) ;; Before ; on the same line ((and next-is-on-same-line (eq next-type '\;)) - (swift-mode:calculate-indent-of-expression + (swift-mode:find-and-align-with-parents (remove '\; (remove 'implicit-\; swift-mode:statement-parent-tokens)) 0 '(implicit-\; \;))) ;; After if, guard, while ((member previous-text '("if" "guard" "while")) - (swift-mode:calculate-indent-of-expression + (swift-mode:find-and-align-with-parents swift-mode:statement-parent-tokens swift-mode:multiline-statement-offset)) @@ -534,21 +502,19 @@ (goto-char (swift-mode:token:end previous-token)) (swift-mode:backward-token-or-list) (swift-mode:calculate-indent-of-expression - swift-mode:expression-parent-tokens - swift-mode:multiline-statement-offset - 'any))))) + swift-mode:multiline-statement-offset))))) -(defun swift-mode:calculate-indent-of-expression +(defun swift-mode:find-and-align-with-parents (parents &optional offset stop-at-eol-token-types stop-at-bol-token-types bol-offset) - "Return start column of the current expressions or statement plus OFFSET. + "Return start column of the current expressions or statement plus offset. -If OFFSET is omitted, it is assumed to be 0. PARENTS is a list of token types that precedes an expression or a statement. +OFFSET is the offset. If it is omitted, assumed to be 0. See `swift-mode:backward-sexps-until' for the details of STOP-AT-EOL-TOKEN-TYPES and STOP-AT-BOL-TOKEN-TYPES. If scanning stops at STOP-AT-EOL-TOKEN-TYPES, align with the next token with @@ -590,6 +556,41 @@ on the previous line." ;; Aligns with this line with bol-offset. (swift-mode:align-with-current-line bol-offset))))) +(defun swift-mode:calculate-indent-of-expression + (&optional + offset + bol-offset) + "Return start column of the current expressions plus offset. + +the cursor is assumed to be on the previous line. + +OFFSET is the offset. If it is omitted, assumed to be 0. +If scanning stops at eol, align with the next token with BOL-OFFSET." + (save-excursion + (let* ((pos (point)) + (parent-of-previous-line + (progn (swift-mode:goto-non-comment-bol-with-same-nesting-level) + (swift-mode:backward-token))) + (parent (progn (goto-char pos) + (swift-mode:find-parent-of-expression)))) + (if (<= (swift-mode:token:start parent-of-previous-line) + (swift-mode:token:start parent)) + ;; let x = + ;; 1 + // here + ;; 2 + + ;; 3 + ;; + ;; Aligns with the parent of the expression with offset. + (swift-mode:align-with-next-token parent offset) + ;; let x = + ;; 1 + + ;; 2 + // here + ;; 3 // or here + ;; + ;; Aligns with the previous line. + (swift-mode:align-with-next-token parent-of-previous-line + bol-offset))))) + (defun swift-mode:calculate-indent-after-open-curly-brace (offset) "Return indentation after open curly braces. @@ -733,12 +734,11 @@ This function is also used for close-curly-brace." (if (< (point) pos) (setq next-token (swift-mode:forward-token-or-list)) (goto-char (1+ pos)))))))) - (swift-mode:calculate-indent-of-expression - swift-mode:statement-parent-tokens - offset - (if is-declaration-or-control-statement-body nil 'any) - nil - offset))) + (if is-declaration-or-control-statement-body + (swift-mode:find-and-align-with-parents + swift-mode:statement-parent-tokens + offset) + (swift-mode:calculate-indent-of-expression offset offset)))) (defun swift-mode:calculate-indent-of-prefix-comma () "Return indentation for prefix comma. @@ -781,8 +781,8 @@ This is also known as Utrecht-style in the Haskell community." Assuming the cursor is on the comma." (swift-mode:align-with-next-token (swift-mode:find-parent-of-list-element nil))) -(defun swift-mode:find-parent-of-list-element (utrecht-sytle) - "Move point backward to the parent token of comma under the cursor. +(defun swift-mode:find-parent-of-list-element (&optional utrecht-sytle) + "Move point backward to the parent token of the comma under the cursor. If UTRECHT-SYTLE is non-nil, stop at a comma at bol. Otherwise, stop at a comma at eol." ;; Various examples: @@ -904,10 +904,10 @@ comma at eol." '("guard" "while" "let" "var" "case" "where")) (setq result next-token)) - ((eq (swift-mode:token:type next-token) 'typing-:) + ((eq (swift-mode:token:type next-token) 'supertype-:) (goto-char pos) (setq result (swift-mode:backward-sexps-until - '(typing-: "where"))))) + '(supertype-: "where"))))) (setq next-token (swift-mode:forward-token-or-list))) (when (and (> (point) pos) @@ -915,12 +915,82 @@ comma at eol." ;; The comma was inside <> but scanner misunderstood < as ;; a binary-operator. (swift-mode:backward-token-or-list) - (setq result (swift-mode:backward-token))) + (setq result (swift-mode:forward-token))) (when (null result) (setq result parent)) (goto-char (swift-mode:token:start result)) result))))) +(defun swift-mode:find-parent-of-expression () + "Move point backward to the parent token of the expression under the cursor." + ;; TODO Unify with swift-mode:find-parent-of-list-element + (let ((pos (point)) + (parent (swift-mode:backward-sexps-until + swift-mode:expression-parent-tokens))) + (cond + ((memq (swift-mode:token:type parent) '(\( \[)) + parent) + + ((or + (memq (swift-mode:token:type parent) + swift-mode:statement-parent-tokens) + (member (swift-mode:token:text parent) + swift-mode:statement-parent-tokens) + (eq (swift-mode:token:type parent) 'outside-of-buffer)) + (goto-char (swift-mode:token:end parent)) + (let ((next-token (swift-mode:forward-token-or-list)) + result) + (while (and (<= (point) pos) (not result)) + (cond + ((member (swift-mode:token:text next-token) + '("guard" "while" "case" "where")) + (setq result next-token)) + + ((member (swift-mode:token:text next-token) + '("let" "var")) + ;; Special handling for "let" and "var". + ;; + ;; Declaring multiple variables with single let statement doesn't + ;; seem to be popular. Rather, we choose saving columns for the + ;; first variable: + ;; + ;; let x = + ;; foo(), + ;; y = + ;; foo() + ;; + ;; rather than: + ;; + ;; let x = + ;; foo(), + ;; y = + ;; foo() + ;; + ;; TODO make customizable + (setq result parent)) + + ((eq (swift-mode:token:type next-token) 'supertype-:) + parent)) + + (forward-comment (point-max)) + (if (< (point) pos) + (setq next-token (swift-mode:forward-token-or-list)) + (setq next-token (swift-mode:forward-token)))) + (when (and (> (point) pos) + (eq (swift-mode:token:type next-token) '<>)) + ;; The expression was inside <> but scanner misunderstood < as + ;; a binary-operator. + (swift-mode:backward-token-or-list) + (setq result (swift-mode:forward-token))) + (when (null result) + (setq result parent)) + (goto-char (swift-mode:token:start result)) + result)) + + (t + parent)))) + + (defun swift-mode:backward-sexps-until (token-types &optional stop-at-eol-token-types @@ -1202,6 +1272,12 @@ Newlines inside comments are ignored." (forward-comment (- (point))) (<= (point) pos)))) +(defun swift-mode:goto-non-comment-bol-with-same-nesting-level () + "Back to the beginning of line that is not inside a comment." + (while (not (swift-mode:bol-other-than-comments-p)) + (swift-mode:backward-token-or-list))) + + (defun swift-mode:bolp () "Return t if there is nothing in the front of this line. diff --git a/swift-mode-lexer.el b/swift-mode-lexer.el index 4465e9e..44a20dc 100644 --- a/swift-mode-lexer.el +++ b/swift-mode-lexer.el @@ -82,7 +82,7 @@ ;; - implicit-; ;; - < (as an angle bracket) ;; - > (as an angle bracket) -;; - typing-: (colon for supertype declaration or type declaration of let or var) +;; - supertype-: (colon for supertype declaration or type declaration of let or var) ;; - case-: (colon for case or default label) ;; - : (part of conditional operator, key-value separator, label-statement separator) ;; - anonymous-function-parameter-in ("in" after anonymous function parameter) @@ -391,38 +391,43 @@ ;; Otherwise, inserts implicit semicolon. (t t)))) -(defun swift-mode:type-colon-p () - "Return t if a colon at the cursor is the colon for type annotation. +(defun swift-mode:supertype-colon-p () + "Return t if a colon at the cursor is the colon for supertype. That is supertype declaration or type declaration of let or var." (save-excursion (let ((previous-token (swift-mode:backward-token-simple))) - ;; class Foo<T>: Bar ← type colon - ;; class Foo<T> : Bar ← type colon - ;; class Foo<T where T: Bar<[(Int, String)]>> : Bar ← type colon - ;; case Foo: ← not a type colon - ;; case Foo where foo: ← not a type colon - ;; default: ← not a type colon - ;; foo ? bar : baz ← not a type colon + ;; class Foo<T>: Bar ← supertype colon + ;; class Foo<T> : Bar ← supertype colon + ;; class Foo<T where T: Bar<[(Int, String)]>> : Bar ← supertype colon + ;; case Foo: ← not a supertype colon + ;; case Foo where foo: ← not a supertype colon + ;; case let Foo(x) where x is Foo<Int>: ← not a supertype colon + ;; default: ← not a supertype colon + ;; foo ? bar : baz ← not a supertype colon ;; [ - ;; foo: ← not a type colon + ;; foo: ← not a supertype colon ;; bar ;; ] - ;; foo(bar, baz: baz) ← not a type colon + ;; foo(bar, baz: baz) ← not a supertype colon + ;; protocol Foo { + ;; associatedtype Bar<A>: Baz ← supertype colon + ;; } (or + ;; FIXME case let Foo(x) where x is Foo<Int> (eq (swift-mode:token:type previous-token) '>) - ;; class Foo: ← type colon - ;; extension Foo: ← type colon - ;; let foo: ← type colon - ;; var foo: ← type colon + ;; class Foo: ← supertype colon + ;; extension Foo: ← supertype colon + ;; let foo: ← not a supertype colon + ;; var foo: ← not a supertype colon ;; protocol Foo { - ;; typealias Bar: Baz ← type colon + ;; associatedtype Bar: Baz ← supertype colon ;; } (member (swift-mode:token:text (swift-mode:backquote-identifier-if-after-dot (swift-mode:backward-token-simple))) '("class" "extension" "enum" "struct" "protocol" "typealias" - "associatedtype" "let" "var")))))) + "associatedtype")))))) (defun swift-mode:case-colon-p () "Return t if a colon at the cursor is the colon for case or default label." @@ -545,7 +550,7 @@ type `out-of-buffer'" ;; Colon ((eq (char-after) ?:) (swift-mode:token (cond - ((swift-mode:type-colon-p) 'typing-:) + ((swift-mode:supertype-colon-p) 'supertype-:) ((swift-mode:case-colon-p) 'case-:) (t ':)) ":" @@ -715,7 +720,7 @@ type `out-of-buffer'." ((eq (char-before) ?:) (backward-char) (swift-mode:token (cond - ((swift-mode:type-colon-p) 'typing-:) + ((swift-mode:supertype-colon-p) 'supertype-:) ((swift-mode:case-colon-p) 'case-:) (t ':)) ":" diff --git a/test/swift-files/declarations.swift b/test/swift-files/declarations.swift index 3a353de..e16fed6 100644 --- a/test/swift-files/declarations.swift +++ b/test/swift-files/declarations.swift @@ -292,13 +292,13 @@ fileprivate B = D<E> { case A = - 1, // swift-mode:test:known-bug + 1, B = 2, C = 3 case D - = 1, // swift-mode:test:known-bug + = 1, E = 2, F diff --git a/test/swift-files/statements.swift b/test/swift-files/statements.swift index 67c6a28..3decd4f 100644 --- a/test/swift-files/statements.swift +++ b/test/swift-files/statements.swift @@ -365,6 +365,16 @@ if foo() { foo() } +if foo.bar +++ { + foo() + }, + foo.bar +++ { + foo() + } { + foo() +} + + // Guard statement guard @@ -566,7 +576,7 @@ default: switch foo { case let - .P(x) // swift-mode:test:known-bug + .P(x) where // swift-mode:test:known-bug foo .bar(), @@ -590,7 +600,7 @@ default: switch foo { case let - .P(x) // swift-mode:test:known-bug + .P(x) where // swift-mode:test:known-bug foo .bar(), @@ -614,7 +624,7 @@ default: switch foo { case let Foo - .P(x) // swift-mode:test:known-bug + .P(x) where // swift-mode:test:known-bug foo .bar(), @@ -632,7 +642,7 @@ case foo() case Foo - .P, // swift-mode:test:known-bug + .P, Foo .Q, Foo @@ -645,7 +655,7 @@ default: switch foo { case let - Foo // swift-mode:test:known-bug + Foo .P(x) where // swift-mode:test:known-bug foo @@ -672,7 +682,7 @@ default: switch foo { case is - Foo // swift-mode:test:known-bug + Foo where // swift-mode:test:known-bug foo .bar(), @@ -694,6 +704,15 @@ default: foo() } +switch foo { +case let .P(x) where x is Foo<Int>: + foo() // swift-mode:test:known-bug + foo() // swift-mode:test:known-bug +default: + foo() + foo() +} + // swift-mode:test:eval (setq-local swift-mode:switch-case-offset 2) switch foo { diff --git a/test/swift-files/types.swift b/test/swift-files/types.swift index e058e44..cacf1e5 100644 --- a/test/swift-files/types.swift +++ b/test/swift-files/types.swift @@ -59,7 +59,7 @@ let foo class Foo: @A - A, + A, // swift-mode:test:known-bug B { } @@ -94,7 +94,7 @@ let foo: class Foo: A. - B, // swift-mode:test:known-bug + B, A. B, A @@ -103,7 +103,7 @@ class Foo: class Foo : A. - B, // swift-mode:test:known-bug + B, A. B, A @@ -111,7 +111,7 @@ class Foo } class Foo: A. - B, // swift-mode:test:known-bug + B , A. B , A @@ -121,7 +121,7 @@ class Foo: A. class Foo : A. - B // swift-mode:test:known-bug + B , A. B, , A