1.0.0 - Remade structure for GNU Stow

This commit is contained in:
2020-09-30 10:39:09 +02:00
parent d5c2147ed1
commit af44571593
766 changed files with 131648 additions and 18 deletions

View File

@@ -0,0 +1,4 @@
.*.sw[a-z]
.*.un~
doc/tags

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
REF = HEAD
VERSION = $(shell git describe --always $(REF))
ARCHIVE = vim-coffee-script-$(VERSION).zip
ARCHIVE_DIRS = after autoload compiler doc ftdetect ftplugin indent syntax
# Don't do anything by default.
all:
# Make vim.org zipball.
archive:
git archive $(REF) -o $(ARCHIVE) -- $(ARCHIVE_DIRS)
# Remove zipball.
clean:
-rm -f $(ARCHIVE)
# Build the list of syntaxes for @coffeeAll.
coffeeAll:
@grep -E 'syn (match|region)' syntax/coffee.vim |\
grep -v 'contained' |\
awk '{print $$3}' |\
uniq
.PHONY: all archive clean hash coffeeAll

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
" Language: CoffeeScript
" Maintainer: Mick Koch <mick@kochm.co>
" URL: http://github.com/kchmck/vim-coffee-script
" License: WTFPL
" Set up some common global/buffer variables.
function! coffee#CoffeeSetUpVariables()
" Path to coffee executable
if !exists('g:coffee_compiler')
let g:coffee_compiler = 'coffee'
endif
" Options passed to coffee with make
if !exists('g:coffee_make_options')
let g:coffee_make_options = ''
endif
" Path to cake executable
if !exists('g:coffee_cake')
let g:coffee_cake = 'cake'
endif
" Extra options passed to cake
if !exists('g:coffee_cake_options')
let g:coffee_cake_options = ''
endif
" Path to coffeelint executable
if !exists('g:coffee_linter')
let g:coffee_linter = 'coffeelint'
endif
" Options passed to CoffeeLint
if !exists('g:coffee_lint_options')
let g:coffee_lint_options = ''
endif
" Pass the litcoffee flag to tools in this buffer if a litcoffee file is open.
" Let the variable be overwritten so it can be updated if a different filetype
" is set.
if &filetype == 'litcoffee'
let b:coffee_litcoffee = '--literate'
else
let b:coffee_litcoffee = ''
endif
endfunction
function! coffee#CoffeeSetUpErrorFormat()
CompilerSet errorformat=Error:\ In\ %f\\,\ %m\ on\ line\ %l,
\Error:\ In\ %f\\,\ Parse\ error\ on\ line\ %l:\ %m,
\SyntaxError:\ In\ %f\\,\ %m,
\%f:%l:%c:\ error:\ %m,
\%-G%.%#
endfunction

View File

@@ -0,0 +1,15 @@
" Language: CoffeeScript
" Maintainer: Mick Koch <mick@kochm.co>
" URL: http://github.com/kchmck/vim-coffee-script
" License: WTFPL
if exists('current_compiler')
finish
endif
let current_compiler = 'cake'
call coffee#CoffeeSetUpVariables()
exec 'CompilerSet makeprg=' . escape(g:coffee_cake . ' ' .
\ g:coffee_cake_options . ' $*', ' ')
call coffee#CoffeeSetUpErrorFormat()

View File

