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

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

[nongnu] elpa/swift-mode d59cae7 355/496: Fix indentation of string inte


From: ELPA Syncer
Subject: [nongnu] elpa/swift-mode d59cae7 355/496: Fix indentation of string interpolations
Date: Sun, 29 Aug 2021 11:34:06 -0400 (EDT)

branch: elpa/swift-mode
commit d59cae7fbbf53a9e1698886bb29a598cf7e8b4b4
Author: taku0 <mxxouy6x3m_github@tatapa.org>
Commit: taku0 <mxxouy6x3m_github@tatapa.org>

    Fix indentation of string interpolations
---
 swift-mode-indent.el           |  72 +++++++++--
 swift-mode-lexer.el            | 269 ++++++++++++++++++++++++++++++++++-------
 swift-mode.el                  |   3 +
 test/swift-files/strings.swift |  62 ++++++++++
 4 files changed, 352 insertions(+), 54 deletions(-)

diff --git a/swift-mode-indent.el b/swift-mode-indent.el
index 25963ce..937e10e 100644
--- a/swift-mode-indent.el
+++ b/swift-mode-indent.el
@@ -102,7 +102,8 @@ Intended for debugging."
 
 (defconst swift-mode:expression-parent-tokens
   (append swift-mode:statement-parent-tokens
-          '(\, < supertype-: "where" "if" "guard" "while" "for" "catch"))
+          '(\, < supertype-: "where" "if" "guard" "while" "for" "catch"
+            string-chunk-before-interpolation))
   "Parent tokens for expressions.")
 
 (defvar-local swift-mode:anchor-overlay nil)
