swift3-mode-2.1.1/ 0000755 0000000 0000000 00000000000 12767506611 012055 5 ustar 00 0000000 0000000 swift3-mode-2.1.1/swift3-mode-beginning-of-defun.el 0000644 0000000 0000000 00000013010 12767506611 020172 0 ustar 00 0000000 0000000 ;;; swift3-mode-beginning-of-defun.el --- Major-mode for Apple's Swift programming language, beginning/end-of-defun. -*- lexical-binding: t -*-
;; Copyright (C) 2014-2016 taku0
;; Authors: taku0 (http://github.com/taku0)
;;
;; Version: 2.1.1
;; Package-Requires: ((emacs "24.4"))
;; Keywords: languages swift
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;;; Commentary:
;; beginning-of-defun and end-of-defun
;;; Code:
(require 'swift3-mode-lexer)
(require 'swift3-mode-indent)
(defun swift3-mode:beginning-of-defun (&optional arg)
"Move backward to the beginning of a defun."
(interactive)
(setq arg (or arg 1))
(let (result
pos)
(if (<= 0 arg)
(while (< 0 arg)
(setq result (swift3-mode:beginning-of-defun-1
#'swift3-mode:backward-token-or-list))
(setq arg (1- arg)))
(while (< arg 0)
(setq pos (point))
(swift3-mode:beginning-of-statement)
(when (<= (point) pos)
(while (not
(memq
(swift3-mode:token:type (swift3-mode:forward-token-or-list))
'({} outside-of-buffer)))))
(setq result (swift3-mode:beginning-of-defun-1
(lambda ()
(prog1 (swift3-mode:forward-token-or-list)
(forward-comment (point-max))))))
(setq arg (1+ arg))))
result))
(defun swift3-mode:beginning-of-defun-1 (next-token-function)
(catch 'swift3-mode:found-defun
(while (not (eq (swift3-mode:token:type (funcall next-token-function))
'outside-of-buffer))
(when (save-excursion (swift3-mode:is-point-before-body-of-defun))
(swift3-mode:beginning-of-statement)
(throw 'swift3-mode:found-defun t)))
nil))
(defun swift3-mode:is-point-before-body-of-defun ()
(and
(= (char-after) ?{)
(progn
;; Skips implicit ;
(forward-comment (- (point)))
(let* ((defun-keywords '("class" "struct" "protocol" "enum" "extension"
"func" "operator" "var" "get" "set" "willSet"
"didSet" "deinit" "subscript"))
(previous-token (swift3-mode:backward-token-or-list))
(previous-type (swift3-mode:token:type previous-token))
(previous-text (swift3-mode:token:text previous-token)))
(while (and
(not (eq previous-type 'outside-of-buffer))
(not (memq previous-type swift3-mode:statement-parent-tokens))
(not (member previous-text swift3-mode:statement-parent-tokens))
(not (member previous-text defun-keywords))
(not (and (equal previous-text "init")
(save-excursion
;; Excludes self.init() {}
(not
(equal
(swift3-mode:token:text (swift3-mode:backward-token))
"."))))))
(setq previous-token (swift3-mode:backward-token-or-list))
(setq previous-type (swift3-mode:token:type previous-token))
(setq previous-text (swift3-mode:token:text previous-token)))
(unless (bobp)
(swift3-mode:forward-token-simple))
(or (equal previous-text "init")
(member previous-text defun-keywords))))))
(defun swift3-mode:beginning-of-statement ()
"Move backward to the beginning of a statement or some kind of expression.
Intended for internal use."
(let ((parent (swift3-mode:backward-sexps-until
swift3-mode:statement-parent-tokens)))
(forward-comment (point-max))
(swift3-mode:goto-non-comment-bol)
(when (< (point) (swift3-mode:token:end parent))
(goto-char (swift3-mode:token:end parent)))
(swift3-mode:skip-whitespaces)))
(defun swift3-mode:end-of-defun (&optional arg)
"Move forward to the end of a defun."
(interactive)
(setq arg (or arg 1))
(let (result)
(if (<= 0 arg)
(while (< 0 arg)
(setq result (swift3-mode:end-of-defun-1
#'swift3-mode:forward-token-or-list
))
(setq arg (1- arg)))
(while (< arg 0)
(setq result (swift3-mode:end-of-defun-1
(lambda ()
(prog1 (swift3-mode:backward-token-or-list)
(forward-comment (- (point)))))))
(setq arg (1+ arg))))
result))
(defun swift3-mode:end-of-defun-1 (next-token-function)
(catch 'swift3-mode:found-defun
(while (not (eq (swift3-mode:token:type (funcall next-token-function))
'outside-of-buffer))
(when (and (= (char-before) ?})
(save-excursion
(backward-list)
(swift3-mode:is-point-before-body-of-defun)))
(throw 'swift3-mode:found-defun t)))
nil))
(provide 'swift3-mode-beginning-of-defun)
;;; swift3-mode-beginning-of-defun.el ends here
swift3-mode-2.1.1/swift3-mode-font-lock.el 0000644 0000000 0000000 00000017003 12767506611 016433 0 ustar 00 0000000 0000000 ;;; swift3-mode-font-lock.el --- Major-mode for Apple's Swift programming language, Font Locks. -*- lexical-binding: t -*-
;; Copyright (C) 2014-2016 taku0, Chris Barrett, Bozhidar Batsov, Arthur Evstifeev
;; Authors: taku0 (http://github.com/taku0)
;; Chris Barrett
;; Bozhidar Batsov
;; Arthur Evstifeev
;;
;; Version: 2.1.1
;; Package-Requires: ((emacs "24.4"))
;; Keywords: languages swift
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;;; Commentary:
;; Routines for Font Locks
;;; Code:
(defun swift3-mode:function-name-pos-p (pos)
"Return t if POS is at just before afunction name."
(save-excursion
(save-match-data
(goto-char pos)
(forward-comment (- (point)))
(skip-syntax-backward "w_")
(looking-at "\\<\\(func\\|enum\\|struct\\|class\\|protocol\\|extension\\)\\>"))))
(defun swift3-mode:font-lock-match-function-names (limit)
"Move the cursor just after a function name or others.
Others includes enum, struct, class, protocol name.
Set `match-data', and return t if a function name found before position LIMIT.
Return nil otherwise."
(and
(re-search-forward "\\<\\(\\sw\\|\\s_\\)+\\>" limit t)
(or
(swift3-mode:function-name-pos-p (match-beginning 0))
(swift3-mode:font-lock-match-function-names limit))))
(defconst swift3-mode:font-lock-keywords
'(
;; Attributes
"@\\(\\sw\\|\\s_\\)*"
;; Constants
("\\" . font-lock-constant-face)
("\\" . font-lock-constant-face)
("\\" . font-lock-constant-face)
;; Keywords
;; https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID410
;; Keywords that begin with a number sign (#)
("#available\\>" . font-lock-preprocessor-face)
("#column\\>" . font-lock-preprocessor-face)
("#elseif\\>" . font-lock-preprocessor-face)
("#else\\>" . font-lock-preprocessor-face)
("#endif\\>" . font-lock-preprocessor-face)
("#file\\>" . font-lock-preprocessor-face)
("#function\\>" . font-lock-preprocessor-face)
("#if\\>" . font-lock-preprocessor-face)
("#line\\>" . font-lock-preprocessor-face)
("#selector\\>" . font-lock-preprocessor-face)
;; Keywords used in declarations
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
;; Keywords used in statements
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
;; Keywords used in expressions and types (without true, false, and keywords begin with a number sign)
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
;; Keywords reserved in particular contexts
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
"\\"
;; Standard library functions
;; https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_StandardLibrary_Functions/index.html#//apple_ref/doc/uid/TP40016052
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
;; keywords for build configuration statements
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
("\\" . font-lock-builtin-face)
(swift3-mode:font-lock-match-function-names . font-lock-function-name-face)
)
"Swift mode keywords for Font Lock.")
(provide 'swift3-mode-font-lock)
;;; swift3-mode-font-lock.el ends here
swift3-mode-2.1.1/swift3-mode-indent.el 0000644 0000000 0000000 00000135500 12767506611 016023 0 ustar 00 0000000 0000000 ;;; swift3-mode-indent.el --- Major-mode for Apple's Swift programming language, indentation. -*- lexical-binding: t -*-
;; Copyright (C) 2014-2016 taku0, Chris Barrett, Bozhidar Batsov, Arthur Evstifeev
;; Authors: taku0 (http://github.com/taku0)
;; Chris Barrett
;; Bozhidar Batsov
;; Arthur Evstifeev
;;
;; Version: 2.1.1
;; Package-Requires: ((emacs "24.4"))
;; Keywords: languages swift
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;;; Commentary:
;; Routines for Indentation
;;; Code:
(require 'swift3-mode-lexer)
;;;###autoload
(defcustom swift3-mode:basic-offset 4
"Amount of indentation for block contents."
:type 'integer
:group 'swift3
:safe 'integerp)
;;;###autoload
(defcustom swift3-mode:parenthesized-expression-offset 2
"Amount of indentation inside parentheses and square brackets."
:type 'integer
:group 'swift3
:safe 'integerp)
;;;###autoload
(defcustom swift3-mode:multiline-statement-offset 2
"Amount of indentation for continuations of expressions."
:type 'integer
:group 'swift3
:safe 'integerp)
;;;###autoload
(defcustom swift3-mode:switch-case-offset 0
"Amount of indentation for case labels in switch statements."
:type 'integer
:group 'swift3
:safe 'integerp)
;;;###autoload
(defcustom swift3-mode:insert-space-after-asterisk-in-comment t
"Automatically insert a space after asterisk in comment if non-nil."
:type 'boolean
:group 'swift3
:safe 'booleanp)
;;;###autoload
(defcustom swift3-mode:auto-close-multiline-comment t
"If non-nil, `indent-new-comment-line' automatically close multiline comment."
:type 'boolean
:group 'swift3
:safe 'booleanp)
;;;###autoload
(defcustom swift3-mode:fix-comment-close t
"Fix \"* /\" in incomplete multiline comment to \"*/\" if non-nil."
:type 'boolean
:group 'swift3
:safe 'booleanp)
(defconst swift3-mode:statement-parent-tokens
'(implicit-\; \; case-: { \( \[ anonymous-function-parameter-in)
"Parent tokens for statements.")
(defconst swift3-mode:expression-parent-tokens
(append swift3-mode:statement-parent-tokens
'(\, < "where" "if" "guard" "while"))
"Parent tokens for expressions.")
(defun swift3-mode:indent-line ()
(let ((indent (save-excursion (swift3-mode:calculate-indent)))
(current-indent
(save-excursion (back-to-indentation) (current-column))))
(if (<= (current-column) current-indent)
;; The cursor is on the left margin. Moving to the new indent.
(indent-line-to indent)
;; Keeps current relative position.
(save-excursion (indent-line-to indent)))))
(defun swift3-mode:calculate-indent ()
(back-to-indentation)
(if (nth 4 (syntax-ppss))
;; If the 4th element of `(syntax-ppss)' is non-nil, the cursor is on
;; the 2nd or following lines of a multiline comment, because:
;;
;; - The 4th element of `(syntax-ppss)' is nil on the comment starter.
;; - We have called `back-to-indentation`.
(swift3-mode:calculate-indent-of-multiline-comment)
(swift3-mode:calculate-indent-of-code)))
(defun swift3-mode:calculate-indent-of-multiline-comment ()
(back-to-indentation)
(let ((comment-beginning-position (nth 8 (syntax-ppss))))
(forward-line -1)
(back-to-indentation)
(if (<= (point) comment-beginning-position)
;; The cursor was on the 2nd line of the comment, so aligns with
;; the asterisk of the comment starter.
(progn
(goto-char comment-beginning-position)
(forward-char)
(current-column))
;; The cursor was on the 3rd or following lines of the comment, so aligns
;; with a non-empty preceding line.
(if (eolp)
;; The cursor is on an empty line, so seeks a non-empty-line.
(swift3-mode:calculate-indent-of-multiline-comment)
(current-column)))))
(defun swift3-mode:calculate-indent-of-code ()
(back-to-indentation)
(let* ((previous-token (save-excursion (swift3-mode:backward-token)))
(previous-type (swift3-mode:token:type previous-token))
(previous-text (swift3-mode:token:text previous-token))
(next-token (save-excursion (swift3-mode:forward-token)))
(next-type (swift3-mode:token:type next-token))
(next-text (swift3-mode:token:text next-token))
(next-is-on-same-line
(<= (swift3-mode:token:end next-token) (line-end-position))))
(cond
;; Beginning of the buffer
((eq previous-type 'outside-of-buffer)
0)
;; Before } on the same line
((and next-is-on-same-line (eq next-type '}))
(goto-char (swift3-mode:token:end next-token))
(backward-list)
(swift3-mode:calculate-indent-after-open-curly-brace 0))
;; Before ) or ] on the same line
((and next-is-on-same-line (memq next-type '(\) \])))
(goto-char (swift3-mode:token:end next-token))
(backward-list)
(swift3-mode:calculate-indent-of-expression
swift3-mode:expression-parent-tokens
0
;; Stops scanning at BOL:
;;
;; foo
;; .bar(
;; 1
;; )
;;
;; rather than
;;
;; foo
;; .bar(
;; 1
;; )
'any))
;; Before , on the same line
((and next-is-on-same-line (eq next-type '\,))
(swift3-mode:calculate-indent-of-prefix-comma))
;; After ,
((eq previous-type '\,)
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:calculate-indent-after-comma))
;; 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":
;;
;; for
;; x
;; in
;; foo
;;
;; for x
;; in
;; foo
;;
;; When it is anonymous function, align with the token after {:
;;
;; foo {
;; x
;; in
;; ...
;; }
;;
;;
;; foo { x
;; in
;; ...
;; }
;;
;; foo { [
;; weak self
;; ]
;; (
;; x,
;; y
;; )
;; -> Void
;; in
;; a
;; }
(swift3-mode:calculate-indent-of-expression '("for" {)))
;; Before "case" or "default" on the same line, for switch statement
((and
next-is-on-same-line
(member next-text '("case" "default"))
(save-excursion
(equal (swift3-mode:token:text
(swift3-mode:backward-sexps-until
'("switch" "enum" "for" "while" "if" "guard")))
"switch")))
;; "case" is used for "switch", "enum", "for", "while", "if", and "guard".
;; Only switch statement has special indentation rule.
;;
;; switch foo {
;; default:
;; aaa
;; case A:
;; aaa
;; case B, C, D:
;; aaa
;; case E(1, 2, 3, (4, 5)) where aaa,
;; F(1, 2, 3, (4, 5)) where aaa:
;; aaa
;; case G: print(1); case H: print(2)
;; case I:
;; ...
;; }
;;
;; enum Foo {
;; case A
;; case B, C, D
;; indirect
;; case E(x: Int, y: Int)
;; }
;;
;; enum Foo: Int, A, B {
;; case A = 1, B = 2
;; case C = 3
;; }
;;
;; for
;; case let (x, y) in tuples {
;; }
;; if
;; case let (x, y) = tuple {
;; }
;; while
;; case let (x, y) = tuple {
;; }
;;
;; Searches sibling "case" at the beginning of a line. If found, aligns
;; with it.
;;
;; Otherwise, searches "switch" and aligh with it with offset.
(let ((parent (swift3-mode:backward-sexps-until
'("switch") nil '("case" "default"))))
(if (equal (swift3-mode:token:text parent) "switch")
;; Inside a switch-statement. Aligns with the "switch"
(swift3-mode:calculate-indent-of-expression
swift3-mode:statement-parent-tokens
swift3-mode:switch-case-offset)
;; Other cases. Aligns with the previous case.
(swift3-mode:align-with-current-line))))
;; Before "where" on the same line
((and next-is-on-same-line (equal next-text "where"))
;; switch {
;; case let P(x)
;; where
;; a,
;; let Q(x)
;; where
;; a:
;; aaa
;; }
;;
;; for case (x, y) in xys
;; where
;; aaa {
;; }
;;
;; do {
;; } catch let P(x)
;; where
;; aaa
;;
;; func foo() {
;; }
;;
;; class Foo: AAA,
;; BBB,
;; CCC
;; where
;; ABC {
;; }
(let ((parent (save-excursion (swift3-mode:backward-sexps-until
(append swift3-mode:statement-parent-tokens
'("case"))))))
(swift3-mode:calculate-indent-of-expression
(append swift3-mode:statement-parent-tokens
'(< "case" "catch" "for")
(if (equal (swift3-mode:token:text parent) "case") '(\,) '()))
swift3-mode:multiline-statement-offset)))
;; After {
((eq previous-type '{)
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:calculate-indent-after-open-curly-brace
swift3-mode:basic-offset))
;; After ( or [
((memq previous-type '(\( \[))
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:calculate-indent-of-expression
swift3-mode:expression-parent-tokens
swift3-mode:parenthesized-expression-offset
;; Stops scanning at BOL:
;;
;; foo
;; .bar(
;; 1
;; )
;;
;; rather than
;;
;; foo
;; .bar(
;; 1
;; )
'any
nil
swift3-mode:parenthesized-expression-offset))
;; After "where"
((equal previous-text "where")
;; switch {
;; case let P(x) where
;; A,
;; let Q(x) where
;; A:
;; aaa
;; case let P(x)
;; where
;; a,
;; let Q(x)
;; where
;; a:
;; aaa
;; case let P(x), let Q(x) where
;; a
;; }
;;
;; for case let (x, y) in xys where
;; aaa {
;; }
;;
;; for case let (x, y) in xys
;; where
;; aaa {
;; }
;;
;; do {
;; } catch let P(x) where
;; aaa
;; do {
;; } catch let P(x)
;; where
;; aaa
;;
;;
;;
;; func foo() {
;; }
;;
;; func foo() {
;; }
;;
;; class Foo A,
;; B,
;; C where
;; ABC {
;; }
;;
;; class Foo: A,
;; B,
;; C
;; where
;; ABC {
;; }
(goto-char (swift3-mode:token:start previous-token))
(if (swift3-mode:bol-other-than-comments-p)
(swift3-mode:align-with-current-line
swift3-mode:multiline-statement-offset)
(let ((parent (save-excursion
(swift3-mode:backward-sexps-until
(append swift3-mode:statement-parent-tokens
'("case"))))))
(swift3-mode:calculate-indent-of-expression
(append swift3-mode:statement-parent-tokens
'(< "case" "catch" "for")
(if (equal (swift3-mode:token:text parent) "case") '(\,) '()))
swift3-mode:multiline-statement-offset))))
;; After implicit-\; or ;
((memq previous-type '(implicit-\; \;))
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:calculate-indent-of-expression
(remove '\; (remove 'implicit-\; swift3-mode:statement-parent-tokens))
0
'(implicit-\; \;)))
;; After "in" for anonymous function parameters
((eq previous-type 'anonymous-function-parameter-in)
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:backward-sexps-until '({))
(swift3-mode:calculate-indent-after-open-curly-brace
swift3-mode:basic-offset))
;; After "in" for "for" statements
((equal previous-text "in")
;; Aligns with "in" if it is at the start of the line:
;;
;; for
;; x
;; in
;; foo
;;
;; for x
;; in
;; foo
;;
;; Otherwise, aligns with the next token of the "for".
;;
;; for x in
;; foo
;;
;; for
;; x in
;; foo
(goto-char (swift3-mode:token:start previous-token))
(if (swift3-mode:bol-other-than-comments-p)
(swift3-mode:align-with-current-line)
(let ((parent (swift3-mode:backward-sexps-until '("for" {))))
(swift3-mode:align-with-next-token parent))))
;; After case ... : or default:
((eq previous-type 'case-:)
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:calculate-indent-of-expression
swift3-mode:statement-parent-tokens
swift3-mode:basic-offset))
;; Before ; on the same line
((and next-is-on-same-line (eq next-type '\;))
(swift3-mode:calculate-indent-of-expression
(remove '\; (remove 'implicit-\; swift3-mode:statement-parent-tokens))
0
'(implicit-\; \;)))
;; After if, guard, while
((member previous-text '("if" "guard" "while"))
(swift3-mode:calculate-indent-of-expression
swift3-mode:statement-parent-tokens
swift3-mode:multiline-statement-offset))
;; After attributes at the beginning of a statement, without arguments
((and
(string-prefix-p "@" previous-text)
(memq (save-excursion
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:token:type (swift3-mode:backward-token)))
swift3-mode:statement-parent-tokens))
;; Aligns with the attribute.
(goto-char (swift3-mode:token:start previous-token))
(swift3-mode:align-with-next-token (swift3-mode:backward-token)))
;; After attributes at the beginning of a statement, with arguments
((and
(eq previous-type '\))
(save-excursion
(backward-list)
(and
(string-prefix-p
"@"
(swift3-mode:token:text (swift3-mode:backward-token)))
(memq (swift3-mode:token:type (swift3-mode:backward-token))
swift3-mode:statement-parent-tokens))))
(backward-list)
(swift3-mode:backward-token)
(swift3-mode:align-with-next-token (swift3-mode:backward-token)))
;; Otherwise, it is continuation of the previous line
(t
(goto-char (swift3-mode:token:end previous-token))
(swift3-mode:backward-token-or-list)
(swift3-mode:calculate-indent-of-expression
swift3-mode:expression-parent-tokens
swift3-mode:multiline-statement-offset
'any)))))
(defun swift3-mode:calculate-indent-of-expression
(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.
If OFFSET is omitted, it is assumed to be 0.
PARENTS is a list of token types that precedes an expression or a statement.
See `swift3-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
BOL-OFFSET.
If scanning stops at STOP-AT-BOL-TOKEN-TYPES, align with that token with
BOL-OFFSET.
If STOP-AT-BOL-TOKEN-TYPES is the symbol `any', the cursor is assumed to be
on the previous line."
(save-excursion
(let* ((parent (swift3-mode:backward-sexps-until
parents
stop-at-eol-token-types
stop-at-bol-token-types))
(parent-end (swift3-mode:token:end parent))
(stopped-at-parent
(or (memq (swift3-mode:token:type parent) parents)
(member (swift3-mode:token:text parent) parents)
(eq (swift3-mode:token:type parent) 'outside-of-buffer)))
(stopped-at-eol
(and
(not stopped-at-parent)
stop-at-eol-token-types
(or
(eq stop-at-eol-token-types 'any)
(memq (swift3-mode:token:type parent)
stop-at-eol-token-types)
(member (swift3-mode:token:text parent)
stop-at-eol-token-types)))))
(when (or stopped-at-parent stopped-at-eol)
(goto-char parent-end)
(forward-comment (point-max)))
;; Now, the cursor is at the first token of the expression.
(if stopped-at-parent
;; The cursor is at the start of the entire expression.
;; Aligns with the start of the expression with offset.
(swift3-mode:align-with-next-token parent offset)
;; The cursor is at the middle of the expression.
;; Aligns with this line with bol-offset.
(swift3-mode:align-with-current-line bol-offset)))))
(defun swift3-mode:calculate-indent-after-open-curly-brace (offset)
"Return indentation after open curly braces.
Assuming the cursor is on the open parenthesis.
OFFSET is the offset of the contents.
This function is also used for close-curly-brace."
;; If the statement is multiline expression, aligns with the start of
;; the line on which the open brace is:
;;
;; foo()
;; .then { x in
;; foo()
;; foo()
;; }
;; .then { x in
;; foo()
;; foo()
;; }
;;
;; rather than
;;
;; foo()
;; .then { x in
;; foo()
;; foo()
;; }
;; .then { x in
;; foo()
;; foo()
;; }
;;
;; Otherwise, aligns with the start of the whole statement:
;;
;; for x in
;; xs
;; .foo() {
;; }
;;
;; rather than
;;
;; for x in
;; xs
;; .foo() {
;; }
;;
;; Note that curly brace after binary operator is a part of
;; a multiline expression:
;;
;; for x in
;; xs
;; +++ { x in
;; // this is not the body of the for-statement.
;; } {
;; // The body of the for-statement.
;; }
(let ((pos (point))
next-token
is-declaration-or-control-statement-body)
(if (save-excursion
(eq (swift3-mode:token:type (swift3-mode:backward-token))
'binary-operator))
;; for x in
;; xs
;; +++ { x in
;; // this is not the body of the for statement.
;; } {
;; ...
;; }
(setq is-declaration-or-control-statement-body nil)
(save-excursion
(swift3-mode:backward-sexps-until swift3-mode:statement-parent-tokens)
(swift3-mode:forward-token)
(setq next-token (swift3-mode:forward-token-or-list))
(while (<= (point) pos)
(cond
((member
(swift3-mode:token:text next-token)
'("for" "while" "repeat" "if" "else" "defer" "do" "catch"
"get" "set" "willSet" "didSet" "func" "init" "subscript"
"enum" "struct" "class" "extension" "prefix" "postfix" "infix"
"precedencegroup"))
(setq is-declaration-or-control-statement-body t)
(goto-char (1+ pos)))
((and
(equal (swift3-mode:token:text next-token) "protocol")
(not (equal (swift3-mode:token:text
(save-excursion (swift3-mode:forward-token)))
"<")))
(setq is-declaration-or-control-statement-body t)
(goto-char (1+ pos)))
((equal (swift3-mode:token:text next-token) "var")
;; There are several cases:
;;
;; var foo = bar
;; .then { x in
;; x
;; }
;; .then { x in
;; x
;; }
;;
;; var foo = bar
;; .baz {
;; willSet {
;; ...
;; }
;; }
;;
;; var foo {
;; get {
;; ...
;; }
;; }
;;
;; Note that the 1st and the 2nd cases cannot be distinguished
;; without inspecting the contents of the block.
;; We indent the 2nd case like the 1st case for now.
;; Future implementation may use more sophisticated logic.
(goto-char pos)
(setq is-declaration-or-control-statement-body
(equal (swift3-mode:token:text
(swift3-mode:backward-sexps-until '("var" "=")))
"var"))
(goto-char (1+ pos)))
(t
;; Suppose indenting the A token below.
;;
;; foo {
;; A
;;
;; This function is called on the open curly brace.
;; If the close curly brace doesn't exist,
;; swift3-mode:forward-token-or-list results in
;; "Unbalanced parentheses" error.
;; So if the point is just before the open curly brace,
;; exits immediately.
(forward-comment (point-max))
(if (< (point) pos)
(setq next-token (swift3-mode:forward-token-or-list))
(goto-char (1+ pos))))))))
(swift3-mode:calculate-indent-of-expression
swift3-mode:statement-parent-tokens
offset
(if is-declaration-or-control-statement-body nil 'any)
nil
offset)))
(defun swift3-mode:calculate-indent-of-prefix-comma ()
"Return indentation for prefix comma.
Example:
let x = [ 1
, 2
, 3
]
class Foo: A
, B
, C
case A
, B
, C
:
var x = 1
, y = 2
, z = 3
This is also known as Utrecht-style in the Haskell community."
(let* ((comma-type-and-statement-parent (swift3-mode:detect-type-of-comma))
(comma-type (nth 0 comma-type-and-statement-parent))
(statement-parent (nth 1 comma-type-and-statement-parent)))
(if (eq comma-type 'condition-list)
(swift3-mode:calculate-indent-of-prefix-comma-of-condition-list
statement-parent)
(let* ((parents (swift3-mode:parents-of-comma comma-type))
(parent (swift3-mode:backward-sexps-until parents
nil
'(\,)))
(parent-end (swift3-mode:token:end parent))
(stopped-at-parent
(or (memq (swift3-mode:token:type parent) parents)
(member (swift3-mode:token:text parent) parents)
(eq (swift3-mode:token:type parent) 'outside-of-buffer))))
(if stopped-at-parent
(progn
;; Aligns with the end of the parent.
(goto-char parent-end)
(backward-char)
(current-column))
;; Aligns with the previous comma.
(swift3-mode:align-with-current-line))))))
(defun swift3-mode:calculate-indent-of-prefix-comma-of-condition-list
(statement-parent)
;; The comma is in a condition-list but not in an enum-case-pattern-list.
;;
;; Example:
;;
;; if case let P(x)
;; , let Q(x) // comma for enum-case-pattern-list
;; , let R(x) = a // comma for enum-case-pattern-list
;; , let x = x // comma for condition-list
;; , foo == bar // comma for condition-list
;;
;; We scan from beginning of the statement and remembers last anchor token,
;; i.e. "if", "guard", "while", or comma at the beginning of the line.
(let ((pos (point))
next-token
(anchor statement-parent)
in-case-pattern-list)
(goto-char (swift3-mode:token:end statement-parent))
(setq next-token (swift3-mode:forward-token-or-list))
(while (< (point) pos)
(cond
((equal (swift3-mode:token:text next-token) "case")
(setq in-case-pattern-list t))
((equal (swift3-mode:token:text next-token) "=")
(setq in-case-pattern-list nil))
((member (swift3-mode:token:text next-token) '("if" "guard" "while"))
(setq anchor next-token))
((and (not in-case-pattern-list)
(eq (swift3-mode:token:type next-token) '\,)
(save-excursion
(goto-char (swift3-mode:token:start next-token))
(swift3-mode:bol-other-than-comments-p)))
(setq anchor next-token)))
(setq next-token (swift3-mode:forward-token-or-list)))
(if (eq (swift3-mode:token:type anchor) '\,)
;; Aligns with the previous comma.
(progn
(goto-char (swift3-mode:token:start anchor))
(swift3-mode:align-with-current-line))
;; Aligns with the end of the anchor
(goto-char (swift3-mode:token:end anchor))
(backward-char)
(current-column))))
(defun swift3-mode:calculate-indent-after-comma ()
"Return indentation after comma.
Assuming the cursor is on the comma."
(let* ((comma-type-and-statement-parent (swift3-mode:detect-type-of-comma))
(comma-type (nth 0 comma-type-and-statement-parent))
(statement-parent (nth 1 comma-type-and-statement-parent)))
(if (eq comma-type 'condition-list)
(swift3-mode:calculate-indent-after-comma-of-condition-list
statement-parent)
(swift3-mode:align-with-next-token
(swift3-mode:backward-sexps-until
(swift3-mode:parents-of-comma comma-type)
'(\,))))))
(defun swift3-mode:calculate-indent-after-comma-of-condition-list
(statement-parent)
;; The comma is in a condition-list but not in an enum-case-pattern-list.
;;
;; Example:
;;
;; if
;; case let P(x), // comma for enum-case-pattern-list
;; let Q(x), // comma for enum-case-pattern-list
;; let R(x) = a, // comma for condition-list
;; let x = x, // comma for condition-list
;; foo == bar
;;
;; We scan from beginning of the statement and remembers last parent token,
;; i.e. "if", "guard", "while", or comma at the end of the line.
(let ((pos (point))
next-token
(parent statement-parent)
in-case-pattern-list)
(goto-char (swift3-mode:token:end statement-parent))
(setq next-token (swift3-mode:forward-token-or-list))
(while (< (point) pos)
(cond
((equal (swift3-mode:token:text next-token) "case")
(setq in-case-pattern-list t))
((equal (swift3-mode:token:text next-token) "=")
(setq in-case-pattern-list nil))
((member (swift3-mode:token:text next-token) '("if" "guard" "while"))
(setq parent next-token))
((and (not in-case-pattern-list)
(eq (swift3-mode:token:type next-token) '\,)
(swift3-mode:eol-other-than-comments-p))
(setq parent next-token)))
(setq next-token (swift3-mode:forward-token-or-list)))
(swift3-mode:align-with-next-token parent)))
(defun swift3-mode:detect-type-of-comma ()
"Return type of comma token under the cursor.
Comma type is a list where:
0th element is one of the following:
- tuple-or-array (inside () or [])
- type-parameters-or-requirements (inside <>)
- enum-case-pattern-list (e.g. if case P, Q, R = x)
- condition-list (e.g. if let x = x, let y = y)
- variable-declarations (e.g. let x = 1, y = 2)
- switch-case-or-enum-case-item-list (e.g. switch {case P, Q, R: a} or
enum {case A, B, C})
- class-like-declarations (supertypes of class, or where clause after
super types)
1st element is the token before the beginning of the statement.
"
;; Various examples:
;;
;; let x = ( // simple case
;; 1,
;; 2,
;; 3
;; )
;;
;; let x = [ // simple case
;; 1,
;; 2,
;; 3
;; ]
;;
;; let x: Foo = a, // "let" is not a part of elements
;; y: Foo = b,
;; z: Foo = c
;;
;; switch foo {
;; case (let x, // "let" is a part of an element
;; Y,
;; Z):
;; aaa
;; }
;;
;; class Foo {
;; }
;;
;; switch foo {
;; case A(x) where p(x), // "case" is not a part of elements
;; B(x) where p(x), // "where" is a part of an element
;; C(x) where p(x):
;; aaa
;; }
;;
;; if // or guard or while
;; let x = x, // "let" is a part of an element
;; let y = y,
;; let z = z,
;; case P(a, b, c), // "case" is a part of an element of condition-list
;; Q(a, b, c) = abc, // "case" is not a part of elements of
;; // enum-case-pattern-list
;; case (a, b, c) = abc,
;; aaa {
;;
;; bbb
;; }
;;
;; See also SE-0099 and SE-0043:
;; https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md
;; https://github.com/apple/swift-evolution/blob/master/proposals/0043-declare-variables-in-case-labels-with-multiple-patterns.md
;;
;; class Foo: A,
;; B,
;; C
;; where
;; T: A,
;; T: B,
;; T: C {
;; }
;;
;; extension _ArrayType
;; where
;; Generator.Element: A,
;; Generator.Element: B,
;; Generator.Element: C {
;; }
;;
;; func foo -> Int
;; where
;; T: A,
;; T: B,
;; T: C {
;; }
;;
;; enum Foo {
;; case A(x: Int),
;; B(y: Int),
;; C(z: Int)
;; case D(x: Int)
;; , E(y: Int)
;; , F(z: Int)
;; }
;;
;; enum Foo: Int {
;; case A,
;; B,
;; C = 2
;; case D = 3
;; , E
;; , F
;; }
(save-excursion
(let ((pos (point))
(parent (swift3-mode:backward-sexps-until
(cons '< swift3-mode:statement-parent-tokens))))
(cond
((memq (swift3-mode:token:type parent) '(\( \[))
(list 'tuple-or-array parent))
((eq (swift3-mode:token:type parent) '<)
(list 'type-parameters-or-requirements parent))
(t
(goto-char (swift3-mode:token:end parent))
(let ((next-token (swift3-mode:forward-token-or-list))
result)
(while (and (<= (point) pos) (not result))
(cond
((member (swift3-mode:token:text next-token)
'("if" "guard" "while"))
;; Conditions
;;
;; Distinguishes condition-list and enum-case-pattern-list:
;;
;; if
;; let x = x,
;; case P(a, b, c),
;; Q(a, b, c),
;; R(a, b, c) = abc,
;; let x = x,
;; foo == bar,
;; case (a, b, c) = abc {
;; }
(goto-char pos)
(if (equal
(swift3-mode:token:text (swift3-mode:backward-sexps-until
'("if" "guard" "while" "case" "=")))
"case")
(setq result 'enum-case-pattern-list)
(setq result 'condition-list)))
((member (swift3-mode:token:text next-token)
'("let" "var"))
(setq result 'variable-declarations))
((equal (swift3-mode:token:text next-token)
"case")
(setq result 'switch-case-or-enum-case-item-list))
((equal (swift3-mode:token:text next-token)
"where")
(setq result 'type-parameters-or-requirements))
((eq (swift3-mode:token:type next-token) 'typing-:)
(setq result 'class-like-declarations)))
(setq next-token (swift3-mode:forward-token-or-list)))
(if (and (> (point) pos) (eq (swift3-mode:token:type next-token) '<>))
;; The comma was inside <> but scanner misunderstand < as
;; a binary-operator.
(list 'type-parameters-or-requirements parent)
(list result parent))))))))
(defun swift3-mode:parents-of-comma (comma-type)
"Return parent token types of comma token Ffrom COMMA-TYPE."
(append
swift3-mode:statement-parent-tokens
(cond
((eq comma-type 'tuple-or-array)
'(\( \[))
((eq comma-type 'type-parameters-or-requirements)
'(< "where"))
((eq comma-type 'enum-case-pattern-list)
'("case"))
((eq comma-type 'variable-declarations)
'("let" "var"))
((eq comma-type 'switch-case-or-enum-case-item-list)
'("case"))
((eq comma-type 'class-like-declarations)
'(typing-: "where")))))
(defun swift3-mode:backward-sexps-until (token-types
&optional
stop-at-eol-token-types
stop-at-bol-token-types)
"Backward sexps until a token with one of given token types appears.
Return the token.
When this function returns, the cursor is at the start of the token.
TOKEN-TYPES is a list of guard token typess. This function backs to a token
with one of those token types.
STOP-AT-EOL-TOKEN-TYPES is a list of token types that if we skipped the end of
a line just after a token with one of given toke typen, the function returns.
Typically, this is a list of token types that separates list elements
\(e.g. ',', ';'). If STOP-AT-EOL-TOKEN-TYPES is the symbol `any', it matches
all tokens.
STOP-AT-BOL-TOKEN-TYPES is a list of token types that if we hit
the beginning of a line just before a token with one of given token types,
the function returns. Typically, this is a list of token types that starts
list element (e.g. 'case' of switch statement body). If STOP-AT-BOL-TOKEN-TYPES
is the symbol `any', it matches all tokens."
(let*
((parent (swift3-mode:backward-token-or-list))
(type (swift3-mode:token:type parent))
(text (swift3-mode:token:text parent)))
(while (not
;; Stops loop when...
(or
;; Hits a guard token.
(member type token-types)
(member text token-types)
;; Beginning of the buffer.
(eq type 'outside-of-buffer)
;; When this function is called on "," token before position (1),
;; this function stops just before the "," token after "Foo".
;;
;; case Foo,
;; Bar, Baz, // (1)
;; AAA
(and stop-at-eol-token-types
(save-excursion
(swift3-mode:forward-token-or-list)
(forward-comment (- (point)))
(swift3-mode:eol-other-than-comments-p))
(or (eq stop-at-eol-token-types 'any)
(member type stop-at-eol-token-types)
(member text stop-at-eol-token-types)))
;; When this function is called on "case" token before position
;; (1), this function stops just before "case Bar".
;;
;; switch foo {
;; case Foo:
;; ...
;; case Bar: case Baz:
;; ...
;; case AAA: // (1)
;; }
(and stop-at-bol-token-types
(and
(or
(eq stop-at-bol-token-types 'any)
(member type stop-at-bol-token-types)
(member text stop-at-bol-token-types))
(swift3-mode:bol-other-than-comments-p)))))
(setq parent (swift3-mode:backward-token-or-list))
(setq type (swift3-mode:token:type parent))
(setq text (swift3-mode:token:text parent)))
parent))
(defun swift3-mode:align-with-next-token (parent &optional offset)
(let ((parent-end (swift3-mode:token:end parent)))
(goto-char parent-end)
(forward-comment (point-max))
(swift3-mode:goto-non-comment-bol)
(when (< (point) parent-end)
(goto-char parent-end))
(swift3-mode:skip-whitespaces)
(+ (or offset 0) (current-column))))
(defun swift3-mode:align-with-current-line (&optional offset)
(swift3-mode:goto-non-comment-bol)
(swift3-mode:skip-whitespaces)
(+ (or offset 0) (current-column)))
(defun swift3-mode:backward-token-or-list ()
"Move point to the start position of the previous token or list.
Return the token skipped."
(let* ((previous-token (swift3-mode:backward-token))
(previous-type (swift3-mode:token:type previous-token))
(previous-text (swift3-mode:token:text previous-token))
(previous-start (swift3-mode:token:start previous-token))
(previous-end (swift3-mode:token:end previous-token)))
(cond
;; List
((memq previous-type '(} \) \]))
(goto-char previous-end)
(backward-list)
(swift3-mode:token
(cdr (assoc previous-type '((} . {})
(\) . \(\))
(\] . \[\]))))
(buffer-substring-no-properties (point) previous-end)
(point)
previous-end))
;; Generic parameter list
((equal previous-text ">")
(swift3-mode:try-backward-generic-parameters)
(if (= (point) previous-start)
previous-token
(swift3-mode:token
'<>
(buffer-substring-no-properties (point) previous-end)
(point)
previous-end)))
;; Other token
(t previous-token))))
(defun swift3-mode:forward-token-or-list ()
"Move point to the end position of the next token or list.
Return the token skipped."
(let* ((next-token (swift3-mode:forward-token))
(next-type (swift3-mode:token:type next-token))
(next-text (swift3-mode:token:text next-token))
(next-start (swift3-mode:token:start next-token))
(next-end (swift3-mode:token:end next-token)))
(cond
;; List
((memq next-type '({ \( \[))
(goto-char next-start)
(forward-list)
(swift3-mode:token
(cdr (assoc next-type '(({ . {})
(\( . \(\))
(\[ . \[\]))))
(buffer-substring-no-properties next-start (point))
next-start
(point)))
;; Generic parameter list
((equal next-text "<")
(swift3-mode:try-forward-generic-parameters)
(if (= (point) next-end)
next-token
(swift3-mode:token
'<>
(buffer-substring-no-properties next-start (point))
next-start
(point))))
;; Other token
(t next-token))))
(defun swift3-mode:try-backward-generic-parameters ()
"Move point to the start of the generic parameter list.
Keep position if the cursor is not at the end of a generic parameter list.
Assuming the cursor is on the close angle bracket.
It is a Generic parameter list if:
- it has matching angle brackets, and
- it does not have tokens that cannot appears in a generic parameter list."
(swift3-mode:try-skip-generic-parameters
#'swift3-mode:backward-token-or-list
"<" ">"))
(defun swift3-mode:try-forward-generic-parameters ()
"Move point to the end of the generic parameter list.
Keep position if the cursor is not at the start of a generic parameter list.
Assuming the cursor is after the open angle bracket.
It is a Generic parameter list if:
- it has matching angle brackets, and
- it does not have tokens that cannot appears in a generic parameter list."
(swift3-mode:try-skip-generic-parameters
#'swift3-mode:forward-token-or-list
">" "<"))
(defconst siwft-mode:tokens-not-in-generic-parameter-list
;; Whitelisting tend to be fragile. So we list tokens that are
;; unlikely to appear in generic parameter lists in the current
;; version and future ones.
;;
;; Example of complex generic parameters:
;; <
;; A: B,
;; C: protocol
;; where
;; A: @aaa(1 + 2 + 3) D!,
;; C == (@aaa(1) inout [E.F]?, G...) throws -> [Int:Int]
;; >
;;
;; We don't need to consider the contents of inner brackets because they are
;; skipped by `swift3-mode:backward-token-or-list'.
;;
;; String literals, implicit parameter names, and numbers are also excluded
;; by `swift3-mode:try-skip-generic-parameters'.
'(outside-of-buffer
\;
{ } \( \) \[ \]
"true" "false"
"class" "struct" "enum" "extension" "func" "operator"
"try" "try?" "try!"
"as" "as?" "as!"
"is"
"in"
"init" "deinit" "get" "set" "willSet" "didSet" "subscript"
"for" "case" "default" "while" "let" "var" "repeat" "if" "else"
"guard" "break" "continue" "fallthrough" "return" "throw" "defer"
"do" "catch" "import" "typealias" "associatedtype"))
(defun swift3-mode:try-skip-generic-parameters
(skip-token-or-list-function matching-bracket-text unmatching-bracket-text)
(let ((pos (point))
(prohibited-tokens (append
unmatching-bracket-text
siwft-mode:tokens-not-in-generic-parameter-list))
(next-token (funcall skip-token-or-list-function)))
(while
(cond
((or (memq (swift3-mode:token:type next-token) prohibited-tokens)
(member (swift3-mode:token:text next-token) prohibited-tokens)
(string-match-p "^[\"$0-9]"
(swift3-mode:token:text next-token)))
;; Not a generic parameter list. Returns to the initial position and
;; stops the loop.
(goto-char pos)
nil)
((equal (swift3-mode:token:text next-token) matching-bracket-text)
;; Found the matching open angle bracket. Stops the loop.
nil)
;; Otherwise, keep scanning
(t t))
(setq next-token (funcall skip-token-or-list-function)))
next-token))
(defun swift3-mode:bol-other-than-comments-p ()
"Return t if there is nothing other than comments in the front of this line.
Return nil otherwise.
Newlines inside comments are ignored."
;; Foo // ← bol
;; /* */ Foo // ← bol
;; X /* */ Foo // ← not bol
;;
;; /*
;; */ /* */ /*
;; */ Foo // ← bol
;;
;; X /*
;; */ /* */ /*
;; */ Foo // ← not bol
;;
;; X
;; /* */ /*
;; */ Foo // ← bol
(save-excursion
(let ((pos (point)))
(swift3-mode:goto-non-comment-bol)
(forward-comment (point-max))
(<= pos (point)))))
(defun swift3-mode:eol-other-than-comments-p ()
"Return t if there is nothing other than comments until the end of this line.
Return nil otherwise.
Newlines inside comments are ignored."
(save-excursion
(let ((pos (point)))
(swift3-mode:goto-non-comment-eol)
(forward-comment (- (point)))
(<= (point) pos))))
(defun swift3-mode:bolp ()
"Return t if there is nothing in the front of this line.
Return nil otherwise."
(save-excursion
(skip-syntax-backward " ")
(bolp)))
(defun swift3-mode:skip-whitespaces ()
"Skips forward whitespaces and newlines."
(skip-syntax-forward " >"))
(defun swift3-mode:incomplete-comment-p ()
(and (nth 4 (syntax-ppss))
(save-excursion
(goto-char (nth 8 (syntax-ppss)))
(not (forward-comment 1)))))
(defun swift3-mode:indent-new-comment-line (&optional soft)
"Break the line at the point and indent the new line.
If the point is inside a comment, continue the comment. If the comment is a
multiline comment, close the previous comment and start new one if
`comment-multi-line' is nil."
(interactive)
(let* ((parser-state (syntax-ppss))
(is-inside-comment (nth 4 parser-state))
(is-single-line-comment (eq (nth 7 parser-state) 1))
(comment-beginning-position (nth 8 parser-state))
(space-after-asterisk
(if swift3-mode:insert-space-after-asterisk-in-comment " " "")))
(delete-horizontal-space)
(if soft (insert-and-inherit ?\n) (newline 1))
(delete-horizontal-space)
(when is-inside-comment
(insert-before-markers-and-inherit
(cond
(is-single-line-comment
"// ")
(comment-multi-line
(save-excursion
(beginning-of-line)
(forward-char -1)
(beginning-of-line)
(if (<= (point) comment-beginning-position)
;; The cursor was on the 2nd line of the comment.
;; Uses default prefix.
(concat "*" space-after-asterisk)
;; The cursor was on the 3nd or following lines of
;; the comment.
;; Use the prefix of the previous line.
(back-to-indentation)
(if (looking-at "\\*+")
(concat (match-string-no-properties 0) space-after-asterisk)
""))))
(t
(backward-char)
(insert-before-markers-and-inherit " */")
(forward-char)
(save-excursion
(goto-char comment-beginning-position)
(looking-at "/\\*+")
(concat (match-string-no-properties 0) space-after-asterisk))))))
(indent-according-to-mode)
;; Closes incomplete multiline comment.
(when (and swift3-mode:auto-close-multiline-comment
(not is-single-line-comment)
(swift3-mode:incomplete-comment-p))
(save-excursion
(end-of-line)
(if soft (insert-and-inherit ?\n) (newline 1))
(insert-before-markers-and-inherit "*/")
(indent-according-to-mode)))))
(defun swift3-mode:post-self-insert ()
(cond
((and
(= last-command-event ?*)
(nth 4 (syntax-ppss))
(save-excursion (backward-char) (skip-syntax-backward " ") (bolp)))
(when swift3-mode:insert-space-after-asterisk-in-comment
(insert-before-markers-and-inherit " "))
(indent-according-to-mode))
((and
(= last-command-event ?/)
swift3-mode:fix-comment-close
(nth 4 (syntax-ppss))
(save-excursion
(let ((pos (point)))
(beginning-of-line)
(and
(looking-at "^[ ]*\\*[ ]+/")
(eq (match-end 0) pos)
(swift3-mode:incomplete-comment-p)))))
(backward-char)
(delete-horizontal-space)
(forward-char))))
(provide 'swift3-mode-indent)
;;; swift3-mode-indent.el ends here
swift3-mode-2.1.1/swift3-mode-lexer.el 0000644 0000000 0000000 00000077366 12767506611 015700 0 ustar 00 0000000 0000000 ;;; swift3-mode-lexer.el --- Major-mode for Apple's Swift programming language, lexer. -*- lexical-binding: t -*-
;; Copyright (C) 2014-2016 taku0, Chris Barrett, Bozhidar Batsov, Arthur Evstifeev
;; Authors: taku0 (http://github.com/taku0)
;; Chris Barrett
;; Bozhidar Batsov
;; Arthur Evstifeev
;;
;; Version: 2.1.1
;; Package-Requires: ((emacs "24.4"))
;; Keywords: languages swift
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;;; Commentary:
;; Routines for Swift tokens
;; Token is a tuple consists of:
;;
;; - Token type
;; - Token text
;; - Start position (inclusive)
;; - End position (exclusive)
;;; Code:
(declare-function swift3-mode:backward-sexps-until "swift3-mode-indent.el"
(token-types
&optional
stop-at-eol-token-types
stop-at-bol-token-types))
(defun swift3-mode:token (type text start end)
"Construct and returns a token."
(list type text start end))
(defun swift3-mode:token:type (token)
"Return the type of TOKEN."
(nth 0 token))
(defun swift3-mode:token:text (token)
"Return the text of TOKEN."
(nth 1 token))
(defun swift3-mode:token:start (token)
"Return the start position of TOKEN."
(nth 2 token))
(defun swift3-mode:token:end (token)
"Return the end position of TOKEN."
(nth 3 token))
;; Token types is one of the follwing symbols:
;;
;; - prefix-operator (including try, try?, and try!)
;; - postfix-operator
;; - binary-operator (including as, as?, as!, is, =, ., and ->)
;; - identifier (including keywords, numbers, implicit parameters, and unknown tokens)
;; - [
;; - ]
;; - {
;; - }
;; - (
;; - )
;; - ,
;; - ;
;; - implicit-;
;; - < (as an angle bracket)
;; - > (as an angle bracket)
;; - typing-: (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)
;; - outside-of-buffer
;;
;; Additionaly, `swift3-mode:backward-token-or-list' may return a parenthesized
;; expression as a token with one of the following types:
;; - ()
;; - []
;; - {}
;; - <>
;;; Syntax table
(defconst swift3-mode:syntax-table
(let ((table (make-syntax-table)))
;; Whitespace characters
;; Word constituents
;; Symbol constituents
(modify-syntax-entry ?_ "_" table)
(modify-syntax-entry ?$ "_" table)
(modify-syntax-entry ?@ "_" table)
(modify-syntax-entry ?# "_" table)
;; Punctuation characters
;;
;; Operators
;; https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID410
;; TODO Unicode operators
;;
;; / and * will be overridden below as comment delimiters
(mapc (lambda (c) (modify-syntax-entry c "." table)) "/=-+!*%<>&|^~?.")
;; Separators
(mapc (lambda (c) (modify-syntax-entry c "." table)) ",;")
;; Parenthesis characters
(modify-syntax-entry ?( "()" table)
(modify-syntax-entry ?) ")(" table)
(modify-syntax-entry ?[ "(]" table)
(modify-syntax-entry ?] ")[" table)
(modify-syntax-entry ?{ "(}" table)
(modify-syntax-entry ?} "){" table)
;; String quotes
(modify-syntax-entry ?\" "\"" table)
(modify-syntax-entry ?` "\"" table)
;; Escape-syntax characters
(modify-syntax-entry ?\\ "\\" table)
;; Character quotes
;; Paired delimiters
;; Expression prefixes
;; Comments
(modify-syntax-entry ?/ ". 124b" table)
(modify-syntax-entry ?* ". 23n" table)
(modify-syntax-entry ?\n "> b" table)
(modify-syntax-entry ?\r "> b" table)
table))
;;; Lexers
(defun swift3-mode:implicit-semi-p ()
"Return t if the cursor is after the end of a statement."
(let
((previous-token (save-excursion
(swift3-mode:backquote-identifier-if-after-dot
(swift3-mode:backward-token-simple))))
(next-token (save-excursion
(swift3-mode:backquote-identifier-if-after-dot
(swift3-mode:forward-token-simple)))))
;; If the cursor is on the empty line, pretend an identifier is the line.
(when (and
(< (swift3-mode:token:end previous-token) (line-beginning-position))
(< (line-end-position) (swift3-mode:token:start next-token)))
(setq next-token (swift3-mode:token 'identifier "" (point) (point))))
(cond
((or
;; Supresses implicit semicolon around binary operators and separators.
(memq (swift3-mode:token:type previous-token)
'(binary-operator \; \, :))
(memq (swift3-mode:token:type next-token)
'(binary-operator \; \, :))
;; Supresses implicit semicolon after try, try?, and try!.
(member (swift3-mode:token:text previous-token)
'("try" "try?" "try!"))
;; Suppress implicit semicolon after open brackets or before close
;; brackets.
(memq (swift3-mode:token:type previous-token) '({ \( \[))
(memq (swift3-mode:token:type next-token) '(} \) \]))
;; Suppress implicit semicolon around keywords that cannot start or end
;; statements.
(member (swift3-mode:token:text previous-token)
'("inout" "throws" "rethrows" "in" "where"))
(member (swift3-mode:token:text next-token)
'("inout" "throws" "rethrows" "in" "where")))
nil)
;; Inserts implicit semicolon around #... directives.
;;
;; Note that we cannot split #if line; the following code is not allowed.
;;
;; #if
;; true
;; #end if
((and
(or
(string-prefix-p "#" (swift3-mode:token:text previous-token))
(string-prefix-p "#" (swift3-mode:token:text next-token)))
(not (member (swift3-mode:token:text previous-token)
'("#file" "#line" "column" "#function")))
(not (member (swift3-mode:token:text next-token)
'("#file" "#line" "column" "#function"))))
t)
;; Suppress implicit semicolon after modifiers.
((member (swift3-mode:token:text previous-token)
'("indirect" "convenience" "dynamic" "final" "infix" "lazy"
"mutating" "nonmutating" "optional" "override" "postfix"
"prefix" "required" "static" "unowned" "weak" "internal"
"private" "public" "open" "fileprivate"))
nil)
;; internal(set) private(set) public(set) open(set) fileprivate(set)
;; unowned(safe) unowned(unsafe)
((and
(eq (swift3-mode:token:type previous-token) '\))
(save-excursion
(and
(eq (swift3-mode:token:type (swift3-mode:backward-token-simple)) '\))
(member (swift3-mode:token:text (swift3-mode:backward-token-simple))
'("set" "safe" "unsafe"))
(eq (swift3-mode:token:type (swift3-mode:backward-token-simple)) '\()
(member (swift3-mode:token:text
(swift3-mode:backquote-identifier-if-after-dot
(swift3-mode:backward-token-simple)))
'("unowned" "internal" "private" "public" "open" "fileprivate")))))
nil)
;; Insert implicit semicolon before modifiers.
;;
;; Preceding mofidiers takes precedence over this.
((member (swift3-mode:token:text next-token)
'("indirect" "convenience" "dynamic" "final" "infix" "lazy"
"mutating" "nonmutating" "optional" "override" "postfix"
"prefix" "required" "static" "unowned" "weak" "internal"
"private" "public" "open" "fileprivate"))
t)
;; Inserts implicit semicolon around keywords that forms single keyword
;; statements.
((or
(member (swift3-mode:token:text previous-token)
'("break" "continue" "fallthrough"))
(member (swift3-mode:token:text next-token)
'("break" "continue" "fallthrough")))
t)
;; Suppress implicit semicolon after keywords that cannot end statements.
((member (swift3-mode:token:text previous-token)
'("while" "for" "switch" "case" "default" "catch" "if" "guard"
"let" "var" "throw" "import" "return"))
nil)
;; Inserts implicit semicolon before keywords that starts a new statement.
((member (swift3-mode:token:text next-token)
'("for" "repeat" "switch" "case" "default" "defer" "do" "if"
"guard" "let" "var" "throw" "import" "return"))
t)
;; Inserts implicit semicolon before `while' unless it is part of
;; `repeat...while'.
((equal (swift3-mode:token:text next-token) "while")
(save-excursion
(not
(and
(eq (swift3-mode:token:type previous-token) '\})
(progn
(backward-list)
(equal (swift3-mode:token:text
(swift3-mode:backquote-identifier-if-after-dot
(swift3-mode:backward-token-simple)))
"repeat"))))))
;; Inserts implicit around else
((or
(equal (swift3-mode:token:text previous-token) "else")
(equal (swift3-mode:token:text next-token) "else"))
t)
;; Supress implicit semicolon after attributes.
((string-prefix-p "@" (swift3-mode:token:text previous-token)) nil)
;; Inserts implicit semicolon before keywords that behave like method
;; names.
((member (swift3-mode:token:text next-token)
'("get" "set" "willSet" "didSet" "subscript" "init" "deinit"))
t)
;; Suppress implicit semicolon after keywords that behave like method
;; names.
;;
;; Note that binary operators take precedence over this:
;;
;; self . // not insert semicolon here
;; init
((member (swift3-mode:token:text previous-token)
'("set" "willSet" "didSet" "subscript" "init" "deinit"))
nil)
;; Suppress implicit semicolon after declaration starters.
((member (swift3-mode:token:text previous-token)
'("class" "struct" "protocol" "enum" "extension" "func" "typealias"
"associatedtype" "precedencegroup" "operator"))
nil)
;; Inserts implicit semicolon before declaration starters.
;; Modifiers take precedence over this.
;;
;; Notes that class-requirement is handled by the `:' rule above:
;;
;; protocol Foo: // not insert semicolon here
;; class
;;
;; `protocol' is handled by the next rule
((member (swift3-mode:token:text next-token)
'("class" "struct" "enum" "extension" "func" "typealias"
"associatedtype" "precedencegroup"))
t)
;; Inserts implicit semicolon before protocol unless it is followed by <.
((equal "protocol" (swift3-mode:token:text next-token))
(not (equal (swift3-mode:token:text
(save-excursion
(swift3-mode:forward-token-simple)
(swift3-mode:forward-token-simple)))
"<")))
;; Inserts implicit semicolon before attributes unless other condtions
;; met.
((string-prefix-p "@" (swift3-mode:token:text previous-token)) t)
;; Inserts implicit semicolon before open square bracket.
;;
;; Open square bracket for array indexing cannot appear at the start of a
;; line.
;; https://github.com/apple/swift/blob/8d4b1cc3c47c7624d57f188d5b227152ccb03163/lib/Parse/ParseExpr.cpp#L1525
;;
;; Note that the following pattern (i.e. after binary-operator) is handled
;; by above case.
;;
;; let x =
;; [
;; 1
;; ]
((eq (swift3-mode:token:type next-token) '\[) t)
;; Inserts implicit semicolon before open parenthesis.
;;
;; Open parenthesis for function arguments cannot appear at the start of a
;; line.
;; https://github.com/apple/swift/blob/8d4b1cc3c47c7624d57f188d5b227152ccb03163/lib/Parse/ParseExpr.cpp#L1251
;;
;; Note that the following pattern (i.e. after binary-operator) is handled
;; by above case.
;;
;; let x =
;; (
;; 1
;; )
((eq (swift3-mode:token:type next-token) '\() t)
;; Inserts semicolon before open curly bracket.
;;
;; Open curly bracket may continue the previous line, but we do not indent
;; there. For example, the code below is parsed as `(foo() { x in ... })'
;; by the Swift compiler, but we indent it like `foo(); { x in ... }'.
;;
;; foo()
;; { // does not indent here
;; x in
;; ...
;; }
((eq (swift3-mode:token:type next-token) '\{) t)
;; Otherwise, inserts implicit semicolon.
(t t))))
(defun swift3-mode:type-colon-p ()
"Return t if a colon at the cursor is the colon for type annotation.
That is supertype declaration or type declaration of let or var."
(save-excursion
(let ((previous-token (swift3-mode:backward-token-simple)))
;; class Foo: Bar ← type colon
;; class Foo : Bar ← type colon
;; class Foo> : 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
;; [
;; foo: ← not a type colon
;; bar
;; ]
;; foo(bar, baz: baz) ← not a type colon
(or
(eq (swift3-mode:token:type previous-token) '>)
;; class Foo: ← type colon
;; extension Foo: ← type colon
;; let foo: ← type colon
;; var foo: ← type colon
;; protocol Foo {
;; typealias Bar: Baz ← type colon
;; }
(member (swift3-mode:token:text
(swift3-mode:backquote-identifier-if-after-dot
(swift3-mode:backward-token-simple)))
'("class" "extension" "enum" "struct" "protocol" "typealias"
"associatedtype" "let" "var"))))))
(defun swift3-mode:case-colon-p ()
"Return t if a colon at the cursor is the colon for case or default label."
(save-excursion
(member
;; This function can be confused by conditional operator.
;;
;;
;; switch foo {
;; case let x where x is Foo ?
;; a : // This function should return nil but it
;; // actually retuns t.
;; b: // This function should return t but it
;; // actually return nil.
;; let y = a ? b : c // This function returns nil correctly for this.
;; }
;; FIXME: mutual dependency
(swift3-mode:token:text (swift3-mode:backward-sexps-until
'(implicit-\; \; { \( \[ "case" "default" 'case-:)))
'("case" "default"))))
(defun swift3-mode:anonyous-parameter-in-p ()
"Return t if a 'in' token at the cursor is for anonymous function parameters."
(save-excursion
(eq
;; FIXME: mutual dependency
(swift3-mode:token:type (swift3-mode:backward-sexps-until
'(\; { \( \[ "for")))
'{)))
(defun swift3-mode:fix-operator-type (token)
"Return new operator token with proper token type."
;; Operator type (i.e. prefix, postfix, infix) is decided from spaces or
;; comments around the operator.
;; https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID410
;; https://github.com/apple/swift-evolution/blob/master/proposals/0037-clarify-comments-and-operators.md
(let*
((text (swift3-mode:token:text token))
(start (swift3-mode:token:start token))
(end (swift3-mode:token:end token))
(has-preceding-space (or
(= start (point-min))
(memq (char-syntax (char-before start)) '(? ?>))
(nth 4 (save-excursion (syntax-ppss (1- start))))))
(has-following-space (or
(= end (point-max))
(memq (char-syntax (char-after end)) '(? ?<))
(save-excursion (goto-char end)
(looking-at "/\\*\\|//"))
(= (char-after end) ?\C-j)))
(has-following-dot (eq (char-after end) ?.))
(is-declaration (save-excursion
;; i.e.
;; func +++(x1: X, x2: X)
;; or operator declarations.
(goto-char start)
(member
(swift3-mode:token:text
(swift3-mode:backquote-identifier-if-after-dot
(swift3-mode:backward-token-simple)))
'("func" "operator"))))
(type
(cond
(is-declaration 'identifier)
((member text '("try" "try?" "try!")) 'prefix-operator)
((equal text ".") 'binary-operator)
((and has-preceding-space has-following-space) 'binary-operator)
(has-preceding-space 'prefix-operator)
((or has-following-space has-following-dot) 'postfix-operator)
(t 'binary-operator))))
(swift3-mode:token type text start end)))
(defun swift3-mode:backquote-identifier-if-after-dot (token)
"Backquote identifiers including keywords if it is after dot.
See SE-0071:
https://github.com/apple/swift-evolution/blob/master/proposals/0071-member-keywords.md"
(if (and (string-match "^[a-z]" (swift3-mode:token:text token))
(save-excursion
(goto-char (swift3-mode:token:start token))
(equal (swift3-mode:token:text (swift3-mode:backward-token-simple))
".")))
(swift3-mode:token
'identifier
(concat "`" (swift3-mode:token:text token) "`")
(swift3-mode:token:start token)
(swift3-mode:token:end token))
token))
(defun swift3-mode:forward-token ()
"Move point forward to the next position of the end of a token.
Return the token object. If no more tokens available, return a token with
type `out-of-buffer'"
(let ((pos (point)))
(forward-comment (point-max))
(cond
;; Outside of buffer
((eobp)
(swift3-mode:token 'outside-of-buffer "" (point) (point)))
;; Implicit semicolon
((and (< pos
(save-excursion
(swift3-mode:goto-non-comment-bol)
(point)))
(save-excursion (goto-char pos) (swift3-mode:implicit-semi-p)))
(swift3-mode:token 'implicit-\;
(buffer-substring-no-properties pos (point))
pos
(point)))
;; Colon
((eq (char-after) ?:)
(swift3-mode:token (cond
((swift3-mode:type-colon-p) 'typing-:)
((swift3-mode:case-colon-p) 'case-:)
(t ':))
":"
(progn (forward-char) (1- (point)))
(point)))
(t
(let ((token (swift3-mode:forward-token-simple)))
(setq token (swift3-mode:backquote-identifier-if-after-dot token))
(when (and (equal (swift3-mode:token:text token) "in")
(save-excursion
(goto-char (swift3-mode:token:start token))
(swift3-mode:anonyous-parameter-in-p)))
(setq token
(swift3-mode:token
'anonymous-function-parameter-in
"in"
(swift3-mode:token:start token)
(swift3-mode:token:end token))))
token)))))
(defun swift3-mode:forward-token-simple ()
"Like `swift3-mode:forward-token' without recursion, and never produces
`implicit-;' or `type-:'."
(forward-comment (point-max))
(cond
;; Outside of buffer
((eobp)
(swift3-mode:token 'outside-of-buffer "" (point) (point)))
;; Separators and parentheses
((memq (char-after) '(?, ?\; ?\{ ?\} ?\[ ?\] ?\( ?\) ?:))
(forward-char)
(swift3-mode:token (intern (string (char-before)))
(string (char-before))
(1- (point))
(point)))
;; Open angle bracket for type parameters
;;
;; We use a heuristic: spaces are inserted around inequality sign, but not
;; for angle bracket, and a type paramer starts with an upper case
;; character, a square bracket, a parenthesis, or keyword 'protocol'.
((and (eq (char-after) ?<) (looking-at "<\\([[:upper:]\\[[(]\\|protocol\\)"))
(forward-char)
(swift3-mode:token '< "<" (1- (point)) (point)))
;; Close angle bracket for type parameters
;;
;; Close angle bracket follows identifier, a square bracket, a parenthesis,
;; or another another bracket (e.g. Foo>)
((and (eq (char-after) ?>)
(save-excursion
;; You know that regular language can be reversed. Thus you may
;; think that `looking-back' reverse the given regexp and scan
;; chars backwards. Nevertheless, `looking' function does not
;; do that. It just repeats `looking-at' with decrementing start
;; position until it succeeds. The document says that it is not
;; recommended to use. So using `skip-chars-backward',
;; `skip-syntax-backward', and `looking-at' here.
(skip-chars-backward "])>")
(skip-syntax-backward "w_")
(looking-at "[[:upper:]_]")))
(forward-char)
(swift3-mode:token '> ">" (1- (point)) (point)))
;; Operator (other than as, try, or is)
;;
;; Operators starts with a dot can contains dots. Other operators cannot
;; contain dots.
;;
;; https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/dot-operator-head
((looking-at "[-/=+!*%<>&|^~?]+\\|[.][-./=+!*%<>&|^~?]*")
(let*
((text (match-string-no-properties 0))
(start (match-beginning 0))
(end (match-end 0)))
(when (string-match ".*/\\*\\|.*//" text)
;; e.g. +++/* */ or +++//
(setq end
(- end
(- (length text) (- (match-end 0) 2))))
(setq text (substring text 0 (- (match-end 0) 2))))
(goto-char end)
(swift3-mode:fix-operator-type
(swift3-mode:token nil text start end))))
;; String and backquoted identifer
((memq (char-after) '(?\" ?`))
(let ((pos-after-comment (point)))
(goto-char (scan-sexps (point) 1))
(swift3-mode:token
'identifier
(buffer-substring-no-properties pos-after-comment (point))
pos-after-comment
(point))))
;; Other tokens including identifers, implicit parameters, keywords, and
;; numbers
(t
(let*
((pos-after-comment (point))
(text
(cond
;; Identifers, implicit parameters, keywords, numbers
;;
;; Note: syntax class _ includes #, @, and $.
((memq (char-syntax (char-after)) '(?w ?_))
(progn (forward-symbol 1)
(buffer-substring-no-properties pos-after-comment (point))))
;; Unknown character type. Treats as a single-letter token.
(t (forward-char) (string (char-before))))))
(cond
((member text '("as" "try"))
;; as?, as!, try?, or try!
(when (member (char-after) '(?? ?!))
(forward-char)
(setq text (concat text (list (char-before)))))
(swift3-mode:token (if (member text '("as" "as?" "as!"))
'binary-operator
'prefix-operator)
text
(- (point) (length text))
(point)))
((equal text "is")
(swift3-mode:token 'binary-operator
text
(- (point) (length text))
(point)))
(t
(swift3-mode:token 'identifer
text
(- (point) (length text))
(point))))))))
(defun swift3-mode:backward-token ()
"Move point backward to the previous position of the end of a token.
Return the token object. If no more tokens available, return a token with
type `out-of-buffer'."
(let ((pos (point)))
(forward-comment (- (point)))
(cond
;; Outside of buffer
((bobp)
(swift3-mode:token 'outside-of-buffer "" (point) (point)))
;; Implicit semicolon
((and (< (save-excursion
(swift3-mode:goto-non-comment-eol)
(point))
pos)
(save-excursion (goto-char pos) (swift3-mode:implicit-semi-p)))
(swift3-mode:token 'implicit-\;
(buffer-substring-no-properties (point) pos)
(point)
pos))
;; Colon
((eq (char-before) ?:)
(backward-char)
(swift3-mode:token (cond
((swift3-mode:type-colon-p) 'typing-:)
((swift3-mode:case-colon-p) 'case-:)
(t ':))
":"
(point)
(1+ (point))))
(t
(let ((token (swift3-mode:backward-token-simple)))
(setq token (swift3-mode:backquote-identifier-if-after-dot token))
(when (and (equal (swift3-mode:token:text token) "in")
(save-excursion
(goto-char (swift3-mode:token:start token))
(swift3-mode:anonyous-parameter-in-p)))
(setq token
(swift3-mode:token
'anonymous-function-parameter-in
"in"
(swift3-mode:token:start token)
(swift3-mode:token:end token))))
token)))))
(defun swift3-mode:backward-token-simple ()
"Like `swift3-mode:backward-token' without recursion, and never produces
`implicit-;' or `type-:'."
(forward-comment (- (point)))
(cond
;; Outside of buffer
((bobp)
(swift3-mode:token 'outside-of-buffer "" (point) (point)))
;; Separators and parentheses
((memq (char-before) '(?, ?\; ?\{ ?\} ?\[ ?\] ?\( ?\) ?:))
(backward-char)
(swift3-mode:token (intern (string (char-after)))
(string (char-after))
(point)
(1+ (point))))
;; >! or >?
((and (memq (char-before) '(?! ??))
(eq (char-before (1- (point))) ?>)
(save-excursion
(backward-char)
(eq (swift3-mode:token:type (swift3-mode:backward-token-simple)) '>)))
(backward-char)
(swift3-mode:token (intern (string (char-after)))
(string (char-after))
(point)
(1+ (point))))
;; Open angle bracket for type parameters
;;
;; We use a heuristic: spaces are inserted around inequality sign, but not
;; for angle bracket, and a type paramer starts with an upper case
;; character, a square bracket, a parenthesis, or keyword `protocol'.
((and (eq (char-before) ?<) (looking-at "\\([[:upper:]\\[[(]\\|protocol\\)"))
(backward-char)
(swift3-mode:token '< "<" (point) (1+ (point))))
;; Close angle bracket for type parameters
;;
;; Close angle bracket follows identifier, a square bracket, a parenthesis,
;; or another another bracket (e.g. Foo>)
((and (eq (char-before) ?>)
(save-excursion
(skip-chars-backward "])>")
(skip-syntax-backward "w_")
(looking-at "[[:upper:]_]")))
(backward-char)
(swift3-mode:token '> ">" (point) (1+ (point))))
;; Operator (other than as, try, or is)
;;
;; Operators which starts with a dot can contain other dots. Other operators
;; cannot contain dots.
;;
;; https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/dot-operator-head
((memq (char-before) '(?. ?- ?/ ?= ?+ ?! ?* ?% ?< ?> ?& ?| ?^ ?~ ??))
(let ((point-before-comments (point)))
(skip-chars-backward "-./=+!*%<>&|^~?")
(cond
((save-excursion
(forward-symbol -1)
(and (looking-at "\\(as\\|try\\)[?!]")
(= point-before-comments (match-end 0))))
;; as?, as!, try?, try!
t)
((looking-at "[.][-./=+!*%<>&|^~?]*")
;; e.g. 1 .++++.++++...+. 1
t)
((and (looking-at "[-/=+!*%<>&|^~?]+")
(<= point-before-comments (match-end 0)))
;; e.g. 1 +++++++++ 1
t)
(t
;; e.g. 1+++++...++++1, that is (1+++++) ...++++ 1
(skip-chars-forward "-/=+!*%<>&|^~?")
(looking-at "[.][-./=+!*%<>&|^~?]*")))
(let*
((start (match-beginning 0))
(end (min point-before-comments (match-end 0)))
(text (substring (match-string-no-properties 0) 0 (- end start))))
(goto-char start)
(swift3-mode:fix-operator-type
(swift3-mode:token nil text start end)))))
;; String and backquoted identifer
((memq (char-before) '(?\" ?`))
(let ((pos-before-comment (point)))
(goto-char (scan-sexps (point) -1))
(swift3-mode:token
'identifier
(buffer-substring-no-properties (point) pos-before-comment)
(point)
pos-before-comment)))
;; Other tokens including identifers, implicit parameters, keywords, and
;; numbers
(t
(let*
((pos-before-comment (point))
(text
(cond
;; Identifers, implicit parameters, keywords, numbers
;;
;; Note: syntax class _ includes #, @, and $.
((memq (char-syntax (char-before)) '(?w ?_))
(progn (forward-symbol -1)
(buffer-substring-no-properties (point) pos-before-comment)))
;; Unknown character type. Treats as a single-letter token.
(t (backward-char) (string (char-after))))))
(cond
((member text '("is" "as"))
(swift3-mode:token 'binary-operator
text
(point)
(+ (point) (length text))))
((equal text "try")
(swift3-mode:token 'prefix-operator
text
(point)
(+ (point) (length text))))
(t
(swift3-mode:token 'identifier
text
(point)
(+ (point) (length text)))))))))
(defun swift3-mode:goto-non-comment-bol ()
"Back to the beginning of line that is not inside a comment."
(beginning-of-line)
(while (nth 4 (syntax-ppss))
;; The cursor is in a comment. Backs to the beginning of the comment.
(goto-char (nth 8 (syntax-ppss)))
(beginning-of-line)))
(defun swift3-mode:goto-non-comment-eol ()
"Proceed to the end of line that is not inside a comment.
If this line ends with a single-line comment, goto just before the comment."
(end-of-line)
(while (nth 4 (syntax-ppss))
;; The cursor is in a comment.
(if (eq (nth 4 (syntax-ppss)) t)
;; This ia a single-line comment
;; Back to the beginning of the comment.
(goto-char (nth 8 (syntax-ppss)))
;; This is a multiline comment
;; Proceed to the end of the comment.
(goto-char (nth 8 (syntax-ppss)))
(forward-comment 1)
(end-of-line))))
(provide 'swift3-mode-lexer)
;;; swift3-mode-lexer.el ends here
swift3-mode-2.1.1/swift3-mode-pkg.el 0000644 0000000 0000000 00000000450 12767506611 015316 0 ustar 00 0000000 0000000 (define-package "swift3-mode" "2.1.1" "Major-mode for Apple's Swift programming language."
'((emacs "24.4"))
:commit "ea34d46bf9a4293e75ffdac9500d34989316d9e9" :keywords
'("languages" "swift")
:url "https://github.com/taku0/swift3-mode")
;; Local Variables:
;; no-byte-compile: t
;; End:
swift3-mode-2.1.1/swift3-mode-repl.el 0000644 0000000 0000000 00000007665 12767506611 015516 0 ustar 00 0000000 0000000 ;;; swift3-mode-repl.el --- Run Apple's Swift processes in Emacs buffers -*- lexical-binding: t -*-
;; Copyright (C) 2014-2016 taku0, Chris Barrett, Bozhidar Batsov, Arthur Evstifeev
;; Authors: taku0 (http://github.com/taku0)
;; Chris Barrett
;; Bozhidar Batsov
;; Arthur Evstifeev
;;
;; Version: 2.1.1
;; Package-Requires: ((emacs "24.4"))
;; Keywords: languages swift
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;;; Commentary:
;; Run Apple's Swift processes in Emacs buffers.
;;; Code:
(require 'comint)
(defcustom swift3-mode:repl-executable
"xcrun swift"
"Path to the Swift CLI."
:type 'string
:group 'swift3
:safe 'stringp)
(defvar swift3-mode:repl-buffer nil
"Stores the name of the current swift REPL buffer, or nil.")
;;;###autoload
(defun swift3-mode:run-repl (cmd &optional dont-switch)
"Run a Swift REPL process.
It input and output via buffer `*CMD*' where CMD is replaced with the CMD given.
If there is a process already running in `*CMD*', switch to that buffer.
With argument CMD allows you to edit the command line (default is value
of `swift3-mode:repl-executable'). This function updates the buffer local
variable `swift3-mode:repl-executable' with the given CMD, so it will be used
as the default value for the next invocatoin in the current buffer.
With DONT-SWITCH cursor will stay in current buffer.
Runs the hook `swift3-repl-mode-hook' \(after the `comint-mode-hook' is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
(interactive
(list (if current-prefix-arg
(read-string "Run swift REPL: " swift3-mode:repl-executable)
swift3-mode:repl-executable)))
(unless (comint-check-proc (concat "*" cmd "*"))
(save-excursion
(let ((cmdlist (split-string cmd)))
(set-buffer
(apply 'make-comint cmd (car cmdlist) nil (cdr cmdlist)))
(swift3-repl-mode))))
(setq-local swift3-mode:repl-executable cmd)
(setq-local swift3-mode:repl-buffer (concat "*" cmd "*"))
(setq-default swift3-mode:repl-buffer swift3-mode:repl-buffer)
(unless dont-switch
(pop-to-buffer swift3-mode:repl-buffer)))
;;;###autoload
(defalias 'run-swift 'swift3-mode:run-repl)
;;;###autoload
(defun swift3-mode:send-region (start end)
"Send the current region to the inferior swift process.
START and END define region within current buffer"
(interactive "r")
(swift3-mode:run-repl swift3-mode:repl-executable t)
(comint-send-region swift3-mode:repl-buffer start end)
(comint-send-string swift3-mode:repl-buffer "\n"))
;;;###autoload
(defun swift3-mode:send-buffer ()
"Send the buffer to the Swift REPL process."
(interactive)
(swift3-mode:send-region (point-min) (point-max)))
(define-derived-mode swift3-repl-mode comint-mode "Swift3 REPL"
"Major mode for interacting with Swift REPL.
A REPL can be fired up with M-x swift3-mode:run-repl or M-x run-swift.
Customization: Entry to this mode runs the hooks on comint-mode-hook and
swift3-repl-mode-hook (in that order).
You can send text to the REPL process from other buffers containing source.
swift3-mode:send-region sends the current region to the REPL process,
swift3-mode:send-buffer sends the current buffer to the REPL process.")
(provide 'swift3-mode-repl)
;;; swift3-mode-repl.el ends here
swift3-mode-2.1.1/swift3-mode.el 0000644 0000000 0000000 00000012703 12767506611 014543 0 ustar 00 0000000 0000000 ;;; swift3-mode.el --- Major-mode for Apple's Swift programming language. -*- lexical-binding: t -*-
;; Copyright (C) 2014-2016 taku0, Chris Barrett, Bozhidar Batsov, Arthur Evstifeev
;; Authors: taku0 (http://github.com/taku0)
;; Chris Barrett
;; Bozhidar Batsov
;; Arthur Evstifeev
;;
;; Version: 2.1.1
;; Package-Requires: ((emacs "24.4"))
;; Keywords: languages swift
;; URL: https://github.com/taku0/swift3-mode
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;;; Commentary:
;; Major-mode for Apple's Swift programming language.
;;; Code:
(require 'swift3-mode-lexer)
(require 'swift3-mode-indent)
(require 'swift3-mode-font-lock)
(require 'swift3-mode-beginning-of-defun)
(require 'swift3-mode-repl)
;;;###autoload
(defgroup swift3 nil
"Major-mode for Apple's Swift programming language."
:group 'languages
:prefix "swift-mode:")
;;; Keymap
(defvar swift3-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map prog-mode-map)
(define-key map (kbd "M-j") #'swift3-mode:indent-new-comment-line)
(define-key map (kbd "C-M-j") #'swift3-mode:indent-new-comment-line)
(define-key map (kbd "C-c C-z") 'swift-mode:run-repl)
(define-key map (kbd "C-c C-f") 'swift-mode:send-buffer)
(define-key map (kbd "C-c C-r") 'swift-mode:send-region)
(easy-menu-define swift-menu map "Swift3 Mode menu"
`("Swift3"
:help "Swift-specific Features"
["Run REPL" swift-mode-run-repl
:help "Run Swift REPL"]
["Send buffer to REPL" swift-mode-send-buffer
:help "Send the current buffer's contents to the REPL"]
["Send region to REPL" swift-mode-send-region
:help "Send currently selected region to the REPL"]))
map)
"Swift3 mode key map.")
;;; `foward-sexp-function'
(defun swift3-mode:forward-sexp (&optional arg)
(setq arg (or arg 1))
(if (< 0 arg)
(while (< 0 arg)
(while (eq
(swift3-mode:token:type (swift3-mode:forward-token-or-list))
'implicit-\;))
(setq arg (1- arg))))
(while (< arg 0)
(while (eq
(swift3-mode:token:type (swift3-mode:backward-token-or-list))
'implicit-\;))
(setq arg (1+ arg))))
;; Imenu
(defun swift3-mode:mk-regex-for-def (keyword)
"Make a regex matching the identifier introduced by KEYWORD."
(concat "\\<" (regexp-quote keyword) "\\>"
"\\s *"
"\\("
"\\(?:" "\\sw" "\\|" "\\s_" "\\)" "+"
"\\)"))
(defconst swift3-mode:imenu-generic-expression
(list
(list "Functions" (swift3-mode:mk-regex-for-def "func") 1)
(list "Classes" (swift3-mode:mk-regex-for-def "class") 1)
(list "Enums" (swift3-mode:mk-regex-for-def "enum") 1)
(list "Protocols" (swift3-mode:mk-regex-for-def "protocol") 1)
(list "Structs" (swift3-mode:mk-regex-for-def "struct") 1)
(list "Extensions" (swift3-mode:mk-regex-for-def "extension") 1)
(list "Constants" (swift3-mode:mk-regex-for-def "let") 1)
(list "Variables" (swift3-mode:mk-regex-for-def "var") 1))
"Value for `imenu-generic-expression' in `swift3-mode'.")
;;;###autoload
(define-derived-mode swift3-mode prog-mode "Swift"
"Major mode for editing Swift code.
\\{swift3-mode-map}"
:syntax-table swift3-mode:syntax-table
:group 'swift3
(setq font-lock-defaults '(swift3-mode:font-lock-keywords))
(setq-local comment-start "// ")
(setq-local comment-end "")
;; ":" is for Playground Rich Comments Markup Syntax:
;; https://developer.apple.com/library/prerelease/ios/documentation/Xcode/Reference/xcode_markup_formatting_ref/PlaygroundRichComments.html
(setq-local comment-start-skip
(concat
"\\s *"
"\\(?:"
;; Single-line comment
"//+" ":?" "\\|"
;; Multi-line comment
"/\\*+" ":?" "\\|"
;; Middle of multi-line-comment
"\\*+ "
"\\)"
"\\s *"))
(setq-local adaptive-fill-regexp comment-start-skip)
(setq-local comment-multi-line t)
(setq-local indent-tabs-mode nil)
(setq-local indent-line-function #'swift3-mode:indent-line)
(setq-local forward-sexp-function #'swift3-mode:forward-sexp)
(setq-local electric-indent-chars
(append "{}()[]:;,." electric-indent-chars))
(add-hook 'post-self-insert-hook #'swift3-mode:post-self-insert nil t)
(setq-local imenu-generic-expression swift3-mode:imenu-generic-expression)
(setq-local beginning-of-defun-function #'swift3-mode:beginning-of-defun)
(setq-local end-of-defun-function #'swift3-mode:end-of-defun)
(message "swift3-mode has been merged into swift-mode. Please uninstall swift3-mode and install swift-mode."))
;;;###autoload (add-to-list 'auto-mode-alist '("\\.swift\\'" . swift3-mode))
(provide 'swift3-mode)
;;; swift3-mode.el ends here