@@ -0,0 +1,82 @@
" Language: CoffeeScript
" Maintainer: Mick Koch <mick@kochm.co>
" URL: http://github.com/kchmck/vim-coffee-script
" License: WTFPL
" All this is needed to support compiling filenames with spaces, quotes, and
" such. The filename is escaped and embedded into the `makeprg` setting.
"
" Because of this, `makeprg` must be updated on every file rename. And because
" of that, `CompilerSet` can't be used because it doesn't exist when the
" rename autocmd is ran. So, we have to do some checks to see whether `compiler`
" was called locally or globally, and respect that in the rest of the script.
if exists('current_compiler')
finish
endif
let current_compiler = 'coffee'
call coffee#CoffeeSetUpVariables()
" Pattern to check if coffee is the compiler
let s:pat = '^' . current_compiler
" Get a `makeprg` for the current filename.
function! s:GetMakePrg()
return g:coffee_compiler .
\ ' -c' .
\ ' ' . b:coffee_litcoffee .
\ ' ' . 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.
exec 'CompilerSet makeprg=' . current_compiler
" Then actually set the compiler.
call s:SetMakePrg()
call coffee#CoffeeSetUpErrorFormat()
function! s:CoffeeMakeDeprecated(bang, args)
echoerr 'CoffeeMake is deprecated! Please use :make instead, its behavior ' .
\ 'is identical.'
sleep 5
exec 'make' . a:bang a:args
endfunction
" Compile the current file.
command! -bang -bar -nargs=* CoffeeMake
\ call s:CoffeeMakeDeprecated(<q-bang>, <q-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 BufWritePre,BufFilePost <buffer> call s:UpdateMakePrg()
else
autocmd BufWritePre,BufFilePost call s:UpdateMakePrg()
endif
augroup END

View File

@@ -0,0 +1,4 @@
Please see the project readme for up-to-date docs:
https://github.com/kchmck/vim-coffee-script
vim:tw=78:ts=8:ft=help:norl:

View File

@@ -0,0 +1,17 @@
" Language: CoffeeScript
" Maintainer: Mick Koch <mick@kochm.co>
" 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,*.ck set filetype=coffee
autocmd BufNewFile,BufRead *._coffee set filetype=coffee
function! s:DetectCoffee()
if getline(1) =~ '^#!.*\<coffee\>'
set filetype=coffee
endif
endfunction
autocmd BufNewFile,BufRead * call s:DetectCoffee()

View File

@@ -0,0 +1,8 @@
" Language: Literate CoffeeScript
" Maintainer: Michael Smith <michael@diglumi.com>
" URL: https://github.com/mintplant/vim-literate-coffeescript
" License: MIT
autocmd BufNewFile,BufRead *.litcoffee set filetype=litcoffee
autocmd BufNewFile,BufRead *.coffee.md set filetype=litcoffee

View File

@@ -0,0 +1,405 @@
" Language: CoffeeScript
" Maintainer: Mick Koch <mick@kochm.co>
" URL: http://github.com/kchmck/vim-coffee-script
" License: WTFPL
if exists('b:did_ftplugin')
finish
endif
let b:did_ftplugin = 1
call coffee#CoffeeSetUpVariables()
setlocal formatoptions-=t formatoptions+=croql
setlocal comments=:# commentstring=#\ %s
setlocal omnifunc=javascriptcomplete#CompleteJS
setlocal suffixesadd+=.coffee
" Create custom augroups.
augroup CoffeeBufUpdate | augroup END
augroup CoffeeBufNew | augroup END
" Enable coffee compiler if a compiler isn't set already.
if !len(&l:makeprg)
compiler coffee
endif
" Switch to the window for buf.
function! s:SwitchWindow(buf)
exec bufwinnr(a:buf) 'wincmd w'
endfunction
" Create a new scratch buffer and return the bufnr of it. After the function
" returns, vim remains in the scratch buffer so more set up can be done.
function! s:ScratchBufBuild(src, vert, size)
if a:size <= 0
if a:vert
let size = winwidth(bufwinnr(a:src)) / 2
else
let size = winheight(bufwinnr(a:src)) / 2
endif
endif
if a:vert
vertical belowright new
exec 'vertical resize' size
else
belowright new
exec 'resize' size
endif
setlocal bufhidden=wipe buftype=nofile nobuflisted noswapfile nomodifiable
nnoremap <buffer> <silent> q :hide<CR>
return bufnr('%')
endfunction
" Replace buffer contents with text and delete the last empty line.
function! s:ScratchBufUpdate(buf, text)
" Move to the scratch buffer.
call s:SwitchWindow(a:buf)
" Double check we're in the scratch buffer before overwriting.
if bufnr('%') != a:buf
throw 'unable to change to scratch buffer'
endif
setlocal modifiable
silent exec '% delete _'
silent put! =a:text
silent exec '$ delete _'
setlocal nomodifiable
endfunction
" Parse the output of coffee into a qflist entry for src buffer.
function! s:ParseCoffeeError(output, src, startline)
" Coffee error is always on first line?
let match = matchlist(a:output,
\ '^\(\f\+\|\[stdin\]\):\(\d\):\(\d\): error: \(.\{-}\)' . "\n")
if !len(match)
return
endif
" Consider the line number from coffee as relative and add it to the beginning
" line number of the range the command was called on, then subtract one for
" zero-based relativity.
call setqflist([{'bufnr': a:src, 'lnum': a:startline + str2nr(match[2]) - 1,
\ 'type': 'E', 'col': str2nr(match[3]), 'text': match[4]}], 'r')
endfunction
" Reset source buffer variables.
function! s:CoffeeCompileResetVars()
" Variables defined in source buffer:
" b:coffee_compile_buf: bufnr of output buffer
" Variables defined in output buffer:
" b:coffee_src_buf: bufnr of source buffer
" b:coffee_compile_pos: previous cursor position in output buffer
let b:coffee_compile_buf = -1
endfunction
function! s:CoffeeWatchResetVars()
" Variables defined in source buffer:
" b:coffee_watch_buf: bufnr of output buffer
" Variables defined in output buffer:
" b:coffee_src_buf: bufnr of source buffer
" b:coffee_watch_pos: previous cursor position in output buffer
let b:coffee_watch_buf = -1
endfunction
function! s:CoffeeRunResetVars()
" Variables defined in CoffeeRun source buffer:
" b:coffee_run_buf: bufnr of output buffer
" Variables defined in CoffeeRun output buffer:
" b:coffee_src_buf: bufnr of source buffer
" b:coffee_run_pos: previous cursor position in output buffer
let b:coffee_run_buf = -1
endfunction
" Clean things up in the source buffers.
function! s:CoffeeCompileClose()
" Switch to the source buffer if not already in it.
silent! call s:SwitchWindow(b:coffee_src_buf)
call s:CoffeeCompileResetVars()
endfunction
function! s:CoffeeWatchClose()
silent! call s:SwitchWindow(b:coffee_src_buf)
silent! autocmd! CoffeeAuWatch * <buffer>
call s:CoffeeWatchResetVars()
endfunction
function! s:CoffeeRunClose()
silent! call s:SwitchWindow(b:coffee_src_buf)
call s:CoffeeRunResetVars()
endfunction
" Compile the lines between startline and endline and put the result into buf.
function! s:CoffeeCompileToBuf(buf, startline, endline)
let src = bufnr('%')
let input = join(getline(a:startline, a:endline), "\n")
" Coffee doesn't like empty input.
if !len(input)
" Function should still return within output buffer.
call s:SwitchWindow(a:buf)
return
endif
" Pipe lines into coffee.
let output = system(g:coffee_compiler .
\ ' -scb' .
\ ' ' . b:coffee_litcoffee .
\ ' 2>&1', input)
" Paste output into output buffer.
call s:ScratchBufUpdate(a:buf, output)
" Highlight as JavaScript if there were no compile errors.
if v:shell_error
call s:ParseCoffeeError(output, src, a:startline)
setlocal filetype=
else
" Clear the quickfix list.
call setqflist([], 'r')
setlocal filetype=javascript
endif
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)
if a:args =~ '\<watch\>'
echoerr 'CoffeeCompile watch is deprecated! Please use CoffeeWatch instead'
sleep 5
call s:CoffeeWatch(a:args)
return
endif
" Switch to the source buffer if not already in it.
silent! call s:SwitchWindow(b:coffee_src_buf)
" Bail if not in source buffer.
if !exists('b:coffee_compile_buf')
return
endif
" Build the output buffer if it doesn't exist.
if bufwinnr(b:coffee_compile_buf) == -1
let src = bufnr('%')
let vert = exists('g:coffee_compile_vert') || a:args =~ '\<vert\%[ical]\>'
let size = str2nr(matchstr(a:args, '\<\d\+\>'))
" Build the output buffer and save the source bufnr.
let buf = s:ScratchBufBuild(src, vert, size)
let b:coffee_src_buf = src
" Set the buffer name.
exec 'silent! file [CoffeeCompile ' . src . ']'
" Clean up the source buffer when the output buffer is closed.
autocmd BufWipeout <buffer> call s:CoffeeCompileClose()
" Save the cursor when leaving the output buffer.
autocmd BufLeave <buffer> let b:coffee_compile_pos = getpos('.')
" Run user-defined commands on new buffer.
silent doautocmd CoffeeBufNew User CoffeeCompile
" Switch back to the source buffer and save the output bufnr. This also
" triggers BufLeave above.
call s:SwitchWindow(src)
let b:coffee_compile_buf = buf
endif
" Fill the scratch buffer.
call s:CoffeeCompileToBuf(b:coffee_compile_buf, a:startline, a:endline)
" Reset cursor to previous position.
call setpos('.', b:coffee_compile_pos)
" Run any user-defined commands on the scratch buffer.
silent doautocmd CoffeeBufUpdate User CoffeeCompile
endfunction
" Update the scratch buffer and switch back to the source buffer.
function! s:CoffeeWatchUpdate()
call s:CoffeeCompileToBuf(b:coffee_watch_buf, 1, '$')
call setpos('.', b:coffee_watch_pos)
silent doautocmd CoffeeBufUpdate User CoffeeWatch
call s:SwitchWindow(b:coffee_src_buf)
endfunction
" Continually compile a source buffer.
function! s:CoffeeWatch(args)
silent! call s:SwitchWindow(b:coffee_src_buf)
if !exists('b:coffee_watch_buf')
return
endif
if bufwinnr(b:coffee_watch_buf) == -1
let src = bufnr('%')
let vert = exists('g:coffee_watch_vert') || a:args =~ '\<vert\%[ical]\>'
let size = str2nr(matchstr(a:args, '\<\d\+\>'))
let buf = s:ScratchBufBuild(src, vert, size)
let b:coffee_src_buf = src
exec 'silent! file [CoffeeWatch ' . src . ']'
autocmd BufWipeout <buffer> call s:CoffeeWatchClose()
autocmd BufLeave <buffer> let b:coffee_watch_pos = getpos('.')
silent doautocmd CoffeeBufNew User CoffeeWatch
call s:SwitchWindow(src)
let b:coffee_watch_buf = buf
endif
" Make sure only one watch autocmd is defined on this buffer.
silent! autocmd! CoffeeAuWatch * <buffer>
augroup CoffeeAuWatch
autocmd InsertLeave <buffer> call s:CoffeeWatchUpdate()
autocmd BufWritePost <buffer> call s:CoffeeWatchUpdate()
augroup END
call s:CoffeeWatchUpdate()
endfunction
" Run a snippet of CoffeeScript between startline and endline.
function! s:CoffeeRun(startline, endline, args)
silent! call s:SwitchWindow(b:coffee_src_buf)
if !exists('b:coffee_run_buf')
return
endif
if bufwinnr(b:coffee_run_buf) == -1
let src = bufnr('%')
let buf = s:ScratchBufBuild(src, exists('g:coffee_run_vert'), 0)
let b:coffee_src_buf = src
exec 'silent! file [CoffeeRun ' . src . ']'
autocmd BufWipeout <buffer> call s:CoffeeRunClose()
autocmd BufLeave <buffer> let b:coffee_run_pos = getpos('.')
silent doautocmd CoffeeBufNew User CoffeeRun
call s:SwitchWindow(src)
let b:coffee_run_buf = buf
endif
if a:startline == 1 && a:endline == line('$')
let output = system(g:coffee_compiler .
\ ' ' . b:coffee_litcoffee .
\ ' ' . fnameescape(expand('%')) .
\ ' ' . a:args)
else
let input = join(getline(a:startline, a:endline), "\n")
if !len(input)
return
endif
let output = system(g:coffee_compiler .
\ ' -s' .
\ ' ' . b:coffee_litcoffee .
\ ' ' . a:args, input)
endif
call s:ScratchBufUpdate(b:coffee_run_buf, output)
call setpos('.', b:coffee_run_pos)
silent doautocmd CoffeeBufUpdate User CoffeeRun
endfunction
" Run coffeelint on a file, and add any errors between startline and endline
" to the quickfix list.
function! s:CoffeeLint(startline, endline, bang, args)
let input = join(getline(a:startline, a:endline), "\n")
if !len(input)
return
endif
let output = system(g:coffee_linter .
\ ' -s --reporter csv' .
\ ' ' . b:coffee_litcoffee .
\ ' ' . g:coffee_lint_options .
\ ' ' . a:args .
\ ' 2>&1', input)
" Convert output into an array and strip off the csv header.
let lines = split(output, "\n")[1:]
let buf = bufnr('%')
let qflist = []
for line in lines
let match = matchlist(line, '^stdin,\(\d\+\),\d*,\(error\|warn\),\(.\+\)$')
" Ignore unmatched lines.
if !len(match)
continue
endif
" The 'type' will result in either 'E' or 'W'.
call add(qflist, {'bufnr': buf, 'lnum': a:startline + str2nr(match[1]) - 1,
\ 'type': toupper(match[2][0]), 'text': match[3]})
endfor
" Replace the quicklist with our items.
call setqflist(qflist, 'r')
" If not given a bang, jump to first error.
if !len(a:bang)
silent! cc 1
endif
endfunction
" Complete arguments for Coffee* commands.
function! s:CoffeeComplete(cmd, cmdline, cursor)
let args = ['vertical']
" If no partial command, return all possibilities.
if !len(a:cmd)
return args
endif
let pat = '^' . a:cmd
for arg in args
if arg =~ pat
return [arg]
endif
endfor
endfunction
" Set initial state variables if they don't exist
if !exists('b:coffee_compile_buf')
call s:CoffeeCompileResetVars()
endif
if !exists('b:coffee_watch_buf')
call s:CoffeeWatchResetVars()
endif
if !exists('b:coffee_run_buf')
call s:CoffeeRunResetVars()
endif
command! -buffer -range=% -bar -nargs=* -complete=customlist,s:CoffeeComplete
\ CoffeeCompile call s:CoffeeCompile(<line1>, <line2>, <q-args>)
command! -buffer -bar -nargs=* -complete=customlist,s:CoffeeComplete
\ CoffeeWatch call s:CoffeeWatch(<q-args>)
command! -buffer -range=% -bar -nargs=* CoffeeRun
\ call s:CoffeeRun(<line1>, <line2>, <q-args>)
command! -buffer -range=% -bang -bar -nargs=* CoffeeLint
\ call s:CoffeeLint(<line1>, <line2>, <q-bang>, <q-args>)