@@ -163,6 +164,7 @@ declaration and its offset is `swift-mode:basic-offset'."
 
      ((eq (nth 3 parser-state) t)
       (swift-mode:calculate-indent-of-multiline-string))
+
      (t
       (swift-mode:calculate-indent-of-code)))))
 
@@ -189,14 +191,17 @@ declaration and its offset is `swift-mode:basic-offset'."
 (defun swift-mode:calculate-indent-of-multiline-string ()
   "Return the indentation of the current line inside a multiline string."
   (back-to-indentation)
-  (let ((string-beginning-position (nth 8 (syntax-ppss))))
+  (let ((string-beginning-position
+         (save-excursion (swift-mode:beginning-of-string))))
     (if (looking-at "\"\"\"")
         ;; The last line.
         (progn
           (goto-char string-beginning-position)
           (swift-mode:calculate-indent-of-expression
            swift-mode:multiline-statement-offset))
-      (forward-line -1)
+      (forward-line 0)
+      (backward-char)
+      (swift-mode:goto-non-string-interpolation-bol)
       (back-to-indentation)
       (if (<= (point) string-beginning-position)
           ;; The cursor was on the 2nd line of the comment, so aligns with
@@ -212,6 +217,20 @@ declaration and its offset is `swift-mode:basic-offset'."
             (swift-mode:calculate-indent-of-multiline-string)
           (swift-mode:indentation (point) 0))))))
 
+(defun swift-mode:goto-non-string-interpolation-bol ()
+  "Back to the beginning of line that is not inside a string interpolation."
+  (let ((string-beginning-position (nth 8 (syntax-ppss)))
+        (matching-parenthesis t))
+    (while (and matching-parenthesis
+                (< (line-beginning-position) string-beginning-position))
+      (setq matching-parenthesis
+            (get-text-property
+             string-beginning-position 'swift-mode:matching-parenthesis))
+      (when matching-parenthesis
+        (goto-char matching-parenthesis)
+        (setq string-beginning-position (nth 8 (syntax-ppss)))))
+    (forward-line 0)))
+
 (defun swift-mode:calculate-indent-of-code ()
   "Return the indentation of the current line outside multiline comments."
   (back-to-indentation)
@@ -222,7 +241,7 @@ declaration and its offset is `swift-mode:basic-offset'."
          (next-type (swift-mode:token:type next-token))
          (next-text (swift-mode:token:text next-token))
          (next-is-on-same-line
-          (<= (swift-mode:token:end next-token) (line-end-position))))
+          (<= (swift-mode:token:start next-token) (line-end-position))))
     (cond
      ;; Beginning of the buffer
      ((eq previous-type 'outside-of-buffer)
@@ -240,6 +259,16 @@ declaration and its offset is `swift-mode:basic-offset'."
       (backward-list)
       (swift-mode:calculate-indent-of-expression 0))
 
+     ;; Before end of a string interpolation on the same line
+     ((and next-is-on-same-line
+           (eq next-type 'string-chunk-after-interpolation))
+      (goto-char (get-text-property
+                  (swift-mode:token:start next-token)
+                  'swift-mode:matching-parenthesis))
+      (forward-char 2)
+      (swift-mode:backward-string-chunk)
+      (swift-mode:calculate-indent-after-beginning-of-string-interpolation 0))
+
      ;; Before , on the same line
      ((and next-is-on-same-line (eq next-type '\,))
       (swift-mode:calculate-indent-of-prefix-comma))
@@ -345,6 +374,12 @@ declaration and its offset is `swift-mode:basic-offset'."
        swift-mode:parenthesized-expression-offset
        swift-mode:parenthesized-expression-offset))
 
+     ;; After beginning of a string interpolation
+     ((eq previous-type 'string-chunk-before-interpolation)
+      (goto-char (swift-mode:token:start previous-token))
+      (swift-mode:calculate-indent-after-beginning-of-string-interpolation
+       swift-mode:parenthesized-expression-offset))
+
      ;; Before "in" on the same line
      ((and next-is-on-same-line (equal next-text "in"))
       ;; When it is for-in statement, align with the token after "for":
@@ -934,7 +969,8 @@ This is also known as Utrecht-style in the Haskell 
community."
   "Return indentation after comma.
 
 Assuming the cursor is on the comma."
-  (swift-mode:align-with-next-token (swift-mode:find-parent-of-list-element 
nil)))
+  (swift-mode:align-with-next-token
+   (swift-mode:find-parent-of-list-element nil)))
 
 (defun swift-mode:find-parent-of-list-element (&optional utrecht-sytle)
   "Move point backward to the parent token of the comma under the cursor.
@@ -1149,6 +1185,19 @@ comma at eol."
      (t
       parent))))
 
+(defun swift-mode:calculate-indent-after-beginning-of-string-interpolation
+    (offset)
+  "Return indentation with OFFSET after the beginning of a string 
interpolation.
+
+Assuming the cursor is before the string chunk."
+  (let ((pos (point)))
+    (swift-mode:forward-string-chunk)
+    (if (< pos (line-beginning-position))
+        (progn
+          (back-to-indentation)
+          (swift-mode:indentation (point) offset))
+      (goto-char pos)
+      (swift-mode:calculate-indent-of-expression offset offset))))
 
 (defun swift-mode:backward-sexps-until (token-types
                                         &optional
@@ -1531,9 +1580,9 @@ See `indent-new-comment-line' for SOFT."
 
 (defun swift-mode:post-self-insert ()
   "Miscellaneous logic for electric indentation."
-  ;; Indents electrically and insert a space when "*" is inserted at the
-  ;; beginning of a line inside a multiline comment.
   (cond
+   ;; Indents electrically and insert a space when "*" is inserted at the
+   ;; beginning of a line inside a multiline comment.
    ((and
      (= last-command-event ?*)
      (nth 4 (syntax-ppss))
@@ -1556,7 +1605,14 @@ See `indent-new-comment-line' for SOFT."
           (swift-mode:incomplete-comment-p)))))
     (backward-char)
     (delete-horizontal-space)
-    (forward-char))))
+    (forward-char))
+
+   ;; Indents electrically when ")" is inserted at bol as the end of a string
+   ;; interpolation.
+   ((and
+     (= last-command-event ?\))
+     (save-excursion (backward-char) (skip-syntax-backward " ") (bolp)))
+    (indent-according-to-mode))))
 
 (defun swift-mode:highlight-anchor (indentation)
   "Highlight the anchor point of the INDENTATION."
diff --git a/swift-mode-lexer.el b/swift-mode-lexer.el
index 4f50177..a3b0369 100644
--- a/swift-mode-lexer.el
+++ b/swift-mode-lexer.el
@@ -92,6 +92,8 @@ END is the point after the token."
 ;; - 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)
