Simple image host.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

coffee.vim 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. " Language: CoffeeScript
  2. " Maintainer: Mick Koch <mick@kochm.co>
  3. " URL: http://github.com/kchmck/vim-coffee-script
  4. " License: WTFPL
  5. if exists('b:did_ftplugin')
  6. finish
  7. endif
  8. let b:did_ftplugin = 1
  9. call coffee#CoffeeSetUpVariables()
  10. setlocal formatoptions-=t formatoptions+=croql
  11. setlocal comments=:# commentstring=#\ %s
  12. setlocal omnifunc=javascriptcomplete#CompleteJS
  13. setlocal suffixesadd+=coffee
  14. " Create custom augroups.
  15. augroup CoffeeBufUpdate | augroup END
  16. augroup CoffeeBufNew | augroup END
  17. " Enable coffee compiler if a compiler isn't set already.
  18. if !len(&l:makeprg)
  19. compiler coffee
  20. endif
  21. " Switch to the window for buf.
  22. function! s:SwitchWindow(buf)
  23. exec bufwinnr(a:buf) 'wincmd w'
  24. endfunction
  25. " Create a new scratch buffer and return the bufnr of it. After the function
  26. " returns, vim remains in the scratch buffer so more set up can be done.
  27. function! s:ScratchBufBuild(src, vert, size)
  28. if a:size <= 0
  29. if a:vert
  30. let size = winwidth(bufwinnr(a:src)) / 2
  31. else
  32. let size = winheight(bufwinnr(a:src)) / 2
  33. endif
  34. endif
  35. if a:vert
  36. vertical belowright new
  37. exec 'vertical resize' size
  38. else
  39. belowright new
  40. exec 'resize' size
  41. endif
  42. setlocal bufhidden=wipe buftype=nofile nobuflisted noswapfile nomodifiable
  43. nnoremap <buffer> <silent> q :hide<CR>
  44. return bufnr('%')
  45. endfunction
  46. " Replace buffer contents with text and delete the last empty line.
  47. function! s:ScratchBufUpdate(buf, text)
  48. " Move to the scratch buffer.
  49. call s:SwitchWindow(a:buf)
  50. " Double check we're in the scratch buffer before overwriting.
  51. if bufnr('%') != a:buf
  52. throw 'unable to change to scratch buffer'
  53. endif
  54. setlocal modifiable
  55. silent exec '% delete _'
  56. silent put! =a:text
  57. silent exec '$ delete _'
  58. setlocal nomodifiable
  59. endfunction
  60. " Parse the output of coffee into a qflist entry for src buffer.
  61. function! s:ParseCoffeeError(output, src, startline)
  62. " Coffee error is always on first line?
  63. let match = matchlist(a:output,
  64. \ '^\(\f\+\|\[stdin\]\):\(\d\):\(\d\): error: \(.\{-}\)' . "\n")
  65. if !len(match)
  66. return
  67. endif
  68. " Consider the line number from coffee as relative and add it to the beginning
  69. " line number of the range the command was called on, then subtract one for
  70. " zero-based relativity.
  71. call setqflist([{'bufnr': a:src, 'lnum': a:startline + str2nr(match[2]) - 1,
  72. \ 'type': 'E', 'col': str2nr(match[3]), 'text': match[4]}], 'r')
  73. endfunction
  74. " Reset source buffer variables.
  75. function! s:CoffeeCompileResetVars()
  76. " Variables defined in source buffer:
  77. " b:coffee_compile_buf: bufnr of output buffer
  78. " Variables defined in output buffer:
  79. " b:coffee_src_buf: bufnr of source buffer
  80. " b:coffee_compile_pos: previous cursor position in output buffer
  81. let b:coffee_compile_buf = -1
  82. endfunction
  83. function! s:CoffeeWatchResetVars()
  84. " Variables defined in source buffer:
  85. " b:coffee_watch_buf: bufnr of output buffer
  86. " Variables defined in output buffer:
  87. " b:coffee_src_buf: bufnr of source buffer
  88. " b:coffee_watch_pos: previous cursor position in output buffer
  89. let b:coffee_watch_buf = -1
  90. endfunction
  91. function! s:CoffeeRunResetVars()
  92. " Variables defined in CoffeeRun source buffer:
  93. " b:coffee_run_buf: bufnr of output buffer
  94. " Variables defined in CoffeeRun output buffer:
  95. " b:coffee_src_buf: bufnr of source buffer
  96. " b:coffee_run_pos: previous cursor position in output buffer
  97. let b:coffee_run_buf = -1
  98. endfunction
  99. " Clean things up in the source buffers.
  100. function! s:CoffeeCompileClose()
  101. " Switch to the source buffer if not already in it.
  102. silent! call s:SwitchWindow(b:coffee_src_buf)
  103. call s:CoffeeCompileResetVars()
  104. endfunction
  105. function! s:CoffeeWatchClose()
  106. silent! call s:SwitchWindow(b:coffee_src_buf)
  107. silent! autocmd! CoffeeAuWatch * <buffer>
  108. call s:CoffeeWatchResetVars()
  109. endfunction
  110. function! s:CoffeeRunClose()
  111. silent! call s:SwitchWindow(b:coffee_src_buf)
  112. call s:CoffeeRunResetVars()
  113. endfunction
  114. " Compile the lines between startline and endline and put the result into buf.
  115. function! s:CoffeeCompileToBuf(buf, startline, endline)
  116. let src = bufnr('%')
  117. let input = join(getline(a:startline, a:endline), "\n")
  118. " Coffee doesn't like empty input.
  119. if !len(input)
  120. " Function should still return within output buffer.
  121. call s:SwitchWindow(a:buf)
  122. return
  123. endif
  124. " Pipe lines into coffee.
  125. let output = system(g:coffee_compiler .
  126. \ ' -scb' .
  127. \ ' ' . b:coffee_litcoffee .
  128. \ ' 2>&1', input)
  129. " Paste output into output buffer.
  130. call s:ScratchBufUpdate(a:buf, output)
  131. " Highlight as JavaScript if there were no compile errors.
  132. if v:shell_error
  133. call s:ParseCoffeeError(output, src, a:startline)
  134. setlocal filetype=
  135. else
  136. " Clear the quickfix list.
  137. call setqflist([], 'r')
  138. setlocal filetype=javascript
  139. endif
  140. endfunction
  141. " Peek at compiled CoffeeScript in a scratch buffer. We handle ranges like this
  142. " to prevent the cursor from being moved (and its position saved) before the
  143. " function is called.
  144. function! s:CoffeeCompile(startline, endline, args)
  145. if a:args =~ '\<watch\>'
  146. echoerr 'CoffeeCompile watch is deprecated! Please use CoffeeWatch instead'
  147. sleep 5
  148. call s:CoffeeWatch(a:args)
  149. return
  150. endif
  151. " Switch to the source buffer if not already in it.
  152. silent! call s:SwitchWindow(b:coffee_src_buf)
  153. " Bail if not in source buffer.
  154. if !exists('b:coffee_compile_buf')
  155. return
  156. endif
  157. " Build the output buffer if it doesn't exist.
  158. if bufwinnr(b:coffee_compile_buf) == -1
  159. let src = bufnr('%')
  160. let vert = exists('g:coffee_compile_vert') || a:args =~ '\<vert\%[ical]\>'
  161. let size = str2nr(matchstr(a:args, '\<\d\+\>'))
  162. " Build the output buffer and save the source bufnr.
  163. let buf = s:ScratchBufBuild(src, vert, size)
  164. let b:coffee_src_buf = src
  165. " Set the buffer name.
  166. exec 'silent! file [CoffeeCompile ' . src . ']'
  167. " Clean up the source buffer when the output buffer is closed.
  168. autocmd BufWipeout <buffer> call s:CoffeeCompileClose()
  169. " Save the cursor when leaving the output buffer.
  170. autocmd BufLeave <buffer> let b:coffee_compile_pos = getpos('.')
  171. " Run user-defined commands on new buffer.
  172. silent doautocmd CoffeeBufNew User CoffeeCompile
  173. " Switch back to the source buffer and save the output bufnr. This also
  174. " triggers BufLeave above.
  175. call s:SwitchWindow(src)
  176. let b:coffee_compile_buf = buf
  177. endif
  178. " Fill the scratch buffer.
  179. call s:CoffeeCompileToBuf(b:coffee_compile_buf, a:startline, a:endline)
  180. " Reset cursor to previous position.
  181. call setpos('.', b:coffee_compile_pos)
  182. " Run any user-defined commands on the scratch buffer.
  183. silent doautocmd CoffeeBufUpdate User CoffeeCompile
  184. endfunction
  185. " Update the scratch buffer and switch back to the source buffer.
  186. function! s:CoffeeWatchUpdate()
  187. call s:CoffeeCompileToBuf(b:coffee_watch_buf, 1, '$')
  188. call setpos('.', b:coffee_watch_pos)
  189. silent doautocmd CoffeeBufUpdate User CoffeeWatch
  190. call s:SwitchWindow(b:coffee_src_buf)
  191. endfunction
  192. " Continually compile a source buffer.
  193. function! s:CoffeeWatch(args)
  194. silent! call s:SwitchWindow(b:coffee_src_buf)
  195. if !exists('b:coffee_watch_buf')
  196. return
  197. endif
  198. if bufwinnr(b:coffee_watch_buf) == -1
  199. let src = bufnr('%')
  200. let vert = exists('g:coffee_watch_vert') || a:args =~ '\<vert\%[ical]\>'
  201. let size = str2nr(matchstr(a:args, '\<\d\+\>'))
  202. let buf = s:ScratchBufBuild(src, vert, size)
  203. let b:coffee_src_buf = src
  204. exec 'silent! file [CoffeeWatch ' . src . ']'
  205. autocmd BufWipeout <buffer> call s:CoffeeWatchClose()
  206. autocmd BufLeave <buffer> let b:coffee_watch_pos = getpos('.')
  207. silent doautocmd CoffeeBufNew User CoffeeWatch
  208. call s:SwitchWindow(src)
  209. let b:coffee_watch_buf = buf
  210. endif
  211. " Make sure only one watch autocmd is defined on this buffer.
  212. silent! autocmd! CoffeeAuWatch * <buffer>
  213. augroup CoffeeAuWatch
  214. autocmd InsertLeave <buffer> call s:CoffeeWatchUpdate()
  215. autocmd BufWritePost <buffer> call s:CoffeeWatchUpdate()
  216. augroup END
  217. call s:CoffeeWatchUpdate()
  218. endfunction
  219. " Run a snippet of CoffeeScript between startline and endline.
  220. function! s:CoffeeRun(startline, endline, args)
  221. silent! call s:SwitchWindow(b:coffee_src_buf)
  222. if !exists('b:coffee_run_buf')
  223. return
  224. endif
  225. if bufwinnr(b:coffee_run_buf) == -1
  226. let src = bufnr('%')
  227. let buf = s:ScratchBufBuild(src, exists('g:coffee_run_vert'), 0)
  228. let b:coffee_src_buf = src
  229. exec 'silent! file [CoffeeRun ' . src . ']'
  230. autocmd BufWipeout <buffer> call s:CoffeeRunClose()
  231. autocmd BufLeave <buffer> let b:coffee_run_pos = getpos('.')
  232. silent doautocmd CoffeeBufNew User CoffeeRun
  233. call s:SwitchWindow(src)
  234. let b:coffee_run_buf = buf
  235. endif
  236. if a:startline == 1 && a:endline == line('$')
  237. let output = system(g:coffee_compiler .
  238. \ ' ' . b:coffee_litcoffee .
  239. \ ' ' . fnameescape(expand('%')) .
  240. \ ' ' . a:args)
  241. else
  242. let input = join(getline(a:startline, a:endline), "\n")
  243. if !len(input)
  244. return
  245. endif
  246. let output = system(g:coffee_compiler .
  247. \ ' -s' .
  248. \ ' ' . b:coffee_litcoffee .
  249. \ ' ' . a:args, input)
  250. endif
  251. call s:ScratchBufUpdate(b:coffee_run_buf, output)
  252. call setpos('.', b:coffee_run_pos)
  253. silent doautocmd CoffeeBufUpdate User CoffeeRun
  254. endfunction
  255. " Run coffeelint on a file, and add any errors between startline and endline
  256. " to the quickfix list.
  257. function! s:CoffeeLint(startline, endline, bang, args)
  258. let input = join(getline(a:startline, a:endline), "\n")
  259. if !len(input)
  260. return
  261. endif
  262. let output = system(g:coffee_linter .
  263. \ ' -s --reporter csv' .
  264. \ ' ' . b:coffee_litcoffee .
  265. \ ' ' . g:coffee_lint_options .
  266. \ ' ' . a:args .
  267. \ ' 2>&1', input)
  268. " Convert output into an array and strip off the csv header.
  269. let lines = split(output, "\n")[1:]
  270. let buf = bufnr('%')
  271. let qflist = []
  272. for line in lines
  273. let match = matchlist(line, '^stdin,\(\d\+\),\d*,\(error\|warn\),\(.\+\)$')
  274. " Ignore unmatched lines.
  275. if !len(match)
  276. continue
  277. endif
  278. " The 'type' will result in either 'E' or 'W'.
  279. call add(qflist, {'bufnr': buf, 'lnum': a:startline + str2nr(match[1]) - 1,
  280. \ 'type': toupper(match[2][0]), 'text': match[3]})
  281. endfor
  282. " Replace the quicklist with our items.
  283. call setqflist(qflist, 'r')
  284. " If not given a bang, jump to first error.
  285. if !len(a:bang)
  286. silent! cc 1
  287. endif
  288. endfunction
  289. " Complete arguments for Coffee* commands.
  290. function! s:CoffeeComplete(cmd, cmdline, cursor)
  291. let args = ['vertical']
  292. " If no partial command, return all possibilities.
  293. if !len(a:cmd)
  294. return args
  295. endif
  296. let pat = '^' . a:cmd
  297. for arg in args
  298. if arg =~ pat
  299. return [arg]
  300. endif
  301. endfor
  302. endfunction
  303. " Set initial state variables if they don't exist
  304. if !exists('b:coffee_compile_buf')
  305. call s:CoffeeCompileResetVars()
  306. endif
  307. if !exists('b:coffee_watch_buf')
  308. call s:CoffeeWatchResetVars()
  309. endif
  310. if !exists('b:coffee_run_buf')
  311. call s:CoffeeRunResetVars()
  312. endif
  313. command! -buffer -range=% -bar -nargs=* -complete=customlist,s:CoffeeComplete
  314. \ CoffeeCompile call s:CoffeeCompile(<line1>, <line2>, <q-args>)
  315. command! -buffer -bar -nargs=* -complete=customlist,s:CoffeeComplete
  316. \ CoffeeWatch call s:CoffeeWatch(<q-args>)
  317. command! -buffer -range=% -bar -nargs=* CoffeeRun
  318. \ call s:CoffeeRun(<line1>, <line2>, <q-args>)
  319. command! -buffer -range=% -bang -bar -nargs=* CoffeeLint
  320. \ call s:CoffeeLint(<line1>, <line2>, <q-bang>, <q-args>)