View File

@@ -0,0 +1 @@
runtime ftplugin/coffee.vim

View File

@@ -0,0 +1,428 @@
" Language: CoffeeScript
" Maintainer: Mick Koch <mick@kochm.co>
" 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
" If no indenting or outdenting is needed, either keep the indent of the cursor
" (use autoindent) or match the indent of the previous line.
if exists('g:coffee_indent_keep_current')
let s:DEFAULT_LEVEL = '-1'
else
let s:DEFAULT_LEVEL = 'indent(prevnlnum)'
endif
" Only define the function once.
if exists('*GetCoffeeIndent')
finish
endif
" Keywords that begin a block
let s:BEGIN_BLOCK_KEYWORD = '\C^\%(if\|unless\|else\|for\|while\|until\|'
\ . 'loop\|switch\|when\|try\|catch\|finally\|'
\ . 'class\)\>\%(\s*:\)\@!'
" An expression that uses the result of a statement
let s:COMPOUND_EXPRESSION = '\C\%([^-]-\|[^+]+\|[^/]/\|[:=*%&|^<>]\)\s*'
\ . '\%(if\|unless\|for\|while\|until\|loop\|switch\|'
\ . 'try\|class\)\>'
" Combine the two above
let s:BEGIN_BLOCK = s:BEGIN_BLOCK_KEYWORD . '\|' . s:COMPOUND_EXPRESSION
" Operators that begin a block but also count as a continuation
let s:BEGIN_BLOCK_OP = '[([{:=]$'
" Begins a function block
let s:FUNCTION = '[-=]>$'
" Operators that continue a line onto the next line
let s:CONTINUATION_OP = '\C\%(\<\%(is\|isnt\|and\|or\)\>\|'
\ . '[^-]-\|[^+]+\|[^-=]>\|[^.]\.\|[<*/%&|^,]\)$'
" Ancestor operators that prevent continuation indenting
let s:CONTINUATION = s:CONTINUATION_OP . '\|' . s:BEGIN_BLOCK_OP
" A closing bracket by itself on a line followed by a continuation
let s:BRACKET_CONTINUATION = '^\s*[}\])]\s*' . s:CONTINUATION_OP
" A continuation dot access
let s:DOT_ACCESS = '^\.'
" Keywords that break out of a block
let s:BREAK_BLOCK_OP = '\C^\%(return\|break\|continue\|throw\)\>'
" A condition attached to the end of a statement
let s:POSTFIX_CONDITION = '\C\S\s\+\zs\<\%(if\|unless\|when\|while\|until\)\>'
" A then contained in brackets
let s:CONTAINED_THEN = '\C[(\[].\{-}\<then\>.\{-\}[)\]]'
" An else with a condition attached
let s:ELSE_COND = '\C^\s*else\s\+\<\%(if\|unless\)\>'
" A single-line else statement (without a condition attached)
let s:SINGLE_LINE_ELSE = '\C^else\s\+\%(\<\%(if\|unless\)\>\)\@!'
" Pairs of starting and ending keywords, with an initial pattern to match
let s:KEYWORD_PAIRS = [
\ ['\C^else\>', '\C\<\%(if\|unless\|when\|else\s\+\%(if\|unless\)\)\>',
\ '\C\<else\>'],
\ ['\C^catch\>', '\C\<try\>', '\C\<catch\>'],
\ ['\C^finally\>', '\C\<try\>', '\C\<finally\>']
\]
" Pairs of starting and ending brackets
let s:BRACKET_PAIRS = {']': '\[', '}': '{', ')': '('}
" 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
" Compatibility code for shiftwidth() as recommended by the docs, but modified
" so there isn't as much of a penalty if shiftwidth() exists.
if exists('*shiftwidth')
let s:ShiftWidth = function('shiftwidth')
else
function! s:ShiftWidth()
return &shiftwidth
endfunction
endif
" Get the linked syntax name of a character.
function! s:SyntaxName(lnum, col)
return synIDattr(synID(a:lnum, a:col, 1), 'name')
endfunction
" Check if a character is in a comment.
function! s:IsComment(lnum, col)
return s:SyntaxName(a:lnum, a:col) =~ s:SYNTAX_COMMENT
endfunction
" Check if a character is in a string.
function! s:IsString(lnum, col)
return s:SyntaxName(a:lnum, a:col) =~ s:SYNTAX_STRING
endfunction
" Check if a character is in a comment or string.
function! s:IsCommentOrString(lnum, col)
return s:SyntaxName(a:lnum, a:col) =~ s:SYNTAX_STRING_COMMENT
endfunction
" Search a line for a regex until one is found outside a string or comment.
function! s:SearchCode(lnum, regex)
" Start at the first column and look for an initial match (including at the
" cursor.)
call cursor(a:lnum, 1)
let pos = search(a:regex, 'c', a:lnum)
while pos
if !s:IsCommentOrString(a:lnum, col('.'))
return 1
endif
" Move to the match and continue searching (don't accept matches at the
" cursor.)
let pos = search(a:regex, '', a:lnum)
endwhile
return 0
endfunction
" Search for the nearest previous line that isn't a comment.
function! s:GetPrevNormalLine(startlnum)
let curlnum = a:startlnum
while curlnum
let curlnum = prevnonblank(curlnum - 1)
" Return the line if the first non-whitespace character isn't a comment.
if !s:IsComment(curlnum, indent(curlnum) + 1)
return curlnum
endif
endwhile
return 0
endfunction
function! s:SearchPair(startlnum, lookback, skip, open, close)
" Go to the first column so a:close will be matched even if it's at the
" beginning of the line.
call cursor(a:startlnum, 1)
return searchpair(a:open, '', a:close, 'bnW', a:skip, max([1, a:lookback]))
endfunction
" Skip if a match
" - is in a string or comment
" - is a single-line statement that isn't immediately
" adjacent
" - has a postfix condition and isn't an else statement or compound
" expression
function! s:ShouldSkip(startlnum, lnum, col)
return s:IsCommentOrString(a:lnum, a:col) ||
\ s:SearchCode(a:lnum, '\C\<then\>') && a:startlnum - a:lnum > 1 ||
\ s:SearchCode(a:lnum, s:POSTFIX_CONDITION) &&
\ getline(a:lnum) !~ s:ELSE_COND &&
\ !s:SearchCode(a:lnum, s:COMPOUND_EXPRESSION)
endfunction
" Search for the nearest and farthest match for a keyword pair.
function! s:SearchMatchingKeyword(startlnum, open, close)
let skip = 's:ShouldSkip(' . a:startlnum . ", line('.'), line('.'))"
" Search for the nearest match.
let nearestlnum = s:SearchPair(a:startlnum, a:startlnum - s:MAX_LOOKBACK,
\ skip, a:open, a:close)
if !nearestlnum
return []
endif
" Find the nearest previous line with indent less than or equal to startlnum.
let ind = indent(a:startlnum)
let lookback = s:GetPrevNormalLine(a:startlnum)
while lookback && indent(lookback) > ind
let lookback = s:GetPrevNormalLine(lookback)
endwhile
" Search for the farthest match. If there are no other matches, then the
" nearest match is also the farthest one.
let matchlnum = nearestlnum
while matchlnum
let lnum = matchlnum
let matchlnum = s:SearchPair(matchlnum, lookback, skip, a:open, a:close)
endwhile
return [nearestlnum, lnum]
endfunction
" Strip a line of a trailing comment and surrounding whitespace.
function! s:GetTrimmedLine(lnum)
" Try to find a comment starting at the first column.
call cursor(a:lnum, 1)
let pos = search('#', 'c', a:lnum)
" Keep searching until a comment is found or search returns 0.
while pos
if s:IsComment(a:lnum, col('.'))
break
endif
let pos = search('#', '', a:lnum)
endwhile
if !pos
" No comment was found so use the whole line.
let line = getline(a:lnum)
else
" Subtract 1 to get to the column before the comment and another 1 for
" column indexing -> zero-based indexing.
let line = getline(a:lnum)[:col('.') - 2]
endif
return substitute(substitute(line, '^\s\+', '', ''),
\ '\s\+$', '', '')
endfunction
" Get the indent policy when no special rules are used.
function! s:GetDefaultPolicy(curlnum)
" Check whether equalprg is being ran on existing lines.
if strlen(getline(a:curlnum)) == indent(a:curlnum)
" If not indenting an existing line, use the default policy.
return s:DEFAULT_LEVEL
else
" Otherwise let autoindent determine what to do with an existing line.
return '-1'
endif
endfunction
function! GetCoffeeIndent(curlnum)
" Get the previous non-blank line (may be a comment.)
let prevlnum = prevnonblank(a:curlnum - 1)
" Bail if there's no code before.
if !prevlnum
return -1
endif
" Bail if inside a multiline string.
if s:IsString(a:curlnum, 1)
let prevnlnum = prevlnum
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
" Get the code part of the current line.
let curline = s:GetTrimmedLine(a:curlnum)
" Get the previous non-comment line.
let prevnlnum = s:GetPrevNormalLine(a:curlnum)
" Check if the current line is the closing bracket in a bracket pair.
if has_key(s:BRACKET_PAIRS, curline[0])
" Search for a matching opening bracket.
let matchlnum = s:SearchPair(a:curlnum, a:curlnum - s:MAX_LOOKBACK,
\ "s:IsCommentOrString(line('.'), col('.'))",
\ s:BRACKET_PAIRS[curline[0]], curline[0])
if matchlnum
" Match the indent of the opening bracket.
return indent(matchlnum)
else
" No opening bracket found (bad syntax), so bail.
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
endif
" Check if the current line is the closing keyword in a keyword pair.
for pair in s:KEYWORD_PAIRS
if curline =~ pair[0]
" Find the nearest and farthest matches within the same indent level.
let matches = s:SearchMatchingKeyword(a:curlnum, pair[1], pair[2])
if len(matches)
" Don't force indenting/outdenting as long as line is already lined up
" with a valid match
return max([min([indent(a:curlnum), indent(matches[0])]),
\ indent(matches[1])])
else
" No starting keyword found (bad syntax), so bail.
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
endif
endfor
" Check if the current line is a `when` and not the first in a switch block.
if curline =~ '\C^when\>' && !s:SearchCode(prevnlnum, '\C\<switch\>')
" Look back for a `when`.
while prevnlnum
if getline(prevnlnum) =~ '\C^\s*when\>'
" Indent to match the found `when`, but don't force indenting (for when
" indenting nested switch blocks.)
return min([indent(a:curlnum), indent(prevnlnum)])
endif
let prevnlnum = s:GetPrevNormalLine(prevnlnum)
endwhile
" No matching `when` found (bad syntax), so bail.
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
" If the previous line is a comment, use its indentation, but don't force
" indenting.
if prevlnum != prevnlnum
return min([indent(a:curlnum), indent(prevlnum)])
endif
let prevline = s:GetTrimmedLine(prevnlnum)
" Always indent after these operators.
if prevline =~ s:BEGIN_BLOCK_OP
return indent(prevnlnum) + s:ShiftWidth()
endif
" Indent if the previous line starts a function block, but don't force
" indenting if the line is non-blank (for empty function bodies.)
if prevline =~ s:FUNCTION
if strlen(getline(a:curlnum)) > indent(a:curlnum)
return min([indent(prevnlnum) + s:ShiftWidth(), indent(a:curlnum)])
else
return indent(prevnlnum) + s:ShiftWidth()
endif
endif
" Check if continuation indenting is needed. If the line ends in a slash, make
" sure it isn't a regex.
if prevline =~ s:CONTINUATION_OP &&
\ !(prevline =~ '/$' && s:IsString(prevnlnum, col([prevnlnum, '$']) - 1))
" Don't indent if the continuation follows a closing bracket.
if prevline =~ s:BRACKET_CONTINUATION
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
let prevprevnlnum = s:GetPrevNormalLine(prevnlnum)
" Don't indent if not the first continuation.
if prevprevnlnum && s:GetTrimmedLine(prevprevnlnum) =~ s:CONTINUATION
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
" Continuation indenting seems to vary between programmers, so if the line
" is non-blank, don't override the indentation
if strlen(getline(a:curlnum)) > indent(a:curlnum)
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
" Otherwise indent a level.
return indent(prevnlnum) + s:ShiftWidth()
endif
" Check if the previous line starts with a keyword that begins a block.
if prevline =~ s:BEGIN_BLOCK
" Indent if the current line doesn't start with `then` and the previous line
" isn't a single-line statement.
if curline !~ '\C^\<then\>' && !s:SearchCode(prevnlnum, '\C\<then\>') &&
\ prevline !~ s:SINGLE_LINE_ELSE
return indent(prevnlnum) + s:ShiftWidth()
else
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
endif
" Indent a dot access if it's the first.
if curline =~ s:DOT_ACCESS
if prevline !~ s:DOT_ACCESS
return indent(prevnlnum) + s:ShiftWidth()
else
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
endif
" Outdent if a keyword breaks out of a block as long as it doesn't have a
" postfix condition (and the postfix condition isn't a single-line statement.)
if prevline =~ s:BREAK_BLOCK_OP
if !s:SearchCode(prevnlnum, s:POSTFIX_CONDITION) ||
\ s:SearchCode(prevnlnum, '\C\<then\>') &&
\ !s:SearchCode(prevnlnum, s:CONTAINED_THEN)
" Don't force indenting.
return min([indent(a:curlnum), indent(prevnlnum) - s:ShiftWidth()])
else
exec 'return' s:GetDefaultPolicy(a:curlnum)
endif
endif
" Check if inside brackets.
let matchlnum = s:SearchPair(a:curlnum, a:curlnum - s:MAX_LOOKBACK,
\ "s:IsCommentOrString(line('.'), col('.'))",
\ '\[\|(\|{', '\]\|)\|}')
" If inside brackets, indent relative to the brackets, but don't outdent an
" already indented line.
if matchlnum
return max([indent(a:curlnum), indent(matchlnum) + s:ShiftWidth()])
endif
" No special rules applied, so use the default policy.
exec 'return' s:GetDefaultPolicy(a:curlnum)
endfunction

