[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/swift-mode 8fc94c7 097/496: Merge pull request #25 from ap
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/swift-mode 8fc94c7 097/496: Merge pull request #25 from ap4y/smie_indentation |
Date: |
Sun, 29 Aug 2021 11:33:15 -0400 (EDT) |
branch: elpa/swift-mode
commit 8fc94c7f960dca5f51f8baae5f7dae3ed3dec4a1
Merge: b298842 bbd8f91
Author: Bozhidar Batsov <bozhidar@batsov.com>
Commit: Bozhidar Batsov <bozhidar@batsov.com>
Merge pull request #25 from ap4y/smie_indentation
Smie based indentation
---
swift-mode.el | 289 ++++++++++++++++++++++++-----------------
test/indentation-tests.el | 325 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 493 insertions(+), 121 deletions(-)
diff --git a/swift-mode.el b/swift-mode.el
index a53a371..5dceffc 100644
--- a/swift-mode.el
+++ b/swift-mode.el
@@ -59,127 +59,172 @@
;;; Indentation
-(defun swift-indent--paren-level ()
- "Return the paren level at point."
- (nth 0 (syntax-ppss)))
-
-(defun swift-indent--in-str-or-cmnt-p ()
- "Non-nil if point is in a string or comment."
- (nth 8 (syntax-ppss)))
-
-(defun swift-indent--back-to-start-of-level ()
- "Move backwards up to the start of the current indentation level."
- (let ((current-level (swift-indent--paren-level)))
- (back-to-indentation)
- (while (> (swift-indent--paren-level) current-level)
- (backward-up-list)
- (back-to-indentation))))
-
-(defun swift-indent--rewind-past-str-cmnt ()
- "Move to the start of the comment at point."
- (goto-char (nth 8 (syntax-ppss))))
-
-(defun swift-indent--rewind-irrelevant ()
- "Move backward past spaces and comments."
- (let ((starting (point)))
- (skip-chars-backward "[:space:]\n")
- (when (looking-back "\\*/")
- (backward-char))
- (when (swift-indent--in-str-or-cmnt-p)
- (swift-indent--rewind-past-str-cmnt))
- (when (/= starting (point))
- (swift-indent--rewind-irrelevant))))
-
-(defun swift-indent--align-to-expr-after-brace ()
- "Return the column to use for aligning an expression after a brace."
+(require 'smie)
+
+(defconst swift-smie-grammar
+ (smie-prec2->grammar
+ (smie-merge-prec2s
+ (smie-bnf->prec2
+ '((id)
+ (type)
+ (types (type) (type "," type))
+
+ (class-decl-exp (id) (id ":" types))
+ (decl-exp (id) (id ":" type))
+ (decl-exps (decl-exp) (decl-exp "," decl-exp))
+
+ (assign-exp (decl-exp) (id "=" exp))
+
+ (decl (decl ";" decl))
+ (decl (let-decl) (var-decl))
+ (let-decl
+ ("let" id ":" type)
+ ("let" id "=" exp)
+ ("let" id ":" type "=" exp))
+ (var-decl
+ ("var" id ":" type)
+ ("var" id "=" exp)
+ ("var" id ":" type "=" exp))
+
+ (top-level-sts (top-level-st) (top-level-st ";" top-level-st))
+ (top-level-st
+ ("import" type)
+ (decl)
+ ("class" class-decl-exp "{" class-level-sts "}"))
+
+ (class-level-sts (class-level-st) (class-level-st ";" class-level-st))
+ (class-level-st
+ (decl)
+ ("override" "func" func-header "{" insts "}"))
+
+ (func-header (id "(" func-params ")"))
+ (func-param (decl-exp) ("..."))
+ (func-params (func-param "," func-param))
+
+ (insts (inst) (insts ";" insts))
+ (inst (decl)
+ (in-exp)
+ (dot-exp)
+ (dot-exp "{" insts "}")
+ (method-call)
+ (method-call "{" insts "}")
+ ("enum" decl-exp "{" enum-cases "}")
+ ("switch" exp "{" switch-body "}")
+ (if-clause)
+ ("for" for-head "{" insts "}")
+ ("while" exp "{" insts "}"))
+
+ (dot-exp (exp "." exp))
+
+ (method-call (dot-exp "(" method-args ")"))
+ (method-args (method-arg) (method-arg "," method-arg))
+ (method-arg (exp "," "{" insts "}") (exp))
+
+ (exp (op-exp)
+ ("[" decl-exps "]"))
+ (in-exp (id "in" exp))
+ (guard-exp (exp "where" exp))
+ (op-exp (exp "OP" exp))
+
+ (enum-cases (assign-exp)
+ (enum-cases "case" enum-cases))
+
+ (case-exps (guard-exp))
+ (cases (case-exps ":" insts)
+ (cases "case" cases))
+ (switch-body (cases) (cases "default" insts))
+
+ (for-head (in-exp) (op-exp) (for-head ";" for-head))
+
+ (conditional (exp) (let-decl))
+ (if-body ("if" conditional "{" insts "}"))
+ (if-else-if (if-body) (if-else-if "else" if-else-if))
+ (if-clause (if-else-if)))
+ ;; Conflicts
+ '((nonassoc "{") (assoc ",") (assoc ";") (assoc ":"))
+ '((assoc "in") (assoc "where") (assoc "OP"))
+ '((assoc "else"))
+ '((assoc "case")))
+
+ (smie-precs->prec2
+ '(
+ (right "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" "&="
+ "^=" "|=" "&&=" "||=" "=") ;; Assignment
(Right associative, precedence level 90)
+ (left "||") ;; Disjunctive
(Left associative, precedence level 110)
+ (left "&&") ;; Conjunctive
(Left associative, precedence level 120)
+ (nonassoc "<" "<=" ">" ">=" "==" "!=" "===" "!==" "~=") ;; Comparative
(No associativity, precedence level 130)
+ (nonassoc "is" "as" "as?") ;; Cast (No
associativity, precedence level 132)
+ (nonassoc ".." "...") ;; Range (No
associativity, precedence level 135)
+ (left "+" "-" "&+" "&-" "|" "^") ;; Additive
(Left associative, precedence level 140)
+ (left "*" "/" "%" "&*" "&/" "&%" "&") ;;
Multiplicative (Left associative, precedence level 150)
+ (nonassoc "<<" ">>") ;;
Exponentiative (No associativity, precedence level 160)
+ ))
+ )))
+
+(defun verbose-swift-smie-rules (kind token)
+ (let ((value (swift-smie-rules kind token)))
+ (message "%s '%s'; sibling-p:%s parent:%s hanging:%s == %s" kind token
+ (ignore-errors (smie-rule-sibling-p))
+ (ignore-errors smie--parent)
+ (ignore-errors (smie-rule-hanging-p))
+ value)
+ value))
+
+(defvar swift-smie--operators-regexp
+ (regexp-opt '("*=" "/=" "%=" "+=" "-=" "<<=" ">>=" "&=" "^=" "|=" "&&=" "||="
+ "<" "<=" ">" ">=" "==" "!=" "===" "!==" "~=" "||" "&&"
+ "is" "as" "as?" ".." "..."
+ "+" "-" "&+" "&-" "|" "^"
+ "*" "/" "%" "&*" "&/" "&%" "&"
+ "<<" ">>")))
+
+(defun swift-smie--implicit-semi-p ()
(save-excursion
- (forward-char)
- ;; We don't want to indent out to the open bracket if the
- ;; open bracket ends the line.
- (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$"))
- (when (looking-at "[[:space:]]")
- (forward-word 1)
- (backward-word 1))
- (current-column))))
-
-(defun swift-indent--at-enum-case-p ()
- "Non-nil if point is at a case keyword at the top level of an enum
declaration."
- (save-excursion
- (back-to-indentation)
- (when (looking-at (rx bow "case" eow))
- (backward-up-list)
- (swift-indent--back-to-start-of-level)
- (looking-at (rx bow "enum" eow)))))
-
-(defun swift-indent--calculate-indentation ()
- "Calculate the indentation column to use for `swift-indent-line'.
-Returns the column number as an integer."
- (save-excursion
- (back-to-indentation)
- ;; Point is now at beginning of line.
- (let* ((level (swift-indent--paren-level))
- ;; 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.
- (baseline
- (if (zerop level)
- 0
- (save-excursion
- (backward-up-list)
- (swift-indent--back-to-start-of-level)
- (+ (current-column) swift-indent-offset)))))
- (cond
- ;; A function return type is indented to the corresponding function
arguments
- ((looking-at "->")
- (save-excursion
- (backward-list)
- (or (swift-indent--align-to-expr-after-brace)
- (+ baseline swift-indent-offset))))
-
- ;; A closing brace is 1 level unindented
- ((looking-at "}") (- baseline swift-indent-offset))
-
- ;; Doc comments in /** style with leading * indent to line up the *s
- ((and (nth 4 (syntax-ppss)) (looking-at "*"))
- (+ 1 baseline))
-
- ;; If we're in any other token-tree / sexp, then:
- (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
- (swift-indent--rewind-irrelevant)
- (backward-up-list)
- ;; Point is now at the beginning of the containing set of braces
- (swift-indent--align-to-expr-after-brace)))
-
- (progn
- (back-to-indentation)
- ;; Point is now at the beginning of the current line
- (cond
- ((swift-indent--at-enum-case-p)
- baseline)
- ;; Cases are indented to the same level as the enclosing switch
- ;; statement, plus a user-customisable offset.
- ((looking-at (rx bow (or "case" "default") eow))
- (+ (- baseline swift-indent-offset)
- swift-indent-switch-case-offset))
- (t
- baseline)))))))))
-
-(defun swift-indent-line ()
- "Indent the current line. Also see `swift-indent-offset'."
- (interactive "*")
- (let ((indent (swift-indent--calculate-indentation)))
- (if (<= (current-column) (current-indentation))
- (indent-line-to indent)
- (save-excursion
- (indent-line-to indent)))))
+ (not (or (memq (char-before) '(?\{ ?\[ ?\,))
+ (looking-back swift-smie--operators-regexp (- (point) 3) t)
+ ))))
+
+(defun swift-smie--forward-token ()
+ (cond
+ ((and (looking-at "\n") (swift-smie--implicit-semi-p))
+ (if (eolp) (forward-char 1) (forward-comment 1))
+ ";")
+ ((looking-at "{") (forward-char 1) "{")
+ ((looking-at "}") (forward-char 1) "}")
+ ((looking-at swift-smie--operators-regexp)
+ (goto-char (match-end 0)) "OP")
+ (t (smie-default-forward-token))))
+
+(defun swift-smie--backward-token ()
+ (let ((pos (point)))
+ (forward-comment (- (point)))
+ (cond
+ ((and (> pos (line-end-position))
+ (swift-smie--implicit-semi-p))
+ ";")
+ ((eq (char-before) ?\{) (backward-char 1) "{")
+ ((eq (char-before) ?\}) (backward-char 1) "}")
+ ((looking-back swift-smie--operators-regexp (- (point) 3) t)
+ (goto-char (match-beginning 0)) "OP")
+ (t (smie-default-backward-token)))))
+
+(defun swift-smie-rules (kind token)
+ (pcase (cons kind token)
+ (`(:elem . basic) swift-indent-offset)
+
+ (`(:after . "{")
+ (if (smie-rule-parent-p "switch")
+ (smie-rule-parent swift-indent-switch-case-offset)))
+ (`(:before . ";")
+ (if (smie-rule-parent-p "case" "default")
+ (smie-rule-parent swift-indent-offset)))
+
+ (`(:before . "if")
+ (if (smie-rule-prev-p "else")
+ (if (smie-rule-parent-p "{")
+ swift-indent-offset
+ (smie-rule-parent))))
+ ))
;;; Font lock
@@ -457,7 +502,9 @@ You can send text to the REPL process from other buffers
containing source.
(setq-local indent-tabs-mode nil)
(setq-local electric-indent-chars
(append '(?. ?, ?: ?\) ?\] ?\}) electric-indent-chars))
- (setq-local indent-line-function 'swift-indent-line))
+ (smie-setup swift-smie-grammar 'swift-smie-rules ;; 'verbose-swift-smie-rules
+ :forward-token 'swift-smie--forward-token
+ :backward-token 'swift-smie--backward-token))
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.swift\\'" . swift-mode))
diff --git a/test/indentation-tests.el b/test/indentation-tests.el
index 4f07885..502e961 100644
--- a/test/indentation-tests.el
+++ b/test/indentation-tests.el
@@ -159,6 +159,32 @@ if true {
}
")
+(check-indentation indent-if-else
+ "
+if true {
+} else {
+|foo
+}
+" "
+if true {
+} else {
+ |foo
+}
+")
+
+(check-indentation indent-if-else-if
+ "
+if true {
+} else if false {
+|foo
+}
+" "
+if true {
+} else if false {
+ |foo
+}
+")
+
(check-indentation indent-if-body--no-effect-if-already-indented
"
if true {
@@ -170,6 +196,21 @@ if true {
}
")
+(check-indentation indent-if-body-nested
+ "
+if foo {
+ if true {
+|foo
+ }
+}
+" "
+if foo {
+ if true {
+ |foo
+ }
+}
+")
+
(check-indentation indents-case-statements-to-same-level-as-enclosing-switch/1
"
switch true {
@@ -338,6 +379,74 @@ case y:
}
")
+(check-indentation indents-case-statements-with-destucturing/1
+ "
+switch true {
+case let(x, y):
+|foo
+}
+" "
+switch true {
+case let(x, y):
+ |foo
+}
+")
+
+(check-indentation indents-case-statements-with-destucturing/2
+ "
+switch true {
+case let .Foo(x):
+|foo
+}
+" "
+switch true {
+case let .Foo(x):
+ |foo
+}
+")
+
+(check-indentation indents-case-statements-with-guard
+ "
+switch true {
+case foo where bar:
+|foo
+}
+" "
+switch true {
+case foo where bar:
+ |foo
+}
+")
+
+(check-indentation indents-case-statements-with-multiline-guard/1
+ "
+switch true {
+case foo where bar,
+|bar where baz:
+}
+" "
+switch true {
+case foo where bar,
+ |bar where baz:
+}
+")
+
+(check-indentation indents-case-statements-with-multiline-guard/2
+ "
+switch true {
+case foo where bar,
+ bar where baz:
+|foo
+}
+" "
+switch true {
+case foo where bar,
+ bar where baz:
+ |foo
+}
+")
+
+
(check-indentation indents-case-statements-to-user-defined-offset/1
"
switch true {
@@ -385,6 +494,222 @@ enum T {
}
")
+(check-indentation indents-case-statements-in-enum/3
+ "
+enum Foo: Bar {
+ |case
+}
+" "
+enum Foo: Bar {
+ |case
+}
+")
+
+(check-indentation indents-for-statements/1
+ "
+for index in 1..5 {
+|foo
+}
+" "
+for index in 1..5 {
+ |foo
+}
+")
+
+(check-indentation indents-for-statements/2
+ "
+for (key, value) in dict {
+|foo
+}
+" "
+for (key, value) in dict {
+ |foo
+}
+")
+
+(check-indentation indents-for-statements/3
+ "
+for var index = 0; index < 3; ++index {
+|foo
+}
+" "
+for var index = 0; index < 3; ++index {
+ |foo
+}
+")
+
+(check-indentation indents-while-statements
+ "
+while foo < bar{
+|foo
+}
+" "
+while foo < bar{
+ |foo
+}
+")
+
+(check-indentation indents-import-statements/1
+ "
+import Foo
+ |import Bar
+" "
+import Foo
+|import Bar
+")
+
+(check-indentation indents-class-declaration/1
+ "
+class Foo {
+ |foo
+}
+" "
+class Foo {
+ |foo
+}
+")
+
+(check-indentation indents-class-declaration/2
+ "
+class Foo: Bar {
+ |foo
+}
+" "
+class Foo: Bar {
+ |foo
+}
+")
+
+(check-indentation indents-class-declaration/3
+ "
+class Foo: Foo, Bar, Baz {
+ |foo
+}
+" "
+class Foo: Foo, Bar, Baz {
+ |foo
+}
+")
+
+(check-indentation indents-class-declaration/4
+ "
+class Foo: Bar {
+|class Baz: Bar {
+ }
+}
+" "
+class Foo: Bar {
+ |class Baz: Bar {
+ }
+}
+")
+
+(check-indentation indents-func-declaration/1
+ "
+func Foo(a: String) {
+|foo
+}
+" "
+func Foo(a: String) {
+ |foo
+}
+")
+
+(check-indentation indents-func-declaration/2
+ "
+override func Foo() {
+|foo
+}
+" "
+override func Foo() {
+ |foo
+}
+")
+
+(check-indentation indents-func-declaration/3
+ "
+func Foo(b: Double...) -> Bool {
+|foo
+}
+" "
+func Foo(b: Double...) -> Bool {
+ |foo
+}
+")
+
+(check-indentation indents-func-declaration/4
+ "
+class Foo {
+ override func Foo(b: Double...) -> Bool {
+|foo
+ }
+}
+" "
+class Foo {
+ override func Foo(b: Double...) -> Bool {
+ |foo
+ }
+}
+")
+
+(check-indentation indents-declaration/1
+ "
+var foo = bar + baz
+ |
+" "
+var foo = bar + baz
+|
+")
+
+(check-indentation indents-declaration/2
+ "
+let foo = bar +
+|baz
+" "
+let foo = bar +
+ |baz
+")
+
+(check-indentation indents-declaration/3
+ "
+let foo = [foo: bar, bar: baz]
+ |
+" "
+let foo = [foo: bar, bar: baz]
+|
+")
+
+(check-indentation indents-declaration/4
+ "
+let foo = [
+|bar: baz
+ ]
+" "
+let foo = [
+ |bar: baz
+ ]
+")
+
+(check-indentation indents-declaration/5
+ "
+let foo = [foo, bar]
+ |
+" "
+let foo = [foo, bar]
+|
+")
+
+(check-indentation indents-declaration/6
+ "
+let foo = [
+|bar
+ ]
+" "
+let foo = [
+ |bar
+ ]
+")
+
(provide 'indentation-tests)
;;; indentation-tests.el ends here
- [nongnu] elpa/swift-mode 5adb37d 070/496: More indentation tests, (continued)
- [nongnu] elpa/swift-mode 5adb37d 070/496: More indentation tests, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 34be010 074/496: Update features in readme, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 6553249 062/496: Rename predicate (add question mark), ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode ff32221 068/496: Add font lock tests, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 5f97c99 060/496: Add tests, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode c3a0a66 065/496: Fix custom variable types, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode a17f9e8 066/496: Rename predicate functions ('?' -> -p), ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 8dc7eb9 078/496: Clean up after pull request, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 98b627a 075/496: Add @ap4y to contributors, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 812b4f6 084/496: Remove redundant hook definition, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 8fc94c7 097/496: Merge pull request #25 from ap4y/smie_indentation,
ELPA Syncer <=
- [nongnu] elpa/swift-mode 889586c 102/496: Merge pull request #28 from ap4y/highlight_interpolation, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 0fcf3ab 115/496: Restructure test for multi-line hash in method call to test against case, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode b0f5d13 113/496: Make commas non sticky, fixes indentation issues with optionals and, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode d77b2df 122/496: Enable nested flag 'n' for C-style multi-line comments, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode b43c1e2 129/496: Merge pull request #38 from ap4y/fix_36, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode a028f68 127/496: Add "_" to the allowed symbols in string interpolation expression, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 2593659 131/496: Improve indentation for multi-line dot expression with dot positioned on, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 835c3d3 132/496: Merge pull request #40 from ap4y/improve_37, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 2eb3952 136/496: Merge pull request #45 from ap4y/dot_in_string_interpolation, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 5e3ab8c 139/496: Apply swift-indent-multiline-statement-offset to the multi-line, ELPA Syncer, 2021/08/29