+;; - string-chunk-after-interpolation (part of a string ending with ")")
+;; - string-chunk-before-interpolation (part of a string ending with "\\(")
 ;; - outside-of-buffer
 ;;
 ;; Additionaly, `swift-mode:backward-token-or-list' may return a parenthesized
@@ -152,12 +154,30 @@ END is the point after the token."
 
     table))
 
+(defun swift-mode:syntax-propertize-extend-region (start end)
+  "Return region to be propertized.
+The returned region contains the region (START . END).
+If the region is not modified, return nil.
+Intended for `syntax-propertize-extend-region-functions'."
+  (when (get-text-property (max (point-min) (1- start)) 'swift-mode:string)
+    (cons (or (previous-single-property-change
+               (max (point-min) (1- start))
+               'swift-mode:string)
+              (point-min))
+          end)))
+
 (defun swift-mode:syntax-propertize (start end)
-  "Update text properties for multiline strings.
-Mark the beginning of and the end of multiline strings as general string
-delimiters between position START and END.
-Intended for `syntax-propertize-function'"
-  (remove-text-properties start end '(syntax-table nil))
+  "Update text properties for strings.
+Mark the beginning of and the end of single-line/multiline strings between
+the position START and END as general string delimiters.
+Intended for `syntax-propertize-function'."
+  (remove-text-properties start end
+                          '(syntax-table
+                            nil
+                            swift-mode:string
+                            nil
+                            swift-mode:matching-parenthesis
+                            nil))
   (let* ((parser-state (syntax-ppss start))
          (inside-string (nth 3 parser-state))
          (comment-nesting (nth 4 parser-state))
@@ -173,50 +193,112 @@ Intended for `syntax-propertize-function'"
       (goto-char comment-beginning-position)
       (forward-comment (point-max)))))
 
-  (while (search-forward-regexp
-          (mapconcat #'regexp-quote '("\"\"\"" "\"" "//" "/*") "\\|")
-          end t)
-    (cond
-     ((equal "\"\"\"" (match-string-no-properties 0))
-      (put-text-property (match-beginning 0) (1+ (match-beginning 0))
-                         'syntax-table
-                         (string-to-syntax "|"))
-      (swift-mode:syntax-propertize:end-of-multiline-string end))
-
-     ((equal "\"" (match-string-no-properties 0))
-      (swift-mode:syntax-propertize:end-of-single-line-string end))
-
-     ((equal "//" (match-string-no-properties 0))
-      (goto-char (match-beginning 0))
-      (forward-comment (point-max)))
+  (swift-mode:syntax-propertize:scan end 0))
+
+(defun swift-mode:syntax-propertize:scan (end nesting-level)
+  "Update text properties for strings.
+Mark the beginning of and the end of single-line/multiline strings between
+the current position and END as general string delimiters.
+Assuming the cursor is not on strings nor comments.
+If NESTING-LEVEL is non-zero, nesting of parentheses are tracked and the scan
+stops where the level becomes zero."
+  (let ((found-matching-parenthesis nil)
+        (pattern (mapconcat #'regexp-quote
+                            '("\"\"\"" "\"" "//" "/*" "(" ")")
+                            "\\|")))
+    (while (and (not found-matching-parenthesis)
+                (search-forward-regexp pattern end t))
+      (cond
+       ((equal "\"\"\"" (match-string-no-properties 0))
+        (put-text-property (match-beginning 0) (1+ (match-beginning 0))
+                           'syntax-table
+                           (string-to-syntax "|"))
+        (let ((start (match-beginning 0)))
+          (swift-mode:syntax-propertize:end-of-multiline-string end)
+          (put-text-property start (point) 'swift-mode:string t)))
 
-     ((equal "/*" (match-string-no-properties 0))
-      (goto-char (match-beginning 0))
-      (forward-comment (point-max))))))
+       ((equal "\"" (match-string-no-properties 0))
+        (put-text-property (match-beginning 0) (1+ (match-beginning 0))
+                           'syntax-table
+                           (string-to-syntax "|"))
+        (let ((start (match-beginning 0)))
+          (swift-mode:syntax-propertize:end-of-single-line-string end)
+          (put-text-property start (point) 'swift-mode:string t)))
+
+       ((equal "//" (match-string-no-properties 0))
+        (goto-char (match-beginning 0))
+        (forward-comment (point-max)))
+
+       ((equal "/*" (match-string-no-properties 0))
+        (goto-char (match-beginning 0))
+        (forward-comment (point-max)))
+
+       ((and (equal "(" (match-string-no-properties 0))
+             (/= nesting-level 0))
+        (setq nesting-level (1+ nesting-level)))
+
+       ((and (equal ")" (match-string-no-properties 0))
+             (/= nesting-level 0))
+        (setq nesting-level (1- nesting-level))
+        (when (= nesting-level 0)
+          (setq found-matching-parenthesis t)))))
+    (unless found-matching-parenthesis
+      (goto-char end))
+    found-matching-parenthesis))
 
 (defun swift-mode:syntax-propertize:end-of-multiline-string (end)
   "Move point to the end of multiline string.
 Assuming the cursor is on a multiline string.
 If the end of the string found, put a text property on it.
-If the multiline string go beyond END, stop there."
-  ;; FIXME string interpolation
-  (if (search-forward "\"\"\"" end t)
-      (if (swift-mode:escaped-p (match-beginning 0))
-          (swift-mode:syntax-propertize:end-of-multiline-string end)
+If the string go beyond END, stop there."
+  (swift-mode:syntax-propertize:end-of-string end "\"\"\""))
+
+(defun swift-mode:syntax-propertize:end-of-single-line-string (end)
+  "Move point to the end of single-line string.
+Assuming the cursor is on a single-line string.
+If the string go beyond END, stop there."
+  (swift-mode:syntax-propertize:end-of-string end "\""))
+
+(defun swift-mode:syntax-propertize:end-of-string (end quotation)
+  "Move point to the end of single-line/multiline string.
+Assuming the cursor is on a string.
+If the string go beyond END, stop there.
+The string should be terminated with QUOTATION."
+  (if (search-forward-regexp (concat (regexp-quote quotation) "\\|(") end t)
+      (cond
+       ((and (equal quotation (match-string-no-properties 0))
+             (not (swift-mode:escaped-p (match-beginning 0))))
         (put-text-property (1- (point)) (point)
                            'syntax-table
                            (string-to-syntax "|")))
+       ((and (equal "(" (match-string-no-properties 0))
+             (swift-mode:escaped-p (match-beginning 0)))
+        ;; Found string interpolation. Skips the expression.
+        ;; We cannot use `scan-sexps' because multiline strings are not yet
+        ;; propertized.
+        (let ((start (- (point) 2)))
+          (put-text-property start (1+ start)
+                             'syntax-table
+                             (string-to-syntax "w"))
+          (put-text-property (1+ start) (+ 2 start)
+                             'syntax-table
+                             (string-to-syntax "|"))
+          (when (swift-mode:syntax-propertize:scan end 1)
+            ;; Found matching parenthesis. Going further.
+            (put-text-property (1- (point)) (point)
+                               'syntax-table
+                               (string-to-syntax "|"))
+            (put-text-property (1- (point)) (point)
+                               'swift-mode:matching-parenthesis
+                               start)
+            (put-text-property start (+ 2 start)
+                               'swift-mode:matching-parenthesis
+                               (1- (point)))
+            (swift-mode:syntax-propertize:end-of-string end quotation))))
+       (t
+        (swift-mode:syntax-propertize:end-of-string end quotation)))
     (goto-char end)))
 
