diff --git a/README.md b/README.md index 517246c..7271e6c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A collection of language packs for Vim. > One to rule them all, one to find them, one to bring them all and in the darkness bind them. - It **won't affect your startup time**, as scripts are loaded only on demand\*. -- It **installs and updates 100+ times faster** than the 116 packages it consists of. +- It **installs and updates 100+ times faster** than the 117 packages it consists of. - Solid syntax and indentation support (other features skipped). Only the best language packs. - All unnecessary files are ignored (like enormous documentation from php support). - No support for esoteric languages, only most popular ones (modern too, like `slim`). @@ -103,6 +103,7 @@ If you need full functionality of any plugin, please use it directly with your p - [mako](https://github.com/sophacles/vim-bundle-mako) (syntax, indent, ftplugin) - [markdown](https://github.com/plasticboy/vim-markdown) (syntax, indent) - [mathematica](https://github.com/rsmenon/vim-mathematica) (syntax, ftplugin) +- [moonscript](https://github.com/leafo/moonscript-vim) (syntax, indent, ftplugin) - [nginx](https://github.com/chr4/nginx.vim) (syntax, indent, ftplugin) - [nim](https://github.com/zah/nim.vim) (syntax, compiler, indent) - [nix](https://github.com/LnL7/vim-nix) (syntax, indent, compiler, ftplugin) diff --git a/build b/build index ace4120..7ebb289 100755 --- a/build +++ b/build @@ -212,6 +212,7 @@ PACKS=" mako:sophacles/vim-bundle-mako markdown:plasticboy/vim-markdown:_SYNTAX mathematica:rsmenon/vim-mathematica + moonscript:leafo/moonscript-vim nginx:chr4/nginx.vim nim:zah/nim.vim:_BASIC nix:LnL7/vim-nix diff --git a/ftdetect/polyglot.vim b/ftdetect/polyglot.vim index a63014d..5eaa932 100644 --- a/ftdetect/polyglot.vim +++ b/ftdetect/polyglot.vim @@ -650,6 +650,27 @@ au BufRead,BufNewFile *.{md,mdown,mkd,mkdn,markdown,mdwn}.{des3,des,bf,bfa,aes,i augroup end endif +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'moonscript') == -1 + augroup filetypedetect + " moonscript, from moon.vim in leafo/moonscript-vim +" Language: MoonScript +" Maintainer: leafo +" Based On: CoffeeScript by Mick Koch +" URL: http://github.com/leafo/moonscript-vim +" License: WTFPL + +autocmd BufNewFile,BufRead *.moon set filetype=moon + +function! s:DetectMoon() + if getline(1) =~ '^#!.*\' + set filetype=moon + endif +endfunction + +autocmd BufNewFile,BufRead * call s:DetectMoon() + augroup end +endif + if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nginx') == -1 augroup filetypedetect " nginx, from nginx.vim in chr4/nginx.vim diff --git a/ftplugin/moon.vim b/ftplugin/moon.vim new file mode 100644 index 0000000..4dc259f --- /dev/null +++ b/ftplugin/moon.vim @@ -0,0 +1,20 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'moonscript') == -1 + +" Language: MoonScript +" Maintainer: leafo +" Based On: CoffeeScript by Mick Koch +" URL: http://github.com/leafo/moonscript-vim +" License: WTFPL + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal formatoptions-=t +setlocal comments=:-- +setlocal commentstring=--\ %s + +let b:undo_ftplugin = "setlocal commentstring< comments< formatoptions<" + +endif diff --git a/indent/moon.vim b/indent/moon.vim new file mode 100644 index 0000000..85d5ce3 --- /dev/null +++ b/indent/moon.vim @@ -0,0 +1,327 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'moonscript') == -1 + +" Language: MoonScript +" Maintainer: leafo +" Based On: CoffeeScript by Mick Koch +" URL: http://github.com/leafo/moonscript-vim +" License: WTFPL + +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetMoonIndent(v:lnum) +" Make sure GetMoonIndent is run when these are typed so they can be +" indented or outdented. +setlocal indentkeys+=0],0),0.,=else,=elseif + +" Only define the function once. +if exists("*GetMoonIndent") + finish +endif + +" Keywords to indent after +let s:INDENT_AFTER_KEYWORD = '^\%(if\|unless\|else\|for\|while\|with\|elseif\|' +\ . 'class\|switch\|when\)\>' + +" Operators to indent after +let s:INDENT_AFTER_OPERATOR = '\%([([{:=]\|[-=]>\)$' + +" Keywords and operators that continue a line +let s:CONTINUATION = '\<\%(is\|isnt\|and\|or\)\>$' +\ . '\|' +\ . '\%(-\@\|\%(export \)\@' + +" A compound assignment like `... = if ...` +let s:COMPOUND_ASSIGNMENT = '[:=]\s*\%(if\|unless\|for\|while\|' +\ . 'with\|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\)\>\)\@!' + +" Max lines to look back for a match +let s:MAX_LOOKBACK = 50 + +" Syntax names for strings +let s:SYNTAX_STRING = 'moon\%(String\|AssignString\|Embed\|Regex\|Heregex\|' +\ . 'Heredoc\)' + +" Syntax names for comments +let s:SYNTAX_COMMENT = 'moon\%(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, '\') && 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\)\>', '\') +" elseif a:curline =~ '^catch\>' +" return s:SearchPair('\', '\') +" elseif a:curline =~ '^finally\>' +" return s:SearchPair('\', '\') + 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 = 0 + " 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:GetMoonIndent(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, '\') +" 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) + 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, '\') && 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, '\') + return previndent - &shiftwidth + endif + endif + + " No indenting or outdenting is needed. + return -1 +endfunction + +" Wrap s:GetMoonIndent to keep the cursor position. +function! GetMoonIndent(curlinenum) + let oldcursor = getpos('.') + let indent = s:GetMoonIndent(a:curlinenum) + call setpos('.', oldcursor) + + return indent +endfunction + +endif diff --git a/syntax/moon.vim b/syntax/moon.vim new file mode 100644 index 0000000..327e135 --- /dev/null +++ b/syntax/moon.vim @@ -0,0 +1,321 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'moonscript') == -1 + +" Language: MoonScript +" Maintainer: leafo +" Based On: CoffeeScript by Mick Koch +" URL: http://github.com/leafo/moonscript-vim +" License: WTFPL + +" Bail if our syntax is already loaded. +if exists('b:current_syntax') && b:current_syntax == 'moon' + finish +endif + +if version < 600 + syn clear +endif + +" Highlight long strings. +syn sync minlines=100 + +" 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 moonAssign and moonDot. +syn match moonStatement /\<\%(return\|break\|continue\)\>/ display +hi def link moonStatement Statement + +syn match moonRepeat /\<\%(for\|while\)\>/ display +hi def link moonRepeat Repeat + +syn match moonConditional /\<\%(if\|else\|elseif\|then\|switch\|when\|unless\)\>/ +\ display +hi def link moonConditional Conditional + +" syn match moonException /\<\%(try\|catch\|finally\)\>/ display +" hi def link moonException Exception + +syn match moonKeyword /\<\%(export\|local\|import\|from\|with\|in\|and\|or\|not\|class\|extends\|super\|using\|do\)\>/ +\ display +hi def link moonKeyword Keyword + +" all built in funcs from Lua 5.1 +syn keyword moonLuaFunc assert collectgarbage dofile error next +syn keyword moonLuaFunc print rawget rawset tonumber tostring type _VERSION +syn keyword moonLuaFunc _G getfenv getmetatable ipairs loadfile +syn keyword moonLuaFunc loadstring pairs pcall rawequal +syn keyword moonLuaFunc require setfenv setmetatable unpack xpcall +syn keyword moonLuaFunc load module select +syn match moonLuaFunc /package\.cpath/ +syn match moonLuaFunc /package\.loaded/ +syn match moonLuaFunc /package\.loadlib/ +syn match moonLuaFunc /package\.path/ +syn match moonLuaFunc /package\.preload/ +syn match moonLuaFunc /package\.seeall/ +syn match moonLuaFunc /coroutine\.running/ +syn match moonLuaFunc /coroutine\.create/ +syn match moonLuaFunc /coroutine\.resume/ +syn match moonLuaFunc /coroutine\.status/ +syn match moonLuaFunc /coroutine\.wrap/ +syn match moonLuaFunc /coroutine\.yield/ +syn match moonLuaFunc /string\.byte/ +syn match moonLuaFunc /string\.char/ +syn match moonLuaFunc /string\.dump/ +syn match moonLuaFunc /string\.find/ +syn match moonLuaFunc /string\.len/ +syn match moonLuaFunc /string\.lower/ +syn match moonLuaFunc /string\.rep/ +syn match moonLuaFunc /string\.sub/ +syn match moonLuaFunc /string\.upper/ +syn match moonLuaFunc /string\.format/ +syn match moonLuaFunc /string\.gsub/ +syn match moonLuaFunc /string\.gmatch/ +syn match moonLuaFunc /string\.match/ +syn match moonLuaFunc /string\.reverse/ +syn match moonLuaFunc /table\.maxn/ +syn match moonLuaFunc /table\.concat/ +syn match moonLuaFunc /table\.sort/ +syn match moonLuaFunc /table\.insert/ +syn match moonLuaFunc /table\.remove/ +syn match moonLuaFunc /math\.abs/ +syn match moonLuaFunc /math\.acos/ +syn match moonLuaFunc /math\.asin/ +syn match moonLuaFunc /math\.atan/ +syn match moonLuaFunc /math\.atan2/ +syn match moonLuaFunc /math\.ceil/ +syn match moonLuaFunc /math\.sin/ +syn match moonLuaFunc /math\.cos/ +syn match moonLuaFunc /math\.tan/ +syn match moonLuaFunc /math\.deg/ +syn match moonLuaFunc /math\.exp/ +syn match moonLuaFunc /math\.floor/ +syn match moonLuaFunc /math\.log/ +syn match moonLuaFunc /math\.log10/ +syn match moonLuaFunc /math\.max/ +syn match moonLuaFunc /math\.min/ +syn match moonLuaFunc /math\.fmod/ +syn match moonLuaFunc /math\.modf/ +syn match moonLuaFunc /math\.cosh/ +syn match moonLuaFunc /math\.sinh/ +syn match moonLuaFunc /math\.tanh/ +syn match moonLuaFunc /math\.pow/ +syn match moonLuaFunc /math\.rad/ +syn match moonLuaFunc /math\.sqrt/ +syn match moonLuaFunc /math\.frexp/ +syn match moonLuaFunc /math\.ldexp/ +syn match moonLuaFunc /math\.random/ +syn match moonLuaFunc /math\.randomseed/ +syn match moonLuaFunc /math\.pi/ +syn match moonLuaFunc /io\.stdin/ +syn match moonLuaFunc /io\.stdout/ +syn match moonLuaFunc /io\.stderr/ +syn match moonLuaFunc /io\.close/ +syn match moonLuaFunc /io\.flush/ +syn match moonLuaFunc /io\.input/ +syn match moonLuaFunc /io\.lines/ +syn match moonLuaFunc /io\.open/ +syn match moonLuaFunc /io\.output/ +syn match moonLuaFunc /io\.popen/ +syn match moonLuaFunc /io\.read/ +syn match moonLuaFunc /io\.tmpfile/ +syn match moonLuaFunc /io\.type/ +syn match moonLuaFunc /io\.write/ +syn match moonLuaFunc /os\.clock/ +syn match moonLuaFunc /os\.date/ +syn match moonLuaFunc /os\.difftime/ +syn match moonLuaFunc /os\.execute/ +syn match moonLuaFunc /os\.exit/ +syn match moonLuaFunc /os\.getenv/ +syn match moonLuaFunc /os\.remove/ +syn match moonLuaFunc /os\.rename/ +syn match moonLuaFunc /os\.setlocale/ +syn match moonLuaFunc /os\.time/ +syn match moonLuaFunc /os\.tmpname/ +syn match moonLuaFunc /debug\.debug/ +syn match moonLuaFunc /debug\.gethook/ +syn match moonLuaFunc /debug\.getinfo/ +syn match moonLuaFunc /debug\.getlocal/ +syn match moonLuaFunc /debug\.getupvalue/ +syn match moonLuaFunc /debug\.setlocal/ +syn match moonLuaFunc /debug\.setupvalue/ +syn match moonLuaFunc /debug\.sethook/ +syn match moonLuaFunc /debug\.traceback/ +syn match moonLuaFunc /debug\.getfenv/ +syn match moonLuaFunc /debug\.getmetatable/ +syn match moonLuaFunc /debug\.getregistry/ +syn match moonLuaFunc /debug\.setfenv/ +syn match moonLuaFunc /debug\.setmetatable/ + +hi def link moonLuaFunc Identifier + +" The first case matches symbol operators only if they have an operand before. +syn match moonExtendedOp /\%(\S\s*\)\@<=[+\-*/%&|\^=!<>?#]\+\|\.\|\\/ +\ display +hi def link moonExtendedOp moonOperator +hi def link moonOperator Operator + +syntax match moonFunction /->\|=>\|)\|(\|\[\|]\|{\|}\|!/ +highlight default link moonFunction Function + +" This is separate from `moonExtendedOp` to help differentiate commas from +" dots. +syn match moonSpecialOp /[,;]/ display +hi def link moonSpecialOp SpecialChar + +syn match moonBoolean /\<\%(true\|false\)\>/ display +hi def link moonBoolean Boolean + +syn match moonGlobal /\<\%(nil\)\>/ display +hi def link moonGlobal Type + +" A special variable +syn match moonSpecialVar /\<\%(self\)\>/ display +" An @-variable +syn match moonSpecialVar /@\%(\I\i*\)\?/ display +hi def link moonSpecialVar Structure + +" A class-like name that starts with a capital letter +syn match moonObject /\<\u\w*\>/ display +hi def link moonObject Structure + +" A constant-like name in SCREAMING_CAPS +syn match moonConstant /\<\u[A-Z0-9_]\+\>/ display +hi def link moonConstant Constant + +" A variable name +syn cluster moonIdentifier contains=moonSpecialVar,moonObject, +\ moonConstant + +" A non-interpolated string +syn cluster moonBasicString contains=@Spell,moonEscape +" An interpolated string +syn cluster moonInterpString contains=@moonBasicString,moonInterp + +" Regular strings +syn region moonString start=/"/ skip=/\\\\\|\\"/ end=/"/ +\ contains=@moonInterpString +syn region moonString start=/'/ skip=/\\\\\|\\'/ end=/'/ +\ contains=@moonBasicString +hi def link moonString String + +syn region moonString2 matchgroup=moonString start="\[\z(=*\)\[" end="\]\z1\]" contains=@Spell +hi def link moonString2 String + + +" A integer, including a leading plus or minus +syn match moonNumber /\i\@/ display +hi def link moonNumber Number + +" A floating-point number, including a leading plus or minus +syn match moonFloat /\i\@/ + \ display + hi def link moonReservedError Error +endif + +" This is separate from `moonExtendedOp` since assignments require it. +syn match moonAssignOp /:/ contained display +hi def link moonAssignOp moonOperator + +" Strings used in string assignments, which can't have interpolations +syn region moonAssignString start=/"/ skip=/\\\\\|\\"/ end=/"/ contained +\ contains=@moonBasicString +syn region moonAssignString start=/'/ skip=/\\\\\|\\'/ end=/'/ contained +\ contains=@moonBasicString +hi def link moonAssignString String + +" A normal object assignment +syn match moonObjAssign /@\?\I\i*\s*:\@