vimclojure.vim (16868B)
1 " Part of Vim filetype plugin for Clojure 2 " Language: Clojure 3 " Maintainer: Meikel Brandmeyer <mb@kotka.de> 4 5 let s:save_cpo = &cpo 6 set cpo&vim 7 8 function! vimclojure#SynIdName() 9 return synIDattr(synID(line("."), col("."), 0), "name") 10 endfunction 11 12 function! vimclojure#WithSaved(closure) 13 let v = a:closure.get(a:closure.tosafe) 14 let r = a:closure.f() 15 call a:closure.set(a:closure.tosafe, v) 16 return r 17 endfunction 18 19 function! vimclojure#WithSavedPosition(closure) 20 let a:closure['tosafe'] = "." 21 let a:closure['get'] = function("getpos") 22 let a:closure['set'] = function("setpos") 23 return vimclojure#WithSaved(a:closure) 24 endfunction 25 26 function! vimclojure#WithSavedRegister(closure) 27 let a:closure['get'] = function("getreg") 28 let a:closure['set'] = function("setreg") 29 return vimclojure#WithSaved(a:closure) 30 endfunction 31 32 function! vimclojure#Yank(r, how) 33 let closure = {'tosafe': a:r, 'yank': a:how} 34 35 function closure.f() dict 36 silent execute self.yank 37 return getreg(self.tosafe) 38 endfunction 39 40 return vimclojure#WithSavedRegister(closure) 41 endfunction 42 43 function! vimclojure#EscapePathForOption(path) 44 let path = fnameescape(a:path) 45 46 " Hardcore escapeing of whitespace... 47 let path = substitute(path, '\', '\\\\', 'g') 48 let path = substitute(path, '\ ', '\\ ', 'g') 49 50 return path 51 endfunction 52 53 function! vimclojure#AddPathToOption(path, option) 54 let path = vimclojure#EscapePathForOption(a:path) 55 execute "setlocal " . a:option . "+=" . path 56 endfunction 57 58 function! vimclojure#AddCompletions(ns) 59 let completions = split(globpath(&rtp, "ftplugin/clojure/completions-" . a:ns . ".txt"), '\n') 60 if completions != [] 61 call vimclojure#AddPathToOption('k' . completions[0], 'complete') 62 endif 63 endfunction 64 65 " Nailgun part: 66 function! vimclojure#ExtractSexpr(toplevel) 67 let closure = { "flag" : (a:toplevel ? "r" : "") } 68 69 function closure.f() dict 70 if searchpairpos('(', '', ')', 'bW' . self.flag, 71 \ 'vimclojure#SynIdName() !~ "clojureParen\\d"') != [0, 0] 72 return vimclojure#Yank('l', 'normal! "ly%') 73 end 74 return "" 75 endfunction 76 77 return vimclojure#WithSavedPosition(closure) 78 endfunction 79 80 function! vimclojure#BufferName() 81 let file = expand("%") 82 if file == "" 83 let file = "UNNAMED" 84 endif 85 return file 86 endfunction 87 88 " Key mappings and Plugs 89 function! vimclojure#MakePlug(mode, plug, f) 90 execute a:mode . "noremap <Plug>Clojure" . a:plug 91 \ . " :call " . a:f . "<CR>" 92 endfunction 93 94 function! vimclojure#MapPlug(mode, keys, plug) 95 if !hasmapto("<Plug>Clojure" . a:plug) 96 execute a:mode . "map <buffer> <unique> <silent> <LocalLeader>" . a:keys 97 \ . " <Plug>Clojure" . a:plug 98 endif 99 endfunction 100 101 " A Buffer... 102 let vimclojure#Buffer = {} 103 104 function! vimclojure#Buffer.goHere() dict 105 execute "buffer! " . self._buffer 106 endfunction 107 108 function! vimclojure#Buffer.resize() dict 109 call self.goHere() 110 let size = line("$") 111 if size < 3 112 let size = 3 113 endif 114 execute "resize " . size 115 endfunction 116 117 function! vimclojure#Buffer.showText(text) dict 118 call self.goHere() 119 if type(a:text) == type("") 120 let text = split(a:text, '\n') 121 else 122 let text = a:text 123 endif 124 call append(line("$"), text) 125 endfunction 126 127 function! vimclojure#Buffer.close() dict 128 execute "bdelete! " . self._buffer 129 endfunction 130 131 " The transient buffer, used to display results. 132 let vimclojure#PreviewWindow = copy(vimclojure#Buffer) 133 134 function! vimclojure#PreviewWindow.New() dict 135 pclose! 136 137 execute &previewheight . "new" 138 set previewwindow 139 set winfixheight 140 141 setlocal noswapfile 142 setlocal buftype=nofile 143 setlocal bufhidden=wipe 144 145 let leader = exists("g:maplocalleader") ? g:maplocalleader : "\\" 146 147 call append(0, "; Use " . leader . "p to close this buffer!") 148 149 return copy(self) 150 endfunction 151 152 function! vimclojure#PreviewWindow.goHere() dict 153 wincmd P 154 endfunction 155 156 function! vimclojure#PreviewWindow.close() dict 157 pclose 158 endfunction 159 160 " Nails 161 if !exists("vimclojure#NailgunClient") 162 let vimclojure#NailgunClient = "ng" 163 endif 164 165 function! vimclojure#ExecuteNailWithInput(nail, input, ...) 166 if type(a:input) == type("") 167 let input = split(a:input, '\n', 1) 168 else 169 let input = a:input 170 endif 171 172 let inputfile = tempname() 173 try 174 call writefile(input, inputfile) 175 176 let cmdline = [g:vimclojure#NailgunClient, 177 \ "de.kotka.vimclojure.nails." . a:nail] 178 \ + map(copy(a:000), 'shellescape(v:val)') 179 let cmd = join(cmdline, " ") . " <" . inputfile 180 181 let result = system(cmd) 182 183 if v:shell_error 184 echoerr "Couldn't execute Nail! " 185 \ . substitute(result, '\n\(\t\?\)', ' ', 'g') 186 endif 187 finally 188 call delete(inputfile) 189 endtry 190 191 return substitute(result, '\n$', '', '') 192 endfunction 193 194 function! vimclojure#ExecuteNail(nail, ...) 195 return call(function("vimclojure#ExecuteNailWithInput"), [a:nail, ""] + a:000) 196 endfunction 197 198 function! vimclojure#FilterNail(nail, rngStart, rngEnd, ...) 199 let cmdline = [g:vimclojure#NailgunClient, 200 \ "de.kotka.vimclojure.nails." . a:nail] 201 \ + map(copy(a:000), 'shellescape(v:val)') 202 let cmd = a:rngStart . "," . a:rngEnd . "!" . join(cmdline, " ") 203 204 silent execute cmd 205 endfunction 206 207 function! vimclojure#DocLookup(word) 208 let docs = vimclojure#ExecuteNailWithInput("DocLookup", a:word, 209 \ "-n", b:vimclojure_namespace) 210 let resultBuffer = g:vimclojure#PreviewWindow.New() 211 call resultBuffer.showText(docs) 212 wincmd p 213 endfunction 214 215 function! vimclojure#FindDoc() 216 let pattern = input("Pattern to look for: ") 217 let result = vimclojure#ExecuteNailWithInput("FindDoc", pattern) 218 219 let resultBuffer = g:vimclojure#PreviewWindow.New() 220 call resultBuffer.showText(result) 221 222 wincmd p 223 endfunction 224 225 let s:DefaultJavadocPaths = { 226 \ "java" : "http://java.sun.com/javase/6/docs/api/", 227 \ "org/apache/commons/beanutils" : "http://commons.apache.org/beanutils/api/", 228 \ "org/apache/commons/chain" : "http://commons.apache.org/chain/api-release/", 229 \ "org/apache/commons/cli" : "http://commons.apache.org/cli/api-release/", 230 \ "org/apache/commons/codec" : "http://commons.apache.org/codec/api-release/", 231 \ "org/apache/commons/collections" : "http://commons.apache.org/collections/api-release/", 232 \ "org/apache/commons/logging" : "http://commons.apache.org/logging/apidocs/", 233 \ "org/apache/commons/mail" : "http://commons.apache.org/email/api-release/", 234 \ "org/apache/commons/io" : "http://commons.apache.org/io/api-release/" 235 \ } 236 237 if !exists("vimclojure#JavadocPathMap") 238 let vimclojure#JavadocPathMap = {} 239 endif 240 241 for k in keys(s:DefaultJavadocPaths) 242 if !has_key(vimclojure#JavadocPathMap, k) 243 let vimclojure#JavadocPathMap[k] = s:DefaultJavadocPaths[k] 244 endif 245 endfor 246 247 if !exists("vimclojure#Browser") 248 if has("win32") || has("win64") 249 let vimclojure#Browser = "start" 250 elseif has("mac") 251 let vimclojure#Browser = "open" 252 else 253 let vimclojure#Browser = "firefox -new-window" 254 endif 255 endif 256 257 function! vimclojure#JavadocLookup(word) 258 let word = substitute(a:word, "\\.$", "", "") 259 let path = vimclojure#ExecuteNailWithInput("JavadocPath", word, 260 \ "-n", b:vimclojure_namespace) 261 262 let match = "" 263 for pattern in keys(g:vimclojure#JavadocPathMap) 264 if path =~ "^" . pattern && len(match) < len(pattern) 265 let match = pattern 266 endif 267 endfor 268 269 if match == "" 270 throw "No matching Javadoc URL found for " . path 271 endif 272 273 let url = g:vimclojure#JavadocPathMap[match] . path 274 call system(join([g:vimclojure#Browser, url], " ")) 275 endfunction 276 277 function! vimclojure#MetaLookup(word) 278 let docs = vimclojure#ExecuteNailWithInput("MetaLookup", a:word, 279 \ "-n", b:vimclojure_namespace) 280 let resultBuffer = g:vimclojure#PreviewWindow.New() 281 call resultBuffer.showText(docs) 282 setfiletype clojure 283 wincmd p 284 endfunction 285 286 function! vimclojure#SourceLookup(word) 287 let source = vimclojure#ExecuteNailWithInput("SourceLookup", a:word, 288 \ "-n", b:vimclojure_namespace) 289 let resultBuffer = g:vimclojure#PreviewWindow.New() 290 call resultBuffer.showText(source) 291 setfiletype clojure 292 wincmd p 293 endfunction 294 295 function! vimclojure#GotoSource(word) 296 let result = vimclojure#ExecuteNailWithInput("SourceLocation", a:word, 297 \ "-n", b:vimclojure_namespace) 298 execute "let pos = " . result 299 300 if !filereadable(pos.file) 301 let file = findfile(pos.file) 302 if file == "" 303 echoerr pos.file . " not found in 'path'" 304 return 305 endif 306 let pos.file = file 307 endif 308 309 execute "edit " . pos.file 310 execute pos.line 311 endfunction 312 313 " Evaluators 314 function! vimclojure#MacroExpand(firstOnly) 315 let sexp = vimclojure#ExtractSexpr(0) 316 let ns = b:vimclojure_namespace 317 318 let resultBuffer = g:vimclojure#PreviewWindow.New() 319 320 let cmd = ["MacroExpand", sexp, "-n", ns] 321 if a:firstOnly 322 let cmd = cmd + [ "-o" ] 323 endif 324 325 let result = call(function("vimclojure#ExecuteNailWithInput"), cmd) 326 call resultBuffer.showText(result) 327 setfiletype clojure 328 329 wincmd p 330 endfunction 331 332 function! vimclojure#RequireFile(all) 333 let ns = b:vimclojure_namespace 334 let all = a:all ? "-all" : "" 335 336 let resultBuffer = g:vimclojure#PreviewWindow.New() 337 338 let require = "(require :reload" . all . " :verbose '". ns. ")" 339 let result = vimclojure#ExecuteNailWithInput("Repl", require, "-r") 340 341 call resultBuffer.showText(result) 342 setfiletype clojure 343 344 wincmd p 345 endfunction 346 347 function! vimclojure#EvalFile() 348 let content = getbufline(bufnr("%"), 1, line("$")) 349 let file = vimclojure#BufferName() 350 let ns = b:vimclojure_namespace 351 352 let result = vimclojure#ExecuteNailWithInput("Repl", content, 353 \ "-r", "-n", ns, "-f", file) 354 355 let resultBuffer = g:vimclojure#PreviewWindow.New() 356 call resultBuffer.showText(result) 357 setfiletype clojure 358 359 wincmd p 360 endfunction 361 362 function! vimclojure#EvalLine() 363 let theLine = line(".") 364 let content = getline(theLine) 365 let file = vimclojure#BufferName() 366 let ns = b:vimclojure_namespace 367 368 let result = vimclojure#ExecuteNailWithInput("Repl", content, 369 \ "-r", "-n", ns, "-f", file, "-l", theLine) 370 371 let resultBuffer = g:vimclojure#PreviewWindow.New() 372 call resultBuffer.showText(result) 373 setfiletype clojure 374 375 wincmd p 376 endfunction 377 378 function! vimclojure#EvalBlock() range 379 let file = vimclojure#BufferName() 380 let ns = b:vimclojure_namespace 381 382 let content = getbufline(bufnr("%"), a:firstline, a:lastline) 383 let result = vimclojure#ExecuteNailWithInput("Repl", content, 384 \ "-r", "-n", ns, "-f", file, "-l", a:firstline - 1) 385 386 let resultBuffer = g:vimclojure#PreviewWindow.New() 387 call resultBuffer.showText(result) 388 setfiletype clojure 389 390 wincmd p 391 endfunction 392 393 function! vimclojure#EvalToplevel() 394 let file = vimclojure#BufferName() 395 let ns = b:vimclojure_namespace 396 397 let pos = searchpairpos('(', '', ')', 'bWnr', 398 \ 'vimclojure#SynIdName() !~ "clojureParen\\d"') 399 400 if pos == [0, 0] 401 throw "Error: Not in toplevel expression!" 402 endif 403 404 let expr = vimclojure#ExtractSexpr(1) 405 let result = vimclojure#ExecuteNailWithInput("Repl", expr, 406 \ "-r", "-n", ns, "-f", file, "-l", pos[0] - 1) 407 408 let resultBuffer = g:vimclojure#PreviewWindow.New() 409 call resultBuffer.showText(result) 410 setfiletype clojure 411 412 wincmd p 413 endfunction 414 415 function! vimclojure#EvalParagraph() 416 let file = vimclojure#BufferName() 417 let ns = b:vimclojure_namespace 418 let startPosition = line(".") 419 420 let closure = {} 421 422 function! closure.f() dict 423 normal! } 424 return line(".") 425 endfunction 426 427 let endPosition = vimclojure#WithSavedPosition(closure) 428 429 let content = getbufline(bufnr("%"), startPosition, endPosition) 430 let result = vimclojure#ExecuteNailWithInput("Repl", content, 431 \ "-r", "-n", ns, "-f", file, "-l", startPosition - 1) 432 433 let resultBuffer = g:vimclojure#PreviewWindow.New() 434 call resultBuffer.showText(result) 435 setfiletype clojure 436 437 wincmd p 438 endfunction 439 440 " The Repl 441 let vimclojure#Repl = copy(vimclojure#Buffer) 442 443 let vimclojure#Repl._prompt = "Clojure=>" 444 let vimclojure#Repl._history = [] 445 let vimclojure#Repl._historyDepth = 0 446 let vimclojure#Repl._replCommands = [ ",close", ",st", ",ct" ] 447 448 function! vimclojure#Repl.New() dict 449 let instance = copy(self) 450 451 new 452 setlocal buftype=nofile 453 setlocal noswapfile 454 455 if !hasmapto("<Plug>ClojureReplEnterHook") 456 imap <buffer> <silent> <CR> <Plug>ClojureReplEnterHook 457 endif 458 if !hasmapto("<Plug>ClojureReplUpHistory") 459 imap <buffer> <silent> <C-Up> <Plug>ClojureReplUpHistory 460 endif 461 if !hasmapto("<Plug>ClojureReplDownHistory") 462 imap <buffer> <silent> <C-Down> <Plug>ClojureReplDownHistory 463 endif 464 465 call append(line("$"), ["Clojure", self._prompt . " "]) 466 467 let instance._id = vimclojure#ExecuteNail("Repl", "-s") 468 call vimclojure#ExecuteNailWithInput("Repl", 469 \ "(require 'clojure.contrib.stacktrace)", "-r", 470 \ "-i", instance._id) 471 let instance._buffer = bufnr("%") 472 473 let b:vimclojure_repl = instance 474 475 setfiletype clojure 476 477 normal! G 478 startinsert! 479 endfunction 480 481 function! vimclojure#Repl.isReplCommand(cmd) dict 482 for candidate in self._replCommands 483 if candidate == a:cmd 484 return 1 485 endif 486 endfor 487 return 0 488 endfunction 489 490 function! vimclojure#Repl.doReplCommand(cmd) dict 491 if a:cmd == ",close" 492 call vimclojure#ExecuteNail("Repl", "-S", "-i", self._id) 493 call self.close() 494 stopinsert 495 elseif a:cmd == ",st" 496 let result = vimclojure#ExecuteNailWithInput("Repl", 497 \ "(clojure.contrib.stacktrace/print-stack-trace *e)", "-r", 498 \ "-i", self._id) 499 call self.showText(result) 500 call self.showText(self._prompt . " ") 501 normal! G 502 startinsert! 503 elseif a:cmd == ",ct" 504 let result = vimclojure#ExecuteNailWithInput("Repl", 505 \ "(clojure.contrib.stacktrace/print-cause-trace *e)", "-r", 506 \ "-i", self._id) 507 call self.showText(result) 508 call self.showText(self._prompt . " ") 509 normal! G 510 startinsert! 511 endif 512 endfunction 513 514 function! vimclojure#Repl.showPrompt() dict 515 call self.showText(self._prompt . " ") 516 normal! G 517 startinsert! 518 endfunction 519 520 function! vimclojure#Repl.getCommand() dict 521 let ln = line("$") 522 523 while getline(ln) !~ "^" . self._prompt && ln > 0 524 let ln = ln - 1 525 endwhile 526 527 " Special Case: User deleted Prompt by accident. Insert a new one. 528 if ln == 0 529 call self.showPrompt() 530 return "" 531 endif 532 533 let cmd = vimclojure#Yank("l", ln . "," . line("$") . "yank l") 534 535 let cmd = substitute(cmd, "^" . self._prompt . "\\s*", "", "") 536 let cmd = substitute(cmd, "\n$", "", "") 537 return cmd 538 endfunction 539 540 function! vimclojure#Repl.enterHook() dict 541 let cmd = self.getCommand() 542 543 " Special Case: Showed prompt (or user just hit enter). 544 if cmd == "" 545 return 546 endif 547 548 if self.isReplCommand(cmd) 549 call self.doReplCommand(cmd) 550 return 551 endif 552 553 let result = vimclojure#ExecuteNailWithInput("CheckSyntax", cmd) 554 if result == "false" 555 execute "normal! GA\<CR>x" 556 normal! ==x 557 startinsert! 558 else 559 let result = vimclojure#ExecuteNailWithInput("Repl", cmd, 560 \ "-r", "-i", self._id) 561 call self.showText(result) 562 563 let self._historyDepth = 0 564 let self._history = [cmd] + self._history 565 call self.showPrompt() 566 endif 567 endfunction 568 569 function! vimclojure#Repl.upHistory() dict 570 let histLen = len(self._history) 571 let histDepth = self._historyDepth 572 573 if histLen > 0 && histLen > histDepth 574 let cmd = self._history[histDepth] 575 let self._historyDepth = histDepth + 1 576 577 call self.deleteLast() 578 579 call self.showText(self._prompt . " " . cmd) 580 endif 581 582 normal! G$ 583 endfunction 584 585 function! vimclojure#Repl.downHistory() dict 586 let histLen = len(self._history) 587 let histDepth = self._historyDepth 588 589 if histDepth > 0 && histLen > 0 590 let self._historyDepth = histDepth - 1 591 let cmd = self._history[self._historyDepth] 592 593 call self.deleteLast() 594 595 call self.showText(self._prompt . " " . cmd) 596 elseif histDepth == 0 597 call self.deleteLast() 598 call self.showText(self._prompt . " ") 599 endif 600 601 normal! G$ 602 endfunction 603 604 function! vimclojure#Repl.deleteLast() dict 605 normal! G 606 607 while getline("$") !~ self._prompt 608 normal! dd 609 endwhile 610 611 normal! dd 612 endfunction 613 614 " Highlighting 615 function! vimclojure#ColorNamespace(highlights) 616 for [category, words] in items(a:highlights) 617 if words != [] 618 execute "syntax keyword clojure" . category . " " . join(words, " ") 619 endif 620 endfor 621 endfunction 622 623 " Omni Completion 624 function! vimclojure#OmniCompletion(findstart, base) 625 if a:findstart == 1 626 let line = getline(".") 627 let start = col(".") - 1 628 629 while start > 0 && line[start - 1] =~ '\w\|-\|\.\|+\|*\|/' 630 let start -= 1 631 endwhile 632 633 return start 634 else 635 let slash = stridx(a:base, '/') 636 if slash > -1 637 let prefix = strpart(a:base, 0, slash) 638 let base = strpart(a:base, slash + 1) 639 else 640 let prefix = "" 641 let base = a:base 642 endif 643 644 let completions = vimclojure#ExecuteNail("Complete", 645 \ "-n", b:vimclojure_namespace, 646 \ "-p", prefix, "-b", base) 647 execute "let result = " . completions 648 return result 649 endif 650 endfunction 651 652 function! vimclojure#InitBuffer() 653 if exists("g:clj_want_gorilla") && g:clj_want_gorilla == 1 654 if !exists("b:vimclojure_namespace") 655 " Get the namespace of the buffer. 656 if &previewwindow 657 let b:vimclojure_namespace = "user" 658 else 659 try 660 let content = getbufline(bufnr("%"), 1, line("$")) 661 let b:vimclojure_namespace = 662 \ vimclojure#ExecuteNailWithInput( 663 \ "NamespaceOfFile", content) 664 catch /.*/ 665 endtry 666 endif 667 endif 668 endif 669 endfunction 670 671 " Epilog 672 let &cpo = s:save_cpo