Skip to content
Browse files

Add watch and vertical modes to CoffeeCompile (#39)

Watch mode emulates the live preview on the coffee homepage. Use it like:

  :CoffeeCompile watch vert
  • Loading branch information...
1 parent d821074 commit 899e8a886f258d283af64622356e8628f425a34b @kchmck committed
Showing with 195 additions and 55 deletions.
  1. +39 −2 Readme.md
  2. +156 −53 ftplugin/coffee.vim
View
41 Readme.md
@@ -112,8 +112,11 @@ variable along to the compiler. This can be used to set default options:
### CoffeeCompile: Compile Snippets of CoffeeScript
The `CoffeeCompile` command shows how the current file or a snippet of
-CoffeeScript is compiled to JavaScript. Calling `CoffeeCompile` without a range
-compiles the whole file:
+CoffeeScript is compiled to JavaScript. The full signature of the command is:
+
+ :[RANGE] CoffeeCompile [watch|unwatch] [vert[ical]] [WINDOW-SIZE]
+
+Calling `CoffeeCompile` without a range compiles the whole file:
![CoffeeCompile](http://i.imgur.com/pTesp.png)
@@ -128,6 +131,40 @@ snippet of CoffeeScript:
This scratch buffer can be quickly closed by hitting the `q` key.
+Using `vert` splits the CoffeeCompile buffer vertically instead of horizontally:
+
+ :CoffeeCompile vert
+
+The initial size of the CoffeeCompile buffer can be given as a number:
+
+ :CoffeeCompile 4
+
+#### Watch mode
+
+Watch mode emulates the "Try CoffeeScript" live preview box on the CoffeeScript
+homepage:
+
+ ![Watch Mode](http://i.imgur.com/wIN6h.png)
+ ![Watch Mode](http://i.imgur.com/GgdCo.png)
+ ![Watch Mode](http://i.imgur.com/QdpAP.png)
+
+Use `watch` to start watching a buffer (`vert` is also recommended):
+
+ :CoffeeCompile watch vert
+
+After making some changes in insert mode, hit escape and the CoffeeScript will
+be recompiled. Changes made outside of insert mode don't trigger this recompile,
+but calling `CoffeeCompile` will compile these changes without any bad effects.
+
+To get synchronized scrolling of a CoffeeScript and CoffeeCompile buffer, set
+`scrollbind` on each:
+
+ :setl scrollbind
+
+Use `unwatch` to stop watching a buffer:
+
+ :CoffeeCompile unwatch
+
### CoffeeRun: Run some CoffeeScript
The `CoffeeRun` command compiles the current file or selected snippet and runs
View
209 ftplugin/coffee.vim
@@ -9,15 +9,6 @@ endif
let b:did_ftplugin = 1
-" Don't let new windows overwrite these.
-if !exists("s:coffee_compile_prev_buf")
- " Buffer and cursor position before the `CoffeeCompile` buffer was opened
- let s:coffee_compile_prev_buf = -1
- let s:coffee_compile_prev_pos = []
- " Previously-opened `CoffeeCompile` buffer
- let s:coffee_compile_buf = -1
-endif
-
setlocal formatoptions-=t formatoptions+=croql
setlocal comments=:#
setlocal commentstring=#\ %s
@@ -49,86 +40,198 @@ call s:SetMakePrg()
" Reset `makeprg` on rename.
autocmd BufFilePost,BufWritePost,FileWritePost <buffer> call s:SetMakePrg()
-" Save the cursor position.
+" 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_prev_buf = buf
- let s:coffee_compile_prev_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
-" Try to reset the cursor position.
-function! s:CoffeeCompileResetPos()
- let win = bufwinnr(s:coffee_compile_prev_buf)
+" 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_prev_pos)
+ call setpos('.', s:coffee_compile_src_pos)
endif
+endfunction
- autocmd! CoffeeCompileSavePos
+" Close the CoffeeCompile buffer and clean things up.
+function! s:CoffeeCompileClose()
+ silent! autocmd! CoffeeCompileAuPos
+ silent! autocmd! CoffeeCompileAuWatch
+
+ call s:CoffeeCompileRestorePos()
+ call s:CoffeeCompileResetVars()
endfunction
-" Compile some CoffeeScript and show it in a scratch buffer. We handle ranges
-" like this to stop the cursor from being moved before the function is called.
-function! s:CoffeeCompile(startline, endline)
- " Store the current buffer and cursor.
+" 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
- " Build stdin lines.
- let lines = join(getline(a:startline, a:endline), "\n")
- " Get compiler output.
- let output = system('coffee -scb 2>&1', lines)
+" 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
- " Use at most half of the screen.
- let max_height = winheight('%') / 2
- " Try to get the old window.
- let win = bufwinnr(s:coffee_compile_buf)
+ " Parse arguments.
+ let watch = a:args =~ '\<watch\>'
+ let unwatch = a:args =~ '\<unwatch\>'
+ let vert = a:args =~ '\<vert\%[ical]\>'
+ let size = str2nr(matchstr(a:args, '\<\d\+\>'))
- if win == -1
- " Make a new window and store its ID.
- botright new
- let s:coffee_compile_buf = bufnr('%')
+ " 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 noswapfile nowrap
- autocmd BufWipeout <buffer> call s:CoffeeCompileResetPos()
+ autocmd BufWipeout <buffer> call s:CoffeeCompileClose()
nnoremap <buffer> <silent> q :hide<CR>
" Save the cursor position on each buffer switch.
- augroup CoffeeCompileSavePos
+ augroup CoffeeCompileAuPos
autocmd BufEnter,BufLeave * call s:CoffeeCompileSavePos()
augroup END
- else
- " Move to the old window and clear the buffer.
- exec win 'wincmd w'
- setlocal modifiable
- exec '% delete _'
+
+ let s:coffee_compile_buf = bufnr('%')
+ let s:coffee_compile_win = bufwinnr(s:coffee_compile_buf)
endif
- " Paste in the output and delete the last empty line.
- put! =output
- exec '$ delete _'
+ " Go back to the source buffer and do the initial compile.
+ call s:CoffeeCompileRestorePos()
- exec 'resize' min([max_height, line('$') + 1])
- call cursor(1, 1)
+ if s:coffee_compile_watch
+ call s:CoffeeCompileWatchUpdate()
- if v:shell_error
- " A compile error occurred.
- setlocal filetype=
+ augroup CoffeeCompileAuWatch
+ autocmd InsertLeave <buffer> call s:CoffeeCompileWatchUpdate()
+ augroup END
else
- " Coffee was compiled successfully.
- setlocal filetype=javascript
+ 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
- setlocal nomodifiable
+ 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 CoffeeCompile call s:CoffeeCompile(<line1>, <line2>)
+command! -range=% -bar -nargs=* -complete=customlist,s:CoffeeCompileComplete
+\ CoffeeCompile call s:CoffeeCompile(<line1>, <line2>, <q-args>)
" Compile the current file.
command! -bang -bar -nargs=* CoffeeMake make<bang> <args>
" Run some CoffeeScript.

0 comments on commit 899e8a8

Please sign in to comment.
Something went wrong with that request. Please try again.