View File

@@ -0,0 +1,22 @@
if exists('b:did_indent')
finish
endif
runtime! indent/coffee.vim
let b:did_indent = 1
setlocal indentexpr=GetLitCoffeeIndent()
if exists('*GetLitCoffeeIndent')
finish
endif
function GetLitCoffeeIndent()
if searchpair('^ \|\t', '', '$', 'bWnm') > 0
return GetCoffeeIndent(v:lnum)
else
return -1
endif
endfunc

View File

@@ -0,0 +1,221 @@
" Language: CoffeeScript
" Maintainer: Mick Koch <mick@kochm.co>
" 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
silent! unlet b:current_syntax
" Highlight long strings.
syntax sync fromstart
" 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\|from\|by\|and\|or\|not\|is\|isnt\|class\|extends\|super\|do\|yield\|debugger\|import\|export\|default\|await\)\>/
\ 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*\)\@<=[+\-*/%&|\^=!<>?.]\{-1,}\|[-=]>\|--\|++\|:/
\ 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
hi def link coffeeSpecialVar Special
" An @-variable
syn match coffeeSpecialIdent /@\%(\%(\I\|\$\)\%(\i\|\$\)*\)\?/ display
hi def link coffeeSpecialIdent Identifier
" 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,coffeeSpecialIdent,
\ 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\+\%(e[+-]\?\d\+\)\?/ display
" A hex, binary, or octal number
syn match coffeeNumber /\<0[xX]\x\+\>/ display
syn match coffeeNumber /\<0[bB][01]\+\>/ display
syn match coffeeNumber /\<0[oO][0-7]\+\>/ display
syn match coffeeNumber /\<\%(Infinity\|NaN\)\>/ 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, taken from the RESERVED array:
" http://coffeescript.org/documentation/docs/lexer.html#section-67
syn match coffeeReservedError /\<\%(case\|function\|var\|void\|with\|const\|let\|enum\|native\|implements\|interface\|package\|private\|protected\|public\|static\)\>/
\ display
hi def link coffeeReservedError Error
" A normal object assignment
syn match coffeeObjAssign /@\?\%(\I\|\$\)\%(\i\|\$\)*\s*\ze::\@!/ contains=@coffeeIdentifier display
hi def link coffeeObjAssign Identifier
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=/`/ keepend
\ 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\@!#
\ end=#/[gimy]\{,4}\d\@!#
\ oneline contains=@coffeeBasicString,coffeeRegexCharSet
syn region coffeeRegexCharSet start=/\[/ end=/]/ contained
\ contains=@coffeeBasicString
hi def link coffeeRegex String
hi def link coffeeRegexCharSet coffeeRegex
" A heregex
syn region coffeeHeregex start=#///# end=#///[gimy]\{,4}#
\ contains=@coffeeInterpString,coffeeHeregexComment,
\ coffeeHeregexCharSet
\ fold
syn region coffeeHeregexCharSet start=/\[/ end=/]/ contained
\ contains=@coffeeInterpString
hi def link coffeeHeregex coffeeRegex
hi def link coffeeHeregexCharSet coffeeHeregex
" 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
syn match coffeeSpaceError /\S\@<=\s\+$/ display
hi def link coffeeSpaceError Error
" An error for trailing semicolons, for help transitioning from JavaScript
syn match coffeeSemicolonError /;$/ display
hi def link coffeeSemicolonError Error
" 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,coffeeSpecialIdent,
\ coffeeObject,coffeeConstant,coffeeString,
\ coffeeNumber,coffeeFloat,coffeeReservedError,
\ coffeeObjAssign,coffeeComment,coffeeBlockComment,
\ coffeeEmbed,coffeeRegex,coffeeHeregex,
\ coffeeHeredoc,coffeeSpaceError,
\ coffeeSemicolonError,coffeeDotAccess,
\ coffeeProtoAccess,coffeeCurlies,coffeeBrackets,
\ coffeeParens
if !exists('b:current_syntax')
let b:current_syntax = 'coffee'
endif

View File

@@ -0,0 +1,23 @@
" Language: Literate CoffeeScript
" Maintainer: Michael Smith <michael@diglumi.com>
" URL: https://github.com/mintplant/vim-literate-coffeescript
" License: MIT
if exists('b:current_syntax') && b:current_syntax == 'litcoffee'
finish
endif
syn include @markdown syntax/markdown.vim
syn include @coffee syntax/coffee.vim
" Partition the file into notCoffee and inlineCoffee. Each line will match
" exactly one of these regions. notCoffee matches with a zero-width
" look-behind.
syn region notCoffee start='^\%( \|\t\)\@<!' end='$' contains=@markdown
syn region inlineCoffee start='^ \|\t' end='$' contains=@coffee
" We defined notCoffee as a region so we can highlight every element in it
" that doesn't have it's own explicit rule.
highlight default link notCoffee Comment
let b:current_syntax = "litcoffee"

View File

@@ -0,0 +1,3 @@
# Nested curlies
" >> #{ == { { { } } } == } << "
" >> #{ == { abc: { def: 42 } } == } << "

View File

@@ -0,0 +1,90 @@
# Various operators
abc instanceof def
typeof abc
delete abc
abc::def
abc + def
abc - def
abc * def
abc / def
abc % def
abc & def
abc | def
abc ^ def
abc >> def
abc << def
abc >>> def
abc ? def
abc && def
abc and def
abc || def
abc or def
abc += def
abc -= def
abc *= def
abc /= def
abc %= def
abc &= def
abc |= def
abc ^= def
abc >>= def
abc <<= def
abc >>>= def
abc ?= def
abc &&= def
abc ||= def
abc and= def
abc or= def
abc.def.ghi
abc?.def?.ghi
abc < def
abc > def
abc = def
abc == def
abc != def
abc <= def
abc >= def
abc++
abc--
++abc
--abc
# Nested operators
abc[def] = ghi
abc[def[ghi: jkl]] = 42
@abc[def] = ghi
abc["#{def = 42}"] = 42
abc["#{def.ghi = 42}"] = 42
abc["#{def[ghi] = 42}"] = 42
abc["#{def['ghi']}"] = 42
# Object assignments
abc =
def: 123
DEF: 123
@def: 123
Def: 123
'def': 123
42: 123
# Operators shouldn't be highlighted
vector=
wand=
abc+++
abc---
abc ** def
abc &&& def
abc ^^ def
abc ===== def
abc <==== def
abc >==== def
abc +== def
abc =^= def

View File

@@ -0,0 +1,27 @@
# Should be an error
function = 42
var = 42
# Shouldn't be an error
abc.with = 42
function: 42
var: 42
# Keywords shouldn't be highlighted
abc.function
abc.do
abc.break
abc.true
abc::function
abc::do
abc::break
abc::true
abc:: function
abc. function
# Numbers should be highlighted
def.42
def .42
def::42

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
:coffeescript
class Hello
# test

View File

@@ -0,0 +1,17 @@
<head>
<script type="text/coffeescript">
abc = {
def: 42
}
</script>
<script type='text/coffeescript'>
abc = {
def: 42
}
</script>
<script type=text/coffeescript>
abc = {
def: 42
}
</script>
</head>

View File

@@ -0,0 +1,117 @@
The **Scope** class regulates lexical scoping within CoffeeScript. As you
generate code, you create a tree of scopes in the same shape as the nested
function bodies. Each scope knows about the variables declared within it,
and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with `var`, and which are shared
with external scopes.
Import the helpers we plan to use.
{extend, last} = require './helpers'
exports.Scope = class Scope
The `root` is the top-level **Scope** object for a given file.
@root: null
Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the **Block** node it belongs to, which is
where it should declare its variables, and a reference to the function that
it belongs to.
constructor: (@parent, @expressions, @method) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@positions = {}
Scope.root = this unless @parent
Adds a new variable or overrides an existing one.
add: (name, type, immediate) ->
return @parent.add name, type, immediate if @shared and not immediate
if Object::hasOwnProperty.call @positions, name
@variables[@positions[name]].type = type
else
@positions[name] = @variables.push({name, type}) - 1
When `super` is called, we need to find the name of the current method we're
in, so that we know how to invoke the same method of the parent class. This
can get complicated if super is being called from an inner function.
`namedMethod` will walk up the scope tree until it either finds the first
function object that has a name filled in, or bottoms out.
namedMethod: ->
return @method if @method.name or !@parent
@parent.namedMethod()
Look up a variable name in lexical scope, and declare it if it does not
already exist.
find: (name) ->
return yes if @check name
@add name, 'var'
no
Reserve a variable name as originating from a function parameter for this
scope. No `var` required for internal references.
parameter: (name) ->
return if @shared and @parent.check name, yes
@add name, 'param'
Just check to see if a variable has already been declared, without reserving,
walks up to the root scope.
check: (name) ->
!!(@type(name) or @parent?.check(name))
Generate a temporary variable name at the given index.
temporary: (name, index) ->
if name.length > 1
'_' + name + if index > 1 then index - 1 else ''
else
'_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a'
Gets the type of a variable.
type: (name) ->
return v.type for v in @variables when v.name is name
null
If we need to store an intermediate result, find an available name for a
compiler-generated variable. `_var`, `_var2`, and so on...
freeVariable: (name, reserve=true) ->
index = 0
index++ while @check((temp = @temporary name, index))
@add temp, 'var', yes if reserve
temp
Ensure that an assignment is made at the top of this scope
(or at the top-level scope, if requested).
assign: (name, value) ->
@add name, {value, assigned: yes}, yes
@hasAssignments = yes
Does this scope have any declared variables?
hasDeclarations: ->
!!@declaredVariables().length
Return the list of variables first declared in this scope.
declaredVariables: ->
realVars = []
tempVars = []
for v in @variables when v.type is 'var'
(if v.name.charAt(0) is '_' then tempVars else realVars).push v.name
realVars.sort().concat tempVars.sort()
Return the list of assignments that are supposed to be made at the top
of this scope.
assignedVariables: ->
"#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned