ADDED vim/ftdetect/swift.vim Index: vim/ftdetect/swift.vim ================================================================== --- vim/ftdetect/swift.vim +++ vim/ftdetect/swift.vim @@ -0,0 +1,12 @@ +autocmd BufNewFile,BufRead *.swift set filetype=swift +autocmd BufRead * call s:Swift() +function! s:Swift() + if !empty(&filetype) + return + endif + + let line = getline(1) + if line =~ "^#!.*swift" + setfiletype swift + endif +endfunction ADDED vim/ftplugin/swift.vim Index: vim/ftplugin/swift.vim ================================================================== --- vim/ftplugin/swift.vim +++ vim/ftplugin/swift.vim @@ -0,0 +1,4 @@ +setlocal commentstring=//\ %s +" @-@ adds the literal @ to iskeyword for @IBAction and similar +setlocal iskeyword+=@-@,# +setlocal completefunc=syntaxcomplete#Complete ADDED vim/indent/swift.vim Index: vim/indent/swift.vim ================================================================== --- vim/indent/swift.vim +++ vim/indent/swift.vim @@ -0,0 +1,294 @@ +" File: swift.vim +" Author: Keith Smiley +" Description: The indent file for Swift +" Last Modified: December 05, 2014 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +let s:cpo_save = &cpo +set cpo&vim + +setlocal nosmartindent +setlocal indentkeys-=e +setlocal indentkeys+=0] +setlocal indentexpr=SwiftIndent() + +function! s:NumberOfMatches(char, string, index) + let instances = 0 + let i = 0 + while i < strlen(a:string) + if a:string[i] == a:char && !s:IsExcludedFromIndentAtPosition(a:index, i + 1) + let instances += 1 + endif + + let i += 1 + endwhile + + return instances +endfunction + +function! s:SyntaxNameAtPosition(line, column) + return synIDattr(synID(a:line, a:column, 0), "name") +endfunction + +function! s:SyntaxName() + return s:SyntaxNameAtPosition(line("."), col(".")) +endfunction + +function! s:IsExcludedFromIndentAtPosition(line, column) + let name = s:SyntaxNameAtPosition(a:line, a:column) + return s:IsSyntaxNameExcludedFromIndent(name) +endfunction + +function! s:IsExcludedFromIndent() + return s:IsSyntaxNameExcludedFromIndent(s:SyntaxName()) +endfunction + +function! s:IsSyntaxNameExcludedFromIndent(name) + return a:name ==# "swiftComment" || a:name ==# "swiftString" || a:name ==# "swiftInterpolatedWrapper" || a:name ==# "swiftMultilineInterpolatedWrapper" || a:name ==# "swiftMultilineString" +endfunction + +function! s:IsCommentLine(lnum) + return synIDattr(synID(a:lnum, + \ match(getline(a:lnum), "\\S") + 1, 0), "name") + \ ==# "swiftComment" +endfunction + +function! SwiftIndent(...) + let clnum = a:0 ? a:1 : v:lnum + + let line = getline(clnum) + let previousNum = prevnonblank(clnum - 1) + while s:IsCommentLine(previousNum) != 0 + let previousNum = prevnonblank(previousNum - 1) + endwhile + + let previous = getline(previousNum) + let cindent = cindent(clnum) + let previousIndent = indent(previousNum) + + let numOpenParens = s:NumberOfMatches("(", previous, previousNum) + let numCloseParens = s:NumberOfMatches(")", previous, previousNum) + let numOpenBrackets = s:NumberOfMatches("{", previous, previousNum) + let numCloseBrackets = s:NumberOfMatches("}", previous, previousNum) + + let currentOpenBrackets = s:NumberOfMatches("{", line, clnum) + let currentCloseBrackets = s:NumberOfMatches("}", line, clnum) + + let numOpenSquare = s:NumberOfMatches("[", previous, previousNum) + let numCloseSquare = s:NumberOfMatches("]", previous, previousNum) + + let currentCloseSquare = s:NumberOfMatches("]", line, clnum) + if numOpenSquare > numCloseSquare && currentCloseSquare < 1 + return previousIndent + shiftwidth() + endif + + if currentCloseSquare > 0 && line !~ '\v\[.*\]' + let column = col(".") + call cursor(line("."), 1) + let openingSquare = searchpair("\\[", "", "\\]", "bWn", "s:IsExcludedFromIndent()") + call cursor(line("."), column) + + if openingSquare == 0 + return -1 + endif + + " - Line starts with closing square, indent as opening square + if line =~ '\v^\s*]' + return indent(openingSquare) + endif + + " - Line contains closing square and more, indent a level above opening + return indent(openingSquare) + shiftwidth() + endif + + if line =~ ":$" && (line =~ '^\s*case\W' || line =~ '^\s*default\W') + let switch = search("switch", "bWn") + return indent(switch) + elseif previous =~ ":$" && (previous =~ '^\s*case\W' || previous =~ '^\s*default\W') + return previousIndent + shiftwidth() + endif + + if numOpenParens == numCloseParens + if numOpenBrackets > numCloseBrackets + if currentCloseBrackets > currentOpenBrackets || line =~ "\\v^\\s*}" + let column = col(".") + call cursor(line("."), 1) + let openingBracket = searchpair("{", "", "}", "bWn", "s:IsExcludedFromIndent()") + call cursor(line("."), column) + if openingBracket == 0 + return -1 + else + return indent(openingBracket) + endif + endif + + return previousIndent + shiftwidth() + elseif previous =~ "}.*{" + if line =~ "\\v^\\s*}" + return previousIndent + endif + + return previousIndent + shiftwidth() + elseif line =~ "}.*{" + let openingBracket = searchpair("{", "", "}", "bWn", "s:IsExcludedFromIndent()") + + let bracketLine = getline(openingBracket) + let numOpenParensBracketLine = s:NumberOfMatches("(", bracketLine, openingBracket) + let numCloseParensBracketLine = s:NumberOfMatches(")", bracketLine, openingBracket) + if numOpenParensBracketLine > numCloseParensBracketLine + let line = line(".") + let column = col(".") + call cursor(openingParen, column) + let openingParenCol = searchpairpos("(", "", ")", "bWn", "s:IsExcludedFromIndent()")[1] + call cursor(line, column) + return openingParenCol + endif + + return indent(openingBracket) + elseif currentCloseBrackets > currentOpenBrackets + let column = col(".") + let line = line(".") + call cursor(line, 1) + let openingBracket = searchpair("{", "", "}", "bWn", "s:IsExcludedFromIndent()") + call cursor(line, column) + + let bracketLine = getline(openingBracket) + + let numOpenParensBracketLine = s:NumberOfMatches("(", bracketLine, openingBracket) + let numCloseParensBracketLine = s:NumberOfMatches(")", bracketLine, openingBracket) + if numCloseParensBracketLine > numOpenParensBracketLine + let line = line(".") + let column = col(".") + call cursor(openingParen, column) + let openingParen = searchpair("(", "", ")", "bWn", "s:IsExcludedFromIndent()") + call cursor(line, column) + return indent(openingParen) + elseif numOpenParensBracketLine > numCloseParensBracketLine + let line = line(".") + let column = col(".") + call cursor(openingParen, column) + let openingParenCol = searchpairpos("(", "", ")", "bWn", "s:IsExcludedFromIndent()")[1] + call cursor(line, column) + return openingParenCol + endif + + return indent(openingBracket) + elseif line =~ '^\s*)$' + let line = line(".") + let column = col(".") + call cursor(line, 1) + let openingParen = searchpair("(", "", ")", "bWn", "s:IsExcludedFromIndent()") + call cursor(line, column) + return indent(openingParen) + else + " - Current line is blank, and the user presses 'o' + return previousIndent + endif + endif + + if numCloseParens > 0 + if currentOpenBrackets > 0 || currentCloseBrackets > 0 + if currentOpenBrackets > 0 + if numOpenBrackets > numCloseBrackets + return previousIndent + shiftwidth() + endif + + if line =~ "}.*{" + let openingBracket = searchpair("{", "", "}", "bWn", "s:IsExcludedFromIndent()") + return indent(openingBracket) + endif + + if numCloseParens > numOpenParens + let line = line(".") + let column = col(".") + call cursor(line - 1, column) + let openingParen = searchpair("(", "", ")", "bWn", "s:IsExcludedFromIndent()") + call cursor(line, column) + return indent(openingParen) + endif + + return previousIndent + endif + + if currentCloseBrackets > 0 + let openingBracket = searchpair("{", "", "}", "bWn", "s:IsExcludedFromIndent()") + return indent(openingBracket) + endif + + return cindent + endif + + if numCloseParens < numOpenParens + if numOpenBrackets > numCloseBrackets + return previousIndent + shiftwidth() + endif + + let previousParen = match(previous, '\v\($') + if previousParen != -1 + return previousIndent + shiftwidth() + endif + + let line = line(".") + let column = col(".") + call cursor(previousNum, col([previousNum, "$"])) + let previousParen = searchpairpos("(", "", ")", "cbWn", "s:IsExcludedFromIndent()") + call cursor(line, column) + + " Match the last non escaped paren on the previous line + return previousParen[1] + endif + + if numOpenBrackets > numCloseBrackets + let line = line(".") + let column = col(".") + call cursor(previousNum, column) + let openingParen = searchpair("(", "", ")", "bWn", "s:IsExcludedFromIndent()") + call cursor(line, column) + return openingParen + 1 + endif + + " - Previous line has close then open braces, indent previous + 1 'sw' + if previous =~ "}.*{" + return previousIndent + shiftwidth() + endif + + let line = line(".") + let column = col(".") + call cursor(previousNum, column) + let openingParen = searchpair("(", "", ")", "bWn", "s:IsExcludedFromIndent()") + call cursor(line, column) + + return indent(openingParen) + endif + + " - Line above has (unmatched) open paren, next line needs indent + if numOpenParens > 0 + let savePosition = getcurpos() + let lastColumnOfPreviousLine = col([previousNum, "$"]) - 1 + " Must be at EOL because open paren has to be above (left of) the cursor + call cursor(previousNum, lastColumnOfPreviousLine) + let previousParen = searchpairpos("(", "", ")", "cbWn", "s:IsExcludedFromIndent()")[1] + " If the paren on the last line is the last character, indent the contents + " at shiftwidth + previous indent + if previousParen == lastColumnOfPreviousLine + return previousIndent + shiftwidth() + endif + + " The previous line opens a closure and doesn't close it + if numOpenBrackets > numCloseBrackets + return previousParen + shiftwidth() + endif + + call setpos(".", savePosition) + return previousParen + endif + + return cindent +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save ADDED vim/syntax/swift.vim Index: vim/syntax/swift.vim ================================================================== --- vim/syntax/swift.vim +++ vim/syntax/swift.vim @@ -0,0 +1,305 @@ +" File: swift.vim +" Author: Keith Smiley +" Description: Runtime files for Swift + +if exists("b:current_syntax") + finish +endif + +" Comments +" Shebang +syntax match swiftShebang "\v#!.*$" + +" Comment contained keywords +syntax keyword swiftTodos contained TODO XXX FIXME NOTE +syntax keyword swiftMarker contained MARK + +" In comment identifiers +function! s:CommentKeywordMatch(keyword) + execute "syntax match swiftDocString \"\\v^\\s*-\\s*". a:keyword . "\\W\"hs=s+1,he=e-1 contained" +endfunction + +syntax case ignore + +call s:CommentKeywordMatch("attention") +call s:CommentKeywordMatch("author") +call s:CommentKeywordMatch("authors") +call s:CommentKeywordMatch("bug") +call s:CommentKeywordMatch("complexity") +call s:CommentKeywordMatch("copyright") +call s:CommentKeywordMatch("date") +call s:CommentKeywordMatch("experiment") +call s:CommentKeywordMatch("important") +call s:CommentKeywordMatch("invariant") +call s:CommentKeywordMatch("note") +call s:CommentKeywordMatch("parameter") +call s:CommentKeywordMatch("postcondition") +call s:CommentKeywordMatch("precondition") +call s:CommentKeywordMatch("remark") +call s:CommentKeywordMatch("remarks") +call s:CommentKeywordMatch("requires") +call s:CommentKeywordMatch("returns") +call s:CommentKeywordMatch("see") +call s:CommentKeywordMatch("since") +call s:CommentKeywordMatch("throws") +call s:CommentKeywordMatch("todo") +call s:CommentKeywordMatch("version") +call s:CommentKeywordMatch("warning") + +syntax case match +delfunction s:CommentKeywordMatch + + +" Literals +" Strings +syntax region swiftString start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=swiftInterpolatedWrapper oneline +syntax region swiftMultilineString start=/"""/ end=/"""/ contains=swiftMultilineInterpolatedWrapper +syntax region swiftMultilineInterpolatedWrapper start='\v\zs\\\(\s*' end='\v\s*\)' contained containedin=swiftMultilineString contains=swiftInterpolatedString oneline +syntax region swiftInterpolatedWrapper start='\v(^|[^\\])\zs\\\(\s*' end='\v\s*\)' contained containedin=swiftString contains=swiftInterpolatedString,swiftString oneline +syntax match swiftInterpolatedString "\v\w+(\(\))?" contained containedin=swiftInterpolatedWrapper,swiftMultilineInterpolatedWrapper oneline + +" Numbers +syntax match swiftNumber "\v<\d+>" +syntax match swiftNumber "\v<(\d+_+)+\d+(\.\d+(_+\d+)*)?>" +syntax match swiftNumber "\v<\d+\.\d+>" +syntax match swiftNumber "\v<\d*\.?\d+([Ee]-?)?\d+>" +syntax match swiftNumber "\v<0x[[:xdigit:]_]+([Pp]-?)?\x+>" +syntax match swiftNumber "\v<0b[01_]+>" +syntax match swiftNumber "\v<0o[0-7_]+>" + +" BOOLs +syntax keyword swiftBoolean + \ true + \ false + + +" Operators +syntax match swiftOperator "\v\~" +syntax match swiftOperator "\v\s+!" +syntax match swiftOperator "\v\%" +syntax match swiftOperator "\v\^" +syntax match swiftOperator "\v\&" +syntax match swiftOperator "\v\*" +syntax match swiftOperator "\v-" +syntax match swiftOperator "\v\+" +syntax match swiftOperator "\v\=" +syntax match swiftOperator "\v\|" +syntax match swiftOperator "\v\/" +syntax match swiftOperator "\v\<" +syntax match swiftOperator "\v\>" +syntax match swiftOperator "\v\?\?" + +" Methods/Functions/Properties +syntax match swiftMethod "\.\@<=\<\D\w*\>\ze(" +syntax match swiftProperty "\.\@<=\<\D\w*\>(\@!" + +" Swift closure arguments +syntax match swiftClosureArgument "\$\d\+\(\.\d\+\)\?" + +syntax match swiftAvailability "\v((\*(\s*,\s*[a-zA-Z="0-9.]+)*)|(\w+\s+\d+(\.\d+(.\d+)?)?\s*,\s*)+\*)" contains=swiftString +syntax keyword swiftPlatforms OSX iOS watchOS OSXApplicationExtension iOSApplicationExtension contained containedin=swiftAvailability +syntax keyword swiftAvailabilityArg renamed unavailable introduced deprecated obsoleted message contained containedin=swiftAvailability + +" Keywords {{{ +syntax keyword swiftKeywords + \ associatedtype + \ associativity + \ atexit + \ break + \ case + \ catch + \ class + \ continue + \ convenience + \ default + \ defer + \ deinit + \ didSet + \ do + \ dynamic + \ else + \ extension + \ fallthrough + \ fileprivate + \ final + \ for + \ func + \ get + \ guard + \ if + \ import + \ in + \ infix + \ init + \ inout + \ internal + \ lazy + \ let + \ mutating + \ nil + \ nonmutating + \ open + \ operator + \ optional + \ override + \ postfix + \ precedence + \ precedencegroup + \ prefix + \ private + \ protocol + \ public + \ repeat + \ required + \ return + \ self + \ set + \ some + \ static + \ subscript + \ super + \ switch + \ throw + \ try + \ typealias + \ unowned + \ var + \ weak + \ where + \ while + \ willSet + +syntax keyword swiftDefinitionModifier + \ rethrows + \ throws + +syntax match swiftMultiwordKeywords "indirect case" +syntax match swiftMultiwordKeywords "indirect enum" +" }}} + +" Names surrounded by backticks. This aren't limited to keywords because 1) +" Swift doesn't limit them to keywords and 2) I couldn't make the keywords not +" highlight at the same time +syntax region swiftEscapedReservedWord start="`" end="`" oneline + +syntax keyword swiftAttributes + \ @_exported + \ @_functionBuilder + \ @_implementationOnly + \ @_silgen_name + \ @assignment + \ @autoclosure + \ @available + \ @convention + \ @discardableResult + \ @escaping + \ @exported + \ @frozen + \ @IBAction + \ @IBDesignable + \ @IBInspectable + \ @IBOutlet + \ @inlinable + \ @noescape + \ @nonobjc + \ @noreturn + \ @NSApplicationMain + \ @NSCopying + \ @NSManaged + \ @objc + \ @propertyWrapper + \ @testable + \ @UIApplicationMain + \ @usableFromInline + \ @warn_unused_result + +syntax keyword swiftConditionStatement #available + +syntax keyword swiftStructure + \ struct + \ enum + +syntax keyword swiftDebugIdentifier + \ #column + \ #file + \ #function + \ #line + \ __COLUMN__ + \ __FILE__ + \ __FUNCTION__ + \ __LINE__ + +syntax keyword swiftLineDirective #setline + +syntax region swiftTypeWrapper start=":\s*\(\.\)\@!\<\u" skip="\s*,\s*$*\s*" end="$\|/"me=e-1 contains=ALLBUT,swiftInterpolatedWrapper,swiftMultilineInterpolatedWrapper transparent +syntax region swiftTypeCastWrapper start="\(as\|is\)\(!\|?\)\=\s\+" end="\v(\s|$|\{)" contains=swiftType,swiftCastKeyword keepend transparent oneline +syntax region swiftGenericsWrapper start="\v\<" end="\v\>" contains=swiftType transparent oneline +syntax region swiftLiteralWrapper start="\v\=\s*" skip="\v[^\[\]]\(\)" end="\v(\[\]|\(\))" contains=ALL transparent oneline +syntax region swiftReturnWrapper start="\v-\>\s*" end="\v(\{|$)" contains=swiftType transparent oneline +syntax match swiftType "\v<\u\w*" contained containedin=swiftTypeWrapper,swiftLiteralWrapper,swiftGenericsWrapper,swiftTypeCastWrapper +syntax match swiftTypeDeclaration /->/ skipwhite nextgroup=swiftType + +syntax keyword swiftImports import +syntax keyword swiftCastKeyword is as contained + +" 'preprocesor' stuff +syntax keyword swiftPreprocessor + \ #if + \ #elseif + \ #else + \ #endif + \ #selector + \ #warning + \ #error + + +" Comment patterns +syntax match swiftComment "\v\/\/.*$" + \ contains=swiftTodos,swiftDocString,swiftMarker,@Spell oneline +syntax region swiftComment start="/\*" end="\*/" + \ contains=swiftTodos,swiftDocString,swiftMarker,@Spell fold + + +" Set highlights +highlight default link swiftTodos Todo +highlight default link swiftDocString String +highlight default link swiftShebang Comment +highlight default link swiftComment Comment +highlight default link swiftMarker Comment + +highlight default link swiftString String +highlight default link swiftMultilineString String +highlight default link swiftInterpolatedWrapper Delimiter +highlight default link swiftMultilineInterpolatedWrapper Delimiter +highlight default link swiftTypeDeclaration Delimiter +highlight default link swiftNumber Number +highlight default link swiftBoolean Boolean + +highlight default link swiftOperator Operator +highlight default link swiftCastKeyword Keyword +highlight default link swiftKeywords Keyword +highlight default link swiftMultiwordKeywords Keyword +highlight default link swiftEscapedReservedWord Normal +highlight default link swiftClosureArgument Operator +highlight default link swiftAttributes PreProc +highlight default link swiftConditionStatement PreProc +highlight default link swiftStructure Structure +highlight default link swiftType Type +highlight default link swiftImports Include +highlight default link swiftPreprocessor PreProc +highlight default link swiftMethod Function +highlight default link swiftProperty Identifier + +highlight default link swiftDefinitionModifier Define +highlight default link swiftConditionStatement PreProc +highlight default link swiftAvailability Normal +highlight default link swiftAvailabilityArg Normal +highlight default link swiftPlatforms Keyword +highlight default link swiftDebugIdentifier PreProc +highlight default link swiftLineDirective PreProc + +" Force vim to sync at least x lines. This solves the multiline comment not +" being highlighted issue +syn sync minlines=100 + +let b:current_syntax = "swift"