-(defun swift-mode:syntax-propertize:end-of-single-line-string (end)
-  "Move point to the end of string.
-Assuming the cursor is on a string.
-If the multiline string go beyond END, stop there."
-  ;; FIXME string interpolation
-  (if (search-forward "\"" end t)
-      (when (swift-mode:escaped-p (match-beginning 0))
-        (swift-mode:syntax-propertize:end-of-single-line-string end))
-    (goto-char end)))
 
 (defun swift-mode:escaped-p (position)
   "Return t if the POSITION is proceeded by odd number of backslashes.
@@ -239,7 +321,7 @@ Return nil otherwise."
        (next-token (save-excursion
                      (swift-mode:backquote-identifier-if-after-dot
                       (swift-mode:forward-token-simple)))))
-    ;; If the cursor is on the empty line, pretend an identifier is the line.
+    ;; If the cursor is on the empty line, pretend an identifier is on the 
line.
     (when (and
            (< (swift-mode:token:end previous-token) (line-beginning-position))
            (< (line-end-position) (swift-mode:token:start next-token)))
@@ -361,7 +443,7 @@ Return nil otherwise."
                      (swift-mode:backward-token-simple)))
                    "repeat"))))))
 
-     ;; Inserts implicit around else
+     ;; Inserts implicit semicolon around else
      ((or
        (equal (swift-mode:token:text previous-token) "else")
        (equal (swift-mode:token:text next-token) "else"))
@@ -454,6 +536,12 @@ Return nil otherwise."
     ;;   )
     ((eq (swift-mode:token:type next-token) '\() t)
 
+    ;; Suppress implicit semicolon after the beginning of a string
+    ;; interpolation.
+    ((eq (swift-mode:token:type previous-token)
+         'string-chunk-before-interpolation)
+     nil)
+
     ;; Otherwise, inserts implicit semicolon.
     (t t))))
 
@@ -662,6 +750,18 @@ This function does not return `implicit-;' or `type-:'."
    ((eobp)
     (swift-mode:token 'outside-of-buffer "" (point) (point)))
 
+   ;; End of string interpolation
+   ((and (eq (char-after) ?\))
+         (equal (get-text-property (point) 'syntax-table)
+                (string-to-syntax "|")))
+    (let ((pos-after-comment (point)))
+      (swift-mode:forward-string-chunk)
+      (swift-mode:token
+       'string-chunk-after-interpolation
+       (buffer-substring-no-properties pos-after-comment (point))
+       pos-after-comment
+       (point))))
+
    ;; Separators and parentheses
    ((memq (char-after) '(?, ?\; ?\{ ?\} ?\[ ?\] ?\( ?\) ?:))
     (forward-char)
@@ -720,10 +820,21 @@ This function does not return `implicit-;' or `type-:'."
       (swift-mode:fix-operator-type
        (swift-mode:token nil text start end))))
 
-   ;; String and backquoted identifer
-   ((memq (char-after) '(?\" ?`))
+   ;; Backquoted identifer
+   ((eq (char-after) ?`)
     (let ((pos-after-comment (point)))
-      (goto-char (scan-sexps (point) 1))
+      (swift-mode:forward-string-chunk)
+      (swift-mode:token
+       'identifier
+       (buffer-substring-no-properties pos-after-comment (point))
+       pos-after-comment
+       (point))))
+
+   ;; String
+   ((eq (char-after) ?\")
+    (let ((pos-after-comment (point)))
+      (forward-char)
+      (swift-mode:end-of-string)
       (swift-mode:token
        'identifier
        (buffer-substring-no-properties pos-after-comment (point))
@@ -850,6 +961,18 @@ This function does not return `implicit-;' or `type-:'."
    ((bobp)
     (swift-mode:token 'outside-of-buffer "" (point) (point)))
 
+   ;; Beginning of string interpolation
+   ((and (eq (char-before) ?\()
+         (equal (get-text-property (1- (point)) 'syntax-table)
+                (string-to-syntax "|")))
+    (let ((pos-before-comment (point)))
+      (swift-mode:backward-string-chunk)
+      (swift-mode:token
+       'string-chunk-before-interpolation
+       (buffer-substring-no-properties (point) pos-before-comment)
+       (point)
+       pos-before-comment)))
+
    ;; Attribute or close-parenthesis
    ((eq (char-before) ?\))
      (let ((pos-before-comment (point)))
@@ -945,10 +1068,21 @@ This function does not return `implicit-;' or `type-:'."
         (swift-mode:fix-operator-type
          (swift-mode:token nil text start end)))))
 
-   ;; String and backquoted identifer
-   ((memq (char-before) '(?\" ?`))
+   ;; Backquoted identifer
+   ((eq (char-before) ?`)
     (let ((pos-before-comment (point)))
-      (goto-char (scan-sexps (point) -1))
+      (swift-mode:backward-string-chunk)
+      (swift-mode:token
+       'identifier
+       (buffer-substring-no-properties (point) pos-before-comment)
+       (point)
+       pos-before-comment)))
+
+   ;; String
+   ((eq (char-before) ?\")
+    (let ((pos-before-comment (point)))
+      (backward-char)
+      (swift-mode:beginning-of-string)
       (swift-mode:token
        'identifier
        (buffer-substring-no-properties (point) pos-before-comment)
@@ -993,6 +1127,49 @@ This function does not return `implicit-;' or `type-:'."
                           (point)
                           (+ (point) (length text)))))))))
 
+(defun swift-mode:forward-string-chunk ()
+  "Skip forward a string chunk.
+A string chunk is a part of single-line/multiline string delimited with
+quotation marks or string interpolations."
+  (condition-case nil
+      (goto-char (scan-sexps (point) 1))
+    (scan-error (goto-char (point-max)))))
+
+(defun swift-mode:backward-string-chunk ()
+  "Skip backward a string chunk.
+A string chunk is a part of single-line/multiline string delimited with
+quotation marks or string interpolations."
+  (condition-case nil
+      (goto-char (scan-sexps (point) -1))
+    (scan-error (goto-char (point-min)))))
+
+(defun swift-mode:beginning-of-string ()
+  "Move point to the beginning of single-line/multiline string.
+Assuming the cursor is on a string."
+  (goto-char (or (nth 8 (syntax-ppss)) (point)))
+  (let (matching-parenthesis)
+    (while (setq matching-parenthesis
+                 (get-text-property
+                  (point)
+                  'swift-mode:matching-parenthesis))
+      (goto-char matching-parenthesis)
+      (goto-char (nth 8 (syntax-ppss))))
+    (point)))
+
+(defun swift-mode:end-of-string ()
+  "Move point to the end of single-line/multiline string.
+Assuming the cursor is on a string."
+  (goto-char (or (nth 8 (syntax-ppss)) (point)))
+  (let (matching-parenthesis)
+    (swift-mode:forward-string-chunk)
+    (while (setq matching-parenthesis
+                 (get-text-property
+                  (1- (point))
+                  'swift-mode:matching-parenthesis))
+      (goto-char matching-parenthesis)
+      (swift-mode:forward-string-chunk)))
+  (point))
+
 (defun swift-mode:goto-non-comment-bol ()
   "Back to the beginning of line that is not inside a comment."
   (beginning-of-line)
diff --git a/swift-mode.el b/swift-mode.el
index 5c80d1e..afbf6a4 100644
--- a/swift-mode.el
+++ b/swift-mode.el
@@ -146,6 +146,9 @@ See `forward-sexp for ARG."
   (setq-local comment-multi-line t)
 
   (setq-local parse-sexp-lookup-properties t)
+  (add-hook 'syntax-propertize-extend-region-functions
+            #'swift-mode:syntax-propertize-extend-region
+            nil t)
   (setq-local syntax-propertize-function #'swift-mode:syntax-propertize)
 
   (setq-local indent-tabs-mode nil)
diff --git a/test/swift-files/strings.swift b/test/swift-files/strings.swift
index a961be5..5774f14 100644
--- a/test/swift-files/strings.swift
+++ b/test/swift-files/strings.swift
@@ -33,4 +33,66 @@ func f() {
         aaa
       """ +
       "abc"
+
+    let x = """
+      aaa
+      \(
+        abc +
+          def(a, b()) +
+          """
+          aaa
+          \(
+            bbb() +
+              ccc()
+          )
+          aaa
+          """
+      )
+      aaa
+      \(
+        foo)
+      aaa
+      \(foo)
+      aaa
+      \(foo
+      )
+      aaa
+      aaa\(
+        foo() +
+          bar()
+      )aaa
+      aaa
+      """ +
+      "abc"
+
+    foo()
+
+    let x = "aaa\(
+      foo() +
+        bar() +
+        "abc"
+    )aaa\(
+      foo() +
+        bar()
+    )aaa"
+
+    let x = ("aaa\(
+               foo() +
+                 bar() +
+                 "abc"
+             )aaa\(
+               foo() +
+                 bar()
+             )aaa")
+
+    let x = (
+      "aaa\(
+        foo() +
+          bar() +
+          "abc"
+      )aaa\(
+        foo() +
+          bar()
+      )aaa"
+    )
 }



reply via email to

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