diff options
author | Matt Singleton <matt@datadoghq.com> | 2011-12-15 12:37:21 -0500 |
---|---|---|
committer | Matt Singleton <matt@datadoghq.com> | 2011-12-15 12:37:21 -0500 |
commit | 02c7d9e3fe9647c72d36ea7cb49e885072e3fcbd (patch) | |
tree | 9b669556982e3f964cf7722794de508948a3fd9b /vim | |
parent | fead075335c4a799a057d311b39c9969fb0be2be (diff) |
adding coffescript vim support
Diffstat (limited to 'vim')
-rwxr-xr-x | vim/after/syntax/haml.vim | 9 | ||||
-rwxr-xr-x | vim/after/syntax/html.vim | 10 | ||||
-rwxr-xr-x | vim/compiler/coffee.vim | 68 | ||||
-rwxr-xr-x | vim/ftdetect/coffee.vim | 8 | ||||
-rwxr-xr-x | vim/ftdetect/eco.vim | 1 | ||||
-rwxr-xr-x | vim/ftplugin/coffee.vim | 221 | ||||
-rwxr-xr-x | vim/indent/coffee.vim | 328 | ||||
-rwxr-xr-x | vim/syntax/coffee.vim | 236 | ||||
-rwxr-xr-x | vim/syntax/eco.vim | 62 |
9 files changed, 943 insertions, 0 deletions
diff --git a/vim/after/syntax/haml.vim b/vim/after/syntax/haml.vim new file mode 100755 index 0000000..d82fbea --- /dev/null +++ b/vim/after/syntax/haml.vim @@ -0,0 +1,9 @@ +" Language: CoffeeScript +" Maintainer: Sven Felix Oberquelle <Svelix.Github@gmail.com> +" URL: http://github.com/kchmck/vim-coffee-script +" License: WTFPL + +" Inherit coffee from html so coffeeComment isn't redefined and given higher +" priority than hamlInterpolation. +syn cluster hamlCoffeescript contains=@htmlCoffeeScript +syn region hamlCoffeescriptFilter matchgroup=hamlFilter start="^\z(\s*\):coffeescript\s*$" end="^\%(\z1 \| *$\)\@!" contains=@hamlCoffeeScript,hamlInterpolation keepend diff --git a/vim/after/syntax/html.vim b/vim/after/syntax/html.vim new file mode 100755 index 0000000..63ebaec --- /dev/null +++ b/vim/after/syntax/html.vim @@ -0,0 +1,10 @@ +" Language: CoffeeScript +" Maintainer: Mick Koch <kchmck@gmail.com> +" URL: http://github.com/kchmck/vim-coffee-script +" License: WTFPL + +" Syntax highlighting for text/coffeescript script tags +syn include @htmlCoffeeScript syntax/coffee.vim +syn region coffeeScript start=+<script [^>]*type *=[^>]*text/coffeescript[^>]*>+ +\ end=+</script>+me=s-1 keepend +\ contains=@htmlCoffeeScript,htmlScriptTag,@htmlPreproc diff --git a/vim/compiler/coffee.vim b/vim/compiler/coffee.vim new file mode 100755 index 0000000..6d97cd8 --- /dev/null +++ b/vim/compiler/coffee.vim @@ -0,0 +1,68 @@ +" Language: CoffeeScript +" Maintainer: Mick Koch <kchmck@gmail.com> +" URL: http://github.com/kchmck/vim-coffee-script +" License: WTFPL + +if exists('current_compiler') + finish +endif + +let current_compiler = 'coffee' +" Pattern to check if coffee is the compiler +let s:pat = '^' . current_compiler + +" Extra options passed to CoffeeMake +if !exists("coffee_make_options") + let coffee_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 'coffee -c ' . g:coffee_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=coffee +call s:SetMakePrg() + +CompilerSet errorformat=Error:\ In\ %f\\,\ %m\ on\ line\ %l, + \Error:\ In\ %f\\,\ Parse\ error\ on\ line\ %l:\ %m, + \SyntaxError:\ In\ %f\\,\ %m, + \%-G%.%# + +" Compile the current file. +command! -bang -bar -nargs=* CoffeeMake make<bang> <args> + +" Set `makeprg` on rename since we embed the filename in the setting. +augroup CoffeeUpdateMakePrg + autocmd! + + " Update `makeprg` if coffee is still the compiler, else stop running this + " function. + function! s:UpdateMakePrg() + if !s:SetMakePrg() + autocmd! CoffeeUpdateMakePrg + 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 diff --git a/vim/ftdetect/coffee.vim b/vim/ftdetect/coffee.vim new file mode 100755 index 0000000..25daf12 --- /dev/null +++ b/vim/ftdetect/coffee.vim @@ -0,0 +1,8 @@ +" Language: CoffeeScript +" Maintainer: Mick Koch <kchmck@gmail.com> +" URL: http://github.com/kchmck/vim-coffee-script +" License: WTFPL + +autocmd BufNewFile,BufRead *.coffee set filetype=coffee +autocmd BufNewFile,BufRead *Cakefile set filetype=coffee +autocmd BufNewFile,BufRead *.coffeekup set filetype=coffee diff --git a/vim/ftdetect/eco.vim b/vim/ftdetect/eco.vim new file mode 100755 index 0000000..b420649 --- /dev/null +++ b/vim/ftdetect/eco.vim @@ -0,0 +1 @@ +autocmd BufNewFile,BufRead *.eco set filetype=eco diff --git a/vim/ftplugin/coffee.vim b/vim/ftplugin/coffee.vim new file mode 100755 index 0000000..12ae3f0 --- /dev/null +++ b/vim/ftplugin/coffee.vim @@ -0,0 +1,221 @@ +" Language: CoffeeScript +" Maintainer: Mick Koch <kchmck@gmail.com> +" URL: http://github.com/kchmck/vim-coffee-script +" 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 CoffeeMake if it won't overwrite any settings. +if !len(&l:makeprg) + compiler coffee +endif + +" Reset the global variables used by CoffeeCompile. +function! s:CoffeeCompileResetVars() + " Position in the source buffer + let s:coffee_compile_src_buf = -1 + let s:coffee_compile_src_pos = [] + + " Position in the CoffeeCompile buffer + let s:coffee_compile_buf = -1 + let s:coffee_compile_win = -1 + let s:coffee_compile_pos = [] + + " If CoffeeCompile is watching a buffer + let s:coffee_compile_watch = 0 +endfunction + +" Save the cursor position when moving to and from the CoffeeCompile buffer. +function! s:CoffeeCompileSavePos() + let buf = bufnr('%') + let pos = getpos('.') + + if buf == s:coffee_compile_buf + let s:coffee_compile_pos = pos + else + let s:coffee_compile_src_buf = buf + let s:coffee_compile_src_pos = pos + endif +endfunction + +" Restore the cursor to the source buffer. +function! s:CoffeeCompileRestorePos() + let win = bufwinnr(s:coffee_compile_src_buf) + + if win != -1 + exec win 'wincmd w' + call setpos('.', s:coffee_compile_src_pos) + endif +endfunction + +" Close the CoffeeCompile buffer and clean things up. +function! s:CoffeeCompileClose() + silent! autocmd! CoffeeCompileAuPos + silent! autocmd! CoffeeCompileAuWatch + + call s:CoffeeCompileRestorePos() + call s:CoffeeCompileResetVars() +endfunction + +" Update the CoffeeCompile buffer given some input lines. +function! s:CoffeeCompileUpdate(startline, endline) + let input = join(getline(a:startline, a:endline), "\n") + + " Coffee doesn't like empty input. + if !len(input) + return + endif + + " Compile input. + let output = system('coffee -scb 2>&1', input) + + " Move to the CoffeeCompile buffer. + exec s:coffee_compile_win 'wincmd w' + + " 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 + + " Restore the cursor in the compiled output. + call setpos('.', s:coffee_compile_pos) +endfunction + +" Update the CoffeeCompile buffer with the whole source buffer and restore the +" cursor. +function! s:CoffeeCompileWatchUpdate() + call s:CoffeeCompileSavePos() + call s:CoffeeCompileUpdate(1, '$') + call s:CoffeeCompileRestorePos() +endfunction + +" Peek at compiled CoffeeScript 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:CoffeeCompile(startline, endline, args) + " Don't compile the CoffeeCompile buffer. + if bufnr('%') == s:coffee_compile_buf + return + 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:coffee_compile_vert") + let vert = 1 + else + let vert = a:args =~ '\<vert\%[ical]\>' + endif + + " Remove any watch listeners. + silent! autocmd! CoffeeCompileAuWatch + + " If just unwatching, don't compile. + if unwatch + let s:coffee_compile_watch = 0 + return + endif + + if watch + let s:coffee_compile_watch = 1 + endif + + call s:CoffeeCompileSavePos() + + " Build the CoffeeCompile buffer if it doesn't exist. + if s:coffee_compile_buf == -1 + let src_win = bufwinnr(s:coffee_compile_src_buf) + + " Create the new window and resize it. + if vert + let width = size ? size : winwidth(src_win) / 2 + + 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 + 2]) + + botright new + exec 'resize' height + endif + + " Set up scratch buffer. + setlocal bufhidden=wipe buftype=nofile + setlocal nobuflisted nomodifiable noswapfile nowrap + + autocmd BufWipeout <buffer> call s:CoffeeCompileClose() + nnoremap <buffer> <silent> q :hide<CR> + + " Save the cursor position on each buffer switch. + augroup CoffeeCompileAuPos + autocmd BufEnter,BufLeave * call s:CoffeeCompileSavePos() + augroup END + + let s:coffee_compile_buf = bufnr('%') + let s:coffee_compile_win = bufwinnr(s:coffee_compile_buf) + endif + + " Go back to the source buffer and do the initial compile. + call s:CoffeeCompileRestorePos() + + if s:coffee_compile_watch + call s:CoffeeCompileWatchUpdate() + + augroup CoffeeCompileAuWatch + autocmd InsertLeave <buffer> call s:CoffeeCompileWatchUpdate() + augroup END + else + call s:CoffeeCompileUpdate(a:startline, a:endline) + endif +endfunction + +" Complete arguments for the CoffeeCompile command. +function! s:CoffeeCompileComplete(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 let new windows overwrite the CoffeeCompile variables. +if !exists("s:coffee_compile_buf") + call s:CoffeeCompileResetVars() +endif + +" Peek at compiled CoffeeScript. +command! -range=% -bar -nargs=* -complete=customlist,s:CoffeeCompileComplete +\ CoffeeCompile call s:CoffeeCompile(<line1>, <line2>, <q-args>) +" Run some CoffeeScript. +command! -range=% -bar CoffeeRun <line1>,<line2>:w !coffee -s diff --git a/vim/indent/coffee.vim b/vim/indent/coffee.vim new file mode 100755 index 0000000..a798cfc --- /dev/null +++ b/vim/indent/coffee.vim @@ -0,0 +1,328 @@ +" Language: CoffeeScript +" Maintainer: Mick Koch <kchmck@gmail.com> +" URL: http://github.com/kchmck/vim-coffee-script +" License: WTFPL + +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetCoffeeIndent(v:lnum) +" Make sure GetCoffeeIndent 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("*GetCoffeeIndent") + finish +endif + +" Keywords to indent after +let s:INDENT_AFTER_KEYWORD = '^\%(if\|unless\|else\|for\|while\|until\|' +\ . 'loop\|switch\|when\|try\|catch\|finally\|' +\ . 'class\)\>' + +" Operators to indent after +let s:INDENT_AFTER_OPERATOR = '\%([([{:=]\|[-=]>\)$' + +" Keywords and operators that continue a line +let s:CONTINUATION = '\<\%(is\|isnt\|and\|or\)\>$' +\ . '\|' +\ . '\%(-\@<!-\|+\@<!+\|<\|[-=]\@<!>\|\*\|/\@<!/\|%\||\|' +\ . '&\|,\|\.\@<!\.\)$' + +" 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\|' +\ . 'switch\|try\|class\)\>' + +" 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 + +" Syntax names for strings +let s:SYNTAX_STRING = 'coffee\%(String\|AssignString\|Embed\|Regex\|Heregex\|' +\ . 'Heredoc\)' + +" Syntax names for comments +let s:SYNTAX_COMMENT = 'coffee\%(Comment\|BlockComment\|HeregexComment\)' + +" Syntax names for strings and comments +let s:SYNTAX_STRING_COMMENT = s:SYNTAX_STRING . '\|' . s:SYNTAX_COMMENT + +" Get the linked syntax name of a character. +function! s:SyntaxName(linenum, col) + return synIDattr(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) =~ s:SYNTAX_COMMENT +endfunction + +" Check if a character is in a string. +function! s:IsString(linenum, col) + return s:SyntaxName(a:linenum, a:col) =~ s:SYNTAX_STRING +endfunction + +" Check if a character is in a comment or string. +function! s:IsCommentOrString(linenum, col) + return s:SyntaxName(a:linenum, a:col) =~ s:SYNTAX_STRING_COMMENT +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\|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 + +" Try to find a comment in a line. +function! s:FindComment(linenum) + let col = 0 + + while 1 + call cursor(a:linenum, col + 1) + let [_, col] = searchpos('#', 'cn', a:linenum) + + if !col + break + endif + + if s:IsComment(a:linenum, col) + return col + endif + endwhile + + return 0 +endfunction + +" Get a line without comments or surrounding whitespace. +function! s:GetTrimmedLine(linenum) + let comment = s:FindComment(a:linenum) + let line = getline(a:linenum) + + if comment + " Subtract 1 to get to the column before the comment and another 1 for + " zero-based indexing. + let line = line[:comment - 2] + endif + + return substitute(substitute(line, '^\s\+', '', ''), + \ '\s\+$', '', '') +endfunction + +function! s:GetCoffeeIndent(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 + + " Try to find a matching `when`. + if curline =~ '^when\>' && !s:SmartSearch(prevlinenum, '\<switch\>') + let linenum = a:curlinenum + + while linenum > 0 + let linenum = s:GetPrevNormalLine(linenum) + + if getline(linenum) =~ '^\s*when\>' + return indent(linenum) + endif + endwhile + + return -1 + 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) + + " If the continuation is the first in the file, don't run the other checks. + if !prevprevlinenum + return previndent + &shiftwidth + endif + + let prevprevline = s:GetTrimmedLine(prevprevlinenum) + + if prevprevline !~ s:CONTINUATION && prevprevline !~ s:CONTINUATION_BLOCK + return previndent + &shiftwidth + endif + + return -1 + 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 + + return -1 + 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 or are + " 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:GetCoffeeIndent to keep the cursor position. +function! GetCoffeeIndent(curlinenum) + let oldcursor = getpos('.') + let indent = s:GetCoffeeIndent(a:curlinenum) + call setpos('.', oldcursor) + + return indent +endfunction diff --git a/vim/syntax/coffee.vim b/vim/syntax/coffee.vim new file mode 100755 index 0000000..c324435 --- /dev/null +++ b/vim/syntax/coffee.vim @@ -0,0 +1,236 @@ +" Language: CoffeeScript +" Maintainer: Mick Koch <kchmck@gmail.com> +" URL: http://github.com/kchmck/vim-coffee-script +" License: WTFPL + +" Bail if our syntax is already loaded. +if exists('b:current_syntax') && b:current_syntax == 'coffee' + finish +endif + +" Include JavaScript for coffeeEmbed. +syn include @coffeeJS syntax/javascript.vim + +" Highlight long strings. +syn sync minlines=100 + +" CoffeeScript identifiers can have dollar signs. +setlocal isident+=$ + +" These are `matches` instead of `keywords` because vim's highlighting +" priority for keywords is higher than matches. This causes keywords to be +" highlighted inside matches, even if a match says it shouldn't contain them -- +" like with coffeeAssign and coffeeDot. +syn match coffeeStatement /\<\%(return\|break\|continue\|throw\)\>/ display +hi def link coffeeStatement Statement + +syn match coffeeRepeat /\<\%(for\|while\|until\|loop\)\>/ display +hi def link coffeeRepeat Repeat + +syn match coffeeConditional /\<\%(if\|else\|unless\|switch\|when\|then\)\>/ +\ display +hi def link coffeeConditional Conditional + +syn match coffeeException /\<\%(try\|catch\|finally\)\>/ display +hi def link coffeeException Exception + +syn match coffeeKeyword /\<\%(new\|in\|of\|by\|and\|or\|not\|is\|isnt\|class\|extends\|super\|do\)\>/ +\ display +" The `own` keyword is only a keyword after `for`. +syn match coffeeKeyword /\<for\s\+own\>/ contained containedin=coffeeRepeat +\ display +hi def link coffeeKeyword Keyword + +syn match coffeeOperator /\<\%(instanceof\|typeof\|delete\)\>/ display +hi def link coffeeOperator Operator + +" The first case matches symbol operators only if they have an operand before. +syn match coffeeExtendedOp /\%(\S\s*\)\@<=[+\-*/%&|\^=!<>?.]\+\|[-=]>\|--\|++\|::/ +\ display +syn match coffeeExtendedOp /\%(and\|or\)=/ display +hi def link coffeeExtendedOp coffeeOperator + +" This is separate from `coffeeExtendedOp` to help differentiate commas from +" dots. +syn match coffeeSpecialOp /[,;]/ display +hi def link coffeeSpecialOp SpecialChar + +syn match coffeeBoolean /\<\%(true\|on\|yes\|false\|off\|no\)\>/ display +hi def link coffeeBoolean Boolean + +syn match coffeeGlobal /\<\%(null\|undefined\)\>/ display +hi def link coffeeGlobal Type + +" A special variable +syn match coffeeSpecialVar /\<\%(this\|prototype\|arguments\)\>/ display +" An @-variable +syn match coffeeSpecialVar /@\%(\I\i*\)\?/ display +hi def link coffeeSpecialVar Special + +" A class-like name that starts with a capital letter +syn match coffeeObject /\<\u\w*\>/ display +hi def link coffeeObject Structure + +" A constant-like name in SCREAMING_CAPS +syn match coffeeConstant /\<\u[A-Z0-9_]\+\>/ display +hi def link coffeeConstant Constant + +" A variable name +syn cluster coffeeIdentifier contains=coffeeSpecialVar,coffeeObject, +\ coffeeConstant + +" A non-interpolated string +syn cluster coffeeBasicString contains=@Spell,coffeeEscape +" An interpolated string +syn cluster coffeeInterpString contains=@coffeeBasicString,coffeeInterp + +" Regular strings +syn region coffeeString start=/"/ skip=/\\\\\|\\"/ end=/"/ +\ contains=@coffeeInterpString +syn region coffeeString start=/'/ skip=/\\\\\|\\'/ end=/'/ +\ contains=@coffeeBasicString +hi def link coffeeString String + +" A integer, including a leading plus or minus +syn match coffeeNumber /\i\@<![-+]\?\d\+\%([eE][+-]\?\d\+\)\?/ display +" A hex number +syn match coffeeNumber /\<0[xX]\x\+\>/ display +hi def link coffeeNumber Number + +" A floating-point number, including a leading plus or minus +syn match coffeeFloat /\i\@<![-+]\?\d*\.\@<!\.\d\+\%([eE][+-]\?\d\+\)\?/ +\ display +hi def link coffeeFloat Float + +" An error for reserved keywords +if !exists("coffee_no_reserved_words_error") + syn match coffeeReservedError /\<\%(case\|default\|function\|var\|void\|with\|const\|let\|enum\|export\|import\|native\|__hasProp\|__extends\|__slice\|__bind\|__indexOf\)\>/ + \ display + hi def link coffeeReservedError Error +endif + +" This is separate from `coffeeExtendedOp` since assignments require it. +syn match coffeeAssignOp /:/ contained display +hi def link coffeeAssignOp coffeeOperator + +" Strings used in string assignments, which can't have interpolations +syn region coffeeAssignString start=/"/ skip=/\\\\\|\\"/ end=/"/ contained +\ contains=@coffeeBasicString +syn region coffeeAssignString start=/'/ skip=/\\\\\|\\'/ end=/'/ contained +\ contains=@coffeeBasicString +hi def link coffeeAssignString String + +" A normal object assignment +syn match coffeeObjAssign /@\?\I\i*\s*:\@<!::\@!/ +\ contains=@coffeeIdentifier,coffeeAssignOp +hi def link coffeeObjAssign Identifier + +" An object-string assignment +syn match coffeeObjStringAssign /\("\|'\)[^\1]*\1\s*;\@<!::\@!'\@!/ +\ contains=coffeeAssignString,coffeeAssignOp +" An object-integer assignment +syn match coffeeObjNumberAssign /\d\+\%(\.\d\+\)\?\s*:\@<!::\@!/ +\ contains=coffeeNumber,coffeeAssignOp + +syn keyword coffeeTodo TODO FIXME XXX contained +hi def link coffeeTodo Todo + +syn match coffeeComment /#.*/ contains=@Spell,coffeeTodo +hi def link coffeeComment Comment + +syn region coffeeBlockComment start=/####\@!/ end=/###/ +\ contains=@Spell,coffeeTodo +hi def link coffeeBlockComment coffeeComment + +" A comment in a heregex +syn region coffeeHeregexComment start=/#/ end=/\ze\/\/\/\|$/ contained +\ contains=@Spell,coffeeTodo +hi def link coffeeHeregexComment coffeeComment + +" Embedded JavaScript +syn region coffeeEmbed matchgroup=coffeeEmbedDelim +\ start=/`/ skip=/\\\\\|\\`/ end=/`/ +\ contains=@coffeeJS +hi def link coffeeEmbedDelim Delimiter + +syn region coffeeInterp matchgroup=coffeeInterpDelim start=/#{/ end=/}/ contained +\ contains=@coffeeAll +hi def link coffeeInterpDelim PreProc + +" A string escape sequence +syn match coffeeEscape /\\\d\d\d\|\\x\x\{2\}\|\\u\x\{4\}\|\\./ contained display +hi def link coffeeEscape SpecialChar + +" A regex -- must not follow a parenthesis, number, or identifier, and must not +" be followed by a number +syn region coffeeRegex start=/\%(\%()\|\i\@<!\d\)\s*\|\i\)\@<!\/=\@!\s\@!/ +\ skip=/\[[^\]]\{-}\/[^\]]\{-}\]/ +\ end=/\/[gimy]\{,4}\d\@!/ +\ oneline contains=@coffeeBasicString +hi def link coffeeRegex String + +" A heregex +syn region coffeeHeregex start=/\/\/\// end=/\/\/\/[gimy]\{,4}/ +\ contains=@coffeeInterpString,coffeeHeregexComment +\ fold +hi def link coffeeHeregex coffeeRegex + +" Heredoc strings +syn region coffeeHeredoc start=/"""/ end=/"""/ contains=@coffeeInterpString +\ fold +syn region coffeeHeredoc start=/'''/ end=/'''/ contains=@coffeeBasicString +\ fold +hi def link coffeeHeredoc String + +" An error for trailing whitespace, as long as the line isn't just whitespace +if !exists("coffee_no_trailing_space_error") + syn match coffeeSpaceError /\S\@<=\s\+$/ display + hi def link coffeeSpaceError Error +endif + +" An error for trailing semicolons, for help transitioning from JavaScript +if !exists("coffee_no_trailing_semicolon_error") + syn match coffeeSemicolonError /;$/ display + hi def link coffeeSemicolonError Error +endif + +" Ignore reserved words in dot accesses. +syn match coffeeDotAccess /\.\@<!\.\s*\I\i*/he=s+1 contains=@coffeeIdentifier +hi def link coffeeDotAccess coffeeExtendedOp + +" Ignore reserved words in prototype accesses. +syn match coffeeProtoAccess /::\s*\I\i*/he=s+2 contains=@coffeeIdentifier +hi def link coffeeProtoAccess coffeeExtendedOp + +" This is required for interpolations to work. +syn region coffeeCurlies matchgroup=coffeeCurly start=/{/ end=/}/ +\ contains=@coffeeAll +syn region coffeeBrackets matchgroup=coffeeBracket start=/\[/ end=/\]/ +\ contains=@coffeeAll +syn region coffeeParens matchgroup=coffeeParen start=/(/ end=/)/ +\ contains=@coffeeAll + +" These are highlighted the same as commas since they tend to go together. +hi def link coffeeBlock coffeeSpecialOp +hi def link coffeeBracket coffeeBlock +hi def link coffeeCurly coffeeBlock +hi def link coffeeParen coffeeBlock + +" This is used instead of TOP to keep things coffee-specific for good +" embedding. `contained` groups aren't included. +syn cluster coffeeAll contains=coffeeStatement,coffeeRepeat,coffeeConditional, +\ coffeeException,coffeeKeyword,coffeeOperator, +\ coffeeExtendedOp,coffeeSpecialOp,coffeeBoolean, +\ coffeeGlobal,coffeeSpecialVar,coffeeObject, +\ coffeeConstant,coffeeString,coffeeNumber, +\ coffeeFloat,coffeeReservedError,coffeeObjAssign, +\ coffeeObjStringAssign,coffeeObjNumberAssign, +\ coffeeComment,coffeeBlockComment,coffeeEmbed, +\ coffeeRegex,coffeeHeregex,coffeeHeredoc, +\ coffeeSpaceError,coffeeSemicolonError, +\ coffeeDotAccess,coffeeProtoAccess, +\ coffeeCurlies,coffeeBrackets,coffeeParens + +if !exists('b:current_syntax') + let b:current_syntax = 'coffee' +endif diff --git a/vim/syntax/eco.vim b/vim/syntax/eco.vim new file mode 100755 index 0000000..485b356 --- /dev/null +++ b/vim/syntax/eco.vim @@ -0,0 +1,62 @@ +" Vim syntax file +" Language: eco +" Maintainer: Jay Adkisson +" Mostly stolen from eruby.vim + +if !exists("g:eco_default_subtype") + let g:eco_default_subtype = "html" +endif + +if !exists("b:eco_subtype") + let s:lines = getline(1)."\n".getline(2)."\n".getline(3)."\n".getline(4)."\n".getline(5)."\n".getline("$") + let b:eco_subtype = matchstr(s:lines,'eco_subtype=\zs\w\+') + if b:eco_subtype == '' + let b:eco_subtype = matchstr(substitute(expand("%:t"),'\c\%(\.eco\)\+$','',''),'\.\zs\w\+$') + endif + if b:eco_subtype == 'rhtml' + let b:eco_subtype = 'html' + elseif b:eco_subtype == 'jst' + let b:eco_subtype = 'html' + elseif b:eco_subtype == 'rb' + let b:eco_subtype = 'ruby' + elseif b:eco_subtype == 'yml' + let b:eco_subtype = 'yaml' + elseif b:eco_subtype == 'js' || b:eco_subtype == 'json' + let b:eco_subtype = 'javascript' + elseif b:eco_subtype == 'txt' + " Conventional; not a real file type + let b:eco_subtype = 'text' + elseif b:eco_subtype == '' + if exists('b:current_syntax') && b:current_syntax != '' + let b:eco_subtype = b:current_syntax + else + let b:eco_subtype = g:eco_default_subtype + endif + endif +endif + +if exists("b:eco_subtype") && b:eco_subtype != '' && b:eco_subtype != 'eco' + exec "runtime! syntax/".b:eco_subtype.".vim" + syn include @coffeeTop syntax/coffee.vim +endif + +syn cluster ecoRegions contains=ecoBlock,ecoExpression,ecoComment + +syn region ecoBlock matchgroup=ecoDelimiter start=/<%/ end=/%>/ contains=@coffeeTop containedin=ALLBUT,@ecoRegions keepend +syn region ecoExpression matchgroup=ecoDelimiter start=/<%[=\-]/ end=/%>/ contains=@coffeeTop containedin=ALLBUT,@ecoRegions keepend +syn region ecoComment matchgroup=ecoComment start=/<%#/ end=/%>/ contains=@coffeeTodo,@Spell containedin=ALLBUT,@ecoRegions keepend + +" eco features not in coffeescript proper +syn keyword ecoEnd end containedin=@ecoRegions +syn match ecoIndentColon /\s+\w+:/ containedin=@ecoRegions + +" Define the default highlighting. + +hi def link ecoDelimiter Delimiter +hi def link ecoComment Comment +hi def link ecoEnd coffeeConditional +hi def link ecoIndentColon None + +let b:current_syntax = 'eco' + +" vim: nowrap sw=2 sts=2 ts=8: |