branch: elpa/swift-mode commit 0b1e5e8ec5c65264690815cd64dd5ae31937d9f3 Author: taku0 <mxxouy6x3m_git...@tatapa.org> Commit: taku0 <mxxouy6x3m_git...@tatapa.org>
Support raw texts https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md --- swift-mode-lexer.el | 121 +++++++++++++++++++++------------- test/swift-files/indent/strings.swift | 63 ++++++++++++++++++ 2 files changed, 137 insertions(+), 47 deletions(-) diff --git a/swift-mode-lexer.el b/swift-mode-lexer.el index ad182a0..eb230da 100644 --- a/swift-mode-lexer.el +++ b/swift-mode-lexer.el @@ -229,10 +229,12 @@ Intended for `syntax-propertize-function'." (let* ((chunk (swift-mode:chunk-after (syntax-ppss start)))) (cond ((swift-mode:chunk:multiline-string-p chunk) - (swift-mode:syntax-propertize:end-of-multiline-string end)) + (swift-mode:syntax-propertize:end-of-string + end "\"\"\"" (swift-mode:chunk:pound-count chunk))) ((swift-mode:chunk:single-line-string-p chunk) - (swift-mode:syntax-propertize:end-of-single-line-string end)) + (swift-mode:syntax-propertize:end-of-string + end "\"" (swift-mode:chunk:pound-count chunk))) ((swift-mode:chunk:comment-p chunk) (goto-char (swift-mode:chunk:start chunk)) @@ -255,21 +257,21 @@ stops where the level becomes zero." (< (point) end) (search-forward-regexp pattern end t)) (cond - ((equal "\"\"\"" (match-string-no-properties 0)) - (put-text-property (match-beginning 0) (1+ (match-beginning 0)) + ((member (match-string-no-properties 0) '("\"\"\"" "\"")) + (let ((start (match-beginning 0)) + (pound-count 0) + (quotation (match-string-no-properties 0))) + (save-excursion + (goto-char start) + (skip-chars-backward "#") + (setq pound-count (- start (point))) + (setq start (point))) + (put-text-property start (1+ start) 'syntax-table (string-to-syntax "|")) - (let ((start (match-beginning 0))) - (swift-mode:syntax-propertize:end-of-multiline-string end) - (put-text-property start (point) 'syntax-multiline t))) - - ((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) 'syntax-multiline t))) + (swift-mode:syntax-propertize:end-of-string + end quotation pound-count) + (put-text-property start (point) 'syntax-multiline t))) ((equal "//" (match-string-no-properties 0)) (goto-char (match-beginning 0)) @@ -292,71 +294,84 @@ stops where the level becomes zero." (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 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) +(defun swift-mode:syntax-propertize:end-of-string (end quotation pound-count) "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." +The string should be terminated with QUOTATION, followed by POUND-COUNT of +pound signs." (if (and (< (point) end) (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)))) + (not (swift-mode:escaped-p (match-beginning 0) pound-count)) + (progn + (skip-chars-forward "#" (min end (+ (point) pound-count))) + (= (- (point) (match-end 0)) pound-count))) (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))) + (swift-mode:escaped-p (match-beginning 0) pound-count)) ;; Found an interpolated expression. Skips the expression. ;; We cannot use `scan-sexps' because multiline strings are not yet ;; propertized. - (let ((start (- (point) 2))) + (let ((pos-after-open-paren (point)) + (start + (save-excursion + (backward-char) ;; ( + (skip-chars-backward "#") + (backward-char) ;; \ + (point)))) + ;; Declares the backslash is not a escape-syntax characters. (put-text-property start (1+ start) 'syntax-table (string-to-syntax "w")) - (put-text-property (1+ start) (+ 2 start) + ;; Declares the open parentheses is a generic string delimiter. + (put-text-property (1- pos-after-open-paren) pos-after-open-paren 'syntax-table (string-to-syntax "|")) (when (swift-mode:syntax-propertize:scan end 1) ;; Found the matching parenthesis. Going further. + ;; Declares the close parentheses is a generic string delimiter. (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax "|")) + ;; Records the positions. (put-text-property (1- (point)) (point) 'swift-mode:matching-parenthesis start) - (put-text-property start (+ 2 start) + (put-text-property start pos-after-open-paren 'swift-mode:matching-parenthesis (1- (point))) - (swift-mode:syntax-propertize:end-of-string end quotation)))) + (swift-mode:syntax-propertize:end-of-string + end quotation pound-count)))) (t - (swift-mode:syntax-propertize:end-of-string end quotation))) + (swift-mode:syntax-propertize:end-of-string end quotation pound-count))) (goto-char end))) -(defun swift-mode:escaped-p (position) - "Return t if the POSITION is proceeded by odd number of backslashes. -Return nil otherwise." +(defun swift-mode:escaped-p (position pound-count) + "Return t if the POSITION in a string is escaped. + +A position is escaped if it is proceeded by POUND-COUNT or more of pound signs +and odd number of backslashes. +Return nil otherwise." ;; FIXME pound-count (let ((p position) - (count 0)) - (while (eq (char-before p) ?\\) - (setq count (1+ count)) + (backslash-count 0)) + (while (eq (char-before p) ?#) (setq p (1- p))) - (= (mod count 2) 1))) + (and + ;; While it is a syntax error to have extra pound signs, we allow them + ;; here to prevent corruption. + (<= pound-count (- position p)) + (progn + (while (eq (char-before p) ?\\) + (setq backslash-count (1+ backslash-count)) + (setq p (1- p))) + (= (mod backslash-count 2) 1))))) ;;; Lexers @@ -911,8 +926,9 @@ This function does not return `implicit-;' or `type-:'." (point)))) ;; String - ((eq (char-after) ?\") + ((looking-at "#*\"") (let ((pos-after-comment (point))) + (skip-chars-forward "#") (forward-char) (swift-mode:end-of-string) (swift-mode:token @@ -1160,8 +1176,11 @@ This function does not return `implicit-;' or `type-:'." pos-before-comment))) ;; String - ((eq (char-before) ?\") + ((save-excursion + (skip-chars-backward "#") + (eq (char-before) ?\")) (let ((pos-before-comment (point))) + (skip-chars-backward "#") (backward-char) (swift-mode:beginning-of-string) (swift-mode:token @@ -1325,6 +1344,14 @@ If this line ends with a single-line comment, goto just before the comment." "Return non-nil if the CHUNK is a multiline string." (eq (swift-mode:chunk:type chunk) 'multiline-string)) +(defun swift-mode:chunk:pound-count (chunk) + "Return the number of pound signs before the start position of the CHUNK." + (save-excursion + (goto-char (swift-mode:chunk:start chunk)) + (swift-mode:beginning-of-string) + (skip-chars-backward "#") + (- (swift-mode:chunk:start chunk) (point)))) + (defun swift-mode:chunk-after (&optional parser-state) "Return the chunk at the cursor. @@ -1342,7 +1369,7 @@ If PARSER-STATE is given, it is used instead of (syntax-ppss)." ;; string delimiters. So (nth 3 parser-state) may be t even for ;; single-line string delimiters. (if (save-excursion (goto-char (nth 8 parser-state)) - (looking-at "\"\"\"")) + (looking-at "#*\"\"\"")) (swift-mode:chunk 'multiline-string (nth 8 parser-state)) (swift-mode:chunk 'single-line-string (nth 8 parser-state)))) ((eq (nth 4 parser-state) t) diff --git a/test/swift-files/indent/strings.swift b/test/swift-files/indent/strings.swift index 1ca635d..862dde7 100644 --- a/test/swift-files/indent/strings.swift +++ b/test/swift-files/indent/strings.swift @@ -98,4 +98,67 @@ func f() { bar() )aaa" ) + + let x = """ + abc \( + 1 + + ( + 1 + + 1 + ) + + 1 + ) + def ghi + \""" + aaa + """ + + let x = #""" + abc \( + 1 + + ( + 1 + 1 + ) + + 1 + ) + abc \#( + 1 + + ( + 1 + 1 + ) + + 1 + ) + def ghi + \""" + \#"""# + """ + aaa + """# + + let x = ##""" + abc \( + 1 + + ( + 1 + 1 + ) + + 1 + ) + abc \#( + 1 + + ( + 1 + 1 + ) + + 1 + ) + def ghi + \""" + \#"""# + """ + aaa + """## + + let x = "abc\( 1 + (2 + 3) ) \" a " + let x = #"abc\( 1 + (2 + 3) ) \#( 1 + (2 + 3) ) \" \#"# " a \"# + let x = ##"abc\( 1 + (2 + 3) ) \#( 1 + (2 + 3) ) \" \#"# " a \"## + let x = 1 }