Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
2a21e1b
Add ternary operator smie grammar
Jan 23, 2015
f2b923e
Improve case statement grammar
Jan 23, 2015
0f57a7d
Add lexer rule for ":" token.
Jan 23, 2015
69289fe
Lexer improvement: don't use implicit semicolon when ":" and "?" token
Jan 23, 2015
d27b851
Add indentation rules for ternary operator
Jan 23, 2015
641681a
Fix small typo in comment string
Jan 24, 2015
2b17f71
Improve grammar and lexer to support access modifier in class definit…
Jan 24, 2015
2d0194e
Correct several indentation cases for class defitinion
Jan 24, 2015
cac2884
Add lists to the "type" grammar
Jan 24, 2015
f8190d6
Extract function into separate grammar and add return typo to the
Jan 24, 2015
fb2f48e
Add rule for assignment expression
Jan 24, 2015
75aa277
Add separate lexer rule for return type arrow symbol "->", prevent
Jan 24, 2015
a6267eb
Improve indentation for the function definitions
Jan 24, 2015
adfe341
Add grammar for the protocol definitions
Jan 24, 2015
1c4253b
Improve indentation for nested hash/array statements
Jan 25, 2015
01bcc35
Improve grammar for functions and contraint protocols grammar to
Jan 25, 2015
20134ed
Use postive lookahead for declaration specifier lexer rule
Jan 25, 2015
302e83d
Distinct generic type parameter from ">" operator when checking for
Jan 25, 2015
73cdda9
Don't put implicit semicolon after "("
Jan 25, 2015
5fabebd
Improve indentation rules for function arguments
Jan 25, 2015
ebc60c8
Make closure block in function argument close at the offset 0
Jan 25, 2015
c09229a
Use semantic hanging-p function instead of custom regex when checking
Jan 25, 2015
c2c3c01
Improve support for optional types
Jan 25, 2015
5c31636
Treat default token as case token
Jan 26, 2015
4ab5f7f
Add implicit semicolon rule for ?? operator
Jan 26, 2015
7a7038b
Remove supported for trailing dot in multi-line expression (invalid
Jan 26, 2015
fd228c0
Improve lexer rule for disctinction between case in switch and enum
Jan 26, 2015
1122ab9
Add grammar rule for return statement in function
Jan 26, 2015
d6483e1
Add ?? to the lexer rule for operators
Feb 7, 2015
38933b9
Don't use implicit semicolon when operator is placed on the second line
Feb 7, 2015
a60e3fa
Don't indent chain of operators multiple times
Feb 7, 2015
9e6c538
Correct DECSPEC regex and lexer rule
Feb 7, 2015
4bdbc23
Normalize indentation for function declration with argument list on the
Feb 8, 2015
d0c65bc
Fix bug with the closing parentheses in method calls and function
Feb 8, 2015
2c67d92
Fix indentation of the closure arguments inside parentheses
Feb 8, 2015
782378c
Correct regex in lexer for the "default" keyword
Feb 8, 2015
e539ba6
Improve support for the multi-line case statements
Feb 8, 2015
c38b721
Support default values for arguments in function declaration
Feb 9, 2015
cf219aa
Improve indentation of the nested arrays and dictionaries
Feb 9, 2015
5a858c7
Improve indentation of the array and dictionary expressions
Feb 9, 2015
107195e
Improve lexer rule for generics with optional or implicit unwrapping
Feb 9, 2015
cdf7ad4
Fix indentation issues with deep nested if cluase
Feb 10, 2015
462ea17
Improve closures grammar and add several related indentation rules
Feb 10, 2015
2cb54c9
Make grammar for if statement a bit more precise
Feb 10, 2015
f42752b
Respect comments when calculating position of the implicit semicolon
Feb 22, 2015
9941c27
Bring failing tests for #81 contributed in #82 by @dougbeal
Feb 22, 2015
b6bad10
Set minimum required version to 24.4
Feb 22, 2015
95e753b
Disable testing against emacs 24.3
Feb 23, 2015
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: emacs-lisp

env:
- EVM_EMACS=emacs-24.3-bin
- EVM_EMACS=emacs-24.4-bin

before_install:
Expand Down
204 changes: 152 additions & 52 deletions swift-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Arthur Evstifeev <lod@pisem.net>
;; Version: 0.4.0-cvs
;; Package-Requires: ((emacs "24.1"))
;; Package-Requires: ((emacs "24.4"))
;; Keywords: languages swift

;; This file is not part of GNU Emacs.
Expand Down Expand Up @@ -56,7 +56,7 @@
:type 'integerp)

(defcustom swift-indent-multiline-statement-offset 2
"Defines the indentation offset for for multiline statements."
"Defines the indentation offset for multiline statements."
:group 'swift
:type 'integerp
:package-version '(swift-mode "0.3.0"))
Expand All @@ -75,7 +75,7 @@
(smie-merge-prec2s
(smie-bnf->prec2
'((id)
(type (type) (type "<T" types "T>"))
(type (type) (type "<T" types "T>") ("[" type "]"))
(types (type) (type "," type))

(class-decl-exp (id) (id ":" types))
Expand All @@ -99,24 +99,36 @@
(top-level-st
("import" type)
(decl)
("class" class-decl-exp "{" class-level-sts "}"))
("ACCESSMOD" "class" class-decl-exp "{" class-level-sts "}")
("ACCESSMOD" "protocol" class-decl-exp "{" protocol-level-sts "}"))

(class-level-sts (class-level-st) (class-level-st ";" class-level-st))
(class-level-st
(decl)
("DECSPEC" "func" func-header "{" insts "}"))
(func))

(protocol-level-sts (protocol-level-st) (protocol-level-st ";" protocol-level-st))
(protocol-level-st
(decl)
(func-decl))

(func-body (insts) ("return" exp))
(func (func-decl "{" func-body "}"))
(func-decl ("DECSPEC" "func" func-header)
(func-decl "->" type))
(func-header (id "(" func-params ")"))
(func-param (decl-exp) ("..."))
(func-param (decl-exp) (decl-exp "=" id) ("..."))
(func-params (func-param "," func-param))

(insts (inst) (insts ";" insts))
(inst (decl)
(exp "=" exp)
(tern-exp)
(in-exp)
(dot-exp)
(dot-exp "{" insts "}")
(dot-exp "{" closure "}")
(method-call)
(method-call "{" insts "}")
(method-call "{" closure "}")
("enum" decl-exp "{" enum-body "}")
("switch" exp "{" switch-body "}")
(if-clause)
Expand All @@ -127,33 +139,36 @@

(method-call (dot-exp "(" method-args ")"))
(method-args (method-arg) (method-arg "," method-arg))
(method-arg (exp "," "{" insts "}") (exp))
(method-arg (exp "," "{" closure "}") (exp))

(exp (op-exp)
("[" decl-exps "]"))
(in-exp (id "in" exp))
(guard-exp (exp "where" exp))
(op-exp (exp "OP" exp))
(tern-exp (op-exp "?" exp ":" exp))

(enum-cases (assign-exp)
(enum-cases ";" "ecase" enum-cases))
(enum-case ("ecase" assign-exp)
("ecase" "(" type ")"))
(enum-cases (enum-case) (enum-case ";" enum-case))
(enum-body (enum-cases) (insts))

(case-exps (guard-exp))
(cases (case-exps ":" insts)
(cases "case" cases))
(switch-body (cases) (cases "default:" insts))
(case-exps (exp) (guard-exp))
(case (case-exps ":" insts))
(switch-body (case) (case "case" case))

(for-head (in-exp) (op-exp) (for-head ";" for-head))

(conditional (exp) (let-decl))
(if-body ("if" conditional "{" insts "}"))
(if-else-if (if-body) (if-else-if "else" if-else-if))
(if-clause (if-else-if)))
(if-conditional (exp) (let-decl))
(if-body ("if" if-conditional "{" insts "}"))
(if-clause (if-body)
(if-body "elseif" if-conditional "{" insts "}")
(if-body "else" "{" insts "}"))

(closure (insts) (exp "in" insts) (exp "->" id "in" insts)))
;; Conflicts
'((nonassoc "{") (assoc ",") (assoc ";") (assoc ":"))
'((nonassoc "{") (assoc "in") (assoc ",") (assoc ";") (assoc ":") (right "="))
'((assoc "in") (assoc "where") (assoc "OP"))
'((assoc "else"))
'((assoc ";") (assoc "ecase"))
'((assoc "case")))

Expand Down Expand Up @@ -187,29 +202,49 @@
"is" "as" "as?" ".." "..."
"+" "-" "&+" "&-" "|" "^"
"*" "/" "%" "&*" "&/" "&%" "&"
"<<" ">>")))
"<<" ">>" "??")))

