Compare commits
339 Commits
Author | SHA1 | Date |
---|---|---|
Tim Pope | 2bab337396 | |
Richard Hull | 0814dd0d2d | |
Tim Pope | b550ac6680 | |
Siyuan Ma | fd18e8481d | |
Tim Pope | fd94f70fff | |
Dominic Monroe | 2d5177abef | |
Tim Pope | 1c75b56ceb | |
Daniel Silva | f57537135a | |
Tim Pope | 8579a41a18 | |
Vitaliy Vlasov | 3474bd5d72 | |
Tim Pope | 3ffdb062c6 | |
Tim Pope | b1725f68a1 | |
Tim Pope | 7d500028e6 | |
Juho Teperi | df563ed15f | |
Tim Pope | 04a7f542f5 | |
Tim Pope | 3d9b9a59cd | |
Janko Marohnić | 9a770d3716 | |
Tim Pope | b3b54b8715 | |
Nicolas Berger | b50e74f342 | |
Tim Pope | 9f7b1844cf | |
kul | 74d392339c | |
Tim Pope | a1571cd258 | |
Tim Pope | f456f04b65 | |
Tim Pope | 09c80c6794 | |
Tim Pope | 459904381b | |
Tim Pope | 69bf9ef519 | |
Tim Pope | 1f478e401a | |
Tim Pope | cb3c270c99 | |
Tim Pope | 7b2ae78bd7 | |
Tim Pope | b0a540c68d | |
Tim Pope | 09eedd05af | |
Tim Pope | 89aee9c00c | |
Juho Teperi | c22e61c69b | |
Roger Gilliar | 13a21bff0f | |
Tim Pope | 1646a01b0b | |
Juho Teperi | aede23c46a | |
Tim Pope | 819438bdf4 | |
Juho Teperi | 9ccaea1f2b | |
Juho Teperi | 44e766d5a5 | |
Tim Pope | 49153a39fc | |
kul | 0aabcdd798 | |
Joshua Davey | 5866d0017a | |
Robert Pitts | 7cebf6847e | |
Tim Pope | 71e44af208 | |
Tim Pope | b999b09cd9 | |
Tim Pope | 506cf288bd | |
Tim Pope | 0ecd9ec587 | |
Tim Pope | 874505e9f2 | |
Tim Pope | 04ce1b64af | |
Tim Pope | db2e70ee3f | |
Tim Pope | 8f7a07cffd | |
Tim Pope | 97758acc16 | |
Tim Pope | eeccb69a2f | |
Tim Pope | c0e574387d | |
Tim Pope | fccb149148 | |
Tim Pope | c1416c89ba | |
Tim Pope | b256399f3f | |
Jonathan Henry | 142fab4e0f | |
Tim Pope | 06f6bc5de6 | |
Tim Pope | d6b3e1c67c | |
Tim Pope | ebb2933f4c | |
Tim Pope | 3e978e4cd4 | |
Tim Pope | a39092ebe3 | |
Tim Pope | 835fdedf5f | |
Tim Pope | e107b2ae17 | |
Tim Pope | 1dc5129c0e | |
Tim Pope | 712205e66a | |
Tim Pope | ffc41e86f3 | |
Tim Pope | 37e3a474c5 | |
Tim Pope | 1f36136346 | |
Tim Pope | ea15c81773 | |
Tim Pope | 25e18b5f82 | |
Tim Pope | a14328c1dc | |
Tim Pope | eae17cfb42 | |
Tim Pope | 845b362a17 | |
Tim Pope | a1eef3dda1 | |
Tim Pope | 0e42998a27 | |
Tim Pope | 9c78e32f4b | |
Tim Pope | fb6975bfdf | |
Tim Pope | 8defd6b17e | |
Tim Pope | 26e467c2e7 | |
Tim Pope | 6dbf627022 | |
Tim Pope | 5ff2eeae1c | |
Tim Pope | 94ff1464a4 | |
Tim Pope | f2b6a2101f | |
Tim Pope | b10259bcca | |
Tim Pope | bfdd052fc7 | |
Tim Pope | c657faaf4e | |
Tim Pope | e4b975eaec | |
Tim Pope | 2cbaacf0d0 | |
Tim Pope | 83a317c10b | |
Joshua Davey | e6c4b5547a | |
Tim Pope | 049005dec7 | |
Bohr Shaw | 88648b2578 | |
Tim Pope | 67078e0c34 | |
Joshua Davey | 99d93ceba1 | |
Tim Pope | 29af4adc46 | |
Tim Pope | ac88f17bb4 | |
Tim Pope | af14811869 | |
Tim Pope | 0b17709b56 | |
Tim Pope | fa7a19140b | |
Tim Pope | 6cea463c97 | |
Tim Pope | fff348ccd0 | |
Tim Pope | 020a10d06c | |
Tim Pope | afb036203e | |
Tim Pope | 58896e23fa | |
Tim Pope | 57c18b778a | |
Tim Pope | 0bc928f0fc | |
Tim Pope | 062256b9fd | |
Tim Pope | a2278afbdd | |
Tim Pope | 9eb5099ba1 | |
Tim Pope | 12386dc0c2 | |
Tim Pope | 05cbc742a1 | |
Tim Pope | e484ad6996 | |
Tim Pope | e974dd4766 | |
Tim Pope | 2d1f3088b2 | |
Tim Pope | f25003deef | |
Tim Pope | c0a4901181 | |
Tim Pope | 7b19c1b2f6 | |
Tim Pope | 672d3b5c82 | |
Tim Pope | 0936b3cb4f | |
Tim Pope | 05206aa2e9 | |
Tim Pope | 5f1cee19c0 | |
Tim Pope | c6e0d55143 | |
Tim Pope | 89acd66d6d | |
Tim Pope | feb8ebc976 | |
Tim Pope | f5ded81baf | |
Tim Pope | 238b526cf2 | |
Tim Pope | 9e826faff4 | |
Tim Pope | 79c2a718da | |
Tim Pope | c48adc045b | |
Tim Pope | bc8f3af299 | |
Tim Pope | 2dec3b49b2 | |
Tim Pope | ea1012e1be | |
Tim Pope | 916fc04ecb | |
Tim Pope | ead617d9c3 | |
Tim Pope | f43fa99703 | |
Tim Pope | 6130d01b33 | |
Tim Pope | b0f9fad9ec | |
Tim Pope | 4425618825 | |
Tim Pope | 092ab0f70e | |
Tim Pope | 73f9a3adf4 | |
Tim Pope | 6c13396262 | |
Tim Pope | 9a689480ef | |
Tim Pope | ab5eee34d8 | |
Tim Pope | 1bfb5aadcf | |
Tim Pope | ab6d9db216 | |
Tim Pope | 095ee57d12 | |
Tim Pope | da3f5857b5 | |
Tim Pope | d65eaa5068 | |
Tim Pope | 90b909b9f4 | |
Tim Pope | 15450eff27 | |
Tim Pope | b139d72da4 | |
Tim Pope | 6fefd47170 | |
Neil Kirsopp | 77b06741ef | |
Neil Kirsopp | 72794b36d9 | |
Tim Pope | 31eaa4455e | |
Steve Losh | 3a843cb1b1 | |
Tim Pope | 544769274a | |
Ben Moss | 9d76d14ca4 | |
Tim Pope | 97d78f6829 | |
Tim Pope | d3e6bd34ae | |
Tim Pope | 5a86ec2c81 | |
Tim Pope | eb27299bd9 | |
Tim Pope | 7bc3ee4c1a | |
Andrew D. Wong | e5f636af2f | |
Tim Pope | 0095241a6f | |
Tim Pope | 226c5a0fd1 | |
Tim Pope | 766c8d6bc9 | |
Tim Pope | 385d8288db | |
Tim Pope | 81ca22bfe2 | |
Tim Pope | 0d7b97df7c | |
Tim Pope | 0520e4fa32 | |
Tim Pope | a568535bcd | |
Tim Pope | 64f0ae7bbe | |
Tim Pope | ee141c0256 | |
Tim Pope | 6e7f22766a | |
Tim Pope | 91e3b35403 | |
Tim Pope | 25f17ac25d | |
Tim Pope | e6c1f9eac1 | |
Neil Kirsopp | fef85624d3 | |
Neil Kirsopp | b92a9730f0 | |
Tim Pope | ef422f66e2 | |
Tim Pope | 15f98b06f7 | |
Tim Pope | 76a99139d1 | |
Tim Pope | 2a6d23f2fc | |
Torben Rasmussen | c5bfcc93f4 | |
Tim Pope | c4f677ee64 | |
Tim Pope | 4d93831925 | |
Tim Pope | 446b0b04b5 | |
Tim Pope | 7c0c524259 | |
Tim Pope | b56e40a2b6 | |
Tim Pope | 98cd5e1515 | |
Tim Pope | af245caf76 | |
Tim Pope | 0cfa1eed58 | |
Tim Pope | 09a0ed93cf | |
Tim Pope | 9c48439da7 | |
Tim Pope | a0fde95d2e | |
Tim Pope | 6a239c93af | |
Tim Pope | 62486afca7 | |
Tim Pope | c2a90eb47f | |
Tim Pope | 355580da7e | |
Tim Pope | c99bda551e | |
Tim Pope | a9c3318817 | |
Tim Pope | 0299eac671 | |
Tim Pope | 1c55f88e7f | |
Tim Pope | f605cd7824 | |
Tim Pope | cf094f970f | |
Tim Pope | 56d667b7d5 | |
Tim Pope | bbab5786df | |
Tim Pope | e90aef2ac7 | |
Tim Pope | fc0ab71587 | |
Tim Pope | 40bad28354 | |
Tim Pope | 28a6c2c262 | |
Tim Pope | 52758fd4ea | |
Tim Pope | 382869c6c7 | |
Tim Pope | 02f28c5dbb | |
Tim Pope | 18d60a3fa5 | |
Tim Pope | 0fdde20654 | |
Tim Pope | 1f3f36505e | |
Tim Pope | bf4ca57db1 | |
Tim Pope | 760f50f46e | |
Tim Pope | 01e16d236b | |
Tim Pope | 02ba3de54e | |
Tim Pope | 15be24fc82 | |
Tim Pope | 280b8d09b5 | |
Tim Pope | c7258fce7c | |
Tim Pope | 023c8573ab | |
Tim Pope | 77df83250f | |
Tim Pope | 2073263c07 | |
Tim Pope | 1b2e58db97 | |
Tim Pope | 9664516d5f | |
Tim Pope | 4ce1f8fbfa | |
Tim Pope | fa4e0ed143 | |
Tim Pope | bb84d556bc | |
Tim Pope | 7277fc4e6d | |
Tim Pope | 9aa2071b17 | |
Tim Pope | 973014a626 | |
Tim Pope | 90692edc35 | |
Tim Pope | fb5c946822 | |
Tim Pope | 95652fc8c0 | |
Tim Pope | cd3bf333bf | |
Tim Pope | d3f7f2cf26 | |
Tim Pope | f1b8096b3f | |
Dave Aitken | b57c21c29b | |
Tim Pope | 92644485d6 | |
Jace Bennett | 740ca20ff1 | |
Tim Pope | 52d906b3a4 | |
Joshua Davey | 4db39ee484 | |
Aaron Miller | 0127e5fc54 | |
Joshua Davey | 235a4ec3e8 | |
Tim Pope | 8f925c9342 | |
Chris Ford | 4490d2a882 | |
Tim Pope | b96ccf7441 | |
Ben Moss | 204a8de243 | |
Torben Rasmussen | 34e0d0c964 | |
Tim Pope | 4f6b8e5237 | |
Tim Pope | 7984a4c260 | |
Tim Pope | 2efe294efb | |
Tim Pope | 55255578bb | |
Tim Pope | 77444275fd | |
Tim Pope | 93df7ca18f | |
Tim Pope | f58065f0d1 | |
Jacek Lach | 5aa33b2cca | |
Tim Pope | 21d5c81eab | |
Sam Umbach | 7b334ef5a3 | |
Tim Pope | 83ce4440ae | |
Tim Pope | 1b92e0a7dd | |
Tim Pope | db27257f7f | |
Tim Pope | fa6e35bc19 | |
Tim Pope | 1a8eab85d9 | |
Tim Pope | a7f460ad1b | |
Tim Pope | d40c8d9c41 | |
Tim Pope | c1c3f9e917 | |
Tim Pope | 699dff447f | |
Tim Pope | 5b57f37802 | |
Tim Pope | 8d253a9fdf | |
Tim Pope | 873623df59 | |
Dave Ray | 7d47c7ec4c | |
Tim Pope | 8c0eae19ab | |
Tim Pope | 9749177034 | |
Tim Pope | eff7bcec61 | |
Tim Pope | a1068949b1 | |
Tim Pope | a6012b5032 | |
Tim Pope | b94a694254 | |
Jeremy Holland | 47ea532d8e | |
Tim Pope | 105b01d7d0 | |
Tim Pope | d2bc102487 | |
Tim Pope | f653f18e64 | |
Jacek Lach | 511a123421 | |
Tim Pope | 53201c89de | |
Tim Pope | 7ff2191a30 | |
Tim Pope | cd54b73e88 | |
Tim Pope | c0bdf9b971 | |
Tim Pope | a80ff87f94 | |
Tim Pope | 274c4da054 | |
Paul deGrandis | da70e71023 | |
Tim Pope | f4e9f1c10a | |
Tim Pope | aed21575b8 | |
Tim Pope | 123bea5850 | |
Tim Pope | fd8d623b7b | |
Tim Pope | 6f93e99ede | |
Tim Pope | fbd8a0844a | |
Tim Pope | 1db6dbed3f | |
Tim Pope | 879443c846 | |
Tim Pope | 286a1f6c47 | |
Tim Pope | 7a36f4e8b8 | |
Raymond W. Ko | c7f2a2ab6b | |
Tim Pope | 058da0d71c | |
Tim Pope | fbfc6e09b4 | |
Tim Pope | 84168b7c5e | |
Tim Pope | 6c2e33f715 | |
Tim Pope | be964782b8 | |
Tim Pope | 528cb5b01a | |
Tim Pope | 0ff9cd710d | |
Tim Pope | 3ee902f648 | |
Tim Pope | 0d96405c1f | |
Tim Pope | 4c60151330 | |
Tim Pope | 8ad87d4fba | |
Raymond W. Ko | fc5167b939 | |
Raymond W. Ko | 9d64e45d8d | |
Tim Pope | fe8277b42f | |
Tim Pope | 113bc5487f | |
Tim Pope | f4a1c5a5fe | |
Tim Pope | fbc0c54548 | |
Tim Pope | e62540fef9 | |
Tim Pope | 39d1e296cf | |
Tim Pope | 3ff1633daa | |
Tim Pope | 3fa8d8396f | |
Tim Pope | 1d51636fa1 | |
Tim Pope | 77c0166262 | |
Tim Pope | 1fa30f77eb | |
Tim Pope | 5e0d266ae4 | |
Tim Pope | 5213b65b97 | |
Tim Pope | 8757e41cf3 | |
Tim Pope | b21e531998 | |
Tim Pope | 36bc4baa61 | |
Tim Pope | 8fc624b04e | |
Tim Pope | a31c98c9ef |
|
@ -1 +1,2 @@
|
||||||
/doc/tags
|
/doc/tags
|
||||||
|
*.pyc
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Open [GitHub issues][] for bug reports and feature requests.
|
||||||
|
|
||||||
|
I'm a stickler for [commit messages][], so if you send me a pull
|
||||||
|
request with so much as superfluous period in the subject line, I will
|
||||||
|
reject it, then TP your house.
|
||||||
|
|
||||||
|
[GitHub issues]: http://github.com/tpope/vim-fireplace/issues
|
||||||
|
[commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
106
README.markdown
106
README.markdown
|
@ -1,24 +1,27 @@
|
||||||
# foreplay.vim
|
# fireplace.vim
|
||||||
|
|
||||||
There's a REPL in foreplay, but you probably wouldn't have noticed if I hadn't
|
There's a REPL in fireplace, but you probably wouldn't have noticed if I hadn't
|
||||||
told you. Such is the way with foreplay.vim. By the way, this plugin is for
|
told you. Such is the way with fireplace.vim. By the way, this plugin is for
|
||||||
Clojure.
|
Clojure.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Foreplay.vim doesn't provide indenting or syntax highlighting, so you'll want
|
First, set up [cider-nrepl][]. (If you skip this step, fireplace.vim will
|
||||||
[a set of Clojure runtime files](https://github.com/guns/vim-clojure-static).
|
make do with eval, which mostly works.) Next, fireplace.vim doesn't provide
|
||||||
|
indenting or syntax highlighting, so you'll want [a set of Clojure runtime
|
||||||
|
files](https://github.com/guns/vim-clojure-static) if you're on a version of
|
||||||
|
Vim earlier than 7.4. You might also want [salve.vim][] for assorted
|
||||||
|
static project support.
|
||||||
|
|
||||||
If you don't have a preferred installation method, I recommend
|
If you don't have a preferred installation method, I recommend
|
||||||
installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and
|
installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and
|
||||||
then simply copy and paste:
|
then simply copy and paste:
|
||||||
|
|
||||||
cd ~/.vim/bundle
|
cd ~/.vim/bundle
|
||||||
git clone git://github.com/tpope/vim-foreplay.git
|
git clone git://github.com/tpope/vim-fireplace.git
|
||||||
git clone git://github.com/guns/vim-clojure-static.git
|
|
||||||
|
|
||||||
Once help tags have been generated, you can view the manual with
|
Once help tags have been generated, you can view the manual with
|
||||||
`:help foreplay`.
|
`:help fireplace`.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -26,17 +29,25 @@ This list isn't exhaustive; see the `:help` for details.
|
||||||
|
|
||||||
### Transparent setup
|
### Transparent setup
|
||||||
|
|
||||||
Foreplay.vim talks to nREPL. With Leiningen 2, it connects automatically
|
Fireplace.vim talks to nREPL. With Leiningen, it connects automatically based
|
||||||
based on `target/repl-port`, otherwise it's just a `:Connect` away. You can
|
on `.nrepl-port`, otherwise it's just a `:Connect` away. You can connect to
|
||||||
connect to multiple instances of nREPL for different projects, and it will
|
multiple instances of nREPL for different projects, and it will use the right
|
||||||
use the right one automatically.
|
one automatically. ClojureScript support is just as seamless with
|
||||||
|
[Piggieback][].
|
||||||
|
|
||||||
The only external dependency is that you have Ruby installed.
|
The only external dependency is that you have either a Vim with Python support
|
||||||
|
compiled in, or `python` in your path.
|
||||||
|
|
||||||
Oh, and if you don't have an nREPL connection, it falls back to using
|
Oh, and if you don't have an nREPL connection, installing [salve.vim][]
|
||||||
`java clojure.main`, using a class path based on your Leiningen or Maven
|
lets it fall back to using `java clojure.main` for some of the basics, using a
|
||||||
config. It's a bit slow, but a two second delay its vastly preferable to
|
class path based on your Leiningen config. It's a bit slow, but a two-second
|
||||||
being forced out of my flow for a single command, in my book.
|
delay is vastly preferable to being forced out of my flow for a single
|
||||||
|
command, in my book.
|
||||||
|
|
||||||
|
[cider-nrepl]: https://github.com/clojure-emacs/cider-nrepl
|
||||||
|
[Piggieback]: https://github.com/cemerick/piggieback
|
||||||
|
[classpath.vim]: https://github.com/tpope/vim-classpath
|
||||||
|
[salve.vim]: https://github.com/tpope/vim-salve
|
||||||
|
|
||||||
### Not quite a REPL
|
### Not quite a REPL
|
||||||
|
|
||||||
|
@ -45,7 +56,7 @@ absolutely flawlessly, never breaking just because you did something innocuous
|
||||||
like backspace through part of the prompt? No? Such a shame, you really
|
like backspace through part of the prompt? No? Such a shame, you really
|
||||||
would have liked it.
|
would have liked it.
|
||||||
|
|
||||||
I've taken a different approach in foreplay.vim. `cq` (Think "Clojure
|
I've taken a different approach in fireplace.vim. `cq` (Think "Clojure
|
||||||
Quasi-REPL") is the prefix for a set of commands that bring up a *command-line
|
Quasi-REPL") is the prefix for a set of commands that bring up a *command-line
|
||||||
window* — the same thing you get when you hit `q:` — but set up for Clojure
|
window* — the same thing you get when you hit `q:` — but set up for Clojure
|
||||||
code.
|
code.
|
||||||
|
@ -57,15 +68,24 @@ cursor. `cqc` gives you a blank line in insert mode.
|
||||||
|
|
||||||
Standard stuff here. `:Eval` evaluates a range (`:%Eval` gets the whole
|
Standard stuff here. `:Eval` evaluates a range (`:%Eval` gets the whole
|
||||||
file), `:Require` requires a namespace with `:reload` (`:Require!` does
|
file), `:Require` requires a namespace with `:reload` (`:Require!` does
|
||||||
`:reload-all`), either the current buffer or a given argument. There's a `cp`
|
`:reload-all`), either the current buffer or a given argument. `:RunTests`
|
||||||
operator that evaluates a given motion (`cpp` for the expression under the
|
kicks off `(clojure.test/run-tests)` and loads the results into the quickfix
|
||||||
cursor).
|
list.
|
||||||
|
|
||||||
|
There's a `cp` operator that evaluates a given motion (`cpp` for the
|
||||||
|
innermost form under the cursor). `cm` and `c1m` are similar, but they only
|
||||||
|
run `clojure.walk/macroexpand-all` and `macroexpand-1` instead of evaluating
|
||||||
|
the form entirely.
|
||||||
|
|
||||||
|
Any failed evaluation loads the stack trace into the location list, which
|
||||||
|
can be easily accessed with `:lopen`.
|
||||||
|
|
||||||
### Navigating and Comprehending
|
### Navigating and Comprehending
|
||||||
|
|
||||||
I'm new to Clojure, so stuff that helps me understand code is a top priority.
|
I was brand new to Clojure when I started this plugin, so stuff that helped me
|
||||||
|
understand code was a top priority.
|
||||||
|
|
||||||
* `:Source`, `:Doc`, `:FindDoc`, and `:Apropros`, which map to the underlying
|
* `:Source`, `:Doc`, and `:FindDoc`, which map to the underlying
|
||||||
`clojure.repl` macro (with tab complete, of course).
|
`clojure.repl` macro (with tab complete, of course).
|
||||||
|
|
||||||
* `K` is mapped to look up the symbol under the cursor with `doc`.
|
* `K` is mapped to look up the symbol under the cursor with `doc`.
|
||||||
|
@ -84,47 +104,17 @@ Where possible, I favor enhancing built-ins over inventing a bunch of
|
||||||
|
|
||||||
Because why not? It works in the quasi-REPL too.
|
Because why not? It works in the quasi-REPL too.
|
||||||
|
|
||||||
### FAQ
|
## FAQ
|
||||||
|
|
||||||
> Why does it take so long for Vim to startup?
|
> Why does it take so long for Vim to startup?
|
||||||
|
|
||||||
The short answer is because the JVM is slow.
|
That's either [classpath.vim][] or [salve.vim][].
|
||||||
|
|
||||||
The first time you load a Clojure file from any given project, foreplay.vim
|
|
||||||
sets about trying to determine your class path, leveraging either
|
|
||||||
`lein classpath` or `mvn dependency:build-classpath`. This takes a couple of
|
|
||||||
seconds or so in the best case scenario, and potentially much longer if it
|
|
||||||
decides to hit the network. (I don't understand why "tell me the class path"
|
|
||||||
requires hitting the network, but what do I know?)
|
|
||||||
|
|
||||||
Because the class path is oh-so-expensive to retrieve, foreplay.vim caches it
|
|
||||||
in `g:CLASSPATH_CACHE`. By default, this disappears when you exit Vim, but
|
|
||||||
you can save it across sessions in `.viminfo` with this handy option:
|
|
||||||
|
|
||||||
set viminfo+=!
|
|
||||||
|
|
||||||
The cache is expired when the timestamp on `project.clj` or `pom.xml` changes.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
More than any other plugin, I'm in over my head here. I tried to do my
|
|
||||||
homework, but you don't learn best practices overnight. Please, open
|
|
||||||
[GitHub issues][] for bug reports and feature requests. Even better than a
|
|
||||||
feature request is just to tell me the pain you're experiencing, and perhaps
|
|
||||||
some ideas for what might eliminate it. I know Vimscript; you know Clojure.
|
|
||||||
Let's synergize.
|
|
||||||
|
|
||||||
I'm a stickler for [commit messages][], so if you send me a pull
|
|
||||||
request with so much as superfluous period in the subject line, I will
|
|
||||||
reject it, then TP your house.
|
|
||||||
|
|
||||||
[GitHub issues]: http://github.com/tpope/vim-foreplay/issues
|
|
||||||
[commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
|
||||||
|
|
||||||
## Self-Promotion
|
## Self-Promotion
|
||||||
|
|
||||||
Like foreplay.vim? Follow the repository on
|
Like fireplace.vim? Follow the repository on
|
||||||
[GitHub](https://github.com/tpope/vim-foreplay). And if
|
[GitHub](https://github.com/tpope/vim-fireplace) and vote for it on
|
||||||
|
[vim.org](http://www.vim.org/scripts/script.php?script_id=4978). And if
|
||||||
you're feeling especially charitable, follow [tpope](http://tpo.pe/) on
|
you're feeling especially charitable, follow [tpope](http://tpo.pe/) on
|
||||||
[Twitter](http://twitter.com/tpope) and
|
[Twitter](http://twitter.com/tpope) and
|
||||||
[GitHub](https://github.com/tpope).
|
[GitHub](https://github.com/tpope).
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
" classpath.vim - Manipulate the Java class path
|
|
||||||
" Maintainer: Tim Pope <http://tpo.pe>
|
|
||||||
|
|
||||||
if exists("g:autoloaded_classpath")
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
let g:autoloaded_classpath = 1
|
|
||||||
|
|
||||||
function! classpath#separator() abort
|
|
||||||
return has('win32') ? ';' : ':'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! classpath#file_separator() abort
|
|
||||||
return exists('shellslash') && !&shellslash ? '\' : '/'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! classpath#split(cp) abort
|
|
||||||
return split(a:cp, classpath#separator())
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! classpath#to_vim(cp) abort
|
|
||||||
let path = []
|
|
||||||
for elem in classpath#split(a:cp)
|
|
||||||
let path += [elem ==# '.' ? '' : elem]
|
|
||||||
endfor
|
|
||||||
if a:cp =~# '\(^\|:\)\.$'
|
|
||||||
let path += ['']
|
|
||||||
endif
|
|
||||||
return join(map(path, 'escape(v:val, ", ")'), ',')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! classpath#from_vim(path) abort
|
|
||||||
if a:path =~# '^,\=$'
|
|
||||||
return '.'
|
|
||||||
endif
|
|
||||||
let path = []
|
|
||||||
for elem in split(substitute(a:path, ',$', '', ''), ',')
|
|
||||||
if elem ==# ''
|
|
||||||
let path += ['.']
|
|
||||||
else
|
|
||||||
let path += split(glob(substitute(elem, '\\\ze[\\ ,]', '', 'g'), 1), "\n")
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
return join(path, classpath#separator())
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! classpath#detect(...) abort
|
|
||||||
let sep = classpath#file_separator()
|
|
||||||
|
|
||||||
let buffer = a:0 ? a:1 : '%'
|
|
||||||
let default = $CLASSPATH ==# '' ? ',' : classpath#to_vim($CLASSPATH)
|
|
||||||
let root = getbufvar(buffer, 'java_root')
|
|
||||||
if root ==# ''
|
|
||||||
let root = simplify(fnamemodify(bufname(buffer), ':p:s?[\/]$??'))
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !isdirectory(fnamemodify(root, ':h'))
|
|
||||||
return default
|
|
||||||
endif
|
|
||||||
|
|
||||||
let previous = ""
|
|
||||||
while root !=# previous
|
|
||||||
if isdirectory(root . '/src')
|
|
||||||
if filereadable(root . '/project.clj')
|
|
||||||
let file = 'project.clj'
|
|
||||||
let cmd = 'lein classpath'
|
|
||||||
let pattern = "[^\n]*\\ze\n*$"
|
|
||||||
let default = join(map(['test', 'src', 'dev-resources', 'resources', 'target'.sep.'classes'], 'escape(root . sep . v:val, ", ")'), ',')
|
|
||||||
let base = ''
|
|
||||||
break
|
|
||||||
endif
|
|
||||||
if filereadable(root . '/pom.xml')
|
|
||||||
let file = 'pom.xml'
|
|
||||||
let cmd = 'mvn dependency:build-classpath'
|
|
||||||
let pattern = '\%(^\|\n\)\zs[^[].\{-\}\ze\n'
|
|
||||||
let base = escape(root.sep.'src'.sep.'*'.sep.'*', ', ') . ','
|
|
||||||
let default = base . default
|
|
||||||
break
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
let previous = root
|
|
||||||
let root = fnamemodify(root, ':h')
|
|
||||||
endwhile
|
|
||||||
|
|
||||||
if !exists('file')
|
|
||||||
if a:0 > 1 && a:2 ==# 'keep'
|
|
||||||
return ''
|
|
||||||
else
|
|
||||||
return default
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists('g:CLASSPATH_CACHE') || type(g:CLASSPATH_CACHE) != type({})
|
|
||||||
unlet! g:CLASSPATH_CACHE
|
|
||||||
let g:CLASSPATH_CACHE = {}
|
|
||||||
endif
|
|
||||||
|
|
||||||
let [when, last, path] = split(get(g:CLASSPATH_CACHE, root, "-1\t-1\t."), "\t")
|
|
||||||
let disk = getftime(root . sep . file)
|
|
||||||
if last ==# disk
|
|
||||||
return path
|
|
||||||
else
|
|
||||||
try
|
|
||||||
if &verbose
|
|
||||||
echomsg 'Determining class path with '.cmd.' ...'
|
|
||||||
endif
|
|
||||||
let out = system('cd ' . shellescape(root) . ' && ' . cmd)
|
|
||||||
catch /^Vim:Interrupt/
|
|
||||||
return default
|
|
||||||
endtry
|
|
||||||
let match = matchstr(out, pattern)
|
|
||||||
if !v:shell_error && exists('out') && out !=# ''
|
|
||||||
let path = base . classpath#to_vim(match)
|
|
||||||
let g:CLASSPATH_CACHE[root] = localtime() . "\t" . disk . "\t" . path
|
|
||||||
return path
|
|
||||||
else
|
|
||||||
echohl WarningMSG
|
|
||||||
echomsg "Couldn't determine class path."
|
|
||||||
echohl NONE
|
|
||||||
echo out
|
|
||||||
return default
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" vim:set et sw=2:
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
" Location: autoload/nrepl/fireplace.vim
|
||||||
|
|
||||||
|
if exists("g:autoloaded_fireplace_nrepl")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let g:autoloaded_fireplace_nrepl = 1
|
||||||
|
|
||||||
|
function! s:function(name) abort
|
||||||
|
return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
if !exists('s:id')
|
||||||
|
let s:vim_id = localtime()
|
||||||
|
let s:id = 0
|
||||||
|
endif
|
||||||
|
function! fireplace#nrepl#next_id() abort
|
||||||
|
let s:id += 1
|
||||||
|
return 'fireplace-'.hostname().'-'.s:vim_id.'-'.s:id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
if !exists('g:fireplace_nrepl_sessions')
|
||||||
|
let g:fireplace_nrepl_sessions = {}
|
||||||
|
endif
|
||||||
|
|
||||||
|
augroup fireplace_nrepl_connection
|
||||||
|
autocmd!
|
||||||
|
autocmd VimLeave * for s:session in values(g:fireplace_nrepl_sessions)
|
||||||
|
\ | call s:session.close()
|
||||||
|
\ | endfor
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
function! fireplace#nrepl#for(transport) abort
|
||||||
|
let client = copy(s:nrepl)
|
||||||
|
let client.transport = a:transport
|
||||||
|
let client.session = client.process({'op': 'clone', 'session': 0})['new-session']
|
||||||
|
let client.describe = client.process({'op': 'describe', 'verbose?': 1})
|
||||||
|
if get(client.describe.versions.nrepl, 'major', -1) == 0 &&
|
||||||
|
\ client.describe.versions.nrepl.minor < 2
|
||||||
|
throw 'nREPL: 0.2.0 or higher required'
|
||||||
|
endif
|
||||||
|
" Handle boot, which sets a fake.class.path entry
|
||||||
|
let response = client.process({'op': 'eval', 'code':
|
||||||
|
\ '[(System/getProperty "path.separator") (System/getProperty "fake.class.path")]', 'session': ''})
|
||||||
|
let cpath = response.value[-1][5:-2]
|
||||||
|
if cpath !=# 'nil'
|
||||||
|
let cpath = eval(cpath)
|
||||||
|
if !empty(cpath)
|
||||||
|
let client._path = split(cpath, response.value[-1][2])
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
if !has_key(client, '_path') && client.has_op('classpath')
|
||||||
|
let response = client.message({'op': 'classpath'})[0]
|
||||||
|
if type(get(response, 'classpath')) == type([])
|
||||||
|
let client._path = response.classpath
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
if !has_key(client, '_path')
|
||||||
|
let response = client.process({'op': 'eval', 'code':
|
||||||
|
\ '[(System/getProperty "path.separator") (System/getProperty "java.class.path")]', 'session': ''})
|
||||||
|
let client._path = split(eval(response.value[-1][5:-2]), response.value[-1][2])
|
||||||
|
endif
|
||||||
|
let g:fireplace_nrepl_sessions[client.session] = client
|
||||||
|
return client
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_close() dict abort
|
||||||
|
if has_key(self, 'session')
|
||||||
|
try
|
||||||
|
unlet! g:fireplace_nrepl_sessions[self.session]
|
||||||
|
call self.message({'op': 'close'}, 'ignore')
|
||||||
|
catch
|
||||||
|
finally
|
||||||
|
unlet self.session
|
||||||
|
endtry
|
||||||
|
endif
|
||||||
|
call self.transport.close()
|
||||||
|
return self
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_clone() dict abort
|
||||||
|
let client = copy(self)
|
||||||
|
if has_key(self, 'session')
|
||||||
|
let client.session = client.process({'op': 'clone'})['new-session']
|
||||||
|
let g:fireplace_nrepl_sessions[client.session] = client
|
||||||
|
endif
|
||||||
|
return client
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_path() dict abort
|
||||||
|
return self._path
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! fireplace#nrepl#combine(responses)
|
||||||
|
let combined = {'status': [], 'session': []}
|
||||||
|
for response in a:responses
|
||||||
|
for key in keys(response)
|
||||||
|
if key ==# 'id' || key ==# 'ns'
|
||||||
|
let combined[key] = response[key]
|
||||||
|
elseif key ==# 'value'
|
||||||
|
let combined.value = extend(get(combined, 'value', []), [response.value])
|
||||||
|
elseif key ==# 'status'
|
||||||
|
for entry in response[key]
|
||||||
|
if index(combined[key], entry) < 0
|
||||||
|
call extend(combined[key], [entry])
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
elseif key ==# 'session'
|
||||||
|
if index(combined[key], response[key]) < 0
|
||||||
|
call extend(combined[key], [response[key]])
|
||||||
|
endif
|
||||||
|
elseif type(response[key]) == type('')
|
||||||
|
let combined[key] = get(combined, key, '') . response[key]
|
||||||
|
else
|
||||||
|
let combined[key] = response[key]
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
endfor
|
||||||
|
return combined
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_process(msg) dict abort
|
||||||
|
let combined = fireplace#nrepl#combine(self.message(a:msg))
|
||||||
|
if index(combined.status, 'error') < 0
|
||||||
|
return combined
|
||||||
|
endif
|
||||||
|
throw 'nREPL: ' . tr(combined.status[0], '-', ' ')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_eval(expr, ...) dict abort
|
||||||
|
let msg = {"op": "eval"}
|
||||||
|
let msg.code = a:expr
|
||||||
|
let options = a:0 ? a:1 : {}
|
||||||
|
if has_key(options, 'ns')
|
||||||
|
let msg.ns = options.ns
|
||||||
|
elseif has_key(self, 'ns')
|
||||||
|
let msg.ns = self.ns
|
||||||
|
endif
|
||||||
|
if has_key(options, 'session')
|
||||||
|
let msg.session = options.session
|
||||||
|
endif
|
||||||
|
if has_key(options, 'id')
|
||||||
|
let msg.id = options.id
|
||||||
|
else
|
||||||
|
let msg.id = fireplace#nrepl#next_id()
|
||||||
|
endif
|
||||||
|
if has_key(options, 'file_path')
|
||||||
|
let msg.op = 'load-file'
|
||||||
|
let msg['file-path'] = options.file_path
|
||||||
|
let msg['file-name'] = fnamemodify(options.file_path, ':t')
|
||||||
|
if has_key(msg, 'ns')
|
||||||
|
let msg.file = "(in-ns '".msg.ns.") ".msg.code
|
||||||
|
call remove(msg, 'ns')
|
||||||
|
else
|
||||||
|
let msg.file = msg.code
|
||||||
|
endif
|
||||||
|
call remove(msg, 'code')
|
||||||
|
endif
|
||||||
|
try
|
||||||
|
let response = self.process(msg)
|
||||||
|
finally
|
||||||
|
if !exists('response')
|
||||||
|
let session = get(msg, 'session', self.session)
|
||||||
|
if !empty(session)
|
||||||
|
call self.message({'op': 'interrupt', 'session': session, 'interrupt-id': msg.id}, 'ignore')
|
||||||
|
endif
|
||||||
|
throw 'Clojure: Interrupt'
|
||||||
|
endif
|
||||||
|
endtry
|
||||||
|
if has_key(response, 'ns') && empty(get(options, 'ns'))
|
||||||
|
let self.ns = response.ns
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(response, 'ex') && !empty(get(msg, 'session', 1))
|
||||||
|
let response.stacktrace = s:extract_last_stacktrace(self, get(msg, 'session', self.session))
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(response, 'value')
|
||||||
|
let response.value = response.value[-1]
|
||||||
|
endif
|
||||||
|
return response
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:extract_last_stacktrace(nrepl, session) abort
|
||||||
|
if a:nrepl.has_op('stacktrace')
|
||||||
|
let stacktrace = a:nrepl.message({'op': 'stacktrace', 'session': a:session})
|
||||||
|
if len(stacktrace) > 0 && has_key(stacktrace[0], 'stacktrace')
|
||||||
|
let stacktrace = stacktrace[0].stacktrace
|
||||||
|
endif
|
||||||
|
|
||||||
|
call filter(stacktrace, 'has_key(v:val, "file")')
|
||||||
|
if !empty(stacktrace)
|
||||||
|
return map(stacktrace, 'v:val.class.".".v:val.method."(".v:val.file.":".v:val.line.")"')
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
let format_st = '(symbol (str "\n\b" (apply str (interleave (repeat "\n") (map str (.getStackTrace *e)))) "\n\b\n"))'
|
||||||
|
let response = a:nrepl.process({'op': 'eval', 'code': '['.format_st.' *3 *2 *1]', 'ns': 'user', 'session': a:session})
|
||||||
|
try
|
||||||
|
let stacktrace = split(get(split(response.value[0], "\n\b\n"), 1, ""), "\n")
|
||||||
|
catch
|
||||||
|
throw string(response)
|
||||||
|
endtry
|
||||||
|
call a:nrepl.message({'op': 'eval', 'code': '(*1 1)', 'ns': 'user', 'session': a:session})
|
||||||
|
call a:nrepl.message({'op': 'eval', 'code': '(*2 2)', 'ns': 'user', 'session': a:session})
|
||||||
|
call a:nrepl.message({'op': 'eval', 'code': '(*3 3)', 'ns': 'user', 'session': a:session})
|
||||||
|
return stacktrace
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:keepalive = tempname()
|
||||||
|
call writefile([getpid()], s:keepalive)
|
||||||
|
|
||||||
|
function! s:nrepl_prepare(msg) dict abort
|
||||||
|
let msg = copy(a:msg)
|
||||||
|
if !has_key(msg, 'id')
|
||||||
|
let msg.id = fireplace#nrepl#next_id()
|
||||||
|
endif
|
||||||
|
if empty(get(msg, 'ns', 1))
|
||||||
|
unlet msg.ns
|
||||||
|
endif
|
||||||
|
if empty(get(msg, 'session', 1))
|
||||||
|
unlet msg.session
|
||||||
|
elseif !has_key(msg, 'session')
|
||||||
|
let msg.session = self.session
|
||||||
|
endif
|
||||||
|
return msg
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! fireplace#nrepl#callback(body, type, callback) abort
|
||||||
|
try
|
||||||
|
let response = {'body': a:body, 'type': a:type}
|
||||||
|
if has_key(g:fireplace_nrepl_sessions, get(a:body, 'session'))
|
||||||
|
let response.session = g:fireplace_nrepl_sessions[a:body.session]
|
||||||
|
endif
|
||||||
|
call call(a:callback[0], [response] + a:callback[1:-1])
|
||||||
|
catch
|
||||||
|
endtry
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_call(msg, ...) dict abort
|
||||||
|
let terms = a:0 ? a:1 : ['done']
|
||||||
|
let sels = a:0 > 1 ? a:2 : {}
|
||||||
|
return call(self.transport.call, [a:msg, terms, sels] + a:000[2:-1], self.transport)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_message(msg, ...) dict abort
|
||||||
|
let msg = self.prepare(a:msg)
|
||||||
|
let sel = {'id': msg.id}
|
||||||
|
return call(self.call, [msg, ['done'], sel] + a:000, self)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_has_op(op) dict abort
|
||||||
|
return has_key(self.describe.ops, a:op)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:nrepl = {
|
||||||
|
\ 'close': s:function('s:nrepl_close'),
|
||||||
|
\ 'clone': s:function('s:nrepl_clone'),
|
||||||
|
\ 'prepare': s:function('s:nrepl_prepare'),
|
||||||
|
\ 'call': s:function('s:nrepl_call'),
|
||||||
|
\ 'message': s:function('s:nrepl_message'),
|
||||||
|
\ 'eval': s:function('s:nrepl_eval'),
|
||||||
|
\ 'has_op': s:function('s:nrepl_has_op'),
|
||||||
|
\ 'path': s:function('s:nrepl_path'),
|
||||||
|
\ 'process': s:function('s:nrepl_process')}
|
|
@ -0,0 +1,156 @@
|
||||||
|
" Location: autoload/nrepl/fireplace_connection.vim
|
||||||
|
|
||||||
|
if exists("g:autoloaded_nrepl_fireplace_connection") || &cp
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let g:autoloaded_nrepl_fireplace_connection = 1
|
||||||
|
|
||||||
|
let s:python_dir = fnamemodify(expand("<sfile>"), ':p:h:h:h') . '/python'
|
||||||
|
|
||||||
|
function! s:function(name) abort
|
||||||
|
return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Bencode {{{1
|
||||||
|
|
||||||
|
function! fireplace#nrepl_connection#bencode(value) abort
|
||||||
|
if type(a:value) == type(0)
|
||||||
|
return 'i'.a:value.'e'
|
||||||
|
elseif type(a:value) == type('')
|
||||||
|
return strlen(a:value).':'.a:value
|
||||||
|
elseif type(a:value) == type([])
|
||||||
|
return 'l'.join(map(copy(a:value),'fireplace#nrepl_connection#bencode(v:val)'),'').'e'
|
||||||
|
elseif type(a:value) == type({})
|
||||||
|
return 'd'.join(map(
|
||||||
|
\ sort(keys(a:value)),
|
||||||
|
\ 'fireplace#nrepl_connection#bencode(v:val) . ' .
|
||||||
|
\ 'fireplace#nrepl_connection#bencode(a:value[v:val])'
|
||||||
|
\ ),'').'e'
|
||||||
|
else
|
||||||
|
throw "Can't bencode ".string(a:value)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" }}}1
|
||||||
|
|
||||||
|
function! s:shellesc(arg) abort
|
||||||
|
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
|
||||||
|
return a:arg
|
||||||
|
elseif &shell =~# 'cmd'
|
||||||
|
throw 'Python interface not working. See :help python-dynamic'
|
||||||
|
else
|
||||||
|
let escaped = shellescape(a:arg)
|
||||||
|
if &shell =~# 'sh' && &shell !~# 'csh'
|
||||||
|
return substitute(escaped, '\\\n', '\n', 'g')
|
||||||
|
else
|
||||||
|
return escaped
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
if !exists('s:id')
|
||||||
|
let s:vim_id = localtime()
|
||||||
|
let s:id = 0
|
||||||
|
endif
|
||||||
|
function! s:id() abort
|
||||||
|
let s:id += 1
|
||||||
|
return 'fireplace-'.hostname().'-'.s:vim_id.'-'.s:id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! fireplace#nrepl_connection#prompt() abort
|
||||||
|
return fireplace#input_host_port()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! fireplace#nrepl_connection#open(arg) abort
|
||||||
|
if a:arg =~# '^\d\+$'
|
||||||
|
let host = 'localhost'
|
||||||
|
let port = a:arg
|
||||||
|
elseif a:arg =~# ':\d\+$'
|
||||||
|
let host = matchstr(a:arg, '.*\ze:')
|
||||||
|
let port = matchstr(a:arg, ':\zs.*')
|
||||||
|
else
|
||||||
|
throw "nREPL: Couldn't find [host:]port in " . a:arg
|
||||||
|
endif
|
||||||
|
let transport = deepcopy(s:nrepl_transport)
|
||||||
|
let transport.host = host
|
||||||
|
let transport.port = port
|
||||||
|
return fireplace#nrepl#for(transport)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_transport_close() dict abort
|
||||||
|
return self
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:keepalive = tempname()
|
||||||
|
call writefile([getpid()], s:keepalive)
|
||||||
|
|
||||||
|
function! s:nrepl_transport_command(cmd, args) dict abort
|
||||||
|
return 'python'
|
||||||
|
\ . ' ' . s:shellesc(s:python_dir.'/nrepl_fireplace.py')
|
||||||
|
\ . ' ' . s:shellesc(self.host)
|
||||||
|
\ . ' ' . s:shellesc(self.port)
|
||||||
|
\ . ' ' . s:shellesc(s:keepalive)
|
||||||
|
\ . ' ' . s:shellesc(a:cmd)
|
||||||
|
\ . ' ' . join(map(copy(a:args), 's:shellesc(fireplace#nrepl_connection#bencode(v:val))'), ' ')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_transport_dispatch(cmd, ...) dict abort
|
||||||
|
let in = self.command(a:cmd, a:000)
|
||||||
|
let out = system(in)
|
||||||
|
if !v:shell_error
|
||||||
|
return eval(out)
|
||||||
|
endif
|
||||||
|
throw 'nREPL: '.out
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_transport_call(msg, terms, sels, ...) dict abort
|
||||||
|
let payload = fireplace#nrepl_connection#bencode(a:msg)
|
||||||
|
let response = self.dispatch('call', payload, a:terms, a:sels)
|
||||||
|
if !a:0
|
||||||
|
return response
|
||||||
|
elseif a:1 !=# 'ignore'
|
||||||
|
return map(response, 'fireplace#nrepl#callback(v:val, "synchronous", a:000)')
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:nrepl_transport = {
|
||||||
|
\ 'close': s:function('s:nrepl_transport_close'),
|
||||||
|
\ 'command': s:function('s:nrepl_transport_command'),
|
||||||
|
\ 'dispatch': s:function('s:nrepl_transport_dispatch'),
|
||||||
|
\ 'call': s:function('s:nrepl_transport_call')}
|
||||||
|
|
||||||
|
if !has('python') || $FIREPLACE_NO_IF_PYTHON
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !exists('s:python')
|
||||||
|
exe 'python sys.path.insert(0, "'.escape(s:python_dir, '\"').'")'
|
||||||
|
let s:python = 1
|
||||||
|
python import nrepl_fireplace
|
||||||
|
else
|
||||||
|
python reload(nrepl_fireplace)
|
||||||
|
endif
|
||||||
|
|
||||||
|
python << EOF
|
||||||
|
import vim
|
||||||
|
|
||||||
|
def fireplace_let(var, value):
|
||||||
|
return vim.command('let ' + var + ' = ' + nrepl_fireplace.vim_encode(value))
|
||||||
|
|
||||||
|
def fireplace_check():
|
||||||
|
vim.eval('getchar(1)')
|
||||||
|
|
||||||
|
def fireplace_repl_dispatch(command, *args):
|
||||||
|
try:
|
||||||
|
fireplace_let('out', nrepl_fireplace.dispatch(vim.eval('self.host'), vim.eval('self.port'), fireplace_check, None, command, *args))
|
||||||
|
except Exception, e:
|
||||||
|
fireplace_let('err', str(e))
|
||||||
|
EOF
|
||||||
|
|
||||||
|
function! s:nrepl_transport_dispatch(command, ...) dict abort
|
||||||
|
python fireplace_repl_dispatch(vim.eval('a:command'), *vim.eval('a:000'))
|
||||||
|
if !exists('err')
|
||||||
|
return out
|
||||||
|
endif
|
||||||
|
throw 'nREPL Connection Error: '.err
|
||||||
|
endfunction
|
|
@ -1,232 +0,0 @@
|
||||||
" nrepl/foreplay_connection.vim
|
|
||||||
" Maintainer: Tim Pope <http://tpo.pe/>
|
|
||||||
|
|
||||||
if exists("g:autoloaded_nrepl_foreplay_connection") || &cp
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
let g:autoloaded_nrepl_foreplay_connection = 1
|
|
||||||
|
|
||||||
function! s:function(name) abort
|
|
||||||
return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" Bencode {{{1
|
|
||||||
|
|
||||||
function! nrepl#foreplay_connection#bencode(value) abort
|
|
||||||
if type(a:value) == type(0)
|
|
||||||
return 'i'.a:value.'e'
|
|
||||||
elseif type(a:value) == type('')
|
|
||||||
return strlen(a:value).':'.a:value
|
|
||||||
elseif type(a:value) == type([])
|
|
||||||
return 'l'.join(map(a:value,'nrepl#foreplay_connection#bencode(v:val)'),'').'e'
|
|
||||||
elseif type(a:value) == type({})
|
|
||||||
return 'd'.join(values(map(a:value,'nrepl#foreplay_connection#bencode(v:key).nrepl#foreplay_connection#bencode(v:val)')),'').'e'
|
|
||||||
else
|
|
||||||
throw "Can't bencode ".string(a:value)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! nrepl#foreplay_connection#bdecode(value) abort
|
|
||||||
return s:bdecode({'pos': 0, 'value': a:value})
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:bdecode(state) abort
|
|
||||||
let value = a:state.value
|
|
||||||
if value[a:state.pos] =~# '\d'
|
|
||||||
let pos = a:state.pos
|
|
||||||
let length = matchstr(value[pos : -1], '^\d\+')
|
|
||||||
let a:state.pos += strlen(length) + length + 1
|
|
||||||
return value[pos+strlen(length)+1 : pos+strlen(length)+length]
|
|
||||||
elseif value[a:state.pos] ==# 'i'
|
|
||||||
let int = matchstr(value[a:state.pos+1:-1], '[^e]*')
|
|
||||||
let a:state.pos += 2 + strlen(int)
|
|
||||||
return str2nr(int)
|
|
||||||
elseif value[a:state.pos] ==# 'l'
|
|
||||||
let values = []
|
|
||||||
let a:state.pos += 1
|
|
||||||
while value[a:state.pos] !=# 'e' && value[a:state.pos] !=# ''
|
|
||||||
call add(values, s:bdecode(a:state))
|
|
||||||
endwhile
|
|
||||||
let a:state.pos += 1
|
|
||||||
return values
|
|
||||||
elseif value[a:state.pos] ==# 'd'
|
|
||||||
let values = {}
|
|
||||||
let a:state.pos += 1
|
|
||||||
while value[a:state.pos] !=# 'e' && value[a:state.pos] !=# ''
|
|
||||||
let key = s:bdecode(a:state)
|
|
||||||
let values[key] = s:bdecode(a:state)
|
|
||||||
endwhile
|
|
||||||
let a:state.pos += 1
|
|
||||||
return values
|
|
||||||
else
|
|
||||||
throw 'bencode parse error: '.string(a:state)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
|
|
||||||
function! s:shellesc(arg) abort
|
|
||||||
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
|
|
||||||
return a:arg
|
|
||||||
elseif &shell =~# 'cmd'
|
|
||||||
return '"'.substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g').'"'
|
|
||||||
else
|
|
||||||
let escaped = shellescape(a:arg)
|
|
||||||
if &shell =~# 'sh' && &shell !~# 'csh'
|
|
||||||
return substitute(escaped, '\\\n', '\n', 'g')
|
|
||||||
else
|
|
||||||
return escaped
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! nrepl#foreplay_connection#prompt() abort
|
|
||||||
return foreplay#input_host_port()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! nrepl#foreplay_connection#open(arg) abort
|
|
||||||
if a:arg =~# '^\d\+$'
|
|
||||||
let host = 'localhost'
|
|
||||||
let port = a:arg
|
|
||||||
elseif a:arg =~# ':\d\+$'
|
|
||||||
let host = matchstr(a:arg, '.*\ze:')
|
|
||||||
let port = matchstr(a:arg, ':\zs.*')
|
|
||||||
else
|
|
||||||
throw "nREPL: Couldn't find [host:]port in " . a:arg
|
|
||||||
endif
|
|
||||||
let client = deepcopy(s:nrepl)
|
|
||||||
let client.host = host
|
|
||||||
let client.port = port
|
|
||||||
let session = client.process({'op': 'clone'})['new-session']
|
|
||||||
let response = client.process({'op': 'eval', 'session': session, 'code':
|
|
||||||
\ '(do (println "success") (symbol (str (System/getProperty "path.separator") (System/getProperty "java.class.path"))))'})
|
|
||||||
let client._path = response.value[-1]
|
|
||||||
if has_key(response, 'out')
|
|
||||||
let client.session = session
|
|
||||||
endif
|
|
||||||
return client
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:nrepl_path() dict abort
|
|
||||||
return split(self._path[1:-1], self._path[0])
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:nrepl_process(payload) dict abort
|
|
||||||
let combined = {'status': [], 'session': []}
|
|
||||||
for response in self.call(a:payload)
|
|
||||||
for key in keys(response)
|
|
||||||
if key ==# 'id' || key ==# 'ns'
|
|
||||||
let combined[key] = response[key]
|
|
||||||
elseif key ==# 'value'
|
|
||||||
let combined.value = extend(get(combined, 'value', []), [response.value])
|
|
||||||
elseif key ==# 'status'
|
|
||||||
for entry in response[key]
|
|
||||||
if index(combined[key], entry) < 0
|
|
||||||
call extend(combined[key], [entry])
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
elseif key ==# 'session'
|
|
||||||
if index(combined[key], response[key]) < 0
|
|
||||||
call extend(combined[key], [response[key]])
|
|
||||||
endif
|
|
||||||
elseif type(response[key]) == type('')
|
|
||||||
let combined[key] = get(combined, key, '') . response[key]
|
|
||||||
else
|
|
||||||
let combined[key] = response[key]
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endfor
|
|
||||||
if index(combined.status, 'error') >= 0
|
|
||||||
throw 'nREPL: ' . tr(combined.status[0], '-', ' ')
|
|
||||||
endif
|
|
||||||
return combined
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:nrepl_eval(expr, ...) dict abort
|
|
||||||
let payload = {"op": "eval", "code": a:expr}
|
|
||||||
if a:0
|
|
||||||
let payload.ns = a:1
|
|
||||||
elseif has_key(self, 'ns')
|
|
||||||
let payload.ns = self.ns
|
|
||||||
endif
|
|
||||||
if has_key(self, 'session')
|
|
||||||
let payload.session = self.session
|
|
||||||
endif
|
|
||||||
let response = self.process(payload)
|
|
||||||
if has_key(response, 'ns') && !a:0
|
|
||||||
let self.ns = response.ns
|
|
||||||
endif
|
|
||||||
|
|
||||||
if has_key(response, 'value')
|
|
||||||
let response.value = response.value[-1]
|
|
||||||
endif
|
|
||||||
return response
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:nrepl_call(payload) dict abort
|
|
||||||
let in = 'ruby -rsocket -e '.s:shellesc(
|
|
||||||
\ 'begin;' .
|
|
||||||
\ 'TCPSocket.open(%(' . self.host . '), ' . self.port . ') {|s|' .
|
|
||||||
\ 's.write(ARGV.first); loop {' .
|
|
||||||
\ 'body = s.readpartial(8192); print body;' .
|
|
||||||
\ 'break if body.include?(%(6:statusl4:done)) }};' .
|
|
||||||
\ 'rescue; abort $!.to_s;' .
|
|
||||||
\ 'end') . ' ' .
|
|
||||||
\ s:shellesc(nrepl#foreplay_connection#bencode(a:payload))
|
|
||||||
let out = system(in)
|
|
||||||
if !v:shell_error
|
|
||||||
return nrepl#foreplay_connection#bdecode('l'.out.'e')
|
|
||||||
endif
|
|
||||||
throw 'nREPL: '.split(out, "\n")[0]
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:nrepl = {
|
|
||||||
\ 'call': s:function('s:nrepl_call'),
|
|
||||||
\ 'eval': s:function('s:nrepl_eval'),
|
|
||||||
\ 'path': s:function('s:nrepl_path'),
|
|
||||||
\ 'process': s:function('s:nrepl_process')}
|
|
||||||
|
|
||||||
if !has('ruby')
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
ruby <<
|
|
||||||
require 'timeout'
|
|
||||||
require 'socket'
|
|
||||||
class << ::VIM
|
|
||||||
def string_encode(str)
|
|
||||||
'"' + str.gsub(/[\000-\037"\\]/) { |x| "\\%03o" % (x.respond_to?(:ord) ? x.ord : x[0]) } + '"'
|
|
||||||
end
|
|
||||||
def let(var, value)
|
|
||||||
command("let #{var} = #{string_encode(value)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
.
|
|
||||||
|
|
||||||
function! s:nrepl_call(payload) dict abort
|
|
||||||
let payload = nrepl#foreplay_connection#bencode(a:payload)
|
|
||||||
ruby <<
|
|
||||||
begin
|
|
||||||
buffer = ''
|
|
||||||
Timeout.timeout(16) do
|
|
||||||
TCPSocket.open(::VIM.evaluate('self.host'), ::VIM.evaluate('self.port').to_i) do |s|
|
|
||||||
s.write(::VIM.evaluate('payload'))
|
|
||||||
loop do
|
|
||||||
body = s.readpartial(8192)
|
|
||||||
buffer << body
|
|
||||||
break if body.include?("6:statusl4:done")
|
|
||||||
end
|
|
||||||
::VIM.let('out', buffer)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
::VIM.let('err', $!.to_s)
|
|
||||||
end
|
|
||||||
.
|
|
||||||
if !exists('err')
|
|
||||||
return nrepl#foreplay_connection#bdecode('l'.out.'e')
|
|
||||||
endif
|
|
||||||
throw 'nREPL: '.err
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" vim:set et sw=2:
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
*fireplace.txt* Clojure REPL support
|
||||||
|
|
||||||
|
Author: Tim Pope <http://tpo.pe/>
|
||||||
|
License: Same terms as Vim itself (see |license|)
|
||||||
|
|
||||||
|
This plugin is only available if 'compatible' is not set.
|
||||||
|
|
||||||
|
*fireplace*
|
||||||
|
While not strictly necessary, this plugin works best with the middleware
|
||||||
|
provided by <https://github.com/clojure-emacs/cider-nrepl>.
|
||||||
|
|
||||||
|
CONNECTING TO A REPL *fireplace-connect*
|
||||||
|
|
||||||
|
Connecting to lein repl happens automatically. If you have a different setup,
|
||||||
|
you can connect by hand.
|
||||||
|
|
||||||
|
*fireplace-:Connect*
|
||||||
|
:Connect {proto}://{host}:{port} {path}
|
||||||
|
Connect to a REPL server. The path is the root of the
|
||||||
|
project that the REPL applies to.
|
||||||
|
|
||||||
|
:Connect Interactively prompt for the options to connect to a
|
||||||
|
REPL server.
|
||||||
|
|
||||||
|
The REPL is used for the commands below. If no REPL is found for the current
|
||||||
|
buffer and 'path' contains at least one jar file, java (or $JAVA_CMD) is
|
||||||
|
invoked directly, which can be quite slow depending on your setup.
|
||||||
|
|
||||||
|
The only adapter shipped with fireplace.vim is for nREPL. You need either
|
||||||
|
|if_pyth| or the python command in your PATH.
|
||||||
|
|
||||||
|
*fireplace-piggieback* *fireplace-clojurescript*
|
||||||
|
ClojureScript can be evaled with Piggieback if the appropriate nREPL
|
||||||
|
middleware is loaded: https://github.com/cemerick/piggieback. Be aware that
|
||||||
|
your ClojureScript files must be available on the classpath for this to work
|
||||||
|
properly, and that not all operations are supported.
|
||||||
|
|
||||||
|
*fireplace-:Piggieback*
|
||||||
|
:Piggieback [{env}] Create a new nREPL session and invoke
|
||||||
|
cemerick.piggieback/cljs-repl with the given or
|
||||||
|
default (Rhino) environment. This will also happen
|
||||||
|
automatically on first eval in a ClojureScript buffer
|
||||||
|
if not invoked explicitly. If {env} is a number, the
|
||||||
|
piggieback repl-env will will use a cljs.repl.browser
|
||||||
|
(rather than a Rhino) env with the port set to the
|
||||||
|
number provided.
|
||||||
|
|
||||||
|
:Piggieback! Terminate the most recently created piggieback
|
||||||
|
session.
|
||||||
|
|
||||||
|
DOCUMENTATION *fireplace-documentation*
|
||||||
|
|
||||||
|
*fireplace-:Doc*
|
||||||
|
:Doc {symbol} Show the docs for the given symbol.
|
||||||
|
|
||||||
|
*fireplace-:Javadoc*
|
||||||
|
:Javadoc {class} Open the java docs for the given class in a browser.
|
||||||
|
|
||||||
|
*fireplace-K*
|
||||||
|
K Look up docs for symbol under cursor.
|
||||||
|
|
||||||
|
*fireplace-:FindDoc*
|
||||||
|
:FindDoc {arg} Wrapper around (clojure.repl/find-doc ...).
|
||||||
|
|
||||||
|
*fireplace-:Source*
|
||||||
|
:Source {symbol} Show the source for the given symbol.
|
||||||
|
|
||||||
|
*fireplace-[d*
|
||||||
|
[d Show source for symbol under cursor.
|
||||||
|
]d
|
||||||
|
|
||||||
|
NAVIGATING *fireplace-navigating*
|
||||||
|
|
||||||
|
These commands will never use a remote REPL, only a local one, as file paths
|
||||||
|
on a remote server wouldn't be very useful locally.
|
||||||
|
|
||||||
|
*fireplace-[_CTRL-D*
|
||||||
|
[<C-D> Jump to the source of the symbol under the cursor.
|
||||||
|
]<C-D>
|
||||||
|
|
||||||
|
*fireplace-CTRL-W_CTRL-D*
|
||||||
|
<C-W><C-D> Jump to the source of the symbol under the cursor in
|
||||||
|
<C-W>d a split.
|
||||||
|
|
||||||
|
*fireplace-gf*
|
||||||
|
gf Go to the file for the namespace under the cursor.
|
||||||
|
|
||||||
|
*fireplace-:Djump*
|
||||||
|
:Djump {symbol} Jump to the definition for the given symbol.
|
||||||
|
|
||||||
|
*fireplace-:Dsplit*
|
||||||
|
:Dsplit {symbol} Jump to the definition for the given symbol in a
|
||||||
|
split.
|
||||||
|
|
||||||
|
EVALUATING CODE *fireplace-eval*
|
||||||
|
|
||||||
|
All code is evaluated in the namespace of the current file, requiring it if
|
||||||
|
necessary. If the current file sits outside the class path (project.clj, for
|
||||||
|
example), the user namespace is used instead. If an exception occurs, the
|
||||||
|
stack trace is loaded into the |location-list|. Use |:lopen| to view it.
|
||||||
|
|
||||||
|
*fireplace-:Require*
|
||||||
|
:Require [ns] Require :reload the given/current namespace.
|
||||||
|
|
||||||
|
*fireplace-:Require!*
|
||||||
|
:Require! [ns] Require :reload-all the given/current namespace.
|
||||||
|
|
||||||
|
*fireplace-:Eval*
|
||||||
|
:Eval Eval/print the outermost form for the current line.
|
||||||
|
|
||||||
|
:{range}Eval Eval/print the given range.
|
||||||
|
|
||||||
|
:Eval {expr} Eval/print the given expression.
|
||||||
|
|
||||||
|
*fireplace-:Eval!*
|
||||||
|
:[range]Eval! Eval the given range or outermost expression and
|
||||||
|
replace it with its result.
|
||||||
|
|
||||||
|
:[range]Eval! {expr} Eval the given expression and insert it after
|
||||||
|
the given range or current line.
|
||||||
|
|
||||||
|
*fireplace-:RunTests*
|
||||||
|
:RunTests [ns] [...] Call clojure.test/run-tests on the given namespaces
|
||||||
|
and load the results into the quickfix list.
|
||||||
|
|
||||||
|
:[range]RunTests Call clojure.test/test-var on the var defined at or
|
||||||
|
above the specicied line and load the results into the
|
||||||
|
quickfix list. Typically invoked as :.RunTests to run
|
||||||
|
the test under the cursor.
|
||||||
|
|
||||||
|
:0RunTests [pattern] Call clojure.test/run-all-tests with the given pattern
|
||||||
|
and load the results into the quickfix list.
|
||||||
|
|
||||||
|
*fireplace-cp*
|
||||||
|
cp{motion} Eval/print the code indicated by {motion}.
|
||||||
|
|
||||||
|
cpp Eval/print the innermost form at the cursor.
|
||||||
|
|
||||||
|
*fireplace-cpr*
|
||||||
|
cpr :Require|RunTests
|
||||||
|
|
||||||
|
*fireplace-c!*
|
||||||
|
c!{motion} Eval/replace the code indicated by {motion}.
|
||||||
|
|
||||||
|
c!! Eval/replace the innermost form at the cursor.
|
||||||
|
|
||||||
|
*fireplace-cm*
|
||||||
|
cm{motion} Fully macroexpand the code indicated by {motion}.
|
||||||
|
|
||||||
|
*fireplace-cmm*
|
||||||
|
cmm Fully macroexpand the innermost form at the cursor.
|
||||||
|
|
||||||
|
*fireplace-c1m*
|
||||||
|
c1m{motion} Macroexpand the code indicated by {motion} once.
|
||||||
|
|
||||||
|
*fireplace-c1mm*
|
||||||
|
c1mm Macroexpand the innermost form at the cursor once.
|
||||||
|
|
||||||
|
*fireplace-cqp*
|
||||||
|
cqp Bring up a prompt for code to eval/print.
|
||||||
|
|
||||||
|
*fireplace-cqq*
|
||||||
|
cqq Bring up a |command-line-window| with innermost form
|
||||||
|
at the cursor prepopulated.
|
||||||
|
|
||||||
|
*fireplace-cqc*
|
||||||
|
cqc Bring up a |command-line-window| for code to
|
||||||
|
eval/print. Equivalent to cqp<C-F>i.
|
||||||
|
|
||||||
|
*fireplace-cq*
|
||||||
|
cq{motion} Bring up a |command-line-window| with text indicated
|
||||||
|
by {motion} prepopulated.
|
||||||
|
|
||||||
|
*fireplace-:Last*
|
||||||
|
:Last Open the result of the last evaluation in the preview
|
||||||
|
window. Use :2Last to get the next-to-last result,
|
||||||
|
and so on. Once the window is open, cycle to older
|
||||||
|
and newer entries with |:lprevious| and |:lnext|.
|
||||||
|
|
||||||
|
And insert mode:
|
||||||
|
|
||||||
|
*fireplace-i_CTRL-R_(*
|
||||||
|
<C-R>( Evaluate the given expression and insert the result.
|
||||||
|
|
||||||
|
There's omnicomplete on |CTRL-X_CTRL-O|, which works in Clojure buffers and
|
||||||
|
in the |command-line-window|, and tab complete at the cqp prompt.
|
||||||
|
|
||||||
|
ABOUT *fireplace-about*
|
||||||
|
|
||||||
|
Grab the latest version or report a bug on GitHub:
|
||||||
|
|
||||||
|
http://github.com/tpope/vim-fireplace
|
||||||
|
|
||||||
|
vim:tw=78:et:ft=help:norl:
|
161
doc/foreplay.txt
161
doc/foreplay.txt
|
@ -1,161 +0,0 @@
|
||||||
*foreplay.txt* Clojure REPL tease
|
|
||||||
|
|
||||||
Author: Tim Pope <http://tpo.pe/>
|
|
||||||
License: Same terms as Vim itself (see |license|)
|
|
||||||
|
|
||||||
This plugin is only available if 'compatible' is not set.
|
|
||||||
|
|
||||||
You need Clojure runtime files to use this plugin. Try VimClojure. You don't
|
|
||||||
need the interactive stuff.
|
|
||||||
|
|
||||||
CLASSPATH *foreplay-classpath*
|
|
||||||
|
|
||||||
Upon loading a Clojure buffer, the 'path' option is automatically set to your
|
|
||||||
class path. The class path is found by `lein classpath`, `mvn
|
|
||||||
dependency:build-classpath`, or failing both of those, $CLASSPATH.
|
|
||||||
|
|
||||||
Finding the class path can be slow. To cache it across multiple invocations
|
|
||||||
of Vim, try
|
|
||||||
|
|
||||||
set viminfo+=!
|
|
||||||
|
|
||||||
LEININGEN *foreplay-leiningen*
|
|
||||||
|
|
||||||
Leiningen support is currently bare bones. If a Leiningen project is found,
|
|
||||||
'makeprg' will be set to "lein".
|
|
||||||
|
|
||||||
Leiningen 2.x writes to target/repl-port when `lein repl` is invoked. If this
|
|
||||||
file is found, an nREPL connection will be established automatically.
|
|
||||||
|
|
||||||
CONNECTING TO A REPL *foreplay-connect*
|
|
||||||
|
|
||||||
*foreplay-:Connect*
|
|
||||||
:Connect {proto}://{host}:{port}
|
|
||||||
Connect to a REPL server.
|
|
||||||
|
|
||||||
:Connect Interactively prompt for the options to connect to a
|
|
||||||
REPL server.
|
|
||||||
|
|
||||||
The REPL is used for the commands below. If no REPL is found for the current
|
|
||||||
buffer, java (or $JAVA_CMD) is invoked directly (using 'path' as the class
|
|
||||||
path), which can be quite slow depending on your setup.
|
|
||||||
|
|
||||||
The only adapter shipped with foreplay.vim is for nREPL. You need Ruby
|
|
||||||
installed and either |if_ruby| or the ruby command in your PATH.
|
|
||||||
|
|
||||||
DOCUMENTATION *foreplay-documentation*
|
|
||||||
|
|
||||||
*foreplay-:Doc*
|
|
||||||
:Doc {symbol} Show the docs for the given symbol.
|
|
||||||
|
|
||||||
*foreplay-:Javadoc*
|
|
||||||
:Javadoc {class} Open the java docs for the given class in a browser.
|
|
||||||
|
|
||||||
*foreplay-K*
|
|
||||||
K Look up docs for keyword under cursor.
|
|
||||||
|
|
||||||
*foreplay-:FindDoc*
|
|
||||||
:FindDoc {arg} Wrapper around (clojure.repl/find-doc ...).
|
|
||||||
|
|
||||||
*foreplay-:Apropos*
|
|
||||||
:Apropos {arg} Wrapper around (clojure.repl/apropos ...).
|
|
||||||
|
|
||||||
*foreplay-:Source*
|
|
||||||
:Source {symbol} Show the source for the given symbol.
|
|
||||||
|
|
||||||
*foreplay-[d*
|
|
||||||
[d Show source for keyword under cursor.
|
|
||||||
]d
|
|
||||||
|
|
||||||
NAVIGATING *foreplay-navigating*
|
|
||||||
|
|
||||||
These commands will never use a remote REPL, only a local one, as file paths
|
|
||||||
on a remote server wouldn't be very useful locally.
|
|
||||||
|
|
||||||
*foreplay-[_CTRL-D*
|
|
||||||
[<C-D> Jump to the source of the keyword under the cursor.
|
|
||||||
]<C-D>
|
|
||||||
|
|
||||||
*foreplay-CTRL-W_CTRL-D*
|
|
||||||
<C-W><C-D> Jump to the source of the keyword under the cursor in
|
|
||||||
<C-W>d a split.
|
|
||||||
|
|
||||||
*foreplay-gf*
|
|
||||||
gf Go to the file for the namespace under the cursor.
|
|
||||||
|
|
||||||
*foreplay-:Djump*
|
|
||||||
:Djump {symbol} Jump to the definition for the given symbol.
|
|
||||||
|
|
||||||
*foreplay-:Dsplit*
|
|
||||||
:Dsplit {symbol} Jump to the definition for the given symbol in a
|
|
||||||
split.
|
|
||||||
|
|
||||||
EVALUATING CODE *foreplay-eval*
|
|
||||||
|
|
||||||
All code is evaluated in the namespace of the current file, requiring it if
|
|
||||||
necessary. If the current file sits outside the class path (project.clj, for
|
|
||||||
example), the user namespace is used instead.
|
|
||||||
|
|
||||||
*foreplay-:Require*
|
|
||||||
:Require [ns] Require :reload the given/current namespace.
|
|
||||||
|
|
||||||
*foreplay-:Require!*
|
|
||||||
:Require! [ns] Require :reload-all the given/current namespace.
|
|
||||||
|
|
||||||
*foreplay-:Eval*
|
|
||||||
:Eval Eval/print the outermost expression for the current
|
|
||||||
line.
|
|
||||||
|
|
||||||
:{range}Eval Eval/print the given range.
|
|
||||||
|
|
||||||
:Eval {expr} Eval/print the given expression.
|
|
||||||
|
|
||||||
*foreplay-:Eval!*
|
|
||||||
:[range]Eval! Eval the given range or outermost expression and
|
|
||||||
replace it with its result.
|
|
||||||
|
|
||||||
:[range]Eval! {expr} Eval the given expression and insert it after
|
|
||||||
the given range or current line.
|
|
||||||
|
|
||||||
*foreplay-cp*
|
|
||||||
cp{motion} Eval/print the code indicated by {motion}.
|
|
||||||
|
|
||||||
cpp Eval/print the inner-most expr at the cursor.
|
|
||||||
|
|
||||||
*foreplay-cpr*
|
|
||||||
cpr Eval a require :reload form.
|
|
||||||
|
|
||||||
*foreplay-cpR*
|
|
||||||
cpR Eval a require :reload-all form.
|
|
||||||
|
|
||||||
*foreplay-c!*
|
|
||||||
c!{motion} Eval/replace the code indicated by {motion}.
|
|
||||||
|
|
||||||
c!! Eval/replace the inner-most expr at the cusror.
|
|
||||||
|
|
||||||
*foreplay-cqp*
|
|
||||||
cqp Bring up a prompt for code to eval/print.
|
|
||||||
|
|
||||||
*foreplay-cqc*
|
|
||||||
cqc Bring up a |command-line-window| for code to
|
|
||||||
eval/print. Equivalent to cqp<C-F>i.
|
|
||||||
|
|
||||||
*foreplay-cq*
|
|
||||||
cq{motion} Bring up a |command-line-window| with text indicated
|
|
||||||
by {motion} prepopulated.
|
|
||||||
|
|
||||||
And insert mode:
|
|
||||||
|
|
||||||
*foreplay-i_CTRL-R_(*
|
|
||||||
<C-R>( Evaluate the given expression and insert the result.
|
|
||||||
|
|
||||||
There's omnicomplete on |CTRL-X_CTRL-O|, which works in Clojure buffers and
|
|
||||||
in the |command-line-window|, and tab complete at the cqp prompt.
|
|
||||||
|
|
||||||
ABOUT *foreplay-about*
|
|
||||||
|
|
||||||
Grab the latest version or report a bug on GitHub:
|
|
||||||
|
|
||||||
http://github.com/tpope/vim-foreplay
|
|
||||||
|
|
||||||
vim:tw=78:et:ft=help:norl:
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,40 @@
|
||||||
|
" Location: plugin/fireplace/zip.vim
|
||||||
|
|
||||||
|
if exists("g:loaded_zip") || &cp || v:version >= 704
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
runtime! autoload/zip.vim
|
||||||
|
|
||||||
|
" Patched to allow loading from the quickfix list. The version that ships
|
||||||
|
" with Vim 7.4 already has this change.
|
||||||
|
|
||||||
|
fun! zip#Read(fname,mode)
|
||||||
|
" call Dfunc("zip#Read(fname<".a:fname.">,mode=".a:mode.")")
|
||||||
|
let repkeep= &report
|
||||||
|
set report=10
|
||||||
|
|
||||||
|
if has("unix")
|
||||||
|
let zipfile = substitute(a:fname,'zipfile:\(.\{-}\)::[^\\].*$','\1','')
|
||||||
|
let fname = substitute(a:fname,'zipfile:.\{-}::\([^\\].*\)$','\1','')
|
||||||
|
else
|
||||||
|
let zipfile = substitute(a:fname,'^.\{-}zipfile:\(.\{-}\)::[^\\].*$','\1','')
|
||||||
|
let fname = substitute(a:fname,'^.\{-}zipfile:.\{-}::\([^\\].*\)$','\1','')
|
||||||
|
let fname = substitute(fname, '[', '[[]', 'g')
|
||||||
|
endif
|
||||||
|
" call Decho("zipfile<".zipfile.">")
|
||||||
|
" call Decho("fname <".fname.">")
|
||||||
|
|
||||||
|
" Changes for fireplace.
|
||||||
|
let temp = tempname()
|
||||||
|
let fn = expand('%:p')
|
||||||
|
exe "sil! ! ".g:zip_unzipcmd." -p -- ".shellescape(zipfile,1)." ".shellescape(fnameescape(fname),1). ' > '.temp
|
||||||
|
silent exe 'keepalt file '.temp
|
||||||
|
silent keepjumps edit!
|
||||||
|
silent exe 'keepalt file '.fnameescape(fn)
|
||||||
|
call delete(temp)
|
||||||
|
filetype detect
|
||||||
|
|
||||||
|
" Resume regularly scheduled programming.
|
||||||
|
set nomod
|
||||||
|
endfun
|
|
@ -1,952 +0,0 @@
|
||||||
" foreplay.vim - Clojure REPL tease
|
|
||||||
" Maintainer: Tim Pope <http://tpo.pe>
|
|
||||||
|
|
||||||
if exists("g:loaded_foreplay") || v:version < 700 || &cp
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
let g:loaded_foreplay = 1
|
|
||||||
|
|
||||||
" File type {{{1
|
|
||||||
|
|
||||||
augroup foreplay_file_type
|
|
||||||
autocmd!
|
|
||||||
autocmd BufNewFile,BufReadPost *.clj setfiletype clojure
|
|
||||||
autocmd FileType clojure
|
|
||||||
\ if expand('%:p') !~# '^zipfile:' |
|
|
||||||
\ let &l:path = classpath#detect() |
|
|
||||||
\ endif
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Shell escaping {{{1
|
|
||||||
|
|
||||||
function! foreplay#shellesc(arg) abort
|
|
||||||
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
|
|
||||||
return a:arg
|
|
||||||
elseif &shell =~# 'cmd'
|
|
||||||
return '"'.substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g').'"'
|
|
||||||
else
|
|
||||||
let escaped = shellescape(a:arg)
|
|
||||||
if &shell =~# 'sh' && &shell !~# 'csh'
|
|
||||||
return substitute(escaped, '\\\n', '\n', 'g')
|
|
||||||
else
|
|
||||||
return escaped
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Completion {{{1
|
|
||||||
|
|
||||||
let s:jar_contents = {}
|
|
||||||
|
|
||||||
function! foreplay#jar_contents(path) abort
|
|
||||||
if !has_key(s:jar_contents, a:path) && executable('zipinfo')
|
|
||||||
let s:jar_contents[a:path] = split(system('zipinfo -1 '.shellescape(a:path)), "\n")
|
|
||||||
if v:shell_error
|
|
||||||
return []
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
return copy(get(s:jar_contents, a:path, []))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#eval_complete(A, L, P) abort
|
|
||||||
let prefix = matchstr(a:A, '\%(.* \|^\)\%(#\=[\[{('']\)*')
|
|
||||||
let keyword = a:A[strlen(prefix) : -1]
|
|
||||||
return sort(map(foreplay#omnicomplete(0, keyword), 'prefix . v:val.word'))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#ns_complete(A, L, P) abort
|
|
||||||
let matches = []
|
|
||||||
for dir in classpath#split(classpath#from_vim(&path))
|
|
||||||
if dir =~# '\.jar$'
|
|
||||||
let files = filter(foreplay#jar_contents(dir), 'v:val =~# "\\.clj$"')
|
|
||||||
else
|
|
||||||
let files = split(glob(dir."/**/*.clj", 1), "\n")
|
|
||||||
call map(files, 'v:val[strlen(dir)+1 : -1]')
|
|
||||||
endif
|
|
||||||
let matches += files
|
|
||||||
endfor
|
|
||||||
return filter(map(matches, 's:tons(v:val)'), 'a:A ==# "" || a:A ==# v:val[0 : strlen(a:A)-1]')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#omnicomplete(findstart, base) abort
|
|
||||||
if a:findstart
|
|
||||||
let line = getline('.')[0 : col('.')-2]
|
|
||||||
return col('.') - strlen(matchstr(line, '\k\+$')) - 1
|
|
||||||
else
|
|
||||||
try
|
|
||||||
let omnifier = '(fn [[k v]] (let [m (meta v)]' .
|
|
||||||
\ ' {:word k :menu (pr-str (:arglists m (symbol ""))) :info (str " " (:doc m)) :kind (if (:arglists m) "f" "v")}))'
|
|
||||||
|
|
||||||
let ns = foreplay#ns()
|
|
||||||
|
|
||||||
let [aliases, namespaces, maps] = foreplay#evalparse(
|
|
||||||
\ '[(ns-aliases '.s:qsym(ns).') (all-ns) '.
|
|
||||||
\ '(sort-by :word (map '.omnifier.' (ns-map '.s:qsym(ns).')))]')
|
|
||||||
|
|
||||||
if a:base =~# '^[^/]*/[^/]*$'
|
|
||||||
let ns = matchstr(a:base, '^.*\ze/')
|
|
||||||
let prefix = ns . '/'
|
|
||||||
let ns = get(aliases, ns, ns)
|
|
||||||
let keyword = matchstr(a:base, '.*/\zs.*')
|
|
||||||
let results = foreplay#evalparse(
|
|
||||||
\ '(sort-by :word (map '.omnifier.' (ns-publics '.s:qsym(ns).')))')
|
|
||||||
for r in results
|
|
||||||
let r.word = prefix . r.word
|
|
||||||
endfor
|
|
||||||
else
|
|
||||||
let keyword = a:base
|
|
||||||
let results = maps + map(sort(keys(aliases) + namespaces), '{"word": v:val."/", "kind": "t", "info": ""}')
|
|
||||||
endif
|
|
||||||
if type(results) == type([])
|
|
||||||
return filter(results, 'a:base ==# "" || a:base ==# v:val.word[0 : strlen(a:base)-1]')
|
|
||||||
else
|
|
||||||
return []
|
|
||||||
endif
|
|
||||||
catch /.*/
|
|
||||||
return []
|
|
||||||
endtry
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_completion
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType clojure setlocal omnifunc=foreplay#omnicomplete
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" REPL client {{{1
|
|
||||||
|
|
||||||
let s:repl = {"requires": {}}
|
|
||||||
|
|
||||||
if !exists('s:repls')
|
|
||||||
let s:repls = []
|
|
||||||
let s:repl_paths = {}
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! s:qsym(symbol)
|
|
||||||
if a:symbol =~# '^[[:alnum:]?*!+/=<>.:-]\+$'
|
|
||||||
return "'".a:symbol
|
|
||||||
else
|
|
||||||
return '(symbol "'.escape(a:symbol, '"').'")'
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:repl.eval(expr, ns) dict abort
|
|
||||||
try
|
|
||||||
let result = self.connection.eval(a:expr, a:ns)
|
|
||||||
catch /^\w\+: Connection/
|
|
||||||
call filter(s:repl_paths, 'v:val isnot self')
|
|
||||||
call filter(s:repls, 'v:val isnot self')
|
|
||||||
throw v:exception
|
|
||||||
endtry
|
|
||||||
return result
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:repl.require(lib) dict abort
|
|
||||||
if a:lib !~# '^\%(user\)\=$' && !get(self.requires, a:lib, 0)
|
|
||||||
let reload = has_key(self.requires, a:lib) ? ' :reload' : ''
|
|
||||||
let self.requires[a:lib] = 0
|
|
||||||
let result = self.eval('(doto '.s:qsym(a:lib).' (require'.reload.') the-ns)', 'user')
|
|
||||||
let self.requires[a:lib] = !has_key(result, 'ex')
|
|
||||||
endif
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:repl.includes_file(file) dict abort
|
|
||||||
let file = substitute(a:file, '\C^zipfile:\(.*\)::', '\1/', '')
|
|
||||||
for path in self.connection.path()
|
|
||||||
if file[0 : len(path)-1] ==? path
|
|
||||||
return 1
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:register_connection(conn, ...)
|
|
||||||
call insert(s:repls, extend({'connection': a:conn}, deepcopy(s:repl)))
|
|
||||||
if a:0 && a:1 !=# ''
|
|
||||||
let s:repl_paths[a:1] = s:repls[0]
|
|
||||||
endif
|
|
||||||
return s:repls[0]
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" :Connect {{{1
|
|
||||||
|
|
||||||
command! -bar -complete=customlist,s:connect_complete -nargs=? ForeplayConnect :exe s:Connect(<q-args>)
|
|
||||||
|
|
||||||
function! foreplay#input_host_port()
|
|
||||||
let arg = input('Host> ', 'localhost')
|
|
||||||
if arg ==# ''
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
echo "\n"
|
|
||||||
let arg .= ':' . input('Port> ')
|
|
||||||
if arg =~# ':$'
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
echo "\n"
|
|
||||||
return arg
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:protos()
|
|
||||||
return map(split(globpath(&runtimepath, 'autoload/*/foreplay_connection.vim'), "\n"), 'fnamemodify(v:val, ":h:t")')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:connect_complete(A, L, P)
|
|
||||||
let proto = matchstr(a:A, '\w\+\ze://')
|
|
||||||
if proto ==# ''
|
|
||||||
let options = map(s:protos(), 'v:val."://"')
|
|
||||||
else
|
|
||||||
let rest = matchstr(a:A, '://\zs.*')
|
|
||||||
try
|
|
||||||
let options = {proto}#foreplay_connection#complete(rest)
|
|
||||||
catch /^Vim(let):E117/
|
|
||||||
let options = ['localhost:']
|
|
||||||
endtry
|
|
||||||
call map(options, 'proto."://".v:val')
|
|
||||||
endif
|
|
||||||
if a:A !=# ''
|
|
||||||
call filter(options, 'v:val[0 : strlen(a:A)-1] ==# a:A')
|
|
||||||
endif
|
|
||||||
return options
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Connect(arg)
|
|
||||||
if a:arg =~# '^\w\+://'
|
|
||||||
let [proto, arg] = split(a:arg, '://')
|
|
||||||
elseif a:arg !=# ''
|
|
||||||
return 'echoerr '.string('Usage: :Connect proto://...')
|
|
||||||
else
|
|
||||||
let protos = s:protos()
|
|
||||||
if empty(protos)
|
|
||||||
return 'echoerr '.string('No protocols available')
|
|
||||||
endif
|
|
||||||
let proto = s:inputlist('Protocol> ', protos)
|
|
||||||
if proto ==# ''
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
redraw!
|
|
||||||
echo ':Connect'
|
|
||||||
echo 'Protocol> '.proto
|
|
||||||
let arg = {proto}#foreplay_connection#prompt()
|
|
||||||
endif
|
|
||||||
try
|
|
||||||
let connection = {proto}#foreplay_connection#open(arg)
|
|
||||||
catch /.*/
|
|
||||||
return 'echoerr '.string(v:exception)
|
|
||||||
endtry
|
|
||||||
if type(connection) !=# type({}) || empty(connection)
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
let client = s:register_connection(connection)
|
|
||||||
echo 'Connected to '.proto.'://'.arg
|
|
||||||
let path = fnamemodify(exists('b:java_root') ? b:java_root : fnamemodify(expand('%'), ':p:s?.*\zs[\/]src[\/].*??'), ':~')
|
|
||||||
let root = input('Scope connection to: ', path, 'dir')
|
|
||||||
if root !=# ''
|
|
||||||
let s:repl_paths[fnamemodify(root, ':p:s?[\/]$??')] = client
|
|
||||||
endif
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_connect
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType clojure command! -bar -complete=customlist,s:connect_complete -nargs=? Connect :ForeplayConnect <args>
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Java runner {{{1
|
|
||||||
|
|
||||||
if !exists('g:java_cmd')
|
|
||||||
let g:java_cmd = exists('$JAVA_CMD') ? $JAVA_CMD : 'java'
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:oneoff = {}
|
|
||||||
|
|
||||||
let s:oneoff_pr = tempname()
|
|
||||||
let s:oneoff_ex = tempname()
|
|
||||||
let s:oneoff_stk = tempname()
|
|
||||||
let s:oneoff_in = tempname()
|
|
||||||
let s:oneoff_out = tempname()
|
|
||||||
let s:oneoff_err = tempname()
|
|
||||||
|
|
||||||
function! s:oneoff.eval(expr, ns) dict abort
|
|
||||||
if &verbose
|
|
||||||
echohl WarningMSG
|
|
||||||
echomsg "No REPL found. Running java clojure.main ..."
|
|
||||||
echohl None
|
|
||||||
endif
|
|
||||||
if a:ns !=# '' && a:ns !=# 'user'
|
|
||||||
let ns = '(require '.s:qsym(a:ns).') (in-ns '.s:qsym(a:ns).') '
|
|
||||||
else
|
|
||||||
let ns = ''
|
|
||||||
endif
|
|
||||||
call writefile([], s:oneoff_pr, 'b')
|
|
||||||
call writefile([], s:oneoff_ex, 'b')
|
|
||||||
call writefile([], s:oneoff_stk, 'b')
|
|
||||||
call writefile(split('(do '.a:expr.')', "\n"), s:oneoff_in, 'b')
|
|
||||||
call writefile([], s:oneoff_out, 'b')
|
|
||||||
call writefile([], s:oneoff_err, 'b')
|
|
||||||
let command = g:java_cmd.' -cp '.shellescape(self.classpath).' clojure.main -e ' .
|
|
||||||
\ foreplay#shellesc(
|
|
||||||
\ '(binding [*out* (java.io.FileWriter. "'.s:oneoff_out.'")' .
|
|
||||||
\ ' *err* (java.io.FileWriter. "'.s:oneoff_err.'")]' .
|
|
||||||
\ ' (try' .
|
|
||||||
\ ' (require ''clojure.repl) '.ns.'(spit "'.s:oneoff_pr.'" (pr-str (eval (read-string (slurp "'.s:oneoff_in.'")))))' .
|
|
||||||
\ ' (catch Exception e' .
|
|
||||||
\ ' (spit *err* (.toString e))' .
|
|
||||||
\ ' (spit "'.s:oneoff_ex.'" (class e))' .
|
|
||||||
\ ' (spit "'.s:oneoff_stk.'" (apply str (interpose "\n" (.getStackTrace e))))))' .
|
|
||||||
\ ' nil)')
|
|
||||||
let wtf = system(command)
|
|
||||||
let result = {}
|
|
||||||
let result.value = join(readfile(s:oneoff_pr, 'b'), "\n")
|
|
||||||
let result.out = join(readfile(s:oneoff_out, 'b'), "\n")
|
|
||||||
let result.err = join(readfile(s:oneoff_err, 'b'), "\n")
|
|
||||||
let result.ex = join(readfile(s:oneoff_ex, 'b'), "\n")
|
|
||||||
let result.stacktrace = readfile(s:oneoff_stk)
|
|
||||||
call filter(result, '!empty(v:val)')
|
|
||||||
if v:shell_error && get(result, 'ex', '') ==# ''
|
|
||||||
throw 'Error running Clojure: '.wtf
|
|
||||||
else
|
|
||||||
return result
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:oneoff.require(symbol)
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Client {{{1
|
|
||||||
|
|
||||||
function! s:client() abort
|
|
||||||
silent doautocmd User ForeplayPreConnect
|
|
||||||
let buf = exists('s:input') ? s:input : '%'
|
|
||||||
let root = simplify(fnamemodify(bufname(buf), ':p:s?[\/]$??'))
|
|
||||||
let previous = ""
|
|
||||||
while root !=# previous
|
|
||||||
if has_key(s:repl_paths, root)
|
|
||||||
return s:repl_paths[root]
|
|
||||||
endif
|
|
||||||
let previous = root
|
|
||||||
let root = fnamemodify(root, ':h')
|
|
||||||
endwhile
|
|
||||||
return foreplay#local_client(1)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#client() abort
|
|
||||||
return s:client()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#local_client(...)
|
|
||||||
if !a:0
|
|
||||||
silent doautocmd User ForeplayPreConnect
|
|
||||||
endif
|
|
||||||
let buf = exists('s:input') ? s:input : '%'
|
|
||||||
for repl in s:repls
|
|
||||||
if repl.includes_file(fnamemodify(bufname(buf), ':p'))
|
|
||||||
return repl
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
let cp = classpath#from_vim(getbufvar(buf, '&path'))
|
|
||||||
return extend({'classpath': cp}, s:oneoff)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#eval(expr, ...) abort
|
|
||||||
let c = s:client()
|
|
||||||
|
|
||||||
if !a:0 && foreplay#ns() !~# '^\%(user\)$'
|
|
||||||
call c.require(foreplay#ns())
|
|
||||||
endif
|
|
||||||
|
|
||||||
let result = c.eval(a:expr, a:0 ? a:1 : foreplay#ns())
|
|
||||||
|
|
||||||
if get(result, 'err', '') !=# ''
|
|
||||||
echohl ErrorMSG
|
|
||||||
echo substitute(result.err, '\n$', '', '')
|
|
||||||
echohl NONE
|
|
||||||
endif
|
|
||||||
if get(result, 'out', '') !=# ''
|
|
||||||
echo substitute(result.out, '\n$', '', '')
|
|
||||||
endif
|
|
||||||
|
|
||||||
if get(result, 'ex', '') !=# ''
|
|
||||||
let err = 'Clojure: '.result.ex
|
|
||||||
elseif has_key(result, 'value')
|
|
||||||
return result.value
|
|
||||||
else
|
|
||||||
let err = 'foreplay.vim: Something went wrong: '.string(result)
|
|
||||||
endif
|
|
||||||
throw err
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#evalparse(expr) abort
|
|
||||||
let body = foreplay#eval(
|
|
||||||
\ '(symbol ((fn *vimify [x]' .
|
|
||||||
\ ' (cond' .
|
|
||||||
\ ' (map? x) (str "{" (apply str (interpose ", " (map (fn [[k v]] (str (*vimify k) ": " (*vimify v))) x))) "}")' .
|
|
||||||
\ ' (coll? x) (str "[" (apply str (interpose ", " (map *vimify x))) "]")' .
|
|
||||||
\ ' (number? x) (pr-str x)' .
|
|
||||||
\ ' (keyword? x) (pr-str (name x))' .
|
|
||||||
\ ' :else (pr-str (str x)))) '.a:expr.'))',
|
|
||||||
\ a:0 ? a:1 : foreplay#ns())
|
|
||||||
if body ==# ''
|
|
||||||
return ''
|
|
||||||
else
|
|
||||||
return eval(body)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Eval {{{1
|
|
||||||
|
|
||||||
let foreplay#skip = 'synIDattr(synID(line("."),col("."),1),"name") =~? "comment\\|string\\|char"'
|
|
||||||
|
|
||||||
function! s:opfunc(type) abort
|
|
||||||
let sel_save = &selection
|
|
||||||
let cb_save = &clipboard
|
|
||||||
let reg_save = @@
|
|
||||||
try
|
|
||||||
set selection=inclusive clipboard-=unnamed clipboard-=unnamedplus
|
|
||||||
if a:type =~ '^\d\+$'
|
|
||||||
silent exe 'normal! ^v'.a:type.'$hy'
|
|
||||||
elseif a:type =~# '^.$'
|
|
||||||
silent exe "normal! `<" . a:type . "`>y"
|
|
||||||
elseif a:type ==# 'line'
|
|
||||||
silent exe "normal! '[V']y"
|
|
||||||
elseif a:type ==# 'block'
|
|
||||||
silent exe "normal! `[\<C-V>`]y"
|
|
||||||
elseif a:type ==# 'outer'
|
|
||||||
call searchpair('(','',')', 'Wbcr', g:foreplay#skip)
|
|
||||||
silent exe "normal! vaby"
|
|
||||||
else
|
|
||||||
silent exe "normal! `[v`]y"
|
|
||||||
endif
|
|
||||||
redraw
|
|
||||||
return @@
|
|
||||||
finally
|
|
||||||
let @@ = reg_save
|
|
||||||
let &selection = sel_save
|
|
||||||
let &clipboard = cb_save
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:filterop(type) abort
|
|
||||||
let reg_save = @@
|
|
||||||
try
|
|
||||||
let expr = s:opfunc(a:type)
|
|
||||||
let @@ = matchstr(expr, '^\n\+').foreplay#eval(expr, foreplay#ns()).matchstr(expr, '\n\+$')
|
|
||||||
if @@ !~# '^\n*$'
|
|
||||||
normal! gvp
|
|
||||||
endif
|
|
||||||
catch /^Clojure:/
|
|
||||||
return ''
|
|
||||||
finally
|
|
||||||
let @@ = reg_save
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:printop(type) abort
|
|
||||||
let s:todo = s:opfunc(a:type)
|
|
||||||
call feedkeys("\<Plug>ForeplayPrintLast")
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:print_last() abort
|
|
||||||
try
|
|
||||||
echo foreplay#eval(s:todo)
|
|
||||||
catch /^Clojure:/
|
|
||||||
endtry
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:editop(type) abort
|
|
||||||
call feedkeys(&cedit . "\<Home>", 'n')
|
|
||||||
let input = s:input(substitute(substitute(s:opfunc(a:type), "\s*;[^\n]*", '', 'g'), '\n\+\s*', ' ', 'g'))
|
|
||||||
try
|
|
||||||
if input !=# ''
|
|
||||||
echo foreplay#eval(input)
|
|
||||||
endif
|
|
||||||
catch /^Clojure:/
|
|
||||||
return ''
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Eval(bang, line1, line2, count, args) abort
|
|
||||||
if a:args !=# ''
|
|
||||||
let expr = a:args
|
|
||||||
else
|
|
||||||
if a:count ==# 0
|
|
||||||
normal! ^
|
|
||||||
let line1 = searchpair('(','',')', 'bcrn', g:foreplay#skip)
|
|
||||||
let line2 = searchpair('(','',')', 'rn', g:foreplay#skip)
|
|
||||||
else
|
|
||||||
let line1 = a:line1
|
|
||||||
let line2 = a:line2
|
|
||||||
endif
|
|
||||||
if !line1 || !line2
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
let expr = join(getline(line1, line2), "\n")
|
|
||||||
if a:bang
|
|
||||||
exe line1.','.line2.'delete _'
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
try
|
|
||||||
let result = foreplay#eval(expr)
|
|
||||||
if a:bang
|
|
||||||
if a:args !=# ''
|
|
||||||
call append(a:line1, result)
|
|
||||||
exe a:line1
|
|
||||||
else
|
|
||||||
call append(a:line1-1, result)
|
|
||||||
exe a:line1-1
|
|
||||||
endif
|
|
||||||
else
|
|
||||||
echo result
|
|
||||||
endif
|
|
||||||
catch /^Clojure:/
|
|
||||||
endtry
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" If we call input() directly inside a try, and the user opens the command
|
|
||||||
" line window and tries to switch out of it (such as with ctrl-w), Vim will
|
|
||||||
" crash when the command line window closes. Adding an indirect function call
|
|
||||||
" works around this.
|
|
||||||
function! s:actually_input(...)
|
|
||||||
return call(function('input'), a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:input(default) abort
|
|
||||||
if !exists('g:FOREPLAY_HISTORY')
|
|
||||||
let g:FOREPLAY_HISTORY = []
|
|
||||||
endif
|
|
||||||
try
|
|
||||||
let s:input = bufnr('%')
|
|
||||||
let s:oldhist = s:histswap(g:FOREPLAY_HISTORY)
|
|
||||||
return s:actually_input(foreplay#ns().'=> ', a:default, 'customlist,foreplay#eval_complete')
|
|
||||||
finally
|
|
||||||
unlet! s:input
|
|
||||||
if exists('s:oldhist')
|
|
||||||
let g:FOREPLAY_HISTORY = s:histswap(s:oldhist)
|
|
||||||
endif
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:inputclose() abort
|
|
||||||
let l = substitute(getcmdline(), '"\%(\\.\|[^"]\)*"\|\\.', '', 'g')
|
|
||||||
let open = len(substitute(l, '[^(]', '', 'g'))
|
|
||||||
let close = len(substitute(l, '[^)]', '', 'g'))
|
|
||||||
if open - close == 1
|
|
||||||
return ")\<CR>"
|
|
||||||
else
|
|
||||||
return ")"
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:inputeval() abort
|
|
||||||
let input = s:input('')
|
|
||||||
redraw
|
|
||||||
if input ==# ''
|
|
||||||
return ''
|
|
||||||
else
|
|
||||||
try
|
|
||||||
echo foreplay#eval(input)
|
|
||||||
return ''
|
|
||||||
catch /^Clojure:/
|
|
||||||
return ''
|
|
||||||
catch
|
|
||||||
return 'echoerr '.string(v:exception)
|
|
||||||
endtry
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:recall() abort
|
|
||||||
try
|
|
||||||
cnoremap <expr> ) <SID>inputclose()
|
|
||||||
let input = s:input('(')
|
|
||||||
if input =~# '^(\=$'
|
|
||||||
return ''
|
|
||||||
else
|
|
||||||
return foreplay#eval(input)
|
|
||||||
endif
|
|
||||||
catch /^Clojure:/
|
|
||||||
return ''
|
|
||||||
finally
|
|
||||||
silent! cunmap )
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:histswap(list)
|
|
||||||
let old = []
|
|
||||||
for i in range(1, histnr('@') * (histnr('@') > 0))
|
|
||||||
call extend(old, [histget('@', i)])
|
|
||||||
endfor
|
|
||||||
call histdel('@')
|
|
||||||
for entry in a:list
|
|
||||||
call histadd('@', entry)
|
|
||||||
endfor
|
|
||||||
return old
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
nnoremap <silent> <Plug>ForeplayPrintLast :exe <SID>print_last()<CR>
|
|
||||||
nnoremap <silent> <Plug>ForeplayPrint :<C-U>set opfunc=<SID>printop<CR>g@
|
|
||||||
xnoremap <silent> <Plug>ForeplayPrint :<C-U>call <SID>printop(visualmode())<CR>
|
|
||||||
|
|
||||||
nnoremap <silent> <Plug>ForeplayFilter :<C-U>set opfunc=<SID>filterop<CR>g@
|
|
||||||
xnoremap <silent> <Plug>ForeplayFilter :<C-U>call <SID>filterop(visualmode())<CR>
|
|
||||||
|
|
||||||
nnoremap <silent> <Plug>ForeplayEdit :<C-U>set opfunc=<SID>editop<CR>g@
|
|
||||||
xnoremap <silent> <Plug>ForeplayEdit :<C-U>call <SID>editop(visualmode())<CR>
|
|
||||||
|
|
||||||
nnoremap <Plug>ForeplayPrompt :exe <SID>inputeval()<CR>
|
|
||||||
|
|
||||||
noremap! <Plug>ForeplayRecall <C-R>=<SID>recall()<CR>
|
|
||||||
|
|
||||||
function! s:setup_eval() abort
|
|
||||||
command! -buffer -bang -range=0 -nargs=? -complete=customlist,foreplay#eval_complete Eval :exe s:Eval(<bang>0, <line1>, <line2>, <count>, <q-args>)
|
|
||||||
|
|
||||||
nmap <buffer> cp <Plug>ForeplayPrint
|
|
||||||
nmap <buffer> cpp <Plug>ForeplayPrintab
|
|
||||||
|
|
||||||
nmap <buffer> c! <Plug>ForeplayFilter
|
|
||||||
nmap <buffer> c!! <Plug>ForeplayFilterab
|
|
||||||
|
|
||||||
nmap <buffer> cq <Plug>ForeplayEdit
|
|
||||||
nmap <buffer> cqq <Plug>ForeplayEditab
|
|
||||||
|
|
||||||
nmap <buffer> cqp <Plug>ForeplayPrompt
|
|
||||||
exe 'nmap <buffer> cqc <Plug>ForeplayPrompt' . &cedit . 'i'
|
|
||||||
|
|
||||||
map! <buffer> <C-R>( <Plug>ForeplayRecall
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:cmdwinenter()
|
|
||||||
setlocal filetype=clojure
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:cmdwinleave()
|
|
||||||
setlocal filetype< omnifunc<
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_eval
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType clojure call s:setup_eval()
|
|
||||||
autocmd CmdWinEnter @ if exists('s:input') | call s:cmdwinenter() | endif
|
|
||||||
autocmd CmdWinLeave @ if exists('s:input') | call s:cmdwinleave() | endif
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" :Require {{{1
|
|
||||||
|
|
||||||
function! s:Require(bang, ns)
|
|
||||||
let cmd = ('(require '.s:qsym(a:ns ==# '' ? foreplay#ns() : a:ns).' :reload'.(a:bang ? '-all' : '').')')
|
|
||||||
echo cmd
|
|
||||||
try
|
|
||||||
call foreplay#eval(cmd)
|
|
||||||
return ''
|
|
||||||
catch /^Clojure:.*/
|
|
||||||
return ''
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:setup_require()
|
|
||||||
command! -buffer -bar -bang -complete=customlist,foreplay#ns_complete -nargs=? Require :exe s:Require(<bang>0, <q-args>)
|
|
||||||
nnoremap <silent><buffer> cpr :Require<CR>
|
|
||||||
nnoremap <silent><buffer> cpR :Require!<CR>
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_require
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType clojure call s:setup_require()
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Go to source {{{1
|
|
||||||
|
|
||||||
function! foreplay#source(symbol) abort
|
|
||||||
let c = foreplay#local_client()
|
|
||||||
call c.require(foreplay#ns())
|
|
||||||
let cmd =
|
|
||||||
\ " (when-let [v (resolve " . s:qsym(a:symbol) .')]' .
|
|
||||||
\ ' (when-let [filepath (:file (meta v))]' .
|
|
||||||
\ ' (when-let [url (.getResource (clojure.lang.RT/baseLoader) filepath)]' .
|
|
||||||
\ ' (symbol (str (str "+" (:line (meta v))) " "' .
|
|
||||||
\ ' (if (= "jar" (.getProtocol url))' .
|
|
||||||
\ ' (str "zip" (.replaceFirst (.getFile url) "!/" "::"))' .
|
|
||||||
\ ' (.getFile url)))))))'
|
|
||||||
let result = get(split(c.eval(cmd, foreplay#ns()).value, "\n"), 0, '')
|
|
||||||
return result ==# 'nil' ? '' : result
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Edit(cmd, keyword) abort
|
|
||||||
if a:keyword =~# '^\k\+/$'
|
|
||||||
let location = foreplay#findfile(a:keyword[0: -2])
|
|
||||||
elseif a:keyword =~# '^\k\+\.[^/.]\+$'
|
|
||||||
let location = foreplay#findfile(a:keyword)
|
|
||||||
else
|
|
||||||
let location = foreplay#source(a:keyword)
|
|
||||||
endif
|
|
||||||
if location !=# ''
|
|
||||||
if matchstr(location, '^+\d\+ \zs.*') ==# expand('%:p') && a:cmd ==# 'edit'
|
|
||||||
return matchstr(location, '\d\+')
|
|
||||||
else
|
|
||||||
return a:cmd.' '.location.'|let &l:path = '.string(&l:path)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
let v:errmsg = "Couldn't find source for ".a:keyword
|
|
||||||
return 'echoerr v:errmsg'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_source
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType clojure setlocal includeexpr=tr(v:fname,'.-','/_')
|
|
||||||
autocmd FileType clojure setlocal suffixesadd=.clj,.java
|
|
||||||
autocmd FileType clojure setlocal define=^\\s*(def\\w*
|
|
||||||
autocmd FileType clojure command! -bar -buffer -nargs=1 -complete=customlist,foreplay#eval_complete Djump :exe s:Edit('edit', <q-args>)
|
|
||||||
autocmd FileType clojure command! -bar -buffer -nargs=1 -complete=customlist,foreplay#eval_complete Dsplit :exe s:Edit('split', <q-args>)
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> [<C-D> :<C-U>exe <SID>Edit('edit', expand('<cword>'))<CR>
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> ]<C-D> :<C-U>exe <SID>Edit('edit', expand('<cword>'))<CR>
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> <C-W><C-D> :<C-U>exe <SID>Edit('split', expand('<cword>'))<CR>
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> <C-W>d :<C-U>exe <SID>Edit('split', expand('<cword>'))<CR>
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> <C-W>gd :<C-U>exe <SID>Edit('tabedit', expand('<cword>'))<CR>
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Go to file {{{1
|
|
||||||
|
|
||||||
function! foreplay#findfile(path) abort
|
|
||||||
let c = foreplay#local_client()
|
|
||||||
call c.require(foreplay#ns())
|
|
||||||
|
|
||||||
let cmd =
|
|
||||||
\ '(symbol' .
|
|
||||||
\ ' (or' .
|
|
||||||
\ ' (when-let [url (.getResource (clojure.lang.RT/baseLoader) %s)]' .
|
|
||||||
\ ' (if (= "jar" (.getProtocol url))' .
|
|
||||||
\ ' (str "zip" (.replaceFirst (.getFile url) "!/" "::"))' .
|
|
||||||
\ ' (.getFile url)))' .
|
|
||||||
\ ' ""))'
|
|
||||||
|
|
||||||
let path = a:path
|
|
||||||
|
|
||||||
if path !~# '[/.]' && path =~# '^\k\+$'
|
|
||||||
let aliascmd = printf(cmd,
|
|
||||||
\ '(if-let [ns ((ns-aliases *ns*) '.s:qsym(path).')]' .
|
|
||||||
\ ' (str (.replace (.replace (str (ns-name ns)) "-" "_") "." "/") ".clj")' .
|
|
||||||
\ ' "'.path.'.clj")')
|
|
||||||
let result = get(split(c.eval(aliascmd, foreplay#ns()).value, "\n"), 0, '')
|
|
||||||
else
|
|
||||||
if path !~# '/'
|
|
||||||
let path = tr(path, '.-', '/_')
|
|
||||||
endif
|
|
||||||
if path !~# '\.\w\+$'
|
|
||||||
let path .= '.clj'
|
|
||||||
endif
|
|
||||||
|
|
||||||
let result = get(split(c.eval(printf(cmd, '"'.escape(path, '"').'"'), foreplay#ns()).value, "\n"), 0, '')
|
|
||||||
|
|
||||||
endif
|
|
||||||
if result ==# ''
|
|
||||||
return findfile(path, &l:path)
|
|
||||||
else
|
|
||||||
return result
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:GF(cmd, file) abort
|
|
||||||
if a:file =~# '^[^/]*/[^/.]*$' && a:file =~# '^\k\+$'
|
|
||||||
let [file, jump] = split(a:file, "/")
|
|
||||||
else
|
|
||||||
let file = a:file
|
|
||||||
endif
|
|
||||||
let file = foreplay#findfile(file)
|
|
||||||
if file ==# ''
|
|
||||||
let v:errmsg = "Couldn't find file for ".a:file
|
|
||||||
return 'echoerr v:errmsg'
|
|
||||||
endif
|
|
||||||
return a:cmd .
|
|
||||||
\ (exists('jump') ? ' +sil!\ djump\ ' . jump : '') .
|
|
||||||
\ ' ' . fnameescape(file) .
|
|
||||||
\ '| let &l:path = ' . string(&l:path)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_go_to_file
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> gf :<C-U>exe <SID>GF('edit', expand('<cfile>'))<CR>
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> <C-W>f :<C-U>exe <SID>GF('split', expand('<cfile>'))<CR>
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> <C-W><C-F> :<C-U>exe <SID>GF('split', expand('<cfile>'))<CR>
|
|
||||||
autocmd FileType clojure nnoremap <silent><buffer> <C-W>gf :<C-U>exe <SID>GF('tabedit', expand('<cfile>'))<CR>
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Documentation {{{1
|
|
||||||
|
|
||||||
function! s:buffer_path(...) abort
|
|
||||||
let buffer = a:0 ? a:1 : exists('s:input') ? s:input : '%'
|
|
||||||
if getbufvar(buffer, '&buftype') =~# '^no'
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
let path = substitute(fnamemodify(bufname(buffer), ':p'), '\C^zipfile:\(.*\)::', '\1/', '')
|
|
||||||
for dir in classpath#split(classpath#from_vim(getbufvar(buffer, '&path')))
|
|
||||||
if dir !=# '' && path[0 : strlen(dir)-1] ==# dir
|
|
||||||
return path[strlen(dir)+1:-1]
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:tons(path) abort
|
|
||||||
return tr(substitute(a:path, '\.\w\+$', '', ''), '\/_', '..-')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! foreplay#ns() abort
|
|
||||||
let lnum = 1
|
|
||||||
while lnum < line('$') && getline(lnum) =~# '^\s*\%(;.*\)\=$'
|
|
||||||
let lnum += 1
|
|
||||||
endwhile
|
|
||||||
let ns = matchstr(getline(lnum), '\C^(\s*\%(in-ns\s*''\|ns\s\+\)\zs\k\+\ze')
|
|
||||||
if ns !=# ''
|
|
||||||
return ns
|
|
||||||
endif
|
|
||||||
let path = s:buffer_path()
|
|
||||||
return s:tons(path ==# '' ? 'user' : path)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Lookup(ns, macro, arg) abort
|
|
||||||
" doc is in clojure.core in older Clojure versions
|
|
||||||
try
|
|
||||||
call foreplay#eval("(require '".a:ns.") (eval (list (if (ns-resolve 'clojure.core '".a:macro.") 'clojure.core/".a:macro." '".a:ns.'/'.a:macro.") '".a:arg.'))')
|
|
||||||
catch /^Clojure:/
|
|
||||||
catch /.*/
|
|
||||||
echohl ErrorMSG
|
|
||||||
echo v:exception
|
|
||||||
echohl None
|
|
||||||
endtry
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:inputlist(label, entries)
|
|
||||||
let choices = [a:label]
|
|
||||||
for i in range(len(a:entries))
|
|
||||||
let choices += [printf('%2d. %s', i+1, a:entries[i])]
|
|
||||||
endfor
|
|
||||||
let choice = inputlist(choices)
|
|
||||||
if choice
|
|
||||||
return a:entries[choice-1]
|
|
||||||
else
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Apropos(pattern) abort
|
|
||||||
if a:pattern =~# '^#\="'
|
|
||||||
let pattern = a:pattern
|
|
||||||
elseif a:pattern =~# '^^'
|
|
||||||
let pattern = '#"' . a:pattern . '"'
|
|
||||||
else
|
|
||||||
let pattern = '"' . a:pattern . '"'
|
|
||||||
endif
|
|
||||||
let matches = foreplay#evalparse('(apropos '.pattern.')')
|
|
||||||
if empty(matches)
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
let choice = s:inputlist('Look up docs for:', matches)
|
|
||||||
if choice !=# ''
|
|
||||||
return 'echo "\n"|Doc '.choice
|
|
||||||
else
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:K()
|
|
||||||
let word = expand('<cword>')
|
|
||||||
let java_candidate = matchstr(word, '^\%(\w\+\.\)*\u\l\w*\ze\%(\.\|\/\w\+\)\=$')
|
|
||||||
if java_candidate !=# ''
|
|
||||||
return 'Javadoc '.java_candidate
|
|
||||||
else
|
|
||||||
return 'Doc '.word
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_doc
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType clojure nnoremap <buffer> K :<C-R>=<SID>K()<CR><CR>
|
|
||||||
autocmd FileType clojure nnoremap <buffer> [d :Source <C-R><C-W><CR>
|
|
||||||
autocmd FileType clojure nnoremap <buffer> ]d :Source <C-R><C-W><CR>
|
|
||||||
autocmd FileType clojure command! -buffer -nargs=1 Apropos :exe s:Apropos(<q-args>)
|
|
||||||
autocmd FileType clojure command! -buffer -nargs=1 FindDoc :exe s:Lookup('clojure.repl', 'find-doc', printf('#"%s"', <q-args>))
|
|
||||||
autocmd FileType clojure command! -buffer -bar -nargs=1 Javadoc :exe s:Lookup('clojure.java.javadoc', 'javadoc', <q-args>)
|
|
||||||
autocmd FileType clojure command! -buffer -bar -nargs=1 -complete=customlist,foreplay#eval_complete Doc :exe s:Lookup('clojure.repl', 'doc', <q-args>)
|
|
||||||
autocmd FileType clojure command! -buffer -bar -nargs=1 -complete=customlist,foreplay#eval_complete Source :exe s:Lookup('clojure.repl', 'source', <q-args>)
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
" Leiningen {{{1
|
|
||||||
|
|
||||||
function! s:hunt(start, anchor) abort
|
|
||||||
let root = simplify(fnamemodify(a:start, ':p:s?[\/]$??'))
|
|
||||||
if !isdirectory(fnamemodify(root, ':h'))
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
let previous = ""
|
|
||||||
while root !=# previous
|
|
||||||
if filereadable(root . '/' . a:anchor) && isdirectory(root . '/src')
|
|
||||||
return root
|
|
||||||
endif
|
|
||||||
let previous = root
|
|
||||||
let root = fnamemodify(root, ':h')
|
|
||||||
endwhile
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
if !exists('s:leiningen_repl_ports')
|
|
||||||
let s:leiningen_repl_ports = {}
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! s:leiningen_connect()
|
|
||||||
if !exists('b:leiningen_root')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let portfile = b:leiningen_root . '/target/repl-port'
|
|
||||||
if getfsize(portfile) > 0 && getftime(portfile) !=# get(s:leiningen_repl_ports, b:leiningen_root, -1)
|
|
||||||
let port = readfile(portfile, 'b', 1)[0]
|
|
||||||
let s:leiningen_repl_ports[b:leiningen_root] = getftime(portfile)
|
|
||||||
try
|
|
||||||
call s:register_connection(nrepl#foreplay_connection#open(port), b:leiningen_root)
|
|
||||||
catch /^nREPL: Connection/
|
|
||||||
call delete(portfile)
|
|
||||||
endtry
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:leiningen_init() abort
|
|
||||||
|
|
||||||
if !exists('b:leiningen_root')
|
|
||||||
let root = s:hunt(expand('%:p'), 'project.clj')
|
|
||||||
if root !=# ''
|
|
||||||
let b:leiningen_root = root
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
if !exists('b:leiningen_root')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let b:java_root = b:leiningen_root
|
|
||||||
|
|
||||||
setlocal makeprg=lein efm=%+G
|
|
||||||
|
|
||||||
call s:leiningen_connect()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup foreplay_leiningen
|
|
||||||
autocmd!
|
|
||||||
autocmd User ForeplayPreConnect call s:leiningen_connect()
|
|
||||||
autocmd FileType clojure call s:leiningen_init()
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}1
|
|
||||||
|
|
||||||
" vim:set et sw=2:
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import select
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
from StringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
def noop():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def vim_encode(data):
|
||||||
|
if isinstance(data, list):
|
||||||
|
return "[" + ",".join([vim_encode(x) for x in data]) + "]"
|
||||||
|
elif isinstance(data, dict):
|
||||||
|
return "{" + ",".join([vim_encode(x)+":"+vim_encode(y) for x,y in data.items()]) + "}"
|
||||||
|
elif isinstance(data, str):
|
||||||
|
str_list = []
|
||||||
|
for c in data:
|
||||||
|
if (0 <= ord(c) and ord(c) <= 31) or c == '"' or c == "\\":
|
||||||
|
str_list.append("\\%03o" % ord(c))
|
||||||
|
else:
|
||||||
|
str_list.append(c)
|
||||||
|
return '"' + ''.join(str_list) + '"'
|
||||||
|
elif isinstance(data, int):
|
||||||
|
return str(data)
|
||||||
|
else:
|
||||||
|
raise TypeError("can't encode a " + type(data).__name__)
|
||||||
|
|
||||||
|
def bdecode(f, char=None):
|
||||||
|
if char == None:
|
||||||
|
char = f.read(1)
|
||||||
|
if char == 'l':
|
||||||
|
l = []
|
||||||
|
while True:
|
||||||
|
char = f.read(1)
|
||||||
|
if char == 'e':
|
||||||
|
return l
|
||||||
|
l.append(bdecode(f, char))
|
||||||
|
elif char == 'd':
|
||||||
|
d = {}
|
||||||
|
while True:
|
||||||
|
char = f.read(1)
|
||||||
|
if char == 'e':
|
||||||
|
return d
|
||||||
|
key = bdecode(f, char)
|
||||||
|
d[key] = bdecode(f)
|
||||||
|
elif char == 'i':
|
||||||
|
i = ''
|
||||||
|
while True:
|
||||||
|
char = f.read(1)
|
||||||
|
if char == 'e':
|
||||||
|
return int(i)
|
||||||
|
i += char
|
||||||
|
elif char.isdigit():
|
||||||
|
i = int(char)
|
||||||
|
while True:
|
||||||
|
char = f.read(1)
|
||||||
|
if char == ':':
|
||||||
|
return f.read(i)
|
||||||
|
i = 10 * i + int(char)
|
||||||
|
elif char == '':
|
||||||
|
raise EOFError("unexpected end of bencode data")
|
||||||
|
else:
|
||||||
|
raise TypeError("unexpected type "+char+"in bencode data")
|
||||||
|
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
def __init__(self, host, port, custom_poll=noop, keepalive_file=None):
|
||||||
|
self.custom_poll = custom_poll
|
||||||
|
self.keepalive_file = keepalive_file
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.settimeout(8)
|
||||||
|
s.connect((host, int(port)))
|
||||||
|
s.setblocking(1)
|
||||||
|
self.socket = s
|
||||||
|
|
||||||
|
def poll(self):
|
||||||
|
self.custom_poll()
|
||||||
|
if self.keepalive_file and not os.path.exists(self.keepalive_file):
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
return self.socket.close()
|
||||||
|
|
||||||
|
def send(self, payload):
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
self.socket.sendall(bytes(payload, 'UTF-8'))
|
||||||
|
else:
|
||||||
|
self.socket.sendall(payload)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def receive(self, char=None):
|
||||||
|
f = self.socket.makefile()
|
||||||
|
while len(select.select([f], [], [], 0.1)[0]) == 0:
|
||||||
|
self.poll()
|
||||||
|
try:
|
||||||
|
return bdecode(f)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def call(self, payload, terminators, selectors):
|
||||||
|
self.send(payload)
|
||||||
|
responses = []
|
||||||
|
while True:
|
||||||
|
response = self.receive()
|
||||||
|
for key in selectors:
|
||||||
|
if response[key] != selectors[key]:
|
||||||
|
continue
|
||||||
|
responses.append(response)
|
||||||
|
if 'status' in response and set(terminators) & set(response['status']):
|
||||||
|
return responses
|
||||||
|
|
||||||
|
def dispatch(host, port, poll, keepalive, command, *args):
|
||||||
|
conn = Connection(host, port, poll, keepalive)
|
||||||
|
try:
|
||||||
|
return getattr(conn, command)(*args)
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def main(host, port, keepalive, command, *args):
|
||||||
|
try:
|
||||||
|
sys.stdout.write(vim_encode(dispatch(host, port, noop, keepalive, command, *[bdecode(StringIO(arg)) for arg in args])))
|
||||||
|
except Exception:
|
||||||
|
print((sys.exc_info()[1]))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv[1:])
|
Loading…
Reference in New Issue