emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/swift-mode 32e08b3 095/496: Add initial implementation of


From: ELPA Syncer
Subject: [nongnu] elpa/swift-mode 32e08b3 095/496: Add initial implementation of smie based indentation
Date: Sun, 29 Aug 2021 11:33:15 -0400 (EDT)

branch: elpa/swift-mode
commit 32e08b37ee66b3c4f29a34a2b8b27da50fc0e749
Author: ap4y <lod@pisem.net>
Commit: ap4y <lod@pisem.net>

    Add initial implementation of smie based indentation
---
 swift-mode.el | 289 ++++++++++++++++++++++++++++++++++------------------------
 1 file changed, 168 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))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]