(defvar swift-smie--decl-specifier-regexp
(rx (? (or "class" "mutating" "override" "static" "unowned" "weak"))
(* space) "func"))
"\\(?1:class\\|mutating\\|override\\|static\\|unowned\\|weak\\)\\(?:[[:space:]]*func\\)")

(defvar swift-smie--access-modifier-regexp
(regexp-opt '("private" "public" "internal")))

(defun swift-smie--implicit-semi-p ()
(save-excursion
(not (or (memq (char-before) '(?\{ ?\[ ?, ?.))
(looking-at "[ \n\t]+[.]")
(looking-back swift-smie--operators-regexp (- (point) 3) t)
(not (or (memq (char-before) '(?\{ ?\[ ?, ?. ?: ?= ?\())
;; Checking for operators form for "?" and "!",
;; they can be a part of the type.
;; Special case: is? and as? are operators.
(looking-back "[[:space:]][?!]" (- (point) 2) t)
;; ??, is? and as? are operators
(looking-back "[?][?]\\|as[?]\\|is[?]" (- (point) 3) t)
;; "in" operator in closure
(looking-back "in" (- (point) 2) t)
;; Characters placed on the second line in multi-line expression
(looking-at "[ \n\t]+[.?:]")
;; Operators placed on the second line in multi-line expression
;; Should respect here possible comments strict before the linebreak
(looking-at (concat "\\(\/\/.*\\)?\n[[:space:]]*" swift-smie--operators-regexp))
(and (looking-back swift-smie--operators-regexp (- (point) 3) t)
;; Not a generic type
(not (looking-back "[[:upper:]]>" (- (point) 2) t)))
))))

(defun swift-smie--forward-token ()
(skip-chars-forward " \t")
(cond
((and (looking-at "\n") (swift-smie--implicit-semi-p))
((and (looking-at "\n\\|\/\/") (swift-smie--implicit-semi-p))
(if (eolp) (forward-char 1) (forward-comment 1))
";")

((looking-at "{") (forward-char 1) "{")
((looking-at "}") (forward-char 1) "}")

((looking-at ",") (forward-char 1) ",")
((looking-at ":") (forward-char 1) ":")

((looking-at "->") (forward-char 2) "->")

((looking-at "<") (forward-char 1)
(if (looking-at "[[:upper:]]") "<T" "OP"))
Expand All @@ -220,12 +255,21 @@
(goto-char (match-end 0)) "OP")

((looking-at swift-smie--decl-specifier-regexp)
(goto-char (match-end 0)) "DECSPEC")
(goto-char (match-end 1)) "DECSPEC")

((looking-at swift-smie--access-modifier-regexp)
(goto-char (match-end 0)) "ACCESSMOD")

((looking-at "\\<default\\>")
(goto-char (match-end 0)) "case")

((looking-at "else if")
(goto-char (match-end 0)) "elseif")

(t (let ((tok (smie-default-forward-token)))
(cond
((equal tok "case")
(if (looking-at ".+\\(,\\|:\\)")
(if (looking-at "\\([\n\t ]\\|.\\)+?\\(where.*[,]\\|:\\)")
"case"
"ecase"))
(t tok))))
Expand All @@ -243,22 +287,36 @@
((eq (char-before) ?\}) (backward-char 1) "}")

((eq (char-before) ?,) (backward-char 1) ",")
((eq (char-before) ?:) (backward-char 1) ":")

((looking-back "->" (- (point) 2) t)
(goto-char (match-beginning 0)) "->")

((eq (char-before) ?<) (backward-char 1)
(if (looking-at "<[[:upper:]]") "<T" "OP"))
((eq (char-before) ?>) (backward-char 1)
((looking-back ">[?!]?" (- (point) 2) t)
(goto-char (match-beginning 0))
(if (looking-back "[[:space:]]" 1 t) "OP" "T>"))

((looking-back swift-smie--operators-regexp (- (point) 3) t)
(goto-char (match-beginning 0)) "OP")

((looking-back swift-smie--decl-specifier-regexp (- (point) 8) t)
(goto-char (match-beginning 0)) "DECSPEC")
(goto-char (match-beginning 1)) "DECSPEC")

((looking-back swift-smie--access-modifier-regexp (- (point) 8) t)
(goto-char (match-beginning 0)) "ACCESSMOD")

((looking-back "\\<default\\>" (- (point) 9) t)
(goto-char (match-beginning 0)) "case")

((looking-back "else if" (- (point) 7) t)
(goto-char (match-beginning 0)) "elseif")

(t (let ((tok (smie-default-backward-token)))
(cond
((equal tok "case")
(if (looking-at ".+\\(,\\|:\\)")
(if (looking-at "\\([\n\t ]\\|.\\)+?\\(where.*[,]\\|:\\)")
"case"
"ecase"))
(t tok))))
Expand All @@ -268,43 +326,73 @@
(pcase (cons kind token)
(`(:elem . basic) swift-indent-offset)

(`(:after . ":") 0)
(`(:before . ":")
(cond
;; Rule for ternary operator in
;; assignment expression.
;; Static indentation relatively to =
((smie-rule-parent-p "=") 2)
;; Rule for the case statement.
((smie-rule-parent-p "case") swift-indent-offset)
;; Rule for the class definition.
((smie-rule-parent-p "class") (smie-rule-parent swift-indent-offset))))

(`(:after . "{")
(if (smie-rule-parent-p "switch")
(smie-rule-parent swift-indent-switch-case-offset)))
(`(:before . ";")
(if (smie-rule-parent-p "case" "default:")
(if (smie-rule-parent-p "case")
(smie-rule-parent swift-indent-offset)))

;; Apply swift-indent-multiline-statement-offset only if
;; - dot is followed by newline, or
;; - if dot is a first token on the line
;; - if is a first token on the line
(`(:before . ".")
(if (or (looking-at "[.][\n]")
(smie-rule-bolp))
swift-indent-multiline-statement-offset))
(when (smie-rule-bolp)
(if (smie-rule-parent-p "{")
(+ swift-indent-offset swift-indent-multiline-statement-offset)
swift-indent-multiline-statement-offset)))

;; Apply swift-indent-multiline-statement-offset if
;; operator is the last symbol on the line
(`(:before . "OP")
(if (and (looking-at ".[\n]")
(not (smie-rule-sibling-p)))
swift-indent-multiline-statement-offset))
(when (and (smie-rule-hanging-p)
(not (smie-rule-parent-p "OP")))
(if (smie-rule-parent-p "{")
(+ swift-indent-offset swift-indent-multiline-statement-offset)
swift-indent-multiline-statement-offset)))

;; Indent second line of the multi-line class
;; definitions with swift-indent-offset
(`(:before . ",")
(if (smie-rule-parent-p "class")
swift-indent-offset))
(smie-rule-parent swift-indent-offset)))

(`(:before . "if")
(if (smie-rule-prev-p "else")
(if (smie-rule-parent-p "{")
swift-indent-offset
(smie-rule-parent))))
;; Disable unnecessary default indentation for
;; "func" and "class" keywords
(`(:after . ,(or `"func" `"class")) (smie-rule-parent))

;; "in" token in closure
(`(:after . "in")
(if (smie-rule-parent-p "{")
(smie-rule-parent swift-indent-offset)))

(`(:after . "(")
(if (smie-rule-parent-p "(") 0
(smie-rule-parent swift-indent-offset)))
(`(:before . "(")
(if (smie-rule-next-p "[") (smie-rule-parent)))
(`(:before . "[") (smie-rule-parent))
(cond
((smie-rule-next-p "[") (smie-rule-parent))
;; Custom indentation for method arguments
((smie-rule-parent-p "." "func") (smie-rule-parent))))

(`(:before . "[")
(cond
((smie-rule-prev-p "->") swift-indent-offset)
((smie-rule-parent-p "[") (smie-rule-parent swift-indent-offset))
((smie-rule-parent-p "{") nil)
(t (smie-rule-parent))))
(`(:after . "->") (smie-rule-parent swift-indent-offset))
))

;;; Font lock
Expand Down Expand Up @@ -602,11 +690,23 @@ You can send text to the REPL process from other buffers containing source.
;; Parenthesis, braces and brackets
(modify-syntax-entry ?\( "()" table)
(modify-syntax-entry ?\) ")(" table)
(modify-syntax-entry ?\{ "(}" table)
(modify-syntax-entry ?\} "){" table)
(modify-syntax-entry ?\[ "(]" table)
(modify-syntax-entry ?\] ")[" table)

;; HACK: This is not a correct syntax table definition
;; for the braces, but it allows us disable smie indentation
;; based on syntax-table. Default behaviour doesn't work with
;; closures in method arguments. For example:
;;
;; foo.bar(10,
;; closure: {
;; })
;;
;; With enabled syntax table, smie doesn't respect closing brace, so
;; it's impossible to provide custom indentation rules
(modify-syntax-entry ?\{ "w" table)
(modify-syntax-entry ?\} "w" table)

table))

(defvar swift-mode-map
Expand Down
Loading