summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2016-07-26 14:06:32 +0200
committerAdam Stankiewicz <sheerun@sher.pl>2016-07-26 14:06:32 +0200
commitcb574b283fd19eea20d95c768b64c6b805d81690 (patch)
treed948536dd865ce76bdec248d221b58dc1789c79f
parentb9ce3df4cd3a93a3ff309301771afbbe92984492 (diff)
downloadvim-polyglot-cb574b283fd19eea20d95c768b64c6b805d81690.tar.gz
vim-polyglot-cb574b283fd19eea20d95c768b64c6b805d81690.zip
Add livescript, closes #135
-rw-r--r--README.md1
-rwxr-xr-xbuild1
-rw-r--r--compiler/ls.vim78
-rw-r--r--extras/flow.vim46
-rw-r--r--extras/jsdoc.vim43
-rw-r--r--extras/ngdoc.vim7
-rw-r--r--ftdetect/polyglot.vim13
-rw-r--r--ftplugin/ls.vim208
-rw-r--r--indent/ls.vim268
-rw-r--r--syntax/ls.vim140
10 files changed, 805 insertions, 0 deletions
diff --git a/README.md b/README.md
index dc32ece8..0f0ec43e 100644
--- a/README.md
+++ b/README.md
@@ -65,6 +65,7 @@ Optionally download one of the [releases](https://github.com/sheerun/vim-polyglo
- [latex](https://github.com/LaTeX-Box-Team/LaTeX-Box) (syntax, indent, ftplugin)
- [less](https://github.com/groenewege/vim-less) (syntax, indent, ftplugin, ftdetect)
- [liquid](https://github.com/tpope/vim-liquid) (syntax, indent, ftplugin, ftdetect)
+- [livescript](https://github.com/gkz/vim-ls) (syntax, indent, compiler, ftplugin, ftdetect)
- [lua](https://github.com/tbastos/vim-lua) (syntax, indent)
- [mako](https://github.com/sophacles/vim-bundle-mako) (syntax, indent, ftplugin, ftdetect)
- [markdown](https://github.com/plasticboy/vim-markdown) (syntax, ftdetect)
diff --git a/build b/build
index 55942220..ebcdb099 100755
--- a/build
+++ b/build
@@ -139,6 +139,7 @@ PACKS="
latex:LaTeX-Box-Team/LaTeX-Box
less:groenewege/vim-less
liquid:tpope/vim-liquid
+ livescript:gkz/vim-ls
lua:tbastos/vim-lua
mako:sophacles/vim-bundle-mako
markdown:plasticboy/vim-markdown:_SYNTAX
diff --git a/compiler/ls.vim b/compiler/ls.vim
new file mode 100644
index 00000000..df792917
--- /dev/null
+++ b/compiler/ls.vim
@@ -0,0 +1,78 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'livescript') == -1
+
+" Language: LiveScript
+" Maintainer: George Zahariev
+" URL: http://github.com/gkz/vim-ls
+" License: WTFPL
+
+if exists('current_compiler')
+ finish
+endif
+
+let current_compiler = 'ls'
+" Pattern to check if livescript is the compiler
+let s:pat = '^' . current_compiler
+
+" Path to LiveScript compiler
+if !exists('livescript_compiler')
+ let livescript_compiler = 'lsc'
+endif
+
+if !exists('livescript_make_options')
+ let livescript_make_options = ''
+endif
+
+" Get a `makeprg` for the current filename. This is needed to support filenames
+" with spaces and quotes, but also not break generic `make`.
+function! s:GetMakePrg()
+ return g:livescript_compiler . ' -c ' . g:livescript_make_options . ' $* '
+ \ . fnameescape(expand('%'))
+endfunction
+
+" Set `makeprg` and return 1 if coffee is still the compiler, else return 0.
+function! s:SetMakePrg()
+ if &l:makeprg =~ s:pat
+ let &l:makeprg = s:GetMakePrg()
+ elseif &g:makeprg =~ s:pat
+ let &g:makeprg = s:GetMakePrg()
+ else
+ return 0
+ endif
+
+ return 1
+endfunction
+
+" Set a dummy compiler so we can check whether to set locally or globally.
+CompilerSet makeprg=ls
+call s:SetMakePrg()
+
+CompilerSet errorformat=%EFailed\ at:\ %f,
+ \%ECan't\ find:\ %f,
+ \%CSyntaxError:\ %m\ on\ line\ %l,
+ \%CError:\ Parse\ error\ on\ line\ %l:\ %m,
+ \%C,%C\ %.%#
+
+" Compile the current file.
+command! -bang -bar -nargs=* LiveScriptMake make<bang> <args>
+
+" Set `makeprg` on rename since we embed the filename in the setting.
+augroup LiveScriptUpdateMakePrg
+ autocmd!
+
+ " Update `makeprg` if livescript is still the compiler, else stop running this
+ " function.
+ function! s:UpdateMakePrg()
+ if !s:SetMakePrg()
+ autocmd! LiveScriptUpdateMakePrg
+ endif
+ endfunction
+
+ " Set autocmd locally if compiler was set locally.
+ if &l:makeprg =~ s:pat
+ autocmd BufFilePost,BufWritePost <buffer> call s:UpdateMakePrg()
+ else
+ autocmd BufFilePost,BufWritePost call s:UpdateMakePrg()
+ endif
+augroup END
+
+endif
diff --git a/extras/flow.vim b/extras/flow.vim
index a18c8825..6ce7ed99 100644
--- a/extras/flow.vim
+++ b/extras/flow.vim
@@ -44,3 +44,49 @@ if version >= 508 || !exists("did_javascript_syn_inits")
endif
endif
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'javascript') == -1
+
+syntax region jsFlowTypeStatement start=/type/ end=/=/ oneline skipwhite skipempty nextgroup=jsFlowTypeObject
+syntax region jsFlowDeclareBlock start=/declare/ end=/[;\n]/ oneline contains=jsFlow,jsFlowDeclareKeyword,jsFlowStorageClass
+syntax region jsFlow start=/:/ end=/\%(\%([),=;\n]\|{\%(.*}\)\@!\|\%({.*}\)\@<=\s*{\)\@=\|void\)/ contains=@jsFlowCluster oneline skipwhite skipempty nextgroup=jsFuncBlock
+syntax region jsFlowReturn contained start=/:/ end=/\%(\S\s*\%({\%(.*}\)\@!\)\@=\|\n\)/ contains=@jsFlowCluster oneline skipwhite skipempty nextgroup=jsFuncBlock keepend
+syntax region jsFlowTypeObject contained start=/{/ end=/}/ contains=jsFlowTypeKey skipwhite skipempty nextgroup=jsFunctionBlock extend
+syntax match jsFlowTypeKey contained /\<[0-9a-zA-Z_$?]*\>\(\s*:\)\@=/ skipwhite skipempty nextgroup=jsFlowTypeValue
+syntax region jsFlowTypeValue contained matchgroup=jsFlowNoise start=/:/ end=/[,}]/ contains=@jsFlowCluster
+syntax region jsFlowObject contained matchgroup=jsFlowNoise start=/{/ end=/}/ oneline contains=@jsFlowCluster
+syntax region jsFlowArray contained matchgroup=jsFlowNoise start=/\[/ end=/\]/ oneline contains=@jsFlowCluster
+syntax region jsFlowArrow contained matchgroup=jsFlowNoise start=/(/ end=/)\s*=>/ oneline contains=@jsFlowCluster
+syntax keyword jsFlowDeclareKeyword contained declare
+syntax keyword jsFlowType contained boolean number string null void any mixed JSON array function object Array
+syntax match jsFlowClassProperty contained /\<[0-9a-zA-Z_$]*\>:\@=/ skipwhite skipempty nextgroup=jsFlow
+syntax match jsFlowNoise contained /[:;,<>]/
+syntax cluster jsFlowCluster contains=jsFlowType,jsFlowArray,jsFlowObject,jsFlowNoise,jsFlowArrow
+syntax keyword jsFlowStorageClass contained const var let
+syntax region jsFlowParenRegion contained start=/:\s*(/ end=/)\%(\s*:\)\@=/ oneline contains=@jsFlowCluster skipwhite skipempty nextgroup=jsObjectValue
+syntax region jsFlowClass contained matchgroup=jsFlowNoise start=/</ end=/>/ oneline contains=@jsFlowCluster skipwhite skipempty nextgroup=jsClassBlock
+
+if version >= 508 || !exists("did_javascript_syn_inits")
+ if version < 508
+ let did_javascript_syn_inits = 1
+ command -nargs=+ HiLink hi link <args>
+ else
+ command -nargs=+ HiLink hi def link <args>
+ endif
+ HiLink jsFlow PreProc
+ HiLink jsFlowReturn PreProc
+ HiLink jsFlowArray PreProc
+ HiLink jsFlowDeclareBlock PreProc
+ HiLink jsFlowObject PreProc
+ HiLink jsFlowParenRegion PreProc
+ HiLink jsFlowClass PreProc
+ HiLink jsFlowTypeObject PreProc
+ HiLink jsFlowTypeKey PreProc
+ HiLink jsFlowTypeValue PreProc
+ HiLink jsFlowClassProperty jsClassProperty
+ HiLink jsFlowType Type
+ HiLink jsFlowDeclareKeyword Type
+ HiLink jsFlowNoise Noise
+ delcommand HiLink
+endif
+
+endif
diff --git a/extras/jsdoc.vim b/extras/jsdoc.vim
index c5d7a572..7ce5f62d 100644
--- a/extras/jsdoc.vim
+++ b/extras/jsdoc.vim
@@ -41,3 +41,46 @@ if version >= 508 || !exists("did_javascript_syn_inits")
endif
endif
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'javascript') == -1
+
+"" syntax coloring for javadoc comments (HTML)
+syntax region jsComment matchgroup=jsComment start="/\*\s*" end="\*/" contains=jsDocTags,jsCommentTodo,jsCvsTag,@jsHtml,@Spell fold
+
+" tags containing a param
+syntax match jsDocTags contained "@\(alias\|api\|augments\|borrows\|class\|constructs\|default\|defaultvalue\|emits\|exception\|exports\|extends\|fires\|kind\|link\|listens\|member\|member[oO]f\|mixes\|module\|name\|namespace\|requires\|template\|throws\|var\|variation\|version\)\>" skipwhite nextgroup=jsDocParam
+" tags containing type and param
+syntax match jsDocTags contained "@\(arg\|argument\|cfg\|param\|property\|prop\)\>" skipwhite nextgroup=jsDocType
+" tags containing type but no param
+syntax match jsDocTags contained "@\(callback\|define\|enum\|external\|implements\|this\|type\|typedef\|return\|returns\)\>" skipwhite nextgroup=jsDocTypeNoParam
+" tags containing references
+syntax match jsDocTags contained "@\(lends\|see\|tutorial\)\>" skipwhite nextgroup=jsDocSeeTag
+" other tags (no extra syntax)
+syntax match jsDocTags contained "@\(abstract\|access\|accessor\|author\|classdesc\|constant\|const\|constructor\|copyright\|deprecated\|desc\|description\|dict\|event\|example\|file\|file[oO]verview\|final\|function\|global\|ignore\|inheritDoc\|inner\|instance\|interface\|license\|localdoc\|method\|mixin\|nosideeffects\|override\|overview\|preserve\|private\|protected\|public\|readonly\|since\|static\|struct\|todo\|summary\|undocumented\|virtual\)\>"
+
+syntax region jsDocType contained matchgroup=jsDocTypeBrackets start="{" end="}" contains=jsDocTypeRecord oneline skipwhite nextgroup=jsDocParam
+syntax match jsDocType contained "\%(#\|\"\|\w\|\.\|:\|\/\)\+" skipwhite nextgroup=jsDocParam
+syntax region jsDocTypeRecord contained start=/{/ end=/}/ contains=jsDocTypeRecord extend
+syntax region jsDocTypeRecord contained start=/\[/ end=/\]/ contains=jsDocTypeRecord extend
+syntax region jsDocTypeNoParam contained start="{" end="}" oneline
+syntax match jsDocTypeNoParam contained "\%(#\|\"\|\w\|\.\|:\|\/\)\+"
+syntax match jsDocParam contained "\%(#\|\$\|-\|'\|\"\|{.\{-}}\|\w\|\.\|:\|\/\|\[.{-}]\|=\)\+"
+syntax region jsDocSeeTag contained matchgroup=jsDocSeeTag start="{" end="}" contains=jsDocTags
+
+if version >= 508 || !exists("did_javascript_syn_inits")
+ if version < 508
+ let did_javascript_syn_inits = 1
+ command -nargs=+ HiLink hi link <args>
+ else
+ command -nargs=+ HiLink hi def link <args>
+ endif
+ HiLink jsDocTags Special
+ HiLink jsDocSeeTag Function
+ HiLink jsDocType Type
+ HiLink jsDocTypeBrackets jsDocType
+ HiLink jsDocTypeRecord jsDocType
+ HiLink jsDocTypeNoParam Type
+ HiLink jsDocParam Label
+ delcommand HiLink
+endif
+
+endif
diff --git a/extras/ngdoc.vim b/extras/ngdoc.vim
index be314a80..7065cb81 100644
--- a/extras/ngdoc.vim
+++ b/extras/ngdoc.vim
@@ -5,3 +5,10 @@ syntax match jsDocType contained "\%(#\|\$\|\w\|\"\|-\|\.\|:\|\/\)\+" n
syntax match jsDocParam contained "\%(#\|\$\|\w\|\"\|-\|\.\|:\|{\|}\|\/\|\[\|]\|=\)\+"
endif
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'javascript') == -1
+
+syntax match jsDocTags contained /@\(link\|method[oO]f\|ngdoc\|ng[iI]nject\|restrict\)/ nextgroup=jsDocParam skipwhite
+syntax match jsDocType contained "\%(#\|\$\|\w\|\"\|-\|\.\|:\|\/\)\+" nextgroup=jsDocParam skipwhite
+syntax match jsDocParam contained "\%(#\|\$\|\w\|\"\|-\|\.\|:\|{\|}\|\/\|\[\|]\|=\)\+"
+
+endif
diff --git a/ftdetect/polyglot.vim b/ftdetect/polyglot.vim
index 6e032523..caa6ad5e 100644
--- a/ftdetect/polyglot.vim
+++ b/ftdetect/polyglot.vim
@@ -475,6 +475,19 @@ au BufNewFile,BufRead */templates/**.liquid,*/layout/**.liquid,*/snippets/**.liq
endif
+" ftdetect/ls.vim
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'livescript') == -1
+
+" Language: LiveScript
+" Maintainer: George Zahariev
+" URL: http://github.com/gkz/vim-ls
+" License: WTFPL
+"
+autocmd BufNewFile,BufRead *.ls set filetype=ls
+autocmd BufNewFile,BufRead *Slakefile set filetype=ls
+
+endif
+
" ftdetect/mako.vim
if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'mako') == -1
diff --git a/ftplugin/ls.vim b/ftplugin/ls.vim
new file mode 100644
index 00000000..6ce6b57a
--- /dev/null
+++ b/ftplugin/ls.vim
@@ -0,0 +1,208 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'livescript') == -1
+
+" Language: LiveScript
+" Maintainer: George Zahariev
+" URL: http://github.com/gkz/vim-ls
+" License: WTFPL
+"
+if exists("b:did_ftplugin")
+ finish
+endif
+
+let b:did_ftplugin = 1
+
+setlocal formatoptions-=t formatoptions+=croql
+setlocal comments=:#
+setlocal commentstring=#\ %s
+setlocal omnifunc=javascriptcomplete#CompleteJS
+
+" Enable LiveScriptMake if it won't overwrite any settings.
+if !len(&l:makeprg)
+ compiler ls
+endif
+
+" Check here too in case the compiler above isn't loaded.
+if !exists('livescript_compiler')
+ let livescript_compiler = 'lsc'
+endif
+
+" Reset the LiveScriptCompile variables for the current buffer.
+function! s:LiveScriptCompileResetVars()
+ " Compiled output buffer
+ let b:livescript_compile_buf = -1
+ let b:livescript_compile_pos = []
+
+ " If LiveScriptCompile is watching a buffer
+ let b:livescript_compile_watch = 0
+endfunction
+
+" Clean things up in the source buffer.
+function! s:LiveScriptCompileClose()
+ exec bufwinnr(b:livescript_compile_src_buf) 'wincmd w'
+ silent! autocmd! LiveScriptCompileAuWatch * <buffer>
+ call s:LiveScriptCompileResetVars()
+endfunction
+
+" Update the LiveScriptCompile buffer given some input lines.
+function! s:LiveScriptCompileUpdate(startline, endline)
+ let input = join(getline(a:startline, a:endline), "\n")
+
+ " Move to the LiveScriptCompile buffer.
+ exec bufwinnr(b:livescript_compile_buf) 'wincmd w'
+
+ " LiveScript doesn't like empty input.
+ if !len(input)
+ return
+ endif
+
+ " Compile input.
+ let output = system(g:livescript_compiler . ' -scb 2>&1', input)
+
+ " Be sure we're in the LiveScriptCompile buffer before overwriting.
+ if exists('b:livescript_compile_buf')
+ echoerr 'LiveScriptCompile buffers are messed up'
+ return
+ endif
+
+ " Replace buffer contents with new output and delete the last empty line.
+ setlocal modifiable
+ exec '% delete _'
+ put! =output
+ exec '$ delete _'
+ setlocal nomodifiable
+
+ " Highlight as JavaScript if there is no compile error.
+ if v:shell_error
+ setlocal filetype=
+ else
+ setlocal filetype=javascript
+ endif
+
+ call setpos('.', b:livescript_compile_pos)
+endfunction
+
+" Update the LiveScriptCompile buffer with the whole source buffer.
+function! s:LiveScriptCompileWatchUpdate()
+ call s:LiveScriptCompileUpdate(1, '$')
+ exec bufwinnr(b:livescript_compile_src_buf) 'wincmd w'
+endfunction
+
+" Peek at compiled LiveScript in a scratch buffer. We handle ranges like this
+" to prevent the cursor from being moved (and its position saved) before the
+" function is called.
+function! s:LiveScriptCompile(startline, endline, args)
+ if !executable(g:livescript_compiler)
+ echoerr "Can't find LiveScript compiler `" . g:livescript_compiler . "`"
+ return
+ endif
+
+ " If in the LiveScriptCompile buffer, switch back to the source buffer and
+ " continue.
+ if !exists('b:livescript_compile_buf')
+ exec bufwinnr(b:livescript_compile_src_buf) 'wincmd w'
+ endif
+
+ " Parse arguments.
+ let watch = a:args =~ '\<watch\>'
+ let unwatch = a:args =~ '\<unwatch\>'
+ let size = str2nr(matchstr(a:args, '\<\d\+\>'))
+
+ " Determine default split direction.
+ if exists('g:livescript_compile_vert')
+ let vert = 1
+ else
+ let vert = a:args =~ '\<vert\%[ical]\>'
+ endif
+
+ " Remove any watch listeners.
+ silent! autocmd! LiveScriptCompileAuWatch * <buffer>
+
+ " If just unwatching, don't compile.
+ if unwatch
+ let b:livescript_compile_watch = 0
+ return
+ endif
+
+ if watch
+ let b:livescript_compile_watch = 1
+ endif
+
+ " Build the LiveScriptCompile buffer if it doesn't exist.
+ if bufwinnr(b:livescript_compile_buf) == -1
+ let src_buf = bufnr('%')
+ let src_win = bufwinnr(src_buf)
+
+ " Create the new window and resize it.
+ if vert
+ let width = size ? size : winwidth(src_win) / 2
+
+ belowright vertical new
+ exec 'vertical resize' width
+ else
+ " Try to guess the compiled output's height.
+ let height = size ? size : min([winheight(src_win) / 2,
+ \ a:endline - a:startline + 5])
+
+ belowright new
+ exec 'resize' height
+ endif
+
+ " We're now in the scratch buffer, so set it up.
+ setlocal bufhidden=wipe buftype=nofile
+ setlocal nobuflisted nomodifiable noswapfile nowrap
+
+ autocmd BufWipeout <buffer> call s:LiveScriptCompileClose()
+ " Save the cursor when leaving the LiveScriptCompile buffer.
+ autocmd BufLeave <buffer> let b:livescript_compile_pos = getpos('.')
+
+ nnoremap <buffer> <silent> q :hide<CR>
+
+ let b:livescript_compile_src_buf = src_buf
+ let buf = bufnr('%')
+
+ " Go back to the source buffer and set it up.
+ exec bufwinnr(b:livescript_compile_src_buf) 'wincmd w'
+ let b:livescript_compile_buf = buf
+ endif
+
+ if b:livescript_compile_watch
+ call s:LiveScriptCompileWatchUpdate()
+
+ augroup LiveScriptCompileAuWatch
+ autocmd InsertLeave <buffer> call s:LiveScriptCompileWatchUpdate()
+ autocmd BufWritePost <buffer> call s:LiveScriptCompileWatchUpdate()
+ augroup END
+ else
+ call s:LiveScriptCompileUpdate(a:startline, a:endline)
+ endif
+endfunction
+
+" Complete arguments for the LiveScriptCompile command.
+function! s:LiveScriptCompileComplete(arg, cmdline, cursor)
+ let args = ['unwatch', 'vertical', 'watch']
+
+ if !len(a:arg)
+ return args
+ endif
+
+ let match = '^' . a:arg
+
+ for arg in args
+ if arg =~ match
+ return [arg]
+ endif
+ endfor
+endfunction
+
+" Don't overwrite the CoffeeCompile variables.
+if !exists("s:livescript_compile_buf")
+ call s:LiveScriptCompileResetVars()
+endif
+
+" Peek at compiled LiveScript.
+command! -range=% -bar -nargs=* -complete=customlist,s:LiveScriptCompileComplete
+\ LiveScriptCompile call s:LiveScriptCompile(<line1>, <line2>, <q-args>)
+" Run some LiveScript.
+command! -range=% -bar LiveScriptRun <line1>,<line2>:w !lsc -sp
+
+endif
diff --git a/indent/ls.vim b/indent/ls.vim
new file mode 100644
index 00000000..240eab07
--- /dev/null
+++ b/indent/ls.vim
@@ -0,0 +1,268 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'livescript') == -1
+
+" Language: LiveScript
+" Maintainer: George Zahariev
+" URL: http://github.com/gkz/vim-ls
+" License: WTFPL
+
+if exists("b:did_indent")
+ finish
+endif
+
+let b:did_indent = 1
+
+setlocal autoindent
+setlocal indentexpr=GetLiveScriptIndent(v:lnum)
+" Make sure GetLiveScriptIndent is run when these are typed so they can be
+" indented or outdented.
+setlocal indentkeys+=0],0),0.,=else,=when,=catch,=finally
+
+" Only define the function once.
+if exists("*GetLiveScriptIndent")
+ finish
+endif
+
+" Keywords to indent after
+let s:INDENT_AFTER_KEYWORD = '^\%(if\|unless\|else\|for\|while\|until\|'
+\ . 'loop\|case\|default\|try\|catch\|finally\|'
+\ . 'class\|do\|new\|let\|with\|function\)\>'
+
+" Operators to indent after
+let s:INDENT_AFTER_OPERATOR = '\%([([{:=]\|[-=]>\)$'
+
+" Keywords and operators that continue a line
+let s:CONTINUATION = '\<\%(is\|isnt\|and\|or\|do\)\>$'
+\ . '\|'
+\ . '\%(-\@<!-\|+\@<!+\|<\|[-~]\@<!>\|\*\|/\@<!/\|%\||\|'
+\ . '&\|,\|\.\@<!\.\)$'
+
+" Operators that block continuation indenting
+let s:CONTINUATION_BLOCK = '[([{:=]$'
+
+" A continuation dot access
+let s:DOT_ACCESS = '^\.'
+
+" Keywords to outdent after
+let s:OUTDENT_AFTER = '^\%(return\|break\|continue\|throw\)\>'
+
+" A compound assignment like `... = if ...`
+let s:COMPOUND_ASSIGNMENT = '[:=]\s*\%(if\|unless\|for\|while\|until\|'
+\ . 'try\|class\|do\|new\|let\|with\)\>'
+
+" A postfix condition like `return ... if ...`.
+let s:POSTFIX_CONDITION = '\S\s\+\zs\<\%(if\|unless\)\>'
+
+" A single-line else statement like `else ...` but not `else if ...
+let s:SINGLE_LINE_ELSE = '^else\s\+\%(\<\%(if\|unless\)\>\)\@!'
+
+" Max lines to look back for a match
+let s:MAX_LOOKBACK = 50
+
+" Get the linked syntax name of a character.
+function! s:SyntaxName(linenum, col)
+ return synIDattr(synIDtrans(synID(a:linenum, a:col, 1)), 'name')
+endfunction
+
+" Check if a character is in a comment.
+function! s:IsComment(linenum, col)
+ return s:SyntaxName(a:linenum, a:col) == 'Comment'
+endfunction
+
+" Check if a character is in a string.
+function! s:IsString(linenum, col)
+ return s:SyntaxName(a:linenum, a:col) == 'Constant'
+endfunction
+
+" Check if a character is in a comment or string.
+function! s:IsCommentOrString(linenum, col)
+ return s:SyntaxName(a:linenum, a:col) =~ 'Comment\|Constant'
+endfunction
+
+" Check if a whole line is a comment.
+function! s:IsCommentLine(linenum)
+ " Check the first non-whitespace character.
+ return s:IsComment(a:linenum, indent(a:linenum) + 1)
+endfunction
+
+" Repeatedly search a line for a regex until one is found outside a string or
+" comment.
+function! s:SmartSearch(linenum, regex)
+ " Start at the first column.
+ let col = 0
+
+ " Search until there are no more matches, unless a good match is found.
+ while 1
+ call cursor(a:linenum, col + 1)
+ let [_, col] = searchpos(a:regex, 'cn', a:linenum)
+
+ " No more matches.
+ if !col
+ break
+ endif
+
+ if !s:IsCommentOrString(a:linenum, col)
+ return 1
+ endif
+ endwhile
+
+ " No good match found.
+ return 0
+endfunction
+
+" Skip a match if it's in a comment or string, is a single-line statement that
+" isn't adjacent, or is a postfix condition.
+function! s:ShouldSkip(startlinenum, linenum, col)
+ if s:IsCommentOrString(a:linenum, a:col)
+ return 1
+ endif
+
+ " Check for a single-line statement that isn't adjacent.
+ if s:SmartSearch(a:linenum, '\<then\>') && a:startlinenum - a:linenum > 1
+ return 1
+ endif
+
+ if s:SmartSearch(a:linenum, s:POSTFIX_CONDITION) &&
+ \ !s:SmartSearch(a:linenum, s:COMPOUND_ASSIGNMENT)
+ return 1
+ endif
+
+ return 0
+endfunction
+
+" Find the farthest line to look back to, capped to line 1 (zero and negative
+" numbers cause bad things).
+function! s:MaxLookback(startlinenum)
+ return max([1, a:startlinenum - s:MAX_LOOKBACK])
+endfunction
+
+" Get the skip expression for searchpair().
+function! s:SkipExpr(startlinenum)
+ return "s:ShouldSkip(" . a:startlinenum . ", line('.'), col('.'))"
+endfunction
+
+" Search for pairs of text.
+function! s:SearchPair(start, end)
+ " The cursor must be in the first column for regexes to match.
+ call cursor(0, 1)
+
+ let startlinenum = line('.')
+
+ " Don't need the W flag since MaxLookback caps the search to line 1.
+ return searchpair(a:start, '', a:end, 'bcn',
+ \ s:SkipExpr(startlinenum),
+ \ s:MaxLookback(startlinenum))
+endfunction
+
+" Try to find a previous matching line.
+function! s:GetMatch(curline)
+ let firstchar = a:curline[0]
+
+ if firstchar == '}'
+ return s:SearchPair('{', '}')
+ elseif firstchar == ')'
+ return s:SearchPair('(', ')')
+ elseif firstchar == ']'
+ return s:SearchPair('\[', '\]')
+ elseif a:curline =~ '^else\>'
+ return s:SearchPair('\<\%(if\|unless\|case\|when\)\>', '\<else\>')
+ elseif a:curline =~ '^catch\>'
+ return s:SearchPair('\<try\>', '\<catch\>')
+ elseif a:curline =~ '^finally\>'
+ return s:SearchPair('\<try\>', '\<finally\>')
+ endif
+
+ return 0
+endfunction
+
+" Get the nearest previous line that isn't a comment.
+function! s:GetPrevNormalLine(startlinenum)
+ let curlinenum = a:startlinenum
+
+ while curlinenum > 0
+ let curlinenum = prevnonblank(curlinenum - 1)
+
+ if !s:IsCommentLine(curlinenum)
+ return curlinenum
+ endif
+ endwhile
+
+ return 0
+endfunction
+
+" Get the contents of a line without leading or trailing whitespace.
+function! s:GetTrimmedLine(linenum)
+ return substitute(substitute(getline(a:linenum), '^\s\+', '', ''),
+ \ '\s\+$', '', '')
+endfunction
+
+function! s:GetLiveScriptIndent(curlinenum)
+ let prevlinenum = s:GetPrevNormalLine(a:curlinenum)
+
+ " Don't do anything if there's no previous line.
+ if !prevlinenum
+ return -1
+ endif
+
+ let curline = s:GetTrimmedLine(a:curlinenum)
+
+ " Try to find a previous matching statement. This handles outdenting.
+ let matchlinenum = s:GetMatch(curline)
+
+ if matchlinenum
+ return indent(matchlinenum)
+ endif
+
+ let prevline = s:GetTrimmedLine(prevlinenum)
+ let previndent = indent(prevlinenum)
+
+ " Always indent after these operators.
+ if prevline =~ s:INDENT_AFTER_OPERATOR
+ return previndent + &shiftwidth
+ endif
+
+ " Indent after a continuation if it's the first.
+ if prevline =~ s:CONTINUATION
+ let prevprevlinenum = s:GetPrevNormalLine(prevlinenum)
+ let prevprevline = s:GetTrimmedLine(prevprevlinenum)
+
+ if prevprevline !~ s:CONTINUATION && prevprevline !~ s:CONTINUATION_BLOCK
+ return previndent + &shiftwidth
+ endif
+ endif
+
+ " Indent after these keywords and compound assignments if they aren't a
+ " single-line statement.
+ if prevline =~ s:INDENT_AFTER_KEYWORD || prevline =~ s:COMPOUND_ASSIGNMENT
+ if !s:SmartSearch(prevlinenum, '\<then\>') && prevline !~ s:SINGLE_LINE_ELSE
+ return previndent + &shiftwidth
+ endif
+ endif
+
+ " Indent a dot access if it's the first.
+ if curline =~ s:DOT_ACCESS && prevline !~ s:DOT_ACCESS
+ return previndent + &shiftwidth
+ endif
+
+ " Outdent after these keywords if they don't have a postfix condition and
+ " aren't a single-line statement.
+ if prevline =~ s:OUTDENT_AFTER
+ if !s:SmartSearch(prevlinenum, s:POSTFIX_CONDITION) ||
+ \ s:SmartSearch(prevlinenum, '\<then\>')
+ return previndent - &shiftwidth
+ endif
+ endif
+
+ " No indenting or outdenting is needed.
+ return -1
+endfunction
+
+" Wrap s:GetLiveScriptIndent to keep the cursor position.
+function! GetLiveScriptIndent(curlinenum)
+ let oldcursor = getpos('.')
+ let indent = s:GetLiveScriptIndent(a:curlinenum)
+ call setpos('.', oldcursor)
+
+ return indent
+endfunction
+
+endif
diff --git a/syntax/ls.vim b/syntax/ls.vim
new file mode 100644
index 00000000..c3edf65e
--- /dev/null
+++ b/syntax/ls.vim
@@ -0,0 +1,140 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'livescript') == -1
+
+" Language: LiveScript "
+" Maintainer: George Zahariev
+" URL: http://github.com/gkz/vim-ls
+" License: WTFPL
+
+if exists('b:current_syntax') && b:current_syntax == 'livescript'
+ finish
+endif
+
+let b:current_syntax = "ls"
+
+" Highlight long strings.
+syntax sync minlines=100
+
+setlocal iskeyword=48-57,A-Z,$,a-z,_,-
+
+syntax match lsIdentifier /[$A-Za-z_]\k*\(-[a-zA-Z]\+\)*/
+highlight default link lsIdentifier Identifier
+
+" These are 'matches' rather than 'keywords' because vim's highlighting priority
+" for keywords (the highest) causes them to be wrongly highlighted when used as
+" dot-properties.
+syntax match lsStatement /\<\%(return\|break\|continue\|throw\)\>/
+highlight default link lsStatement Statement
+
+syntax match lsRepeat /\<\%(for\%( own\| ever\)\?\|while\|until\)\>/
+highlight default link lsRepeat Repeat
+
+syntax match lsConditional /\<\%(if\|else\|unless\|switch\|case\|when\|default\|match\)\>/
+highlight default link lsConditional Conditional
+
+syntax match lsException /\<\%(try\|catch\|finally\)\>/
+highlight default link lsException Exception
+
+syntax match lsKeyword /\<\%(new\|in\%(stanceof\)\?\|typeof\|delete\|and\|o[fr]\|not\|xor\|is\|isnt\|imp\%(ort\%( all\)\?\|lements\)\|extends\|loop\|from\|to\|til\|by\|do\|then\|function\|class\|let\|with\|export\|const\|var\|eval\|super\|fallthrough\|debugger\|where\|yield\)\>/
+highlight default link lsKeyword Keyword
+
+syntax match lsBoolean /\<\%(true\|false\|yes\|no\|on\|off\|null\|void\)\>/
+highlight default link lsBoolean Boolean
+
+" Matches context variables.
+syntax match lsContext /\<\%(this\|arguments\|it\|that\|constructor\|prototype\|superclass\)\>/
+highlight default link lsContext Type
+
+" Keywords reserved by the language
+syntax cluster lsReserved contains=lsStatement,lsRepeat,lsConditional,
+\ lsException,lsOperator,lsKeyword,lsBoolean
+
+" Matches ECMAScript 5 built-in globals.
+syntax match lsGlobal /\<\%(Array\|Boolean\|Date\|Function\|JSON\|Math\|Number\|Object\|RegExp\|String\|\%(Syntax\|Type\|URI\)\?Error\|is\%(NaN\|Finite\)\|parse\%(Int\|Float\)\|\%(en\|de\)codeURI\%(Component\)\?\)\>/
+highlight default link lsGlobal Structure
+
+syntax region lsString start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=@lsInterpString
+syntax region lsString start=/'/ skip=/\\\\\|\\'/ end=/'/ contains=@lsSimpleString
+highlight default link lsString String
+
+" Matches decimal/floating-point numbers like 10.42e-8.
+syntax match lsFloat
+\ /\%(\<-\?\|-\)\zs\d[0-9_]*\%(\.\d[0-9_]*\)\?\%(e[+-]\?\d[0-9_]*\)\?\%([a-zA-Z$][$a-zA-Z0-9_]*\)\?/
+\ contains=lsNumberComment
+highlight default link lsFloat Float
+syntax match lsNumberComment /\d\+\zs\%(e[+-]\?\d\)\@![a-zA-Z$][$a-zA-Z0-9_]*/ contained
+highlight default link lsNumberComment Comment
+" Matches hex numbers like 0xfff, 0x000.
+syntax match lsNumber /\%(\<-\?\|-\)\zs0x\x\+/
+" Matches N radix numbers like 2@1010.
+syntax match lsNumber
+\ /\%(\<-\?\|-\)\zs\%(\d*\)\~[0-9A-Za-z][0-9A-Za-z_]*/
+highlight default link lsNumber Number
+
+" Displays an error for reserved words.
+syntax match lsReservedError /\<\%(enum\|interface\|package\|private\|protected\|public\|static\)\>/
+highlight default link lsReservedError Error
+
+syntax keyword lsTodo TODO FIXME XXX contained
+highlight default link lsTodo Todo
+
+syntax match lsComment /#.*/ contains=@Spell,lsTodo
+syntax region lsComment start=/\/\*/ end=/\*\// contains=@Spell,lsTodo
+highlight default link lsComment Comment
+
+syntax region lsInfixFunc start=/`/ end=/`/
+highlight default link lsInfixFunc Identifier
+
+syntax region lsInterpolation matchgroup=lsInterpDelim
+\ start=/\#{/ end=/}/
+\ contained contains=TOP
+highlight default link lsInterpDelim Delimiter
+
+" Matches escape sequences like \000, \x00, \u0000, \n.
+syntax match lsEscape /\\\d\d\d\|\\x\x\{2\}\|\\u\x\{4\}\|\\./ contained
+highlight default link lsEscape SpecialChar
+
+syntax match lsVarInterpolation /#[$A-Za-z_]\k*\(-[a-zA-Z]\+\)*/ contained
+highlight default link lsVarInterpolation Identifier
+
+" What is in a non-interpolated string
+syntax cluster lsSimpleString contains=@Spell,lsEscape
+" What is in an interpolated string
+syntax cluster lsInterpString contains=@lsSimpleString,
+\ lsInterpolation,lsVarInterpolation
+
+syntax region lsRegex start=/\%(\%()\|\i\@<!\d\)\s*\|\i\)\@<!\/\*\@!/
+\ skip=/\[[^]]\{-}\/[^]]\{-}\]/
+\ end=/\/[gimy$]\{,4}/
+\ oneline contains=@lsSimpleString
+syntax region lsHeregex start=/\/\// end=/\/\/[gimy$?]\{,4}/ contains=@lsInterpString,lsComment,lsSpaceError fold
+highlight default link lsHeregex lsRegex
+highlight default link lsRegex String
+
+syntax region lsHeredoc start=/"""/ end=/"""/ contains=@lsInterpString fold
+syntax region lsHeredoc start=/'''/ end=/'''/ contains=@lsSimpleString fold
+highlight default link lsHeredoc String
+
+syntax match lsWord /\\\S[^ \t\r,;)}\]]*/
+highlight default link lsWord String
+
+syntax region lsWords start=/<\[/ end=/\]>/ contains=fold
+highlight default link lsWords String
+
+" Reserved words can be used as property names.
+syntax match lsProp /[$A-Za-z_]\k*[ \t]*:[:=]\@!/
+highlight default link lsProp Label
+
+syntax match lsKey
+\ /\%(\.\@<!\.\%(=\?\s*\|\.\)\|[]})@?]\|::\)\zs\k\+/
+\ transparent
+\ contains=ALLBUT,lsNumberComment,lsIdentifier,lsContext,lsGlobal,lsReservedError,@lsReserved
+
+" Displays an error for trailing whitespace.
+syntax match lsSpaceError /\s\+$/ display
+highlight default link lsSpaceError Error
+
+if !exists('b:current_syntax')
+ let b:current_syntax = 'livescript'
+endif
+
+endif