├── .hgignore ├── .hgtags ├── LICENSE.txt ├── Makefile ├── Makefile.win ├── README.markdown ├── SHADING.markdown ├── after └── ftplugin │ └── clojure │ └── vimpire.vim ├── autoload └── vimpire │ ├── backend.vim │ ├── backend │ ├── complete.vim │ ├── doc.vim │ ├── dynhighlight.vim │ ├── eval.vim │ ├── macro.vim │ ├── nav.vim │ └── test.vim │ ├── buffer.vim │ ├── connection.vim │ ├── edn.vim │ ├── exc.vim │ ├── repl.vim │ ├── sunscreen.vim │ ├── ui.vim │ ├── util.vim │ ├── venom.vim │ ├── window.vim │ └── window │ └── resultwindow.vim ├── deps.edn ├── doc ├── tags └── vimpire.txt ├── fang.clj ├── plugin └── vimpire.vim └── venom ├── complete ├── LICENSE ├── README.org ├── actions.clj └── src │ ├── compliment │ ├── context.clj │ ├── core.clj │ ├── sources.clj │ ├── sources │ │ ├── class_members.clj │ │ ├── keywords.clj │ │ ├── local_bindings.clj │ │ ├── namespaces_and_classes.clj │ │ ├── ns_mappings.clj │ │ ├── resources.clj │ │ └── special_forms.clj │ └── utils.clj │ └── vimpire │ └── complete.clj ├── doc ├── actions.clj └── src │ └── vimpire │ └── doc.clj ├── dynhighlight ├── actions.clj └── src │ └── vimpire │ └── dynhighlight.clj ├── macro ├── actions.clj └── src │ └── vimpire │ └── macro.clj ├── nav ├── actions.clj └── src │ └── vimpire │ └── nav.clj ├── test ├── actions.clj └── src │ └── vimpire │ └── test.clj ├── unrepl └── src │ └── unrepl │ └── blob.clj └── util ├── actions.clj └── src └── vimpire └── util.clj /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | .DS_Store 3 | *-keys.txt 4 | .cpcache 5 | 6 | # External deps 7 | .git* 8 | project.clj 9 | *_test.clj 10 | 11 | # Demos 12 | demos 13 | 14 | # Compliment 15 | venom/complete/.travis.yml 16 | venom/complete/doc 17 | venom/complete/test 18 | -------------------------------------------------------------------------------- /.hgtags: -------------------------------------------------------------------------------- 1 | e8d8859038f059eaa428bc062ba40572baf95959 v1.0.0 2 | 2d2fd57659f1863b71a1da3867df7f4af9645b12 v1.0.2 3 | 44f0257f69bfb51cd400e7e1fceb64daecf61eaa v1.1.0 4 | 192e23be12b48b0d3b07b5167f767ec959ee780f v1.1.1 5 | d1bf8ed221afbf976617909a0ad31495f7146d36 v1.2.0 6 | 71e475ca98d45ae5e29fae03961e5236c626aeab v1.2.1 7 | e0f82e2807b10dd718a6e20726154d16312fb64b v1.3.0 8 | bab4b136685bd12809ec7d755b3bf080cf24cd26 v2.0.0 9 | 1a8128b68aab1d6e4c6b960b61b6684ac7e63b06 v2.1.0 10 | ebc7d51caa8f7aafdc916c4f681112f9be24444b v2.1.1 11 | 3c4c4bc70e35280175aa9b60a2b438572619a692 v2.1.2 12 | d86f147b463088792300dc25a812a4ff43e604d6 v2.2.0 13 | b4e17e56720c6dd6ca8387dee533ad1fc05013d0 v2.3.0 14 | 48f46bd4c412155583d7ac9b01101f8a2d73c24b v2.3.1 15 | 05647d9e7583d7a9b9d0b6ccb4ba42cd6f535452 v2.3.2 16 | d26f07b8b7848a926225915586d3a5787b471069 v2.3.3 17 | 4ecc0e347d34ab9e0a417c6305dcbab94b5f8b3d v2.3.4 18 | 7e7dfc170071dd6adbbf8f7c299372a1751f5dab v2.3.5 19 | 8a4568c55a5e825385d390e3f622e285d02a423c v2.3.6 20 | dca9788a4f703598f24437c7fe7f4fab846ce427 v3.0.0 21 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2008-2017 © Meikel Brandmeyer. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | ============================================================================= 23 | 24 | The content of the file server/vimpire/clojure/data/json.clj is taken 25 | unmodified from the clojure.data.json library. It's licensed as per: 26 | http://opensource.org/licenses/eclipse-1.0.php. 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | venom/unrepl/blob.clj: 2 | clj -m unrepl.make-blob venom/unrepl/blob.clj 3 | 4 | clean: 5 | rm venom/unrepl/blob.clj 6 | 7 | .PHONY: clean 8 | -------------------------------------------------------------------------------- /Makefile.win: -------------------------------------------------------------------------------- 1 | venom/unrepl/blob.clj: 2 | java -cp $(M2REPO)\org\clojure\clojure\1.8.0\clojure-1.8.0.jar;$(M2REPO)\net\cgrand\unrepl\0.1.0-SNAPSHOT\unrepl-0.1.0-SNAPSHOT.jar clojure.main -m unrepl.make-blob venom/unrepl/blob.clj 3 | 4 | clean: 5 | rm venom/unrepl/blob.clj 6 | 7 | .PHONY: clean 8 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Vimpire – a Clojure environment for Vim 2 | 3 | Vimpire is the undead body of VimClojure which returned into the 4 | Clojuresphere. 5 | 6 | ``` 7 | _..._ 8 | .' '. 9 | ; __ __ ; 10 | |/ \ / \| 11 | |\| -- ' -- |/| 12 | |(| \o| |o/ |)| 13 | _\| > |/_ 14 | .-' | ,.___., | '-. 15 | \ ; V'-'V ; / 16 | `\ \ / /` 17 | `\ '-...-' /` 18 | `\ / \ /` 19 | jgs `\\_//` 20 | ``` 21 | 22 | Vimpire is not intended to be an easy to use Clojure IDE, but a plugin 23 | to make life easier for people already familiar with Vim. So you should 24 | be familiar with Vim and/or Java. Eg. Vimpire won't help you in any way 25 | to set up a correct classpath! This is the responsibility of the build 26 | system of the given project. So before using the dynamic server make 27 | yourself comfortable with Clojure, the JVM and Vim. 28 | 29 | # Requirements 30 | 31 | The plugin is ready to be used as a package. Regarding package managers 32 | you'll have to figure things out yourself. 33 | 34 | It uses channels, so you'll need a Vim 8 or later. 35 | 36 | On the Clojure side, you have to provide a socket repl. That means 37 | either Clojure 1.8 or you'll have to backport the functionality yourself. 38 | 39 | # Here be Dragons 40 | 41 | After starting Vim and ideally before editing Clojure code you have to 42 | start the backend server connection. 43 | 44 | :VimpireBite : 45 | 46 | No further setup is required. The backend part of Vimpire is completely 47 | zero config and self contained. Also, there is no conflict of several 48 | Vimpire instances connecting to the same backend server. They share code 49 | in case of being of the same version. Otherwise they are completely 50 | separated. 51 | 52 | When you got fangs, the connection is ready. Be aware that the first 53 | connect is slow, because things have to be prepared like starting the 54 | side-loader and injecting the venom. Don't bother me with trivial 55 | nonsense like “start-up time.” 56 | 57 | # Demos 58 | 59 | I created a small set of short demos: 60 | 61 | * [Stalking the prey](https://kotka.de/vimpire/vimpire_bite.webm) 62 | * [Dynamic highlighting](https://kotka.de/vimpire/vimpire_dynamic_highlighting.webm) 63 | * [Documentation lookup](https://kotka.de/vimpire/vimpire_doclookup.webm) 64 | * [Goto and show source](https://kotka.de/vimpire/vimpire_source_operators.webm) 65 | * [Eval operators](https://kotka.de/vimpire/vimpire_eval.webm) 66 | * [Macro expansion](https://kotka.de/vimpire/vimpire_macro_expansion.webm) 67 | * [Async completion](https://kotka.de/vimpire/vimpire_completion.webm) 68 | * [The repl](https://kotka.de/vimpire/vimpire_repl.webm) 69 | 70 | # FAQs 71 | 72 | - **Hey, I'd like nifty feature X to be supported!**
73 | Have fun implementing it. I'll support in providing extension points to 74 | reuse Vimpire's infrastructure, but I don't care about including it in 75 | Vimpire itself. 76 | 77 | - **Hey, why are there no default bindings set up?**
78 | Obviously the opinions are too different on this one. So pick your own 79 | style. 80 | 81 | - **Hey, why is my repl messed up when I delete the prompt?**
82 | Because it's a buffer and not a terminal. 83 | 84 | - **Hey, why is the namespace loaded when I open a file? I got toplevel commands!**
85 | Don't have toplevel commands. Put them in a `(defn main …)` and use 86 | `clj -m` to run the script. Starting the rockets on the toplevel is bad 87 | style. There are exceptions. I don't optimise for exceptions. 88 | 89 | - **Hey, your plugin sucks because X!**
90 | Then don't use it. 91 | 92 | # Sources 93 | 94 | The ASCII art was taken from [here](http://www.chris.com/ascii/joan/www.geocities.com/SoHo/7373/haloween.html). 95 | Take care. It tries to play a midi. 96 | 97 | ``` 98 | -- 99 | Meikel Branmdeyer 100 | Erlensee, 2018 101 | ``` 102 | -------------------------------------------------------------------------------- /SHADING.markdown: -------------------------------------------------------------------------------- 1 | # SHAding 2 | 3 | Vimpire (and any other tooling for that matter) has to be very careful. 4 | We connect to the user's precious process. So we must take care to 5 | minimise the effects on the other side. Due to clojure's limited 6 | abilities in term of isolation, we can easily mess up eg. a common 7 | dependency which we may share with the user code. But even if we take 8 | special care to isolate ourselves from the other side, we still may bang 9 | our own heads together, eg. when several people connect with (possibly 10 | different versions of) vimpire (or any other tooling for that matter) to 11 | the same precious user process. 12 | 13 | As [Christophe Grand outlined](http://clj-me.cgrand.net/2018/03/09/content-defined-dependency-shading/) 14 | we can try to minimise our impact to the outer world and protect 15 | ourselves from the sun by putting on proper SHAdes. However that only 16 | moves the battle ground from the backend process to the client. 17 | 18 | The royal House of Vim, although introverted, is not xenophobic. It 19 | welcomes other children of the night, as long as they mind their own 20 | business. 21 | 22 | Therefore we must also take care that all families adhere to the House's 23 | rules. Only the high lords of each family may be approached. Other 24 | members are to be strictly left alone. Even the high House itself won't 25 | take any interest in the family members. However in exchange they have 26 | to apply proper sunscreen and wear their shades to protect ourselves and 27 | the precious user process. Remember that high stress taints the 28 | delicious values, does it not? That would be a shame. 29 | 30 | Each family has to declare their belongings as well as the names of its 31 | high lord's which will be the public face of the family. Obviously each 32 | high lord may only speak for one family. Any attempt of a family to hand 33 | on to the high lord of another family will be interdicted by the Royal 34 | Guards of the House of Vim. 35 | 36 | ```vim 37 | call vimpire#venom#Register(vimpire#sunscreen#Apply( 38 | \ "vimpire-complete", 39 | \ ["venom/complete"], 40 | \ ["vimpire.complete"], 41 | \ ["compliment"], 42 | \ "venom/complete/actions.clj")) 43 | ``` 44 | 45 | This is an example of such a declaration by the “vimpire-complete” family. 46 | The high lord `vimpire.complete` will be known through out the House of Vim 47 | and other families may call upon him. The `compliment` branch of the 48 | family will live in the House of Vim as well, however it shall be left 49 | alone as if it was not there at all. To ensure this, the family is 50 | assigned a dedicated room and every element is transferred too the new 51 | space. 52 | 53 | The family stores its belongings in `venom/complete`. The maids of the 54 | royal House take care to clearly identify the ownership with a SHA256 55 | checksum. They also carefully apply Base64 so that the family's 56 | belongings are protected against the elements during transit. 57 | 58 | That all said, it must be stated that the high lord's of each family 59 | have to live up to a high responsibility. They are the public face of 60 | the family and have to translate (if necessary) the language between the 61 | outer world and the family room itself. 62 | 63 | ## Summary 64 | 65 | So please let me summarise the key rules of the royal House of Vim. 66 | 67 | We create a SHA256 over all the backend code for a vimpire extension. 68 | From this checksum we derive a leading namespace part, which is 69 | prepended to the extension's namespace hierarchy. This applies to 70 | symbols and keywords as well as tagged literals. We do not assume any 71 | cooperation from any 3rd party dependency. We must make the move as 72 | complete as possible. That means also prepending the id marker to things 73 | the could look like the name string of a resource. 74 | 75 | These heuristics may fail. Be prepared for that! 76 | 77 | Possible namespaced keyword arguments etc. have to translated by the entry 78 | points if necessary. Also leaking out “hard” content like records may 79 | cause problems and should be avoided. This includes also namespaces you 80 | do not own. If you included a 3rd party dependency keep it to yourself 81 | and do not expose it to the other extensions. 82 | 83 | The namespaces named explicitly as exposed will be visible to other 84 | extensions. Eg. the “vimpire-complete” extension uses the 85 | `vimpire.util` namespace from the central “vimpire” shading unit. 86 | 87 | By hashing the content with SHA256, we can identify identical code and 88 | share the relevant namespace by different vimpire connections to the 89 | same victim process. 90 | 91 | While this all might sound complicated, please keep in mind: This also 92 | protects us from the barbarous demons from .el. Company we'd rather 93 | avoid, do we not? 94 | -------------------------------------------------------------------------------- /after/ftplugin/clojure/vimpire.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | let s:cpo_save = &cpo 24 | set cpo&vim 25 | 26 | try 27 | call vimpire#backend#InitBuffer() 28 | catch /.*/ 29 | " We swallow a failure here. It means most likely that the 30 | " server is not running. 31 | echohl WarningMsg 32 | echomsg v:exception 33 | echohl None 34 | endtry 35 | 36 | let &cpo = s:cpo_save 37 | -------------------------------------------------------------------------------- /autoload/vimpire/backend.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | if !exists("g:vimpire_dynamic_highlighting") 28 | let g:vimpire_dynamic_highlighting = v:true 29 | endif 30 | 31 | function! s:InitBufferCallback(buffer, nspace) 32 | call setbufvar(a:buffer, "vimpire_namespace", a:nspace) 33 | if exists("g:vimpire_dynamic_highlighting") 34 | \ && g:vimpire_dynamic_highlighting 35 | call vimpire#backend#dynhighlight#DynamicHighlighting() 36 | endif 37 | endfunction 38 | 39 | function! vimpire#backend#InitBuffer(...) 40 | if exists("b:vimpire_namespace") 41 | return 42 | endif 43 | 44 | if !&previewwindow 45 | " Get the namespace of the buffer. 46 | try 47 | let buffer = bufnr("%") 48 | let content = join(getbufline(buffer, 1, line("$")), "\n") 49 | let server = vimpire#connection#ForBuffer() 50 | call vimpire#connection#Action( 51 | \ server, 52 | \ ":vimpire/namespace-of-file", 53 | \ {":content": content}, 54 | \ {"eval": function("s:InitBufferCallback", [buffer])}) 55 | catch /Vimpire: No connection found/ 56 | " Do nothing. Fail silently in this case. 57 | catch /.*/ 58 | if a:000 == [] 59 | call vimpire#ui#ReportError( 60 | \ "Could not determine the Namespace of the file.\n\n" 61 | \ . "This might have different reasons. Please check, that the server\n" 62 | \ . "is running and that the file does not contain syntax errors. The\n" 63 | \ . "interactive features will not be enabled.\n" 64 | \ . "\nReason:\n" . v:exception) 65 | endif 66 | endtry 67 | endif 68 | endfunction 69 | 70 | " Epilog 71 | let &cpo = s:save_cpo 72 | -------------------------------------------------------------------------------- /autoload/vimpire/backend/complete.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | " Async Completion 28 | function! vimpire#backend#complete#AsyncComplete(line, col, cont) 29 | let prefix = matchstr(a:line, '\(\w\|[/_*.<>=+-]\)\+$') 30 | if prefix == "" 31 | return 32 | endif 33 | 34 | let start = a:col - strlen(prefix) 35 | 36 | let server = vimpire#connection#ForBuffer() 37 | call vimpire#connection#Action( 38 | \ server, 39 | \ ":vimpire/complete", 40 | \ {":nspace": b:vimpire_namespace, 41 | \ ":prefix": prefix}, 42 | \ {"eval": function(a:cont, [start])}) 43 | endfunction 44 | 45 | " Epilog 46 | let &cpo = s:save_cpo 47 | -------------------------------------------------------------------------------- /autoload/vimpire/backend/doc.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | function! vimpire#backend#doc#DocLookup(word) 28 | if a:word == "" 29 | return 30 | endif 31 | 32 | let server = vimpire#connection#ForBuffer() 33 | call vimpire#connection#Action( 34 | \ server, 35 | \ ":vimpire/doc-lookup", 36 | \ {":nspace": b:vimpire_namespace, ":sym": a:word}, 37 | \ {"eval": function("vimpire#ui#ShowResult")}) 38 | endfunction 39 | 40 | function! vimpire#backend#doc#FindDoc() 41 | let pattern = input("Pattern to look for: ") 42 | 43 | let server = vimpire#connection#ForBuffer() 44 | call vimpire#connection#Action( 45 | \ server, 46 | \ ":vimpire/find-doc", 47 | \ {":query": pattern}, 48 | \ {"eval": function("vimpire#ui#ShowResult")}) 49 | endfunction 50 | 51 | let s:DefaultJavadocPaths = { 52 | \ "java" : "https://docs.oracle.com/javase/8/docs/api/", 53 | \ "org/apache/commons/beanutils" : "http://commons.apache.org/beanutils/api/", 54 | \ "org/apache/commons/chain" : "http://commons.apache.org/chain/api-release/", 55 | \ "org/apache/commons/cli" : "http://commons.apache.org/cli/api-release/", 56 | \ "org/apache/commons/codec" : "http://commons.apache.org/codec/api-release/", 57 | \ "org/apache/commons/collections" : "http://commons.apache.org/collections/api-release/", 58 | \ "org/apache/commons/logging" : "http://commons.apache.org/logging/apidocs/", 59 | \ "org/apache/commons/mail" : "http://commons.apache.org/email/api-release/", 60 | \ "org/apache/commons/io" : "http://commons.apache.org/io/api-release/" 61 | \ } 62 | 63 | if !exists("g:vimpire_javadoc_path_map") 64 | let g:vimpire_javadoc_path_map = {} 65 | endif 66 | 67 | call extend(g:vimpire_javadoc_path_map, s:DefaultJavadocPaths, "keep") 68 | 69 | if !exists("g:vimpire_browser") 70 | if has("win32") || has("win64") 71 | let g:vimpire_browser = "start" 72 | elseif has("mac") 73 | let g:vimpire_browser = "open" 74 | else 75 | " some freedesktop thing, whatever, issue #67 76 | let g:vimpire_browser = "xdg-open" 77 | endif 78 | endif 79 | 80 | function! s:JavadocLookupCallback(path) 81 | let match = "" 82 | for pattern in keys(g:vimpire_javadoc_path_map) 83 | if a:path =~ "^" . pattern && len(match) < len(pattern) 84 | let match = pattern 85 | endif 86 | endfor 87 | 88 | if match == "" 89 | call vimpire#ui#ReportError("Vimpire: " 90 | \ . "No matching Javadoc URL found for " . a:path) 91 | return 92 | endif 93 | 94 | let url = g:vimpire_javadoc_path_map[match] . a:path 95 | call system(g:vimpire_browser . " " . url) 96 | endfunction 97 | 98 | function! vimpire#backend#doc#JavadocLookup(word) 99 | let word = substitute(a:word, "\\.$", "", "") 100 | 101 | let server = vimpire#connection#ForBuffer() 102 | call vimpire#connection#Action( 103 | \ server, 104 | \ ":vimpire/javadoc-path", 105 | \ {":nspace": b:vimpire_namespace, ":sym": word}, 106 | \ {"eval": function("s:JavadocLookupCallback")}) 107 | endfunction 108 | 109 | function! vimpire#backend#doc#SourceLookup(word) 110 | let nspace = b:vimpire_namespace 111 | 112 | let server = vimpire#connection#ForBuffer() 113 | call vimpire#connection#Action( 114 | \ server, 115 | \ ":vimpire/source-lookup", 116 | \ {":nspace": b:vimpire_namespace, ":sym": a:word}, 117 | \ {"eval": { val -> 118 | \ vimpire#ui#ShowClojureResult(val, nspace) 119 | \ }}) 120 | endfunction 121 | 122 | " Epilog 123 | let &cpo = s:save_cpo 124 | -------------------------------------------------------------------------------- /autoload/vimpire/backend/dynhighlight.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | function! s:DynamicHighlightingCallback(this, nspace, highlights) 28 | let a:this.dynamicHighlightingCache[a:nspace] = a:highlights 29 | 30 | for [category, words] in items(a:highlights) 31 | if len(words) > 0 32 | execute "syntax keyword clojure" . category . " " . join(words, " ") 33 | endif 34 | endfor 35 | endfunction 36 | 37 | function! vimpire#backend#dynhighlight#DynamicHighlighting() 38 | let server = vimpire#connection#ForBuffer() 39 | let nspace = b:vimpire_namespace 40 | 41 | if !has_key(server, "dynamicHighlightingCache") 42 | let server.dynamicHighlightingCache = {} 43 | endif 44 | 45 | if has_key(server.dynamicHighlightingCache, nspace) 46 | return server.dynamicHighlightingCache[nspace] 47 | endif 48 | 49 | call vimpire#connection#Action( 50 | \ server, 51 | \ ":vimpire/dynamic-highlighting", 52 | \ {":nspace": b:vimpire_namespace}, 53 | \ {"eval": 54 | \ function("s:DynamicHighlightingCallback", [server, nspace])}) 55 | endfunction 56 | 57 | " Epilog 58 | let &cpo = s:save_cpo 59 | -------------------------------------------------------------------------------- /autoload/vimpire/backend/eval.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | " Evaluators 28 | function! s:ShowClojureResultCallback(nspace, results) 29 | let text = [] 30 | for unit in a:results 31 | if len(unit.output) > 0 32 | for [event_, output] in unit.output 33 | call extend(text, map(output, '"; " . v:val')) 34 | endfor 35 | endif 36 | if unit.result[0] == "exception" 37 | let ex = vimpire#exc#ReadResponse(unit.result[1]) 38 | let toPrint = vimpire#exc#PPrintException(ex) 39 | call extend(text, split(toPrint, '\r\?\n')) 40 | else 41 | call extend(text, split(vimpire#edn#Write(unit.result[1]), '\r\?\n')) 42 | endif 43 | endfor 44 | 45 | call vimpire#ui#ShowClojureResult(text, a:nspace) 46 | endfunction 47 | 48 | function! vimpire#backend#eval#RequireFile(all) 49 | let nspace = b:vimpire_namespace 50 | let cmd = vimpire#edn#List( 51 | \ [vimpire#edn#Symbol("clojure.core", "require"), 52 | \ vimpire#edn#Keyword(a:all ? "reload-all" : "reload"), 53 | \ vimpire#edn#Keyword("verbose"), 54 | \ vimpire#edn#List( 55 | \ [vimpire#edn#Symbol("clojure.core", "symbol"), nspace])]) 56 | 57 | let server = vimpire#connection#ForBuffer() 58 | 59 | call vimpire#connection#Eval(server, 60 | \ vimpire#edn#Write(cmd), 61 | \ {"result": 62 | \ function("s:ShowClojureResultCallback", 63 | \ [nspace]) 64 | \ }) 65 | endfunction 66 | 67 | function! vimpire#backend#eval#EvalWithPosition(server, fname, line, column, 68 | \ nspace, code, handlers) 69 | let nspace = a:server.namespace 70 | 71 | call vimpire#connection#Eval(a:server, 72 | \ vimpire#edn#Write(vimpire#edn#List( 73 | \ [vimpire#edn#Symbol("clojure.core", "in-ns"), 74 | \ vimpire#edn#List( 75 | \ [vimpire#edn#Symbol("clojure.core", "symbol"), 76 | \ a:nspace])])), 77 | \ {}) 78 | call vimpire#connection#Action( 79 | \ a:server, 80 | \ ":set-source", 81 | \ {":unrepl/sourcename": a:fname, 82 | \ ":unrepl/line": a:line - 1, 83 | \ ":unrepl/column": a:column}, 84 | \ {}) 85 | 86 | call vimpire#connection#Eval(a:server, a:code, a:handlers) 87 | 88 | call vimpire#connection#Eval(a:server, 89 | \ vimpire#edn#Write(vimpire#edn#List( 90 | \ [vimpire#edn#Symbol("clojure.core", "in-ns"), 91 | \ vimpire#edn#List( 92 | \ [vimpire#edn#Symbol("clojure.core", "symbol"), 93 | \ nspace])])), 94 | \ {}) 95 | call vimpire#connection#Action( 96 | \ a:server, 97 | \ ":set-source", 98 | \ {":unrepl/sourcename": "Tooling Repl", 99 | \ ":unrepl/line": 1, 100 | \ ":unrepl/column": 1}, 101 | \ {}) 102 | endfunction 103 | 104 | function! s:EvalOperatorWorker(type) 105 | let server = vimpire#connection#ForBuffer() 106 | 107 | let nspace = b:vimpire_namespace 108 | let file = vimpire#util#BufferName() 109 | 110 | let [ line, col, exp ] = vimpire#util#WithSavedPosition( 111 | \ function("vimpire#util#OpTextExtractor", [a:type])) 112 | 113 | call vimpire#connection#Action( 114 | \ server, 115 | \ ":vimpire/check-syntax", 116 | \ {":nspace": nspace, ":content": exp}, 117 | \ {"eval": function("s:EvalOperatorSyntaxChecked", 118 | \ [server, file, line, col, nspace, exp])}) 119 | endfunction 120 | 121 | function! s:EvalOperatorSyntaxChecked(server, file, line, col, nspace, 122 | \ exp, validSyntax) 123 | if a:validSyntax 124 | call vimpire#backend#eval#EvalWithPosition(a:server, 125 | \ a:file, a:line, a:col, a:nspace, 126 | \ a:exp, 127 | \ {"result": 128 | \ function("s:ShowClojureResultCallback", 129 | \ [a:nspace]) 130 | \ }) 131 | else 132 | call vimpire#ui#ReportError("Syntax check failed:\n\n" . a:exp) 133 | endif 134 | endfunction 135 | 136 | " We have to inline this, operatorfunc cannot take functions. 137 | function! vimpire#backend#eval#EvalOperator(type) 138 | call vimpire#ui#ProtectedPlug( 139 | \ function("vimpire#ui#CommandPlug"), 140 | \ function("s:EvalOperatorWorker"), 141 | \ a:type) 142 | endfunction 143 | 144 | " Epilog 145 | let &cpo = s:save_cpo 146 | -------------------------------------------------------------------------------- /autoload/vimpire/backend/macro.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | function! s:MacroExpandWorker(type, firstOnly) 28 | let server = vimpire#connection#ForBuffer() 29 | let nspace = b:vimpire_namespace 30 | 31 | let [ line, col, exp ] = vimpire#util#WithSavedPosition( 32 | \ function("vimpire#util#OpTextExtractor", [a:type])) 33 | 34 | call vimpire#connection#Action( 35 | \ server, 36 | \ ":vimpire/macro-expand", 37 | \ {":nspace": nspace, 38 | \ ":one?": (a:firstOnly ? v:true : v:false), 39 | \ ":form": exp}, 40 | \ {"eval": { val -> 41 | \ vimpire#ui#ShowClojureResult(val, nspace) 42 | \ }}) 43 | endfunction 44 | 45 | function! vimpire#backend#macro#MacroExpand(type) 46 | call vimpire#ui#ProtectedPlug( 47 | \ function("vimpire#ui#CommandPlug"), 48 | \ function("s:MacroExpandWorker"), 49 | \ a:type, v:false) 50 | endfunction 51 | 52 | function! vimpire#backend#macro#MacroExpand1(type) 53 | call vimpire#ui#ProtectedPlug( 54 | \ function("vimpire#ui#CommandPlug"), 55 | \ function("s:MacroExpandWorker"), 56 | \ a:type, v:true) 57 | endfunction 58 | 59 | " Epilog 60 | let &cpo = s:save_cpo 61 | -------------------------------------------------------------------------------- /autoload/vimpire/backend/nav.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | function! s:GotoSourceCallback(pos) 28 | let pos = vimpire#edn#Simplify(a:pos) 29 | 30 | if !filereadable(pos[":file"]) 31 | let file = globpath(&path, pos[":file"]) 32 | if file == "" 33 | call vimpire#ui#ReportError("Vimpire: " 34 | \ . pos[":file"] . " not found in 'path'") 35 | return 36 | endif 37 | let pos[":file"] = file 38 | endif 39 | 40 | execute "edit " . pos[":file"] 41 | execute pos[":line"] 42 | endfunction 43 | 44 | function! vimpire#backend#nav#GotoSource(word) 45 | let server = vimpire#connection#ForBuffer() 46 | call vimpire#connection#Action( 47 | \ server, 48 | \ ":vimpire/source-location", 49 | \ {":nspace": b:vimpire_namespace, ":sym": a:word}, 50 | \ {"eval": function("s:GotoSourceCallback")}) 51 | endfunction 52 | 53 | " Epilog 54 | let &cpo = s:save_cpo 55 | -------------------------------------------------------------------------------- /autoload/vimpire/backend/test.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | function! vimpire#backend#test#RunTests(all) 28 | let nspace = b:vimpire_namespace 29 | 30 | let server = vimpire#connection#ForBuffer() 31 | call vimpire#connection#Action( 32 | \ server, 33 | \ ":vimpire/run-tests", 34 | \ {":nspace": b:vimpire_namespace, 35 | \ ":all?": (a:all ? v:true : v:false)}, 36 | \ {"eval": { val -> vimpire#ui#ShowResult(val) }}) 37 | endfunction 38 | 39 | " Epilog 40 | let &cpo = s:save_cpo 41 | -------------------------------------------------------------------------------- /autoload/vimpire/buffer.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | let s:save_cpo = &cpo 24 | set cpo&vim 25 | 26 | " A Buffer... 27 | let s:BufferNr = 0 28 | 29 | function! vimpire#buffer#New() 30 | let this = {} 31 | 32 | let nr = s:BufferNr 33 | let bufname = printf("vimpire_buffer_%06d", s:BufferNr) 34 | let s:BufferNr += 1 35 | 36 | execute "badd" bufname 37 | execute "buffer!" bufname 38 | 39 | let this.bufnr = bufnr("%") 40 | let b:vimpire_buffer = this 41 | 42 | return this 43 | endfunction 44 | 45 | function! vimpire#buffer#ShowText(this, text) 46 | call vimpire#buffer#GoHere(a:this) 47 | 48 | if type(a:text) == type("") 49 | " XXX: Opening the box of the pandora. 50 | " 2012-01-09: Adding Carriage Returns here. 51 | let text = split(a:text, '\r\?\n') 52 | else 53 | let text = a:text 54 | endif 55 | call append(line("$"), text) 56 | endfunction 57 | 58 | function! vimpire#buffer#Clear(this) 59 | call vimpire#buffer#GoHere(a:this) 60 | 61 | 1 62 | normal! "_dG 63 | endfunction 64 | 65 | function! vimpire#buffer#GoHere(this) 66 | if bufnr("%") != a:this.bufnr 67 | execute "buffer!" a:this.bufnr 68 | endif 69 | endfunction 70 | 71 | function! vimpire#buffer#Close(this) 72 | execute "bdelete!" a:this.bufnr 73 | endfunction 74 | 75 | function! vimpire#buffer#NewResultBuffer() 76 | let this = vimpire#buffer#New() 77 | 78 | setlocal noswapfile 79 | setlocal buftype=nofile 80 | setlocal bufhidden=wipe 81 | 82 | if !hasmapto("(vimpire_close_result_buffer)", "n") 83 | nmap q (vimpire_close_result_buffer) 84 | endif 85 | 86 | call vimpire#buffer#Clear(this) 87 | 88 | return this 89 | endfunction 90 | 91 | function! vimpire#buffer#NewClojureResultBuffer() 92 | let this = vimpire#buffer#NewResultBuffer() 93 | 94 | if a:0 == 1 95 | let b:vimpire_namespace = a:1 96 | else 97 | let b:vimpire_namespace = "user" 98 | endif 99 | set filetype=vimpire.clojure 100 | 101 | return this 102 | endfunction 103 | 104 | " Epilog 105 | let &cpo = s:save_cpo 106 | -------------------------------------------------------------------------------- /autoload/vimpire/connection.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | if !has("nvim") 28 | function! vimpire#connection#Connect(this, initCallback) 29 | return ch_open(a:this.server, 30 | \ {"mode": "raw", 31 | \ "callback": { ch_, msg -> a:initCallback(msg)}}) 32 | endfunction 33 | 34 | function! vimpire#connection#Disconnect(this) 35 | call ch_close(a:this) 36 | endfunction 37 | 38 | function! vimpire#connection#Send(this, code) 39 | call ch_sendraw(a:this, a:code . "\n") 40 | endfunction 41 | else 42 | function! vimpire#connection#Connect(this, initCallback) 43 | return sockconnect("tcp", a:this.server, 44 | \ {"on_data": 45 | \ vimpire#connection#Dispatch(a:this, a:initCallback)}) 46 | endfunction 47 | 48 | function! vimpire#connection#Disconnect(this) 49 | call chanclose(a:this) 50 | endfunction 51 | 52 | function! vimpire#connection#Send(this, code) 53 | call chansend(a:this, a:code . "\n") 54 | endfunction 55 | 56 | function! vimpire#connection#DoDispatch(this, initCallback, msg) 57 | let msg = join(a:msg, "\n") 58 | 59 | if a:this.state != "raw" 60 | call vimpire#connection#HandleResponse(a:this, msg) 61 | else 62 | call a:initCallback(msg) 63 | endif 64 | endfunction 65 | 66 | function! vimpire#connection#Dispatch(this, initCallback) 67 | return { t_, msg, e_ -> 68 | \ vimpire#connection#DoDispatch(a:this, a:initCallback, msg) 69 | \ } 70 | endfunction 71 | endif 72 | 73 | function! s:Ready() 74 | let fangs = [ 75 | \ ' __ __', 76 | \ ' .-'' "." ''-.', 77 | \ ' .'' ___,___ ''.', 78 | \ ' ;__.-; | | | ;-.__;', 79 | \ ' | \ | | | | | / |', 80 | \ ' \ \/`"`"`"`"`\/ /', 81 | \ ' \_.-,-,-,-,-._/', 82 | \ ' \`-:_|_|_:-''/', 83 | \ 'jgs ''. .''', 84 | \ ' `''---''`', 85 | \ ] 86 | 87 | let padding = repeat(" ", (winwidth(0) - 20) / 2) 88 | 89 | return join( 90 | \ map( 91 | \ copy(fangs), 92 | \ { idx_, line -> padding . line }), 93 | \ "\n") 94 | \ . "\n" 95 | endfunction 96 | 97 | let s:Location = expand(":p:h:h:h") . "/" 98 | 99 | if !exists("s:Registry") 100 | let s:Registry = {} 101 | endif 102 | 103 | function! vimpire#connection#RegisterPrefix(prefix, server) 104 | if !has_key(s:Registry, a:prefix) 105 | let s:Registry[a:prefix] = vimpire#connection#New(a:server) 106 | call vimpire#connection#Start(s:Registry[a:prefix]) 107 | endif 108 | endfunction 109 | 110 | function! vimpire#connection#ForBuffer() 111 | let path = expand("%:p") 112 | 113 | if path == "" 114 | let path = getcwd() 115 | endif 116 | 117 | for [ candidate, conn ] in items(s:Registry) 118 | if strpart(path, 0, len(candidate)) == candidate 119 | return conn 120 | endif 121 | endfor 122 | 123 | throw "Vimpire: No connection found" 124 | endfunction 125 | 126 | let s:DefaultHandlers = { 127 | \ ":read": 128 | \ { t, r -> vimpire#connection#HandleEvent(t, "read", r) }, 129 | \ ":start-eval": 130 | \ { t, r -> vimpire#connection#HandleEvent(t, "startEval", r) }, 131 | \ ":eval": 132 | \ { t, r -> vimpire#connection#HandleEndOfEval(t, "eval", r) }, 133 | \ ":prompt": 134 | \ { t, r -> vimpire#connection#HandlePrompt(t, r) }, 135 | \ ":out": 136 | \ { t, r -> vimpire#connection#HandleOutput(t, "out", r) }, 137 | \ ":err": 138 | \ { t, r -> vimpire#connection#HandleOutput(t, "err", r) }, 139 | \ ":log": 140 | \ { t, r -> vimpire#connection#HandleEvent(t, "log", r) }, 141 | \ ":exception": 142 | \ { t, r -> vimpire#connection#HandleEndOfEval(t, "exception", r) } 143 | \ } 144 | 145 | function! vimpire#connection#New(serverOrSibling) 146 | let this = {} 147 | 148 | let this.unrepled = v:false 149 | let this.namespace = "user" 150 | 151 | if type(a:serverOrSibling) == v:t_dict 152 | let this.server = a:serverOrSibling.server 153 | let this.sibling = a:serverOrSibling 154 | else 155 | let this.server = a:serverOrSibling 156 | let this.sibling = g:vimpire#Nil 157 | endif 158 | 159 | let this.equeue = [] 160 | let this.offset = 0 161 | let this.state = "raw" 162 | let this.queue = "" 163 | let this.handlers = s:DefaultHandlers 164 | let this.venom = vimpire#venom#Inject() 165 | let this.evalUnit = { "id": 0, "output": [] } 166 | 167 | return this 168 | endfunction 169 | 170 | function! vimpire#connection#Start(this) 171 | let a:this.channel = vimpire#connection#Connect( 172 | \ a:this, 173 | \ { msg -> vimpire#connection#UpgradeRepl(a:this, msg)}) 174 | endfunction 175 | 176 | function! vimpire#connection#UpgradeRepl(this, msg) 177 | let a:this.queue .= a:msg 178 | 179 | if a:this.queue =~ '\[:unrepl.upgrade/failed\]' 180 | call vimpire#connection#Disconnect(a:this.channel) 181 | throw "Vimpire: Couldn't upgrade to unrepl." 182 | elseif a:this.queue =~ 'user=> ' && !a:this.unrepled 183 | let a:this.unrepled = v:true 184 | let a:this.queue = "" 185 | 186 | let starter = "" 187 | if a:this.sibling isnot g:vimpire#Nil 188 | let starter = vimpire#edn#Write( 189 | \ vimpire#connection#ExpandAction( 190 | \ a:this.sibling.actions[":start-aux"], 191 | \ {})) 192 | else 193 | let starter = a:this.venom.blob . "\n" . a:this.venom.actions 194 | endif 195 | 196 | call vimpire#connection#Send(a:this.channel, starter) 197 | elseif a:this.queue =~ '\[:unrepl/hello' 198 | let a:this.state = "greeted" 199 | 200 | " Get rid of any possible remnants of a prompt or the like. 201 | let a:this.queue = substitute(a:this.queue, "^.*\[:unrepl/hello", "[:unrepl/hello", "") 202 | 203 | if !has("nvim") 204 | call ch_setoptions(a:this.channel, 205 | \ { "callback": { ch, msg -> 206 | \ vimpire#connection#HandleResponse(a:this, msg) 207 | \ }}) 208 | endif 209 | 210 | call vimpire#connection#HandleResponse(a:this, "") 211 | endif 212 | endfunction 213 | 214 | function! vimpire#connection#HandleResponse(this, msg) 215 | let a:this.queue .= a:msg 216 | 217 | while len(a:this.queue) > 0 218 | " Sideloader not ready, yet. 219 | if a:this.state == "hello" 220 | return 221 | endif 222 | 223 | try 224 | let [ response, nextQueue ] = vimpire#edn#Read(a:this.queue) 225 | catch /EOF/ 226 | let response = g:vimpire#Nil 227 | let nextQueue = a:this.queue 228 | endtry 229 | 230 | let a:this.queue = nextQueue 231 | 232 | if response is g:vimpire#Nil 233 | break 234 | endif 235 | 236 | let tag = vimpire#edn#Simplify(response[0]) 237 | 238 | " :unrepl/hello needs special treatment. If it's the first connection 239 | " for a prefix, ie. there is no sibling, we have to also fire up a 240 | " separate sideloader connection for the tooling backend. 241 | if tag == ":unrepl/hello" 242 | call vimpire#connection#HandleHello(a:this, response) 243 | endif 244 | 245 | if has_key(a:this.handlers, tag) 246 | call call(a:this.handlers[tag], [a:this, response]) 247 | endif 248 | endwhile 249 | endfunction 250 | 251 | function! vimpire#connection#HandleHello(this, response) 252 | let payload = vimpire#edn#SimplifyMap(a:response[1]) 253 | 254 | if has_key(payload, ":actions") 255 | let a:this.actions = vimpire#edn#SimplifyMap(payload[":actions"]) 256 | else 257 | let a:this.actions = {} 258 | endif 259 | 260 | if has_key(payload, ":session") 261 | let a:this.session = payload[":session"] 262 | endif 263 | 264 | if has_key(payload, ":about") 265 | let a:this.about = payload[":about"] 266 | endif 267 | 268 | if a:this.sibling is g:vimpire#Nil 269 | let a:this.state = "hello" 270 | 271 | " This is the tooling repl for this backend server. We have to setup 272 | " the sideloader to get at the tooling venom. Also the tooling repl 273 | " should not use elisions. 274 | let a:this.sideloader = vimpire#connection#NewSideloader(a:this) 275 | 276 | " Disable elisions for tooling repl. 277 | let longMaxValue = vimpire#edn#Symbol("Long/MAX_VALUE") 278 | let action = vimpire#connection#Action( 279 | \ a:this, 280 | \ ":print-limits", 281 | \ {":unrepl/string-length": longMaxValue, 282 | \ ":unrepl/coll-length": longMaxValue, 283 | \ ":unrepl/nesting-depth": longMaxValue}) 284 | 285 | " Require the venom namespaces. 286 | call vimpire#connection#Eval(a:this, a:this.venom.init, {}) 287 | 288 | " Set the name of the tooling repl. 289 | let action = vimpire#connection#Action( 290 | \ a:this, 291 | \ ":set-source", 292 | \ {":unrepl/sourcename": "Tooling Repl", 293 | \ ":unrepl/line": 1, 294 | \ ":unrepl/column": 1}) 295 | 296 | " Await the previous commands to finish. 297 | call vimpire#connection#Eval( 298 | \ a:this, 299 | \ "true", 300 | \ { "eval": { val_ -> vimpire#ui#ShowResult(s:Ready()) }}) 301 | else 302 | let a:this.state = "awaiting-prompt" 303 | endif 304 | endfunction 305 | 306 | function! vimpire#connection#HandleEvent(this, event, response) 307 | if a:this.state != "evaling" 308 | return 309 | endif 310 | 311 | if has_key(a:this.equeue[0].callbacks, a:event) 312 | call a:this.equeue[0].callbacks[a:event](a:response[1]) 313 | endif 314 | endfunction 315 | 316 | function! vimpire#connection#HandleEndOfEval(this, event, response) 317 | let a:this.evalUnit.result = [a:event, a:response[1]] 318 | 319 | if a:this.state == "evaling" 320 | if has_key(a:this.equeue[0].callbacks, a:event) 321 | call a:this.equeue[0].callbacks[a:event](a:response[1]) 322 | endif 323 | else 324 | if a:event == "exception" 325 | echoerr vimpire#edn#Write(a:response[1]) 326 | endif 327 | endif 328 | endfunction 329 | 330 | function! vimpire#connection#HandlePrompt(this, response) 331 | let response = vimpire#edn#Simplify(a:response[1]) 332 | let a:this.namespace = response["clojure.core/*ns*"] 333 | 334 | " Weirdo heuristic. Either the submitted code was just whitespace 335 | " or we did a unrepl/do action. Cleanup the queue. 336 | if a:this.state == "evaling" 337 | let len = response[":offset"] - a:this.offset 338 | let ctx = a:this.equeue[0] 339 | 340 | let ctx.remaining -= len 341 | 342 | " If the current eval unit has no result, 343 | " this was a spurious prompt. 344 | if has_key(a:this.evalUnit, "result") 345 | call add(ctx.evalUnits, a:this.evalUnit) 346 | endif 347 | 348 | if ctx.remaining == 0 349 | if has_key(ctx.callbacks, "result") 350 | call ctx.callbacks.result(ctx.evalUnits) 351 | endif 352 | call remove(a:this.equeue, 0) 353 | let a:this.state = "awaiting-prompt" 354 | else 355 | if has_key(ctx.callbacks, "prompt") 356 | call ctx.prompt(a:response[1]) 357 | endif 358 | endif 359 | endif 360 | 361 | let a:this.offset = response[":offset"] 362 | let a:this.evalUnit = { "id": a:response[2], "output": [] } 363 | 364 | if a:this.state == "awaiting-prompt" 365 | let a:this.state = "prompt" 366 | call vimpire#connection#DoEval(a:this) 367 | endif 368 | endfunction 369 | 370 | function! vimpire#connection#HandleOutput(this, event, response) 371 | if a:response[2] != a:this.evalUnit.id 372 | return 373 | endif 374 | 375 | if a:this.state != "evaling" 376 | return 377 | endif 378 | 379 | if has_key(a:this.equeue[0].callbacks, a:event) 380 | call a:this.equeue[0].callbacks[a:event](a:response[1]) 381 | endif 382 | 383 | call add(a:this.evalUnit.output, 384 | \ [a:event, split(a:response[1], '\r\?\n')]) 385 | endfunction 386 | 387 | function! vimpire#connection#Eval(this, code, ...) 388 | " Note: strchars + 1 for triggering newline. 389 | let ctx = { 390 | \ "code": a:code, 391 | \ "remaining": strchars(a:code) + 1, 392 | \ "callbacks": (a:0 > 0 ? a:1 : {}) 393 | \ } 394 | 395 | call add(a:this.equeue, ctx) 396 | call vimpire#connection#DoEval(a:this) 397 | endfunction 398 | 399 | function! vimpire#connection#DoEval(this) 400 | if a:this.state != "prompt" || len(a:this.equeue) == 0 401 | return 402 | endif 403 | 404 | let a:this.state = "evaling" 405 | 406 | let a:this.equeue[0].evalUnits = [] 407 | 408 | call vimpire#connection#Send(a:this.channel, a:this.equeue[0].code) 409 | endfunction 410 | 411 | let s:SideloaderHandlers = { 412 | \ ":resource": 413 | \ function("vimpire#connection#HandleSideloadedResource"), 414 | \ ":class": 415 | \ { t, response -> 416 | \ vimpire#connection#Send(t.channel, "nil") 417 | \ } 418 | \ } 419 | 420 | function! vimpire#connection#NewSideloader(oniisama) 421 | let this = {} 422 | 423 | let this.server = a:oniisama.server 424 | let this.running = v:false 425 | let this.oniisama = a:oniisama 426 | let this.queue = "" 427 | let this.state = "raw" 428 | let this.channel = vimpire#connection#Connect( 429 | \ this, 430 | \ { msg -> vimpire#connection#UpgradeSideloader(this, msg) }) 431 | 432 | let this.handlers = s:SideloaderHandlers 433 | 434 | return this 435 | endfunction 436 | 437 | function! vimpire#connection#UpgradeSideloader(this, msg) 438 | let a:this.queue .= a:msg 439 | 440 | if a:this.queue =~ 'user=> ' 441 | let a:this.queue = "" 442 | 443 | let starter = vimpire#edn#Write( 444 | \ vimpire#connection#ExpandAction( 445 | \ a:this.oniisama.actions[":unrepl.jvm/start-side-loader"], 446 | \ {})) 447 | 448 | call vimpire#connection#Send(a:this.channel, starter) 449 | elseif a:this.queue =~ '\[:unrepl.jvm.side-loader/hello\]' 450 | let a:this.state = "waiting" 451 | 452 | let [ hello_, nextQueue ] = vimpire#edn#Read(a:this.queue) 453 | let a:this.queue = nextQueue 454 | 455 | if !has("nvim") 456 | call ch_setoptions(a:this.channel, 457 | \ { "callback": { ch_, msg -> 458 | \ vimpire#connection#HandleResponse(a:this, msg) 459 | \ }}) 460 | endif 461 | 462 | let a:this.running = v:true 463 | 464 | " Tell Onii-sama and trigger queue activation. 465 | let a:this.oniisama.state = "awaiting-prompt" 466 | call vimpire#connection#HandleResponse(a:this.oniisama, "") 467 | endif 468 | endfunction 469 | 470 | function! vimpire#connection#HandleSideloadedResource(this, response) 471 | call vimpire#connection#Send(a:this.channel, 472 | \ vimpire#edn#Write(get(a:this.oniisama.venom.resources, 473 | \ a:response[1], v:null))) 474 | endfunction 475 | 476 | function! vimpire#connection#ExpandAction(form, bindings) 477 | if type(a:form) == v:t_dict 478 | if vimpire#edn#IsTaggedLiteral(a:form, 479 | \ {"edn/namespace": "unrepl", "edn/symbol": "param"}) 480 | let k = vimpire#edn#Simplify(a:form["edn/value"]) 481 | 482 | if !has_key(a:bindings, k) 483 | throw "Vimpire: binding " . k . "missing in action expansion" 484 | endif 485 | 486 | return a:bindings[k] 487 | endif 488 | endif 489 | 490 | if type(a:form) == v:t_list 491 | let res = [] 492 | 493 | for item in a:form 494 | call add(res, vimpire#connection#ExpandAction(item, a:bindings)) 495 | endfor 496 | 497 | return res 498 | endif 499 | 500 | if type(a:form) == v:t_dict 501 | let res = {} 502 | 503 | for [ key, value ] in items(a:form) 504 | let key = vimpire#connection#ExpandAction(key, a:bindings) 505 | let res[key] = vimpire#connection#ExpandAction(value, a:bindings) 506 | endfor 507 | 508 | return res 509 | endif 510 | 511 | " Just a value 512 | return a:form 513 | endfunction 514 | 515 | function! vimpire#connection#Action(this, action, bindings, ...) 516 | let action = vimpire#connection#ExpandAction( 517 | \ a:this.actions[a:action], 518 | \ a:bindings) 519 | let code = vimpire#edn#Write(action) 520 | 521 | call vimpire#connection#Eval(a:this, code, a:0 > 0 ? a:1 : {}) 522 | endfunction 523 | 524 | " Epilog 525 | let &cpo = s:save_cpo 526 | -------------------------------------------------------------------------------- /autoload/vimpire/exc.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | " Taken from clj-stacktrace: https://github.com/mmcgrana/clj-stacktrace 28 | " Returns true if the filename is non-null and indicates a clj source file. 29 | function! s:IsClojureCode(className, file) 30 | return a:className =~ '^user' 31 | \ || a:file == "NO_SOURE_FILE" 32 | \ || a:file == "unrepl-session" 33 | \ || a:file =~ "\.clj$" 34 | endfunction 35 | 36 | " Returns the clojure namespace name implied by the bytecode class name. 37 | function! s:ClojureNspace(className) 38 | let nspace = matchstr(a:className, '[^$]\+\$\@=') 39 | if nspace == "" 40 | let nspace = matchlist(a:className, '\(.\+\)\.[^.]\+$')[1] 41 | endif 42 | 43 | return substitute(nspace, "_", "-", "g") 44 | endfunction 45 | 46 | " drop everything before and including the first $ 47 | " drop everything after and including and the second $ 48 | " drop any __xyz suffixes 49 | " sub _PLACEHOLDER_ for the corresponding char 50 | let s:ClojureFnSubs = [ 51 | \ ['^[^$]*\$', ''], 52 | \ ['\$.*', ''], 53 | \ ['__\d\+.*', ''], 54 | \ ['_QMARK_', '?'], 55 | \ ['_BANG_', '!'], 56 | \ ['_PLUS_', '+'], 57 | \ ['_GT_', '>'], 58 | \ ['_LT_', '<'], 59 | \ ['_EQ_', '='], 60 | \ ['_STAR_', '*'], 61 | \ ['_SLASH_', '/'], 62 | \ ['_', '-']] 63 | 64 | " Returns the clojure function name implied by the bytecode class name. 65 | function! s:ClojureFn(className) 66 | let className = a:className 67 | for [ pattern, replacement ] in s:ClojureFnSubs 68 | let className = substitute(className, pattern, replacement, '') 69 | endfor 70 | return className 71 | endfunction 72 | 73 | " Returns true if the bytecode class name implies an anonymous inner fn. 74 | function! s:IsClojureAnonFn(className) 75 | return a:className =~ '\$.*\$' 76 | endfunction 77 | 78 | " Returns a map of information about the java trace element. 79 | " All returned maps have the keys: 80 | " file String of source file name. 81 | " line Number of source line number of the enclosing form. 82 | " type Indicating a clojure or java elem. 83 | " Additionally for elements from Java code: 84 | " class String of the name of the class to which the method belongs. 85 | " method String of the name of the method. 86 | " Additionally for elements from Clojure code: 87 | " nspace String representing the namespace of the function. 88 | " fn String representing the name of the enclosing var for 89 | " the function. 90 | " anonFn v:true iff the function is an anonymous inner fn. 91 | function! s:ParseTraceElement(traceElem) 92 | let [ className, method, file, line ] = a:traceElem 93 | 94 | let parsed = {"file": file, "line": line} 95 | if s:IsClojureCode(className, file) 96 | let parsed.type = "clojure" 97 | let parsed.nspace = s:ClojureNspace(className) 98 | let parsed.fn = s:ClojureFn(className) 99 | let parsed.anonFn = s:IsClojureAnonFn(className) 100 | else 101 | let parsed.type = "java" 102 | let parsed.class = className 103 | let parsed.method = method 104 | endif 105 | 106 | return parsed 107 | endfunction 108 | 109 | function! s:ClojureMethodString(parsed) 110 | return a:parsed.nspace . "/" . a:parsed.fn 111 | \ . (a:parsed.anonFn ? "[fn]" : "") 112 | endfunction 113 | 114 | function! s:JavaMethodString(parsed) 115 | return a:parsed.class . "." . a:parsed.method 116 | endfunction 117 | 118 | function! s:MethodString(parsed) 119 | if a:parsed.type == "java" 120 | return s:JavaMethodString(a:parsed) 121 | else 122 | return s:ClojureMethodString(a:parsed) 123 | endif 124 | endfunction 125 | 126 | function! s:SourceString(parsed) 127 | return "(" . a:parsed.file . ":" . a:parsed.line . ")" 128 | endfunction 129 | 130 | function! s:PrintTraceElement(parsed) 131 | return s:MethodString(a:parsed) . " " . s:SourceString(a:parsed) 132 | endfunction 133 | 134 | function! vimpire#exc#PPrintException(error) 135 | let output = ["Cause: " . a:error.cause] 136 | if len(a:error.trace) == 0 137 | call add(output, " at [empty stack trace]") 138 | else 139 | let [ first; rest ] = map(copy(a:error.trace), 140 | \ 's:ParseTraceElement(v:val)') 141 | call add(output, " at " . s:PrintTraceElement(first)) 142 | call extend(output, map(rest, '" " . s:PrintTraceElement(v:val)')) 143 | endif 144 | 145 | if a:error.incomplete 146 | call add(output, " …") 147 | endif 148 | 149 | return join(output, "\n") 150 | endfunction 151 | 152 | let s:ElisionSymbol = vimpire#edn#Symbol("unrepl", "...") 153 | 154 | function! vimpire#exc#ReadResponse(response) 155 | " Exceptions are tagged as #error. 156 | let ex = vimpire#edn#SimplifyMap(a:response)[":ex"]["edn/value"] 157 | let ex = vimpire#edn#SimplifyMap(ex) 158 | 159 | let stackTrace = [] 160 | let incomplete = v:false 161 | for elem in ex[":trace"] 162 | if vimpire#edn#IsTaggedLiteral(elem, s:ElisionSymbol) 163 | let incomplete = v:true 164 | break 165 | endif 166 | 167 | call add(stackTrace, vimpire#edn#Simplify(elem)) 168 | endfor 169 | return {"cause": ex[":cause"], 170 | \ "trace": stackTrace, "incomplete": incomplete} 171 | endfunction 172 | 173 | " Epilog 174 | let &cpo = s:save_cpo 175 | -------------------------------------------------------------------------------- /autoload/vimpire/sunscreen.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | function! vimpire#sunscreen#GetResources(roots) 28 | let resources = {} 29 | for root in a:roots 30 | let files = glob(root . "**/*", v:false, v:true) 31 | call filter(files, 'getftype(v:val) == "file"') 32 | 33 | for f in files 34 | let resource = substitute(strpart(f, strlen(root)), '\\', '/', 'g') 35 | let resources[resource] = join(readfile(f), "\n") 36 | endfor 37 | endfor 38 | 39 | return resources 40 | endfunction 41 | 42 | function! vimpire#sunscreen#StringToBytes(input) 43 | let bytes = [] 44 | 45 | for i in range(strlen(a:input)) 46 | call add(bytes, char2nr(a:input[i])) 47 | endfor 48 | 49 | return bytes 50 | endfunction 51 | 52 | " Taken from clojure.data.codec.base64 53 | function! vimpire#sunscreen#Base64(input, ...) 54 | let table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 55 | if a:0 > 0 56 | let table = a:1 57 | endif 58 | 59 | let output = "" 60 | 61 | let tailLen = len(a:input) % 3 62 | let loopLim = len(a:input) - tailLen 63 | 64 | let i = 0 65 | while i < loopLim 66 | let x = a:input[i] 67 | let y = a:input[i + 1] 68 | let z = a:input[i + 2] 69 | let a = and(0x3f, x / 4) 70 | let b1 = and(0x03, x) * 16 71 | let b2 = and(0x0f, y / 16) 72 | let b = or(b1, b2) 73 | let c1 = and(0x0f, y) * 4 74 | let c2 = and(0x03, z / 64) 75 | let c = or(c1, c2) 76 | let d = and(0x3f, z) 77 | 78 | let output .= table[a] . table[b] . table[c] . table[d] 79 | let i += 3 80 | endwhile 81 | 82 | if tailLen == 1 83 | let x = a:input[i] 84 | let a = and(0x3f, x / 4) 85 | let b1 = and(0x03, x) * 16 86 | let output .= table[a] . table[b1] . nr2char(61) . nr2char(61) 87 | elseif tailLen == 2 88 | let x = a:input[i] 89 | let y = a:input[i + 1] 90 | let a = and(0x3f, x / 4) 91 | let b1 = and(0x03, x) * 16 92 | let b2 = and(0x0f, y / 16) 93 | let b = or(b1, b2) 94 | let c1 = and(0x0f, y) * 4 95 | let output .= table[a] . table[b] . table[c1] . nr2char(61) 96 | endif 97 | 98 | return output 99 | endfunction 100 | 101 | " Idea taken from Christophe Grand's unrepl 102 | function! vimpire#sunscreen#Marker(input) 103 | " Note: This is a one way street. 104 | let table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-A" 105 | 106 | let sha = sha256(a:input) 107 | let bytes = [] 108 | 109 | for i in range(strlen(sha) / 2) 110 | call add(bytes, str2nr(sha[i:i+1], 16)) 111 | endfor 112 | 113 | let marker = vimpire#sunscreen#Base64(bytes, table) 114 | 115 | " Cut off the padding. 116 | return strpart(marker, 0, strlen(marker) - 1) 117 | endfunction 118 | 119 | function! vimpire#sunscreen#GenerateMarker(resources) 120 | let content = [] 121 | for f in sort(keys(a:resources)) 122 | call add(content, a:resources[f]) 123 | endfor 124 | 125 | let marker = "vv-" . vimpire#sunscreen#Marker(join(content, "\n")) 126 | 127 | return marker 128 | endfunction 129 | 130 | function! vimpire#sunscreen#Base64EncodeResources(resources) 131 | return map(copy(a:resources), { k_, val -> 132 | \ vimpire#sunscreen#Base64( 133 | \ vimpire#sunscreen#StringToBytes(val)) 134 | \ }) 135 | endfunction 136 | 137 | function! vimpire#sunscreen#ShadeResourceNames(marker, resources) 138 | let markerDir = substitute(a:marker, '-', '_', 'g') 139 | 140 | let shadedResources = {} 141 | for resource in keys(a:resources) 142 | let nresource = markerDir . '/' . resource 143 | let shadedResources[nresource] = a:resources[resource] 144 | endfor 145 | 146 | return shadedResources 147 | endfunction 148 | 149 | function! vimpire#sunscreen#ShadeResourceWorker(shadedNamespaceRoots, 150 | \ marker, content) 151 | let markerDir = substitute(a:marker, '-', '_', 'g') 152 | let classesToShade = substitute(a:shadedNamespaceRoots, 153 | \ '-', '_', 'g') 154 | let strsToShade = '"' . substitute(classesToShade, '\.', '/', 'g') 155 | 156 | " Shade symbols. 157 | let content = substitute( 158 | \ a:content, 159 | \ escape(a:shadedNamespaceRoots, '-.'), 160 | \ a:marker . '.\1', 'g') 161 | 162 | " Munge class names. 163 | if classesToShade == a:shadedNamespaceRoots 164 | let content = substitute( 165 | \ content, 166 | \ escape(a:marker, '-') . '\([a-z0-9._]\+\)\?\(\.[A-Z]\)', 167 | \ markerDir . '\1\2', 'g') 168 | else 169 | let content = substitute(content, 170 | \ escape(classesToShade, '.'), 171 | \ markerDir . '.\1', 'g') 172 | endif 173 | 174 | " Escape possible paths. 175 | let content = substitute(content, strsToShade, '"' . markerDir . '/\1', 'g') 176 | 177 | return content 178 | endfunction 179 | 180 | function! vimpire#sunscreen#ShadeResources(namespaceShades, resources) 181 | let shadedResources = copy(a:resources) 182 | 183 | for [ marker, shadedNamespaceRoots ] in items(a:namespaceShades) 184 | call map(shadedResources, { resource_, content -> 185 | \ vimpire#sunscreen#ShadeResourceWorker( 186 | \ shadedNamespaceRoots, marker, content) 187 | \ }) 188 | endfor 189 | 190 | return shadedResources 191 | endfunction 192 | 193 | function! vimpire#sunscreen#ShadeActionsLeafs(namespaceShades, 194 | \ initNamespaces, form) 195 | if vimpire#edn#IsTaggedLiteral(a:form) 196 | " Note: We do not propagate namespace requires into 197 | " tags, since they are not necessarily existing in general. 198 | " We only care for the symbols on the executable level. 199 | let t = vimpire#sunscreen#ShadeActionsLeafs( 200 | \ a:namespaceShades, [], a:form["edn/tag"]) 201 | let v = vimpire#sunscreen#ShadeActionsTree( 202 | \ a:namespaceShades, [], a:form["edn/value"]) 203 | return {"edn/tag": t, "edn/value": v} 204 | elseif vimpire#edn#IsMagical(a:form, "edn/symbol") 205 | if !has_key(a:form, "edn/namespace") 206 | return a:form 207 | endif 208 | 209 | for [marker, namespaces] in items(a:namespaceShades) 210 | if a:form["edn/namespace"] =~ '^' . namespaces 211 | let shadedNamespace = marker . "." . a:form["edn/namespace"] 212 | call add(a:initNamespaces, shadedNamespace) 213 | return vimpire#edn#Symbol(shadedNamespace, a:form["edn/symbol"]) 214 | endif 215 | endfor 216 | 217 | return a:form 218 | elseif vimpire#edn#IsMagical(a:form, "edn/keyword") 219 | if !has_key(a:form, "edn/namespace") 220 | return a:form 221 | endif 222 | 223 | for [marker, namespaces] in items(a:namespaceShades) 224 | if a:form["edn/namespace"] =~ '^' . namespaces 225 | return vimpire#edn#Keyword( 226 | \ marker . "." . a:form["edn/namespace"], 227 | \ a:form["edn/keyword"]) 228 | endif 229 | endfor 230 | 231 | return a:form 232 | else 233 | return a:form 234 | endif 235 | endfunction 236 | 237 | function! vimpire#sunscreen#ShadeActionsTree(namespaceShades, 238 | \ initNamespaces, form) 239 | return vimpire#edn#Traverse(a:form, 240 | \ function("vimpire#sunscreen#ShadeActionsLeafs", 241 | \ [a:namespaceShades, a:initNamespaces])) 242 | endfunction 243 | 244 | function! vimpire#sunscreen#ShadeActions(namespaceShades, 245 | \ initNamespaces, form) 246 | let actions = [] 247 | for [k, v] in vimpire#edn#Items(a:form) 248 | call add(actions, [k, vimpire#sunscreen#ShadeActionsTree( 249 | \ a:namespaceShades, a:initNamespaces, v)]) 250 | endfor 251 | 252 | return vimpire#edn#Map(actions) 253 | endfunction 254 | 255 | function! vimpire#sunscreen#Apply( 256 | \ name, 257 | \ roots, 258 | \ exposedNamespaceRoots, 259 | \ hiddenNamespaceRoots, 260 | \ actionsFile) 261 | return { 262 | \ "name": a:name, 263 | \ "roots": a:roots, 264 | \ "exposed": a:exposedNamespaceRoots, 265 | \ "hidden": a:hiddenNamespaceRoots, 266 | \ "actions": a:actionsFile 267 | \ } 268 | endfunction 269 | 270 | " Epilog 271 | let &cpo = s:save_cpo 272 | -------------------------------------------------------------------------------- /autoload/vimpire/ui.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | let s:save_cpo = &cpo 24 | set cpo&vim 25 | 26 | if !exists("g:vimpire_ui_use_error_buffer") 27 | let g:vimpire_ui_use_error_buffer = v:true 28 | endif 29 | 30 | function! vimpire#ui#ReportError(msg) 31 | if g:vimpire_ui_use_error_buffer 32 | let buf = vimpire#window#resultwindow#New("vimpire#buffer#NewResultBuffer") 33 | call vimpire#window#ShowText(buf, a:msg) 34 | wincmd p 35 | else 36 | throw "Vimpire: A crisis has arisen! " . substitute(a:msg, '\n\(\t\?\)', ' ', 'g') 37 | endif 38 | endfunction 39 | 40 | function! vimpire#ui#ShowResult(result) 41 | let buf = vimpire#window#resultwindow#New("vimpire#buffer#NewResultBuffer") 42 | call vimpire#window#ShowText(buf, a:result) 43 | wincmd p 44 | endfunction 45 | 46 | function! vimpire#ui#ShowClojureResult(result, nspace) 47 | let buf = vimpire#window#resultwindow#New("vimpire#buffer#NewClojureResultBuffer") 48 | let b:vimpire_namespace = a:nspace 49 | call vimpire#window#ShowText(buf, a:result) 50 | wincmd p 51 | endfunction 52 | 53 | " Key mappings and Plugs 54 | function! vimpire#ui#MakeProtectedPlug(mode, plug, f, args) 55 | execute a:mode . "noremap (vimpire_" . a:plug . ")" 56 | \ . " :call vimpire#ui#ProtectedPlug(" 57 | \ . "\"" . a:f . "\", " . a:args . ")" 58 | endfunction 59 | 60 | function! vimpire#ui#MakeCommandPlug(mode, plug, f, args) 61 | execute a:mode . "noremap (vimpire_" . a:plug . ")" 62 | \ . " :call vimpire#ui#ProtectedPlug(" 63 | \ . " \"vimpire#ui#CommandPlug\", " 64 | \ . " \"" . a:f . "\", " . a:args . ")" 65 | endfunction 66 | 67 | function! vimpire#ui#CommandPlug(f, ...) 68 | if exists("b:vimpire_namespace") 69 | call call(a:f, a:000) 70 | else 71 | let msg = "Vimpire could not initialise the server connection.\n" 72 | \ . "That means you will not be able to use the interactive features.\n" 73 | \ . "Reasons might be that the server is not running.\n\n" 74 | \ . "Vimpire will *not* start the server for you or handle the classpath.\n" 75 | \ . "There is a plethora of tools like ivy, maven, gradle and leiningen,\n" 76 | \ . "which do this better than Vimpire could ever do it.\n\n" 77 | \ . "Another reason might be, that you forgot to connect to the server\n" 78 | \ . "for your prefix with VimpireBite." 79 | throw msg 80 | endif 81 | endfunction 82 | 83 | function! vimpire#ui#ProtectedPlug(f, ...) 84 | try 85 | return call(a:f, a:000) 86 | catch /.*/ 87 | call vimpire#ui#ReportError(v:exception) 88 | endtry 89 | endfunction 90 | 91 | " Epilog 92 | let &cpo = s:save_cpo 93 | -------------------------------------------------------------------------------- /autoload/vimpire/util.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | let s:save_cpo = &cpo 24 | set cpo&vim 25 | 26 | function! vimpire#util#WithSaved(f, save, restore) 27 | let v = a:save() 28 | try 29 | let r = a:f() 30 | finally 31 | call a:restore(v) 32 | endtry 33 | return r 34 | endfunction 35 | 36 | function! s:SavePosition() 37 | let [ _b, l, c, _o ] = getpos(".") 38 | let b = bufnr("%") 39 | return [b, l, c] 40 | endfunction 41 | 42 | function! s:RestorePosition(value) 43 | let [b, l, c] = a:value 44 | 45 | if bufnr("%") != b 46 | execute b "buffer!" 47 | endif 48 | call setpos(".", [0, l, c, 0]) 49 | endfunction 50 | 51 | function! vimpire#util#WithSavedPosition(f) 52 | return vimpire#util#WithSaved(a:f, 53 | \ function("s:SavePosition"), 54 | \ function("s:RestorePosition")) 55 | endfunction 56 | 57 | function! s:SaveRegister(reg) 58 | return [a:reg, getreg(a:reg, 1), getregtype(a:reg)] 59 | endfunction 60 | 61 | function! s:SaveRegisters(reg) 62 | return map([a:reg, "", "/", "-", 63 | \ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], 64 | \ "s:SaveRegister(v:val)") 65 | endfunction 66 | 67 | function! s:RestoreRegisters(registers) 68 | for register in a:registers 69 | call call(function("setreg"), register) 70 | endfor 71 | endfunction 72 | 73 | function! vimpire#util#WithSavedRegister(reg, f) 74 | return vimpire#util#WithSaved(a:f, 75 | \ function("s:SaveRegisters", [a:reg]), 76 | \ function("s:RestoreRegisters")) 77 | endfunction 78 | 79 | function! s:SaveOption(option) 80 | return eval("&" . a:option) 81 | endfunction 82 | 83 | function! s:RestoreOption(option, value) 84 | execute "let &" . a:option . " = a:value" 85 | endfunction 86 | 87 | function! vimpire#util#WithSavedOption(option, closure) 88 | return vimpire#util#WithSaved(a:closure 89 | \ function("s:SaveOption", [a:option]), 90 | \ function("s:RestoreOption, [a:option])) 91 | endfunction 92 | 93 | function! s:DoYank(reg, yank) 94 | silent execute a:yank 95 | return getreg(a:reg) 96 | endfunction 97 | 98 | function! vimpire#util#Yank(r, how) 99 | return vimpire#util#WithSavedRegister(a:r, 100 | \ function("s:DoYank", [a:r, a:how])) 101 | endfunction 102 | 103 | function! vimpire#util#BufferName() 104 | let file = expand("%") 105 | if file == "" 106 | let file = "UNNAMED" 107 | endif 108 | return file 109 | endfunction 110 | 111 | function! vimpire#util#OpTextExtractor(type) 112 | if a:type == "line" 113 | normal! '[ 114 | return [line("."), col("."), vimpire#util#Yank("l", "normal! V']\"ly")] 115 | else 116 | normal! `[ 117 | return [line("."), col("."), vimpire#util#Yank("l", "normal! v`]\"ly")] 118 | endif 119 | endfunction 120 | 121 | " Epilog 122 | let &cpo = s:save_cpo 123 | -------------------------------------------------------------------------------- /autoload/vimpire/venom.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Prolog 24 | let s:save_cpo = &cpo 25 | set cpo&vim 26 | 27 | let vimpire#venom#PoisonCabinet = [] 28 | let vimpire#venom#Venom = g:vimpire#Nil 29 | 30 | function! vimpire#venom#Register(venom) 31 | call add(g:vimpire#venom#PoisonCabinet, a:venom) 32 | endfunction 33 | 34 | function! vimpire#venom#NamespacesToRegex(namespaces) 35 | return '\(' . join(a:namespaces, '\|') . '\)' 36 | endfunction 37 | 38 | function! vimpire#venom#Inject() 39 | if g:vimpire#venom#Venom isnot g:vimpire#Nil 40 | return g:vimpire#venom#Venom 41 | endif 42 | 43 | " Step 1+2: Read resources & actions and generate markers. 44 | let markers = {} 45 | for vial in g:vimpire#venom#PoisonCabinet 46 | let vial.resources = 47 | \ vimpire#sunscreen#GetResources(vial.roots) 48 | 49 | if vial.actions isnot g:vimpire#Nil 50 | let vial.actions = 51 | \ vimpire#edn#Read(join( 52 | \ readfile(vial.actions), "\n"))[0] 53 | else 54 | let vial.actions = {} 55 | endif 56 | 57 | let vial.marker = 58 | \ vimpire#sunscreen#GenerateMarker(vial.resources) 59 | if len(vial.exposed) > 0 60 | let markers[vial.marker] = 61 | \ vimpire#venom#NamespacesToRegex(vial.exposed) 62 | endif 63 | endfor 64 | 65 | " Step 3: Shade resources and actions. 66 | let blob = g:vimpire#Nil 67 | let initNamespaces = [] 68 | for vial in g:vimpire#venom#PoisonCabinet 69 | let localMarkers = copy(markers) 70 | if len(vial.exposed) > 0 || len(vial.hidden) > 0 71 | let localMarkers[vial.marker] = vimpire#venom#NamespacesToRegex( 72 | \ vial.exposed + vial.hidden) 73 | endif 74 | 75 | let vial.resources = vimpire#sunscreen#ShadeResourceNames( 76 | \ vial.marker, vial.resources) 77 | let vial.resources = vimpire#sunscreen#ShadeResources( 78 | \ localMarkers, vial.resources) 79 | 80 | " Look out for the unrepl blob. It must not be base64 encoded. 81 | for k in keys(vial.resources) 82 | if k =~ 'unrepl/blob\.clj$' 83 | if blob isnot g:vimpire#Nil 84 | throw "Vimpire: multiple unrepl blobs defined in resources!" 85 | endif 86 | 87 | let blob = vial.resources[k] 88 | unlet vial.resources[k] 89 | 90 | break 91 | endif 92 | endfor 93 | 94 | let vial.resources = vimpire#sunscreen#Base64EncodeResources( 95 | \ vial.resources) 96 | let vial.actions = vimpire#sunscreen#ShadeActions( 97 | \ localMarkers, initNamespaces, vial.actions) 98 | endfor 99 | 100 | " Check that there is actually a blob. 101 | if blob is g:vimpire#Nil 102 | throw "Vimpire: no unrepl blob defined in venom!" 103 | endif 104 | 105 | " Step 4: Distill the venom! 106 | let resources = {} 107 | let actions = [] 108 | let keys = [] 109 | for vial in g:vimpire#venom#PoisonCabinet 110 | try 111 | " It is an error for peers to overwrite each others resources. 112 | " In fact this should never happen. But hey, you never know 113 | " what people come up with. 114 | call extend(resources, vial.resources, "error") 115 | catch 116 | throw "Vimpire: " . vial.name . " is trying to overwrite existing resources" 117 | endtry 118 | 119 | for [k, v] in vimpire#edn#Items(vial.actions) 120 | if index(keys, k) > -1 121 | throw "Vimpire: " 122 | \ . vial.name 123 | \ . " is trying to overwrite existing action " 124 | \ . vimpire#edn#Write(k) 125 | endif 126 | 127 | call add(keys, k) 128 | call add(actions, [k, v]) 129 | endfor 130 | endfor 131 | 132 | let actions = vimpire#edn#Write(vimpire#edn#Map(actions)) 133 | call map(uniq(sort(initNamespaces)), { idx_, nspace -> 134 | \ vimpire#edn#List([ 135 | \ vimpire#edn#Symbol("clojure.core", "symbol"), 136 | \ nspace 137 | \ ]) 138 | \ }) 139 | 140 | let g:vimpire#venom#Venom = { 141 | \ "actions": actions, 142 | \ "resources": resources, 143 | \ "blob": blob, 144 | \ "init": vimpire#edn#Write(vimpire#edn#List([ 145 | \ vimpire#edn#Symbol("clojure.core", "require") 146 | \ ] + initNamespaces))} 147 | 148 | return g:vimpire#venom#Venom 149 | endfunction 150 | 151 | " Epilog 152 | let &cpo = s:save_cpo 153 | -------------------------------------------------------------------------------- /autoload/vimpire/window.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | let s:save_cpo = &cpo 24 | set cpo&vim 25 | 26 | if !exists("g:vimpire_window_split_pos") 27 | let g:vimpire_window_split_pos = "top" 28 | endif 29 | 30 | if !exists("g:vimpire_window_split_size") 31 | let g:vimpire_window_split_size = "" 32 | endif 33 | 34 | function! vimpire#window#New(bufCtor) 35 | let this = {} 36 | 37 | if g:vimpire_window_split_pos == "left" || g:vimpire_window_split_pos == "right" 38 | let o_sr = &splitright 39 | if g:vimpire_window_split_pos == "left" 40 | set nosplitright 41 | else 42 | set splitright 43 | end 44 | execute printf("%svsplit", g:vimpire_window_split_size) 45 | let &splitright = o_sr 46 | else 47 | let o_sb = &splitbelow 48 | if g:vimpire_window_split_pos == "bottom" 49 | set splitbelow 50 | else 51 | set nosplitbelow 52 | end 53 | execute printf("%ssplit", g:vimpire_window_split_size) 54 | let &splitbelow = o_sb 55 | endif 56 | 57 | let this.buffer = call(a:bufCtor, []) 58 | let w:vimpire_window = this 59 | 60 | return this 61 | endfunction 62 | 63 | function! vimpire#window#GoHere(this) 64 | let wn = vimpire#window#FindThis(a:this) 65 | if wn == -1 66 | throw 'Vimpire: A crisis has arisen! Cannot find my window.' 67 | endif 68 | execute wn . "wincmd w" 69 | call vimpire#buffer#GoHere(a:this.buffer) 70 | endfunction 71 | 72 | function! vimpire#window#Resize(this) 73 | call vimpire#window#GoHere(a:this) 74 | let size = line("$") 75 | if size < 3 76 | let size = 3 77 | endif 78 | execute "resize " . size 79 | endfunction 80 | 81 | function! vimpire#window#ShowText(this, text) 82 | call vimpire#window#GoHere(a:this) 83 | call vimpire#buffer#ShowText(a:this.buffer, a:text) 84 | endfunction 85 | 86 | function! vimpire#window#Clear(this) 87 | call vimpire#window#GoHere(a:this) 88 | call vimpire#buffer#Clear(a:this.buffer) 89 | endfunction 90 | 91 | function! vimpire#window#Close(this) 92 | call vimpire#buffer#Close(a:this.buffer) 93 | endfunction 94 | 95 | function! vimpire#window#FindThis(this) 96 | for w in range(1, winnr("$")) 97 | if type(getwinvar(w, "vimpire_window")) == type({}) 98 | if getwinvar(w, "vimpire_window") == a:this 99 | return w 100 | endif 101 | endif 102 | endfor 103 | 104 | return -1 105 | endfunction 106 | 107 | " Epilog 108 | let &cpo = s:save_cpo 109 | -------------------------------------------------------------------------------- /autoload/vimpire/window/resultwindow.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | let s:save_cpo = &cpo 24 | set cpo&vim 25 | 26 | function! vimpire#window#resultwindow#New(bufCtor) 27 | if exists("t:vimpire_result_window") 28 | " Otherwise the result window was closed. 29 | if vimpire#window#FindThis(t:vimpire_result_window) != -1 30 | call vimpire#window#GoHere(t:vimpire_result_window) 31 | 32 | let t:vimpire_result_window.buffer = call(a:bufCtor, []) 33 | return t:vimpire_result_window 34 | else 35 | unlet t:vimpire_result_window 36 | endif 37 | endif 38 | 39 | let this = vimpire#window#New(a:bufCtor) 40 | 41 | let b:vimpire_result_buffer = 1 42 | let t:vimpire_result_window = this 43 | 44 | augroup VimpireResultWindow 45 | autocmd! 46 | autocmd BufDelete call vimpire#window#resultwindow#Demote( 47 | \ getbufvar(eval(expand("")), "vimpire_buffer")) 48 | augroup END 49 | 50 | return this 51 | endfunction 52 | 53 | " Remove the buffer object from the window. The buffer is removed 54 | " automatically by Vim, when it is removed from the window. 55 | function! vimpire#window#resultwindow#Demote(buffer) 56 | if exists("t:vimpire_result_window") 57 | if t:vimpire_result_window.buffer is a:buffer 58 | let t:vimpire_result_window.buffer = g:vimpire#Nil 59 | endif 60 | endif 61 | endfunction 62 | 63 | function! vimpire#window#resultwindow#CloseWindow() 64 | if exists("t:vimpire_result_window") 65 | call vimpire#window#Close(t:vimpire_result_window) 66 | unlet t:vimpire_result_window 67 | endif 68 | endfunction 69 | 70 | " Epilog 71 | let &cpo = s:save_cpo 72 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["server" "venom/complete/src"] 2 | :deps {net.cgrand/unrepl {:mvn/version "0.1.0-SNAPSHOT"}}} 3 | -------------------------------------------------------------------------------- /doc/tags: -------------------------------------------------------------------------------- 1 | :VimpireBite vimpire.txt /*:VimpireBite* 2 | :VimpireRepl vimpire.txt /*:VimpireRepl* 3 | VimpireDocLookupInteractive vimpire.txt /*VimpireDocLookupInteractive* 4 | VimpireDocLookupWord vimpire.txt /*VimpireDocLookupWord* 5 | VimpireEval vimpire.txt /*VimpireEval* 6 | VimpireFindDoc vimpire.txt /*VimpireFindDoc* 7 | VimpireGotoSourceInteractive vimpire.txt /*VimpireGotoSourceInteractive* 8 | VimpireGotoSourceWord vimpire.txt /*VimpireGotoSourceWord* 9 | VimpireJavadocLookupInteractive vimpire.txt /*VimpireJavadocLookupInteractive* 10 | VimpireJavadocLookupWord vimpire.txt /*VimpireJavadocLookupWord* 11 | VimpireMacroExpand vimpire.txt /*VimpireMacroExpand* 12 | VimpireMacroExpand1 vimpire.txt /*VimpireMacroExpand1* 13 | VimpireRequireFile vimpire.txt /*VimpireRequireFile* 14 | VimpireRequireFileAll vimpire.txt /*VimpireRequireFileAll* 15 | VimpireRunTests vimpire.txt /*VimpireRunTests* 16 | VimpireSourceLookupInteractive vimpire.txt /*VimpireSourceLookupInteractive* 17 | VimpireSourceLookupWord vimpire.txt /*VimpireSourceLookupWord* 18 | ^ vimpire.txt /*^* 19 | clojure vimpire.txt /*clojure* 20 | vimpire vimpire.txt /*vimpire* 21 | vimpire.txt vimpire.txt /*vimpire.txt* 22 | -------------------------------------------------------------------------------- /doc/vimpire.txt: -------------------------------------------------------------------------------- 1 | *vimpire.txt* *vimpire* *clojure* 2 | 3 | Vimpire - A Clojure Environment 4 | =============================== 5 | 6 | Introduction 7 | ------------ 8 | 9 | Vimpire is a development environment for Clojure. It provides interactive 10 | features like async completion, documentation lookup and a Repl running in a 11 | Vim buffer. 12 | 13 | Socket Server 14 | ------------- 15 | 16 | To use the interactive part you have to start a socket server on the clojure 17 | side. Example invocation: 18 | > 19 | clj '-J-Dclojure.server.repl={:port 5432 :accept clojure.core.server/repl}' -r 20 | < 21 | This may look different depending on your system. Note, that no further setup 22 | on the backend is necessary. 23 | 24 | Before using the interactive features you have to connect to the Clojure 25 | backend server. 26 | 27 | :VimpireBite *:VimpireBite* 28 | Start a new connection to the backend server. 29 | are coordinates of the form :. 30 | Example: 31 | > 32 | :VimpireBite localhost:5432 33 | < 34 | There may be connections to several different backend servers. Start one 35 | connection in the root of one project. Change you current working directory to 36 | the second project. Start the second connection. Now all the files should see 37 | the correct connection depending on their project root. 38 | 39 | If a buffer has no associated filename, the current working directory is used 40 | to the select the backend connection. 41 | 42 | However, this functionality is not really well tested, since I usually don't 43 | work that way. So expect mayhem. 44 | 45 | Windows 46 | ------- 47 | 48 | Vimpire might pop up windows, like the preview window or the Repl. The place 49 | where this is done may be controlled with the SplitPos variable. Possible 50 | values are "left", "right", "top" and "bottom". The default is "top". 51 | 52 | Example: 53 | > 54 | let g:vimpire_window_split_pos = "left" 55 | < 56 | It is also possible to specify the size of the new window. The size is 57 | specified in lines/columns. 58 | > 59 | let g:vimpire_window_split_size = 10 60 | < 61 | You can close the popup result window by binding the 62 | (vimpire_close_result_buffer) to your liking. 63 | 64 | Errors 65 | ------ 66 | 67 | Errors are reported in a temporary buffer. This is to make error messages 68 | more readable. In particular when they contain stacktraces from the Java 69 | side. However this may interfer with scripts which do not expect that a 70 | new buffer pops up. So one can go back to the old behaviour. 71 | > 72 | let g:vimpire_ui_use_error_buffer = 0 73 | < 74 | Keybindings 75 | ----------- 76 | 77 | Vimpire does not define any keybindings on its own. However it provides 78 | so-called plugs for each command. 79 | 80 | (vimpire_eval) *VimpireEval* 81 | The eval operator. Send off the expression covered 82 | by the following motion to the backend server. 83 | This plays with the vim-sexp plugin. 84 | 85 | (vimpire_require_file) *VimpireRequireFile* 86 | Require the namespace of the current file with 87 | the :reload flag. Note: For this to work with 88 | a remote Clojure server, the files have to put in 89 | place before issuing the command, eg. via scp 90 | or NFS. 91 | 92 | (vimpire_require_file_all) *VimpireRequireFileAll* 93 | Require the namespace of the current file with 94 | the :reload-all flag. Note: For this to work with 95 | a remote Clojure server, the files have to put in 96 | place before issuing the command, eg. via scp 97 | or NFS. 98 | 99 | (vimpire_run_tests) *VimpireRunTests* 100 | Require the namespace of the filename with the 101 | :reload flag. Then use clojure.test to run the 102 | tests of the namespace via run-tests. 103 | Note: For this to work with a remote Clojure 104 | server, the files have to put in place before 105 | issuing the command, eg. via scp or NFS. 106 | 107 | (vimpire_macro_expand) *VimpireMacroExpand* 108 | The macro expansion operator. Send the expression 109 | covered by the following motion to the backend 110 | server for macro expansion. Play well with the 111 | vim-sexp plugin. 112 | 113 | (vimpire_macro_expand1) *VimpireMacroExpand1* 114 | Same as MacroExpand, but use macroexpand-1. 115 | 116 | (vimpire_doc_lookup_word) *VimpireDocLookupWord* 117 | Lookup up the word under the cursor and print 118 | the documentation for it via (doc). 119 | 120 | (vimpire_doc_lookup_interactive) *VimpireDocLookupInteractive* 121 | Lookup the documentation of an arbitrary word. 122 | The user is prompted for input. 123 | 124 | (vimpire_find_doc) *VimpireFindDoc* 125 | Find a the documentation for a given pattern 126 | with (find-doc). The user is prompted for input. 127 | 128 | (vimpire_javadoc_lookup_word) *VimpireJavadocLookupWord* 129 | Open the javadoc for the word under the cursor 130 | in an external browser. You may specify URLs for 131 | package directories in the configuration variable 132 | g:vimpire_javadoc_path_map, which maps package 133 | prefixes to URLs. Longest match wins. The browser 134 | used should be the default browser configured for 135 | system, but may be changed with the configuration 136 | variable g:vimpire_browser. 137 | 138 | (vimpire_javadoc_lookup_interactive) *VimpireJavadocLookupInteractive* 139 | Open the javadoc for an arbitrary word in an 140 | external browser. The user is prompted for input. 141 | 142 | (vimpire_source_lookup_word) *VimpireSourceLookupWord* 143 | Show a read-only view of the source the word under 144 | the cursor. For this to work, the source must be 145 | available in the Classpath or as a file (depending 146 | on how the source was loaded). 147 | 148 | (vimpire_source_lookup_interactive) *VimpireSourceLookupInteractive* 149 | Show a read-only view of the source of an arbitrary 150 | word. For this to work, the source must be available 151 | in the Classpath or as a file (depending on how the 152 | source was loaded). 153 | 154 | (vimpire_goto_source_word) *VimpireGotoSourceWord* 155 | Goto the source of the word under the cursor. For this 156 | to work, the source must be available in a directory 157 | of the |'path'| option. 158 | 159 | (vimpire_goto_source_interactive) *VimpireGotoSourceInteractive* 160 | Goto the source of an arbitrary word. For this to work, 161 | the source must be available in a directory of the 162 | |'path'| option. 163 | 164 | Vim Repl 165 | -------- 166 | 167 | Start a Repl via the |:VimpireRepl| command. At the prompt just type 168 | expressions. Hitting enter will determine, whether the expression is 169 | complete and will send it to the Clojure instance. In case the expression 170 | is incomplete, eg. after "(defn foo" will result in a newline for multiline 171 | expressions. 172 | 173 | A newline will also be inserted if you are inside of the expression. The 174 | expression will only be submitted to the Repl when you hit enter after 175 | the last character of the buffer. If you are inside the expression and 176 | want to start the evaluation immediately you may use instead of 177 | the plain . 178 | 179 | Previously sent expressions may be recalled via and . 180 | Note: sending multiple expressions will save them in the same history 181 | entry. So playing back with will again send all of the contained 182 | expressions. 183 | 184 | If the current line starts with a repl prompt, the *^* command moves to 185 | the end of the prompt and not to the beginning of the line. 186 | 187 | The Plugs are: 188 | - (vimpire_repl_enter_hook) for the enter key 189 | - (vimpire_repl_evaluate) for evaluation () 190 | - (vimpire_repl_hat_hook) for ^ navigation 191 | - (vimpire_repl_up_history) for going backwards in history () 192 | - (vimpire_repl_down_history) for going forwards in history () 193 | 194 | The following convenience commands are provided: 195 | 196 | - ,close - close the Repl and free the Repl resources in the server process 197 | 198 | After command submission the repl changes into stdin mode until a new prompt 199 | is requested by the backend side. In this mode each typed line will be sent 200 | over the wire after hitting . If this input is not consumed by your 201 | previously submitted command, it will be consumed by the repl after the 202 | prompt. However it will not show up in the repl history in this case. So be 203 | warned of race conditions. 204 | 205 | :VimpireRepl *:VimpireRepl* 206 | Start a new Vim Repl in a fresh buffer. There 207 | might be multiple Repls at the same time. This 208 | command works independently of any loaded clojure 209 | files. 210 | 211 | Here be Dragons! 212 | 213 | The repl is just a buffer. It's not a terminal. So we have to sync on 214 | something. That something is the prompt. If you delete it, you will get into 215 | trouble. If you don't allow the repl to sync up with the backend and keep its 216 | state clean, you will get into trouble. 217 | 218 | The repl is all in all functional, but rather fragile. If you cannot cope 219 | with that, then don't use it. 220 | 221 | Unrepl Elisions 222 | --------------- 223 | 224 | Unrepl provides so-called elisions. From the standard clojure repl we 225 | know that eg. very long collections are cut-off to prevent an overflow 226 | of printed information. Unrepl lifts that to the next level in providing 227 | a way to actually retrieve the elided values and extend the already shown 228 | information. The Vimpire repl shows at the corresponding points so called 229 | elision markers. (vimpire_repl_expand_elision) may be used to 230 | expand the elision marker under the cursor. Likewise, to specify the 231 | elision id manually (vimpire_repl_expand_elision_interactive) may 232 | be used. Example: 233 | > 234 | user=> (range) 235 | (0 1 2 3 4 5 6 7 8 9 vv1) 236 | user=> 237 | < 238 | As you can see the normally very dangerous act of printing an infinite 239 | sequence, works perfectly well with this approach since the values are 240 | cut-off at some point. The fangs at the end of the printed sequence 241 | indicate, that there are more values which can be sucked from the victim 242 | process. Placing the cursor marker and executing the expands the 243 | marker in place. 244 | > 245 | user=> (range) 246 | (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 vv2) 247 | user=> 248 | < 249 | Again, there is another marker, so the process may be repeated. Of course 250 | the prompt and any already entered text will be kept intact. Should the 251 | expansion of the elision not be possible anymore, the marker is turned 252 | into an ellipsis. 253 | > 254 | user=> (range) 255 | (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 …) 256 | user=> 257 | < 258 | This works also for vectors, sets, maps and string. While for the 259 | collections the markers are displayed in line with the other values, 260 | for strings the marker immediately follows the string, which also ends 261 | in a ellipsis to indicate that there are more characters available. 262 | > 263 | user=> (apply str (repeat 200 \a)) 264 | "aaaaaaaaaa…"vv1 265 | user=> (vec (take 25 (range))) 266 | [0 1 2 3 4 5 6 7 8 9 vv1] 267 | user=> (set (take 25 (range))) 268 | #{0 7 20 1 24 4 15 21 13 22 vv1} 269 | user=> (into {} (map-indexed vector (repeat 25 \a))) 270 | {0 "a" 7 "a" 20 "a" 1 "a" 24 "a" 4 "a" 15 "a" 21 "a" … vv1} 271 | user=> 272 | < 273 | 274 | Async Completion 275 | ---------------- 276 | 277 | Vimpire supports async completion for clojure code via the asyncomplete.vim 278 | plugin. Please refer to the asyncomplete-vimpire.vim plugin. 279 | > 280 | https://bitbucket.org/kotarak/asyncomplete-vimpire.vim 281 | https://github.com/kotarak/asyncomplete-vimpire.vim 282 | < 283 | If you want to support other async completion systems, you may contact me for 284 | extension points, so that you can write the necessary glue yourself. 285 | 286 | Dynamic Highlighting 287 | -------------------- 288 | 289 | Upon opening a file, Vimpire loads the namespace in the backend server and 290 | traverses all required and aliased namespaces. It collects all functions and 291 | macro definitions and provides the given information back to Vim to properly 292 | highlight the used function and macro names. However this might be slow 293 | depending on the circumstances. Therefore you can disable dynamic 294 | highlighting. 295 | > 296 | let g:vimpire_dynamic_highlighting = v:false 297 | < 298 | 299 | License 300 | ------- 301 | 302 | Copyright © 2008-2018 Meikel Brandmeyer, Frankfurt am Main 303 | All rights reserved. 304 | 305 | Permission is hereby granted, free of charge, to any person obtaining a copy 306 | of this software and associated documentation files (the "Software"), to deal 307 | in the Software without restriction, including without limitation the rights 308 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 309 | copies of the Software, and to permit persons to whom the Software is 310 | furnished to do so, subject to the following conditions: 311 | 312 | The above copyright notice and this permission notice shall be included in 313 | all copies or substantial portions of the Software. 314 | 315 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 316 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 317 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 318 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 319 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 320 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 321 | THE SOFTWARE. 322 | ============================================================================== 323 | .. vim: set ft=help norl ts=8 tw=78 et : 324 | -------------------------------------------------------------------------------- /fang.clj: -------------------------------------------------------------------------------- 1 | (require '[clojure.edn :as edn] 2 | '[clojure.java.io :as io] 3 | '[clojure.string :as str]) 4 | 5 | (import 'java.io.ByteArrayInputStream) 6 | (import 'java.io.InputStream) 7 | (import 'java.io.SequenceInputStream) 8 | (import 'java.security.MessageDigest) 9 | (import 'java.util.Enumeration) 10 | 11 | (defn base64-encode 12 | "Non-standard base64 to avoid name munging" 13 | [^InputStream in] 14 | (let [table "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$" 15 | sb (StringBuilder.)] 16 | (loop [shift 4 17 | buf 0] 18 | (let [got (.read in)] 19 | (if (neg? got) 20 | (do 21 | (when-not (= shift 4) 22 | (let [n (bit-and (bit-shift-right buf 6) 63)] 23 | (.append sb (.charAt table n)))) 24 | (str sb)) 25 | (let [buf (bit-or buf (bit-shift-left got shift)) 26 | n (bit-and (bit-shift-right buf 6) 63)] 27 | (.append sb (.charAt table n)) 28 | (let [shift (- shift 2)] 29 | (if (neg? shift) 30 | (do 31 | (.append sb (.charAt table (bit-and buf 63))) 32 | (recur 4 0)) 33 | (recur shift (bit-shift-left buf 6)))))))))) 34 | 35 | (defn >enum 36 | [s] 37 | (let [s (atom s)] 38 | (reify 39 | Enumeration 40 | (hasMoreElements [this] (boolean (seq @s))) 41 | (nextElement [this] 42 | (let [x (first @s)] (swap! s rest) x))))) 43 | 44 | (defn output-for 45 | [f marker] 46 | (-> f 47 | .getPath 48 | (str/replace #"server[/\\]" 49 | (str/re-quote-replacement (str "venom/" marker "/"))) 50 | io/file)) 51 | 52 | (defn input-files 53 | [] 54 | (->> "server" 55 | io/file 56 | file-seq 57 | (filter #(.isFile %)) 58 | (filter #(.endsWith (.getName %) ".clj")))) 59 | 60 | (defn marker 61 | [] 62 | (with-open [input (->> (input-files) 63 | (map io/input-stream) 64 | >enum 65 | SequenceInputStream. 66 | io/reader)] 67 | (-> input 68 | ^String slurp 69 | ^bytes (.getBytes "UTF-8") 70 | (->> (.digest (MessageDigest/getInstance "SHA-1"))) 71 | ByteArrayInputStream. 72 | base64-encode 73 | (->> (str "vv_") (spit "marker"))))) 74 | 75 | (defn encode-sources 76 | [files m mns] 77 | (doseq [f files] 78 | (let [outputf (output-for f m)] 79 | (println "Writing " (.getPath outputf)) 80 | (.mkdirs (.getParentFile outputf)) 81 | (with-open [input (io/reader f) 82 | output (io/writer outputf)] 83 | (-> input 84 | slurp 85 | (str/replace #"(?> (spit output))))))) 88 | 89 | (defn encode-actions 90 | [mns] 91 | (-> "actions.clj" 92 | slurp 93 | (str/replace #"(?> (spit "actions_poisoned.clj")))) 96 | 97 | (defn main 98 | [] 99 | (let [files (input-files) 100 | m (slurp "marker") 101 | mns (str/replace m #"_" "-")] 102 | (encode-sources files m mns) 103 | (encode-actions mns))) 104 | -------------------------------------------------------------------------------- /plugin/vimpire.vim: -------------------------------------------------------------------------------- 1 | "- 2 | " Copyright 2009-2017 © Meikel Brandmeyer. 3 | " All rights reserved. 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining a copy 6 | " of this software and associated documentation files (the "Software"), to deal 7 | " in the Software without restriction, including without limitation the rights 8 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | " copies of the Software, and to permit persons to whom the Software is 10 | " furnished to do so, subject to the following conditions: 11 | " 12 | " The above copyright notice and this permission notice shall be included in 13 | " all copies or substantial portions of the Software. 14 | " 15 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | " THE SOFTWARE. 22 | 23 | " Only do this when not done yet 24 | if exists("vimpire_loaded") 25 | finish 26 | endif 27 | 28 | let vimpire_loaded = "3.0.0" 29 | 30 | " Prolog 31 | let s:cpo_save = &cpo 32 | set cpo&vim 33 | 34 | let g:vimpire#Nil = [] 35 | 36 | command! -nargs=? VimpireRepl call vimpire#repl#StartRepl(vimpire#connection#ForBuffer(), ) 37 | command! -nargs=* VimpireBite call vimpire#connection#RegisterPrefix(getcwd(), ) 38 | 39 | call vimpire#ui#MakeCommandPlug("n", "doc_lookup_word", "vimpire#backend#doc#DocLookup", "expand(\"\")") 40 | call vimpire#ui#MakeCommandPlug("n", "doc_lookup_interactive", "vimpire#backend#doc#DocLookup", "input(\"Symbol to look up: \")") 41 | call vimpire#ui#MakeCommandPlug("n", "javadoc_look_word", "vimpire#backend#doc#JavadocLookup", "expand(\"\")") 42 | call vimpire#ui#MakeCommandPlug("n", "javadoc_look_interactive", "vimpire#backend#doc#JavadocLookup", "input(\"Class to lookup: \")") 43 | call vimpire#ui#MakeCommandPlug("n", "find_doc", "vimpire#backend#doc#FindDoc", "") 44 | 45 | call vimpire#ui#MakeCommandPlug("n", "source_lookup_word", "vimpire#backend#doc#SourceLookup", "expand(\"\")") 46 | call vimpire#ui#MakeCommandPlug("n", "source_lookup_interactive", "vimpire#backend#doc#SourceLookup", "input(\"Symbol to look up: \")") 47 | 48 | call vimpire#ui#MakeCommandPlug("n", "goto_source_word", "vimpire#backend#nav#GotoSource", "expand(\"\")") 49 | call vimpire#ui#MakeCommandPlug("n", "goto_source_interactive", "vimpire#backend#nav#GotoSource", "input(\"Symbol to go to: \")") 50 | 51 | call vimpire#ui#MakeCommandPlug("n", "require_file", "vimpire#backend#eval#RequireFile", "0") 52 | call vimpire#ui#MakeCommandPlug("n", "require_file_all", "vimpire#backend#eval#RequireFile", "1") 53 | 54 | call vimpire#ui#MakeCommandPlug("n", "run_tests", "vimpire#backend#test#RunTests", "0") 55 | 56 | " Operators 57 | nnoremap (vimpire_eval) :set operatorfunc=vimpire#backend#eval#EvalOperatorg@ 58 | nnoremap (vimpire_macro_expand) :set operatorfunc=vimpire#backend#macro#MacroExpandg@ 59 | nnoremap (vimpire_macro_expand1) :set operatorfunc=vimpire#backend#macro#MacroExpand1g@ 60 | 61 | inoremap (vimpire_repl_enter_hook) :call vimpire#repl#EnterHook(b:vimpire_repl) 62 | inoremap (vimpire_repl_evaluate) G$:call vimpire#repl#EnterHook(b:vimpire_repl) 63 | nnoremap (vimpire_repl_hat_hook) :call vimpire#repl#HatHook(b:vimpire_repl) 64 | inoremap (vimpire_repl_up_history) :call vimpire#repl#UpHistory(b:vimpire_repl) 65 | inoremap (vimpire_repl_down_history) :call vimpire#repl#DownHistory(b:vimpire_repl) 66 | 67 | nnoremap (vimpire_repl_expand_elision) :call vimpire#repl#ExpandElision(b:vimpire_repl, vimpire#repl#GetElisionId(expand(""))) 68 | nnoremap (vimpire_repl_expand_elision_interactive) :call vimpire#repl#ExpandElision(b:vimpire_repl, input("Elision ID: ")) 69 | 70 | nnoremap (vimpire_close_result_buffer) :call vimpire#window#resultwindow#CloseWindow() 71 | 72 | let s:Here = expand(":p:h:h") 73 | 74 | call vimpire#venom#Register( 75 | \ vimpire#sunscreen#Apply( 76 | \ "vimpire-unrepl", 77 | \ [s:Here . "/venom/unrepl/src/"], 78 | \ [], 79 | \ ["unrepl.core", "unrepl.repl", "unrepl.print"], 80 | \ g:vimpire#Nil)) 81 | 82 | call vimpire#venom#Register( 83 | \ vimpire#sunscreen#Apply( 84 | \ "vimpire-util", 85 | \ [s:Here . "/venom/util/src/"], 86 | \ ["vimpire.util"], 87 | \ [], 88 | \ s:Here . "/venom/util/actions.clj")) 89 | 90 | call vimpire#venom#Register( 91 | \ vimpire#sunscreen#Apply( 92 | \ "vimpire-complete", 93 | \ [s:Here . "/venom/complete/src/"], 94 | \ [], 95 | \ ["vimpire.complete", "compliment"], 96 | \ s:Here . "/venom/complete/actions.clj")) 97 | 98 | call vimpire#venom#Register( 99 | \ vimpire#sunscreen#Apply( 100 | \ "vimpire-doc", 101 | \ [s:Here . "/venom/doc/src/"], 102 | \ [], 103 | \ ["vimpire.doc"], 104 | \ s:Here . "/venom/doc/actions.clj")) 105 | 106 | call vimpire#venom#Register( 107 | \ vimpire#sunscreen#Apply( 108 | \ "vimpire-dynhighlight", 109 | \ [s:Here . "/venom/dynhighlight/src/"], 110 | \ [], 111 | \ ["vimpire.dynhighlight"], 112 | \ s:Here . "/venom/dynhighlight/actions.clj")) 113 | 114 | call vimpire#venom#Register( 115 | \ vimpire#sunscreen#Apply( 116 | \ "vimpire-macro", 117 | \ [s:Here . "/venom/macro/src/"], 118 | \ [], 119 | \ ["vimpire.macro"], 120 | \ s:Here . "/venom/macro/actions.clj")) 121 | 122 | call vimpire#venom#Register( 123 | \ vimpire#sunscreen#Apply( 124 | \ "vimpire-nav", 125 | \ [s:Here . "/venom/nav/src/"], 126 | \ [], 127 | \ ["vimpire.nav"], 128 | \ s:Here . "/venom/nav/actions.clj")) 129 | 130 | call vimpire#venom#Register( 131 | \ vimpire#sunscreen#Apply( 132 | \ "vimpire-test", 133 | \ [s:Here . "/venom/test/src/"], 134 | \ [], 135 | \ ["vimpire.test"], 136 | \ s:Here . "/venom/test/actions.clj")) 137 | 138 | " Epilog 139 | let &cpo = s:cpo_save 140 | -------------------------------------------------------------------------------- /venom/complete/LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF 5 | THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and 12 | documentation distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | 16 | i) changes to the Program, and 17 | 18 | ii) additions to the Program; 19 | 20 | where such changes and/or additions to the Program originate from and 21 | are distributed by that particular Contributor. A Contribution 22 | 'originates' from a Contributor if it was added to the Program by such 23 | Contributor itself or anyone acting on such Contributor's 24 | behalf. Contributions do not include additions to the Program which: 25 | (i) are separate modules of software distributed in conjunction with 26 | the Program under their own license agreement, and (ii) are not 27 | derivative works of the Program. 28 | 29 | "Contributor" means any person or entity that distributes the Program. 30 | 31 | "Licensed Patents" mean patent claims licensable by a Contributor 32 | which are necessarily infringed by the use or sale of its Contribution 33 | alone or when combined with the Program. 34 | 35 | "Program" means the Contributions distributed in accordance with this 36 | Agreement. 37 | 38 | "Recipient" means anyone who receives the Program under this 39 | Agreement, including all Contributors. 40 | 41 | 2. GRANT OF RIGHTS 42 | 43 | a) Subject to the terms of this Agreement, each Contributor hereby 44 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 45 | license to reproduce, prepare derivative works of, publicly display, 46 | publicly perform, distribute and sublicense the Contribution of such 47 | Contributor, if any, and such derivative works, in source code and 48 | object code form. 49 | 50 | b) Subject to the terms of this Agreement, each Contributor hereby 51 | grants Recipient a non-exclusive, worldwide, royalty-free patent 52 | license under Licensed Patents to make, use, sell, offer to sell, 53 | import and otherwise transfer the Contribution of such Contributor, if 54 | any, in source code and object code form. This patent license shall 55 | apply to the combination of the Contribution and the Program if, at 56 | the time the Contribution is added by the Contributor, such addition 57 | of the Contribution causes such combination to be covered by the 58 | Licensed Patents. The patent license shall not apply to any other 59 | combinations which include the Contribution. No hardware per se is 60 | licensed hereunder. 61 | 62 | c) Recipient understands that although each Contributor grants the 63 | licenses to its Contributions set forth herein, no assurances are 64 | provided by any Contributor that the Program does not infringe the 65 | patent or other intellectual property rights of any other entity. Each 66 | Contributor disclaims any liability to Recipient for claims brought by 67 | any other entity based on infringement of intellectual property rights 68 | or otherwise. As a condition to exercising the rights and licenses 69 | granted hereunder, each Recipient hereby assumes sole responsibility 70 | to secure any other intellectual property rights needed, if any. For 71 | example, if a third party patent license is required to allow 72 | Recipient to distribute the Program, it is Recipient's responsibility 73 | to acquire that license before distributing the Program. 74 | 75 | d) Each Contributor represents that to its knowledge it has sufficient 76 | copyright rights in its Contribution, if any, to grant the copyright 77 | license set forth in this Agreement. 78 | 79 | 3. REQUIREMENTS 80 | 81 | A Contributor may choose to distribute the Program in object code form 82 | under its own license agreement, provided that: 83 | 84 | a) it complies with the terms and conditions of this Agreement; and 85 | 86 | b) its license agreement: 87 | 88 | i) effectively disclaims on behalf of all Contributors all warranties 89 | and conditions, express and implied, including warranties or 90 | conditions of title and non-infringement, and implied warranties or 91 | conditions of merchantability and fitness for a particular purpose; 92 | 93 | ii) effectively excludes on behalf of all Contributors all liability 94 | for damages, including direct, indirect, special, incidental and 95 | consequential damages, such as lost profits; 96 | 97 | iii) states that any provisions which differ from this Agreement are 98 | offered by that Contributor alone and not by any other party; and 99 | 100 | iv) states that source code for the Program is available from such 101 | Contributor, and informs licensees how to obtain it in a reasonable 102 | manner on or through a medium customarily used for software exchange. 103 | 104 | When the Program is made available in source code form: 105 | 106 | a) it must be made available under this Agreement; and 107 | 108 | b) a copy of this Agreement must be included with each copy of the Program. 109 | 110 | Contributors may not remove or alter any copyright notices contained 111 | within the Program. 112 | 113 | Each Contributor must identify itself as the originator of its 114 | Contribution, if any, in a manner that reasonably allows subsequent 115 | Recipients to identify the originator of the Contribution. 116 | 117 | 4. COMMERCIAL DISTRIBUTION 118 | 119 | Commercial distributors of software may accept certain 120 | responsibilities with respect to end users, business partners and the 121 | like. While this license is intended to facilitate the commercial use 122 | of the Program, the Contributor who includes the Program in a 123 | commercial product offering should do so in a manner which does not 124 | create potential liability for other Contributors. Therefore, if a 125 | Contributor includes the Program in a commercial product offering, 126 | such Contributor ("Commercial Contributor") hereby agrees to defend 127 | and indemnify every other Contributor ("Indemnified Contributor") 128 | against any losses, damages and costs (collectively "Losses") arising 129 | from claims, lawsuits and other legal actions brought by a third party 130 | against the Indemnified Contributor to the extent caused by the acts 131 | or omissions of such Commercial Contributor in connection with its 132 | distribution of the Program in a commercial product offering. The 133 | obligations in this section do not apply to any claims or Losses 134 | relating to any actual or alleged intellectual property 135 | infringement. In order to qualify, an Indemnified Contributor must: a) 136 | promptly notify the Commercial Contributor in writing of such claim, 137 | and b) allow the Commercial Contributor tocontrol, and cooperate with 138 | the Commercial Contributor in, the defense and any related settlement 139 | negotiations. The Indemnified Contributor may participate in any such 140 | claim at its own expense. 141 | 142 | For example, a Contributor might include the Program in a commercial 143 | product offering, Product X. That Contributor is then a Commercial 144 | Contributor. If that Commercial Contributor then makes performance 145 | claims, or offers warranties related to Product X, those performance 146 | claims and warranties are such Commercial Contributor's responsibility 147 | alone. Under this section, the Commercial Contributor would have to 148 | defend claims against the other Contributors related to those 149 | performance claims and warranties, and if a court requires any other 150 | Contributor to pay any damages as a result, the Commercial Contributor 151 | must pay those damages. 152 | 153 | 5. NO WARRANTY 154 | 155 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 156 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 157 | KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY 158 | WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 159 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 160 | responsible for determining the appropriateness of using and 161 | distributing the Program and assumes all risks associated with its 162 | exercise of rights under this Agreement , including but not limited to 163 | the risks and costs of program errors, compliance with applicable 164 | laws, damage to or loss of data, programs or equipment, and 165 | unavailability or interruption of operations. 166 | 167 | 6. DISCLAIMER OF LIABILITY 168 | 169 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR 170 | ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 171 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 172 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 173 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 174 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 175 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 176 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 177 | 178 | 7. GENERAL 179 | 180 | If any provision of this Agreement is invalid or unenforceable under 181 | applicable law, it shall not affect the validity or enforceability of 182 | the remainder of the terms of this Agreement, and without further 183 | action by the parties hereto, such provision shall be reformed to the 184 | minimum extent necessary to make such provision valid and enforceable. 185 | 186 | If Recipient institutes patent litigation against any entity 187 | (including a cross-claim or counterclaim in a lawsuit) alleging that 188 | the Program itself (excluding combinations of the Program with other 189 | software or hardware) infringes such Recipient's patent(s), then such 190 | Recipient's rights granted under Section 2(b) shall terminate as of 191 | the date such litigation is filed. 192 | 193 | All Recipient's rights under this Agreement shall terminate if it 194 | fails to comply with any of the material terms or conditions of this 195 | Agreement and does not cure such failure in a reasonable period of 196 | time after becoming aware of such noncompliance. If all Recipient's 197 | rights under this Agreement terminate, Recipient agrees to cease use 198 | and distribution of the Program as soon as reasonably 199 | practicable. However, Recipient's obligations under this Agreement and 200 | any licenses granted by Recipient relating to the Program shall 201 | continue and survive. 202 | 203 | Everyone is permitted to copy and distribute copies of this Agreement, 204 | but in order to avoid inconsistency the Agreement is copyrighted and 205 | may only be modified in the following manner. The Agreement Steward 206 | reserves the right to publish new versions (including revisions) of 207 | this Agreement from time to time. No one other than the Agreement 208 | Steward has the right to modify this Agreement. The Eclipse Foundation 209 | is the initial Agreement Steward. The Eclipse Foundation may assign 210 | the responsibility to serve as the Agreement Steward to a suitable 211 | separate entity. Each new version of the Agreement will be given a 212 | distinguishing version number. The Program (including Contributions) 213 | may always be distributed subject to the version of the Agreement 214 | under which it was received. In addition, after a new version of the 215 | Agreement is published, Contributor may elect to distribute the 216 | Program (including its Contributions) under the new version. Except as 217 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives 218 | no rights or licenses to the intellectual property of any Contributor 219 | under this Agreement, whether expressly, by implication, estoppel or 220 | otherwise. All rights in the Program not expressly granted under this 221 | Agreement are reserved. 222 | 223 | This Agreement is governed by the laws of the State of Washington and 224 | the intellectual property laws of the United States of America. No 225 | party to this Agreement will bring a legal action under this Agreement 226 | more than one year after the cause of action arose. Each party waives 227 | its rights to a jury trial in any resulting litigation. 228 | -------------------------------------------------------------------------------- /venom/complete/README.org: -------------------------------------------------------------------------------- 1 | * Compliment [[https://travis-ci.org/alexander-yakushev/compliment/][https://travis-ci.org/alexander-yakushev/compliment.svg?branch=master]] [[https://coveralls.io/r/alexander-yakushev/compliment?branch=master][https://coveralls.io/repos/alexander-yakushev/compliment/badge.svg?branch=master]] [[https://versions.deps.co/alexander-yakushev/compliment][https://versions.deps.co/alexander-yakushev/compliment/downloads.svg]] [[https://versions.deps.co/alexander-yakushev/compliment][https://versions.deps.co/images/up-to-date.svg]] 2 | 3 | Compliment is a state-of-the-art Clojure completion library. It provides a 4 | fast, smart and extensible solution to complete vars, namespaces, class 5 | members, local bindings, and whatever else custom sources might implement. 6 | 7 | Compliment is used as a completion backend in the following editors/IDEs: 8 | 9 | - Emacs --- [[https://cider.readthedocs.io/en/latest/code_completion/][CIDER]] (through [[http://company-mode.github.io/][company-mode]] or, now deprecated, [[https://github.com/clojure-emacs/ac-cider][ac-cider]]) 10 | - Vim --- [[https://github.com/tpope/vim-fireplace][vim-fireplace]] 11 | - Atom --- [[https://atom.io/packages/proto-repl][Proto REPL]] 12 | - [[https://sekao.net/nightcode/][Nightcode]] 13 | - [[https://sekao.net/nightlight/][Nightlight]] 14 | - @bhauman's [[https://github.com/bhauman/rebel-readline/][rebel-readline]] 15 | 16 | Also, I am so glad you came here. You look gorgeous today. 17 | 18 | ** Rationale 19 | 20 | I wrote Compliment specifically for you because you are amazing and I believe 21 | you deserve a matching completion lib. Here are the features it boasts: 22 | 23 | - *Speed.* Your time is too precious to wait for completion to happen. 24 | Compliment is designed to be fast and is carefully benchmarked to make sure 25 | no sudden performance drops appear. 26 | - *Smart completion.* Such a smart person like you is entitled to completion 27 | being smart as well. Default Compliment sources use various techniques to 28 | give more meaningful completion depending on the context, and allow some 29 | fuzziness in prefix. 30 | - *Extensibility.* Your insatiable passion for exploration won't be satisfied 31 | by a set in stone completion list. For this reason Compliment allows every 32 | library developer to write custom sources, so later other users of the 33 | library will have better experience utilizing it. 34 | 35 | ** Installation 36 | 37 | If you use Nightcode, vim-fireplace, or Atom then you don't have to install 38 | anything at all --- Compliment will already be there for you. In case you are 39 | an Emacs+CIDER user you'll also need to install [[http://company-mode.github.io/][company-mode]] and 40 | [[https://github.com/expez/company-quickhelp][company-quickhelp]]. This [[https://cider.readthedocs.io/en/latest/code_completion/][guide]] will help you configure it. 41 | 42 | If you need Compliment embedded directly into your program then add this to 43 | the =:dependencies=: 44 | 45 | [[https://clojars.org/compliment][https://clojars.org/compliment/latest-version.svg]] 46 | 47 | ** Examples 48 | 49 | [[https://github.com/alexander-yakushev/compliment/wiki/Examples][Here]] you can find examples of different completion scenarios 50 | Compliment supports so far. 51 | 52 | ** For developers 53 | 54 | See the test files to get an idea how public API and completion sources work. 55 | 56 | To understand what is a context and how it works see [[https://github.com/alexander-yakushev/compliment/wiki/Context][Context]] wiki 57 | page. 58 | 59 | How to write your own sources is explained on [[https://github.com/alexander-yakushev/compliment/wiki/Custom-sources][Custom sources]] page. 60 | 61 | ** Changelog 62 | 63 | You can view the significant changes on the [[https://github.com/alexander-yakushev/compliment/releases][Releases]] page. 64 | 65 | ** License 66 | 67 | Copyright © 2013-2015 Alexander Yakushev. Distributed under the Eclipse 68 | Public License, the same as Clojure. See [[https://github.com/alexander-yakushev/compliment/blob/master/LICENSE][LICENSE]]. 69 | -------------------------------------------------------------------------------- /venom/complete/actions.clj: -------------------------------------------------------------------------------- 1 | #:vimpire 2 | {:complete 3 | (vimpire.complete/completions #unrepl/param :prefix #unrepl/param :nspace)} 4 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/context.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.context 2 | "Utilities for parsing and storing the current completion context." 3 | (:require [clojure.walk :refer [walk]])) 4 | 5 | (defn- restore-map-literals [context] 6 | (clojure.walk/postwalk (fn [el] 7 | (if (and (sequential? el) 8 | (= (first el) 'compliment-hashmap)) 9 | (apply hash-map 10 | (if (even? (count el)) 11 | (concat (rest el) [nil]) 12 | (rest el))) 13 | el)) context)) 14 | 15 | (defn- safe-read-context-string [^String context] 16 | (try (-> context 17 | (.replace "{" "(compliment-hashmap ") 18 | (.replace "}" ")") 19 | read-string 20 | restore-map-literals) 21 | (catch Exception ex nil))) 22 | 23 | (def ^{:doc "Stores the last completion context." 24 | :private true} 25 | previous-context (atom nil)) 26 | 27 | (def ^{:doc "Special symbol which substitutes prefix in the context, 28 | so the former can be found unambiguously."} 29 | prefix-placeholder '__prefix__) 30 | 31 | (defn parse-context 32 | "Takes a context which is a Lisp form and returns a transformed context. 33 | 34 | The result is a list of maps, each map represents a level of the 35 | context from inside to outside. Map has `:idx` and `:form` values, 36 | and `:map-role` if the level is a map. `:idx` defines the position 37 | of prefix (or the form containing prefix) on the current 38 | level (number for lists and vectors, key or value for maps). 39 | 40 | Example: `(dotimes [i 10] ({:foo {:baz __prefix__}, :bar 42} :quux))` 41 | 42 | Transformed it looks like: 43 | 44 | `({:idx :baz, :map-role :value, :form {:baz __prefix__}} 45 | {:idx :foo, :map-role :key, :form {:foo {:baz __prefix__}, :bar 42}} 46 | {:idx 0, :form ({:foo {:baz __prefix__}, :bar 42} :quux)} 47 | {:idx 2, :form (dotimes [i 10] ({:foo {:baz __prefix__}, :bar 42} :quux))})`." 48 | [context] 49 | (let [parse (fn parse [ctx] 50 | (cond 51 | (sequential? ctx) 52 | (when-let [res (first (keep-indexed (fn [idx el] 53 | (when-let [p (parse el)] 54 | [idx p])) 55 | ctx))] 56 | (cons {:idx (first res) :form ctx} (second res))) 57 | 58 | (map? ctx) 59 | (when-let [res (first (keep (fn [[k v]] 60 | (if-let [p (parse v)] 61 | [k :value p] 62 | (when-let [p (parse k)] 63 | [v :key p]))) 64 | ctx))] 65 | (cons {:idx (first res) :map-role (second res) :form ctx} 66 | (nth res 2))) 67 | 68 | (string? ctx) 69 | (let [idx (.indexOf ^String ctx (name prefix-placeholder))] 70 | (when (>= idx 0) 71 | [{:idx idx :form ctx}])) 72 | 73 | (= ctx prefix-placeholder) ())) 74 | parsed (parse context)] 75 | (when parsed 76 | (reverse parsed)))) 77 | 78 | (defn cache-context 79 | "Parses the context, or returns one from cache if it was unchanged." 80 | [context-string] 81 | (let [context (safe-read-context-string context-string)] 82 | (when-not (= context :same) 83 | (reset! previous-context (parse-context context)))) 84 | @previous-context) 85 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/core.clj: -------------------------------------------------------------------------------- 1 | ;; ## Compliment - a completion library you deserve. 2 | ;; This library provides a fast and extensible way to complete symbols in your 3 | ;; editor. It is intended to be maximally editor-agnostic where 4 | ;; possible, to avoid duplicating implementation in different clients. 5 | 6 | (ns compliment.core 7 | "Core namespace. Most interactions with Compliment should happen 8 | through functions defined here." 9 | (:require (compliment.sources ns-mappings 10 | namespaces-and-classes 11 | class-members 12 | keywords 13 | special-forms 14 | local-bindings 15 | resources) 16 | [compliment.sources :refer [all-sources]] 17 | [compliment.context :refer [cache-context]] 18 | [compliment.utils :refer [*extra-metadata*]] 19 | [clojure.string :refer [join]]) 20 | (:import java.util.Comparator)) 21 | 22 | (def all-files 23 | "List of all Compliment files in an order they should be loaded. This is 24 | required by REPLy." 25 | (map (partial format "compliment/%s.clj") 26 | ["utils" "context" "sources" "sources/class_members" 27 | "sources/ns_mappings" "sources/namespaces_and_classes" 28 | "sources/keywords" "sources/special_forms" "sources/local_bindings" 29 | "sources/resources" 30 | "core"])) 31 | 32 | (def ^:private by-length-comparator 33 | (reify Comparator 34 | (compare [_ s1 s2] 35 | (let [res (compare (count s1) (count s2))] 36 | (if (zero? res) 37 | (compare s1 s2) 38 | res))))) 39 | 40 | (defn sort-by-length 41 | "Sorts list of strings by their length first, and then alphabetically if 42 | length is equal. Works for tagged and non-tagged results." 43 | [candidates] 44 | (sort-by :candidate by-length-comparator candidates)) 45 | 46 | (defn ensure-ns 47 | "Takes either a namespace object or a symbol and returns the corresponding 48 | namespace if it exists, otherwise returns `user` namespace." 49 | [nspc] 50 | (cond (instance? clojure.lang.Namespace nspc) nspc 51 | (symbol? nspc) (or (find-ns nspc) (find-ns 'user) *ns*) 52 | :else *ns*)) 53 | 54 | (defn completions 55 | "Returns a list of completions for the given prefix. Options map can contain 56 | the following options: 57 | - :ns - namespace where completion is initiated; 58 | - :context - code form around the prefix; 59 | - :sort-order (either :by-length or :by-name); 60 | - :plain-candidates - if true, returns plain strings instead of maps; 61 | - :extra-metadata - set of extra fields to add to the maps; 62 | - :sources - list of source keywords to use." 63 | ([prefix] 64 | (completions prefix {})) 65 | ([prefix options-map] 66 | (if (string? options-map) 67 | (completions prefix {:context options-map}) 68 | (let [{:keys [context sort-order sources extra-metadata] 69 | :or {sort-order :by-length}} options-map 70 | nspc (ensure-ns (:ns options-map)) 71 | options-map (assoc options-map :ns nspc) 72 | ctx (cache-context context) 73 | sort-fn (if (= sort-order :by-name) 74 | (partial sort-by :candidate) 75 | (partial sort-by-length true))] 76 | (binding [*extra-metadata* extra-metadata] 77 | (let [candidate-fns (keep (fn [[_ src]] 78 | (when (:enabled src) 79 | (:candidates src))) 80 | (if sources 81 | (all-sources sources) 82 | (all-sources))) 83 | candidates (mapcat 84 | (fn [f] (f prefix nspc ctx)) 85 | candidate-fns) 86 | sorted-cands (if (= sort-order :by-name) 87 | (sort-by 88 | :candidate 89 | candidates) 90 | (sort-by 91 | :candidate by-length-comparator 92 | candidates)) 93 | cands (if (:plain-candidates options-map) 94 | (map :candidate sorted-cands) 95 | sorted-cands)] 96 | (doall cands))))))) 97 | 98 | (defn documentation 99 | "Returns a documentation string that describes the given symbol." 100 | ([symbol-str] 101 | (documentation symbol-str *ns*)) 102 | ([symbol-str ns] 103 | (if (empty? symbol-str) 104 | "" 105 | (->> (for [[_ {:keys [doc enabled]}] (all-sources) 106 | :when enabled 107 | :let [docstr (doc symbol-str (ensure-ns ns))] 108 | :when docstr] 109 | docstr) 110 | (interpose "\n\n") 111 | join)))) 112 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources 2 | "Tools for defining sources for the completion.") 3 | 4 | (def ^{:doc "Stores defined sources." 5 | :private true} 6 | sources (atom nil)) 7 | 8 | (defn all-sources 9 | "Returns the list of all completion sources, or the selected once specified by 10 | `source-kws`." 11 | ([] @sources) 12 | ([source-kws] 13 | (select-keys @sources source-kws))) 14 | 15 | (defn defsource 16 | "Defines a source with the given name and argument map. Map must 17 | contain two keys - `:candidates` and `:doc`. 18 | 19 | Value of `:candidates`should be a function of prefix, namespace and 20 | context. 21 | 22 | Value of `:doc` latter should be a function of symbol name and 23 | namespace." 24 | [name & {:as kw-args}] 25 | {:pre [(every? kw-args [:candidates :doc])]} 26 | (swap! sources assoc name (assoc kw-args :enabled true))) 27 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources/class_members.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources.class-members 2 | "Completion for both static and non-static class members." 3 | (:require [compliment.sources :refer [defsource]] 4 | [compliment.utils :refer [fuzzy-matches-no-skip? resolve-class]] 5 | [clojure.string :refer [join]]) 6 | (:import [java.lang.reflect Method Field Member Modifier])) 7 | 8 | (defn static? 9 | "Tests if class member is static." 10 | [^Member member] 11 | (Modifier/isStatic (.getModifiers member))) 12 | 13 | ;; ## Regular (non-static) members 14 | 15 | (def ^{:doc "Stores cache of all non-static members for every 16 | namespace."} 17 | members-cache (atom {})) 18 | 19 | (defn populate-members-cache 20 | "Populates members cache for a given namespace. `classes-cnt` is a 21 | number that indicates the current number of imported classes in this 22 | namespace." 23 | [ns classes-cnt] 24 | (loop [cache (transient {}) 25 | 26 | [^Member c & r] 27 | (for [^Class class (vals (ns-map ns)) 28 | :when (class? class) 29 | ^Member member (concat (.getMethods class) (.getFields class)) 30 | :when (not (static? member))] 31 | (let [dc (.getDeclaringClass member)] 32 | (if (= dc class) 33 | member 34 | (if (instance? Method member) 35 | (.getMethod dc (.getName member) 36 | (.getParameterTypes ^Method member)) 37 | (.getField dc (.getName member))))))] 38 | (if c 39 | (let [full-name (.getName c)] 40 | (if (cache full-name) 41 | (recur (assoc! cache full-name (conj (cache (.getName c)) c)) r) 42 | (recur (assoc! cache full-name [c]) r))) 43 | (swap! members-cache assoc ns {:classes-cnt classes-cnt 44 | :methods (persistent! cache)})))) 45 | 46 | (defn update-cache 47 | "Updates members cache for a given namespace if necessary." 48 | [ns] 49 | (let [imported-cls-cnt (count (filter class? (vals (ns-map *ns*))))] 50 | (when (or (nil? (@members-cache ns)) 51 | (not= (get-in @members-cache [ns :classes-cnt]) 52 | imported-cls-cnt)) 53 | (populate-members-cache ns imported-cls-cnt)))) 54 | 55 | (defn get-all-members 56 | "Returns all non-static members for a given namespace." 57 | [ns] 58 | (update-cache ns) 59 | (get-in @members-cache [ns :methods])) 60 | 61 | (defn class-member-symbol? 62 | "Tests if a symbol name looks like a non-static class member." 63 | [^String x] 64 | (.startsWith x ".")) 65 | 66 | (defn camel-case-matches? 67 | "Tests if prefix matches the member name following camel case rules. 68 | Thus, prefix `getDeF` matches member `getDeclaredFields`." 69 | [prefix member-name] 70 | (fuzzy-matches-no-skip? prefix member-name #(Character/isUpperCase ^char %))) 71 | 72 | (defn try-get-object-class 73 | "Tries to get the type of the object from the context, which the 74 | member will be applied to. Object should be a Var." 75 | [ns context] 76 | (when (= (:idx (first context)) 0) 77 | (let [sym (second (:form (first context)))] 78 | (when (and (symbol? sym) 79 | (= (type (ns-resolve ns sym)) clojure.lang.Var)) 80 | (type (deref (ns-resolve ns sym))))))) 81 | 82 | (defn members-candidates 83 | "Returns a list of Java non-static fields and methods candidates." 84 | [prefix ns context] 85 | (when (class-member-symbol? prefix) 86 | (let [prefix (subs prefix 1) 87 | inparts? (re-find #"[A-Z]" prefix) 88 | klass (try-get-object-class ns context)] 89 | (for [[member-name members] (get-all-members ns) 90 | :when (if inparts? 91 | (camel-case-matches? prefix member-name) 92 | (.startsWith ^String member-name prefix)) 93 | :when 94 | (or (not klass) 95 | (some #(= klass (.getDeclaringClass ^Member %)) members))] 96 | {:candidate (str "." member-name) 97 | :type (if (instance? Method (first members)) 98 | :method :field)})))) 99 | 100 | ;; ### Member documentation 101 | 102 | (defn type-to-pretty-string 103 | "Takes a type (either a class or a primitive) and returns it's 104 | human-readable name." 105 | [^Class t] 106 | (if (or (.isLocalClass t) 107 | (.isMemberClass t)) 108 | (.getName t) 109 | (.getSimpleName t))) 110 | 111 | (defn doc-method-parameters 112 | "Takes a list of method parameters and stringifies it." 113 | [parameters] 114 | (->> parameters 115 | (map type-to-pretty-string) 116 | (interpose " ") 117 | join 118 | (format "(%s)"))) 119 | 120 | (defn create-members-doc 121 | "Takes a list of members (presumably with the same name) and turns 122 | them into a docstring." 123 | [members] 124 | (->> members 125 | (group-by (fn [^Member m] (.getDeclaringClass m))) 126 | (map (fn [[^Class class, members]] 127 | (let [^Member f-mem (first members)] 128 | (str (.getName class) "." (.getName f-mem) 129 | (if (instance? Field f-mem) 130 | (str " = " (try (.get ^Field f-mem nil) 131 | (catch Exception e "?")) 132 | " (" (type-to-pretty-string (.getType ^Field f-mem)) ")\n" 133 | (Modifier/toString (.getModifiers f-mem))) 134 | (join 135 | (map (fn [^Method member] 136 | (when (instance? Method member) 137 | (str "\n " (doc-method-parameters (.getParameterTypes member)) 138 | " -> " (type-to-pretty-string (.getReturnType ^Method member)) 139 | " (" (Modifier/toString (.getModifiers member)) ")"))) 140 | (distinct members)))) 141 | "\n")))) 142 | (interpose "\n") 143 | join)) 144 | 145 | (defn members-doc 146 | "Documentation function for non-static members." 147 | [member-str ns] 148 | (when (class-member-symbol? member-str) 149 | (update-cache ns) 150 | (when-let [member (get-in @members-cache [ns :methods (subs member-str 1)])] 151 | (create-members-doc member)))) 152 | 153 | (defn classname-doc [^Class class] 154 | (let [members (group-by static? (concat (.getMethods class) 155 | (.getFields class))) 156 | [static non-static] (for [flag [true false]] 157 | (->> (for [^Member m (members flag)] 158 | (.getName m)) 159 | distinct 160 | (interpose ", ") 161 | join))] 162 | (str (.getName class) "\n\n" 163 | " Non-static members:\n " non-static "\n\n" 164 | " Static members:\n " static "\n"))) 165 | 166 | (defsource ::members 167 | :candidates #'members-candidates 168 | :doc #'members-doc 169 | :tag-fn (fn [m {:keys [ns]}] 170 | (assoc m :type (if (->> (get-in @members-cache [ns :methods 171 | (subs (:candidate m) 1)]) 172 | first 173 | (instance? Method)) 174 | :method :field)))) 175 | 176 | ;; ## Static members 177 | 178 | (defn static-member-symbol? 179 | "Tests if prefix looks like a static member symbol." 180 | [x] 181 | (re-matches #"[^\/\:\.][^\:]*\/.*" x)) 182 | 183 | (def ^{:doc "Stores cache of all static members for every class."} 184 | static-members-cache (atom {})) 185 | 186 | (defn populate-static-members-cache 187 | "Populates static members cache for a given class." 188 | [^Class class] 189 | (loop [cache {}, [^Member c & r] (concat (.getMethods class) 190 | (.getFields class))] 191 | (if c 192 | (if (static? c) 193 | (let [full-name (.getName c)] 194 | (if (cache (.getName c)) 195 | (recur (update-in cache [full-name] conj c) r) 196 | (recur (assoc cache full-name [c]) r))) 197 | (recur cache r)) 198 | (swap! static-members-cache assoc class cache)))) 199 | 200 | (defn update-static-cache 201 | "Updates static members cache for a given class if necessary." 202 | [class] 203 | (when-not (@static-members-cache class) 204 | (populate-static-members-cache class))) 205 | 206 | (defn static-members 207 | "Returns all static members for a given class." 208 | [^Class class] 209 | (update-static-cache class) 210 | (@static-members-cache class)) 211 | 212 | (defn static-members-candidates 213 | "Returns a list of static member candidates." 214 | [^String prefix, ns context] 215 | (when (static-member-symbol? prefix) 216 | (let [[cl-name member-prefix] (.split prefix "/") 217 | cl (resolve-class ns (symbol cl-name)) 218 | member-prefix (or member-prefix "")] 219 | (when cl 220 | (let [inparts? (re-find #"[A-Z]" member-prefix)] 221 | (for [[^String member-name members] (static-members cl) 222 | :when (if inparts? 223 | (camel-case-matches? member-prefix member-name) 224 | (.startsWith member-name member-prefix))] 225 | {:candidate (str cl-name "/" member-name) 226 | :type (if (instance? Method (first members)) 227 | :static-method :static-field)})))))) 228 | 229 | (defn resolve-static-member 230 | "Given a string representation of a static member returns Member object." 231 | [^String member-str ns] 232 | (let [[cl-name member-name] (.split member-str "/") 233 | cl (resolve-class ns (symbol cl-name))] 234 | (when cl 235 | (update-static-cache cl) 236 | (get-in @static-members-cache [cl member-name])))) 237 | 238 | (defn static-member-doc 239 | "Given a member name and class returns its docstring." 240 | [member-str ns] 241 | (when (static-member-symbol? member-str) 242 | (let [member (resolve-static-member member-str ns)] 243 | (when member 244 | (create-members-doc member))))) 245 | 246 | (defsource ::static-members 247 | :candidates #'static-members-candidates 248 | :doc #'static-member-doc) 249 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources/keywords.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources.keywords 2 | "Completion for keywords interned globally across the application" 3 | (:require [compliment.sources :refer [defsource]] 4 | [compliment.utils :refer [defmemoized resolve-namespace]]) 5 | (:import java.lang.reflect.Field)) 6 | 7 | (defmemoized ^:private keywords-table 8 | [] 9 | (let [^Field field (.getDeclaredField clojure.lang.Keyword "table")] 10 | (.setAccessible field true) 11 | (.get field nil))) 12 | 13 | (defn- tagged-candidate [c] 14 | {:candidate c, :type :keyword}) 15 | 16 | (defn qualified-candidates 17 | "Returns a list of namespace-qualified double-colon keywords (like ::foo) 18 | resolved for the given namespace." 19 | [prefix ns] 20 | (let [prefix (subs prefix 2) 21 | ns-name (str ns)] 22 | (for [[kw _] (keywords-table) 23 | :when (= (namespace kw) ns-name) 24 | :when (.startsWith (name kw) prefix)] 25 | (tagged-candidate (str "::" (name kw)))))) 26 | 27 | (defn namespace-alias-candidates 28 | "Returns a list of namespace aliases prefixed by double colon required in the 29 | given namespace." 30 | [prefix ns] 31 | (let [prefix (subs prefix 2) 32 | ns-name (str ns)] 33 | (for [[alias _] (ns-aliases ns) 34 | :let [aname (name alias)] 35 | :when (.startsWith aname prefix)] 36 | (tagged-candidate (str "::" aname))))) 37 | 38 | (defn aliased-candidates 39 | "Returns a list of alias-qualified double-colon keywords (like ::str/foo), 40 | where alias has to be registered in the given namespace." 41 | [prefix ns] 42 | (when-let [[_ alias prefix] (re-matches #"::([^/]+)/(.*)" prefix)] 43 | (let [alias-ns-name (str (resolve-namespace (symbol alias) ns))] 44 | (for [[kw _] (keywords-table) 45 | :when (= (namespace kw) alias-ns-name) 46 | :when (.startsWith (name kw) prefix)] 47 | (tagged-candidate (str "::" alias "/" (name kw))))))) 48 | 49 | (defn candidates 50 | [^String prefix, ns _] 51 | (let [single-colon? (.startsWith prefix ":") 52 | double-colon? (.startsWith prefix "::") 53 | has-slash? (> (.indexOf prefix "/") -1)] 54 | (cond (and double-colon? has-slash?) (aliased-candidates prefix ns) 55 | double-colon? (concat (qualified-candidates prefix ns) 56 | (namespace-alias-candidates prefix ns)) 57 | single-colon? (for [[kw _] (keywords-table) 58 | :when (.startsWith (str kw) (subs prefix 1))] 59 | (tagged-candidate (str ":" kw)))))) 60 | 61 | (defsource ::keywords 62 | :candidates #'candidates 63 | :doc (constantly nil)) 64 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources/local_bindings.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources.local-bindings 2 | "Completion source for local bindings introduced by defn, let and the like." 3 | (:require [compliment.sources :refer [defsource]] 4 | [compliment.sources.ns-mappings :refer [var-symbol? dash-matches?]])) 5 | 6 | (def let-like-forms '#{let if-let when-let if-some when-some loop with-open 7 | dotimes with-local-vars}) 8 | 9 | (def defn-like-forms '#{defn defn- fn defmacro}) 10 | 11 | (def doseq-like-forms '#{doseq for}) 12 | 13 | (def letfn-like-forms '#{letfn}) 14 | 15 | (defn parse-binding 16 | "Given a binding node returns the list of local bindings introduced by that 17 | node. Handles vector and map destructuring." 18 | [binding-node] 19 | (cond (vector? binding-node) 20 | (mapcat parse-binding binding-node) 21 | 22 | (map? binding-node) 23 | (let [normal-binds (->> (keys binding-node) 24 | (remove keyword?) 25 | (mapcat parse-binding)) 26 | keys-binds (if-let [ks (:keys binding-node)] 27 | (mapv str ks) ()) 28 | as-binds (if-let [as (:as binding-node)] 29 | [(str as)] ())] 30 | (concat normal-binds keys-binds as-binds)) 31 | 32 | (not (#{'& '_} binding-node)) 33 | [(str binding-node)])) 34 | 35 | (defn parse-fn-body 36 | "Extract function name and arglists from the function body, return list of all 37 | completable variables." 38 | [fn-body] 39 | (let [fn-name (when (symbol? (first fn-body)) 40 | (name (first fn-body))) 41 | fn-body (if fn-name (rest fn-body) fn-body)] 42 | (cond-> 43 | (mapcat parse-binding 44 | (loop [[c & r] fn-body, bnodes []] 45 | (cond (nil? c) bnodes 46 | (list? c) (recur r (conj bnodes (first c))) ;; multi-arity case 47 | (vector? c) c ;; single-arity case 48 | :else (recur r bnodes)))) 49 | fn-name (conj fn-name)))) 50 | 51 | (defn extract-local-bindings 52 | "When given a form that has a binding vector traverses that binding vector and 53 | returns the list of all local bindings." 54 | [form] 55 | (when (list? form) 56 | (cond (let-like-forms (first form)) 57 | (mapcat parse-binding (take-nth 2 (second form))) 58 | 59 | (defn-like-forms (first form)) (parse-fn-body (rest form)) 60 | 61 | (letfn-like-forms (first form)) 62 | (mapcat parse-fn-body (second form)) 63 | 64 | (doseq-like-forms (first form)) 65 | (->> (partition 2 (second form)) 66 | (mapcat (fn [[left right]] 67 | (if (= left :let) 68 | (take-nth 2 right) [left]))) 69 | (mapcat parse-binding)) 70 | 71 | (= 'as-> (first form)) [(name (nth form 2))]))) 72 | 73 | (defn bindings-from-context 74 | "Returns all local bindings that are established inside the given context." 75 | [ctx] 76 | (try (distinct (mapcat (comp extract-local-bindings :form) ctx)) 77 | (catch Exception ex ()))) 78 | 79 | (defn candidates 80 | "Returns a list of local bindings inside the context that match prefix." 81 | [prefix _ context] 82 | (when (var-symbol? prefix) 83 | (for [binding (bindings-from-context context) 84 | :when (dash-matches? prefix binding)] 85 | {:candidate binding, :type :local}))) 86 | 87 | (defsource ::local-bindings 88 | :candidates #'candidates 89 | :doc (constantly nil)) 90 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources/namespaces_and_classes.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources.namespaces-and-classes 2 | "Completion for namespace and class names." 3 | (:require [compliment.sources :refer [defsource]] 4 | [compliment.utils :refer [fuzzy-matches?] :as utils] 5 | [compliment.sources.class-members :refer [classname-doc]]) 6 | (:import java.io.File)) 7 | 8 | (defn nscl-symbol? 9 | "Tests if prefix looks like a namespace or classname." 10 | [x] 11 | (re-matches #"[^\/\:\.][^\/\:]+" x)) 12 | 13 | (defn nscl-matches? 14 | "Tests if prefix partially matches a var name with periods as 15 | separators." 16 | [prefix namespace] 17 | (fuzzy-matches? prefix namespace \.)) 18 | 19 | ;;; Obtaining the list of classes 20 | 21 | (defn imported-classes 22 | "Returns names of all classes imported into a given namespace." 23 | [ns] 24 | (for [[_ ^Class val] (ns-map ns) :when (class? val)] 25 | (.getName val))) 26 | 27 | (defn all-classes-short-names 28 | "Returns a map where short classnames are matched with vectors with 29 | package-qualified classnames." 30 | [] 31 | (let [all-classes (utils/classes-on-classpath)] 32 | (utils/cache-last-result ::all-classes-short-names all-classes 33 | (group-by #(-> (re-matches #"([^\.]+\.)*([^\.]+)" %) 34 | (nth 2)) 35 | (reduce into [] (vals all-classes)))))) 36 | 37 | (defn- analyze-import-context 38 | "Checks if the completion is called from ns import declaration. If so, and the 39 | prefix is inside import vector, return that package name, otherwise return 40 | `:root`. If not inside :import, return nil." 41 | [ctx] 42 | (let [ns-decl (:form (last ctx)) 43 | import-list (:form (last (butlast ctx))) 44 | prefix-form (:form (first ctx))] 45 | (when (and (sequential? ns-decl) 46 | (= (first ns-decl) 'ns) 47 | (sequential? import-list) 48 | (= (first import-list) :import)) 49 | (if (= prefix-form import-list) 50 | :root 51 | (str (first prefix-form)))))) 52 | 53 | (defn- get-all-full-names 54 | "Returns a list of package-qualified classnames given a short classname." 55 | [prefix] 56 | (reduce-kv (fn [l, ^String short-name, full-names] 57 | (if (.startsWith short-name prefix) 58 | (concat l (map (fn [c] {:candidate c, :type :class}) 59 | full-names)) 60 | l)) 61 | () 62 | (all-classes-short-names))) 63 | 64 | (defn- get-classes-by-package-name 65 | "Returns simple classnames that match the `prefix` and belong to `pkg-name`." 66 | [prefix pkg-name] 67 | (reduce-kv (fn [l, ^String short-name, full-names] 68 | (if (and (.startsWith short-name prefix) 69 | (some #(.startsWith ^String % pkg-name) full-names)) 70 | (conj l {:candidate short-name, :type :class}) 71 | l)) 72 | () 73 | (all-classes-short-names))) 74 | 75 | (defn candidates 76 | "Returns a list of namespace and classname completions." 77 | [^String prefix, ns context] 78 | (when (nscl-symbol? prefix) 79 | (let [has-dot (> (.indexOf prefix ".") -1) 80 | import-ctx (analyze-import-context context)] 81 | ((comp distinct concat) 82 | (for [ns-str (concat (map (comp name ns-name) (all-ns)) 83 | (map name (keys (ns-aliases ns)))) 84 | :when (nscl-matches? prefix ns-str)] 85 | {:candidate ns-str, :type :namespace}) 86 | (for [class-str (imported-classes ns) 87 | :when (nscl-matches? prefix class-str)] 88 | {:candidate class-str, :type :class}) 89 | (cond (= import-ctx :root) (get-all-full-names prefix) 90 | import-ctx (get-classes-by-package-name prefix import-ctx)) 91 | ;; Fuzziness is too slow for all classes, so just startsWith. 92 | ;; Also have to do clever tricks to keep the performance high. 93 | (if has-dot 94 | (concat (for [[root-pkg classes] (utils/classes-on-classpath) 95 | :when (.startsWith prefix root-pkg) 96 | ^String cl-str classes 97 | :when (.startsWith cl-str prefix)] 98 | {:candidate cl-str, :type :class}) 99 | (for [ns-str (utils/namespaces-on-classpath) 100 | :when (nscl-matches? prefix ns-str)] 101 | {:candidate ns-str, :type :namespace})) 102 | (concat (for [[^String root-pkg _] (utils/classes-on-classpath) 103 | :when (.startsWith root-pkg prefix)] 104 | {:candidate (str root-pkg "."), :type :class}) 105 | (for [^String ns-str (utils/namespaces-on-classpath) 106 | :when (.startsWith ns-str prefix)] 107 | {:candidate ns-str, :type :namespace}))))))) 108 | 109 | (defn doc [ns-or-class-str curr-ns] 110 | (when (nscl-symbol? ns-or-class-str) 111 | (if-let [ns (find-ns (symbol ns-or-class-str))] 112 | (str ns "\n" (:doc (meta ns)) "\n") 113 | (when-let [class (try (ns-resolve curr-ns (symbol ns-or-class-str)) 114 | (catch Exception ex nil))] 115 | (when (= (type class) Class) 116 | (classname-doc class)))))) 117 | 118 | (defsource ::namespaces-and-classes 119 | :candidates #'candidates 120 | :doc #'doc) 121 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources/ns_mappings.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources.ns-mappings 2 | "Completion for vars and classes in the current namespace." 3 | (:require [compliment.sources :refer [defsource]] 4 | [compliment.utils :refer [fuzzy-matches? resolve-namespace 5 | *extra-metadata*]]) 6 | (:import java.io.StringWriter)) 7 | 8 | (defn var-symbol? 9 | "Test if prefix resembles a var name." 10 | [x] 11 | (re-matches #"([^\/\:][^\.\/]*([^\/\:]*\/[^\.\/]*)?)?" x)) 12 | 13 | (defn dash-matches? 14 | "Tests if prefix partially matches a var name with dashes as 15 | separators." 16 | [prefix var] 17 | (fuzzy-matches? prefix var \-)) 18 | 19 | (defn get-scope-and-prefix 20 | "Tries to get take apart scope namespace and prefix in prefixes like 21 | `scope/var`." 22 | [^String s, ns] 23 | (let [[scope-name sym] (if (> (.indexOf s "/") -1) 24 | (.split s "/") ()) 25 | scope (when scope-name 26 | (resolve-namespace (symbol scope-name) ns)) 27 | prefix (if scope 28 | (or sym "") s)] 29 | [scope-name scope prefix])) 30 | 31 | (defn try-get-ns-from-context 32 | "Tries to extract a namespace name if context is a `ns` definition." 33 | [context] 34 | (let [[var-list ns-def use-def top-form] context] 35 | (when (and (sequential? (:form var-list)) 36 | (= (first (:form top-form)) 'ns) 37 | (or (and (= (first (:form use-def)) :use) 38 | (= (second (:form ns-def)) :only)) 39 | (and (= (first (:form use-def)) :require) 40 | (= (second (:form ns-def)) :refer)))) 41 | (find-ns (first (:form ns-def)))))) 42 | 43 | (defn generate-docstring 44 | "Generates a docstring from a given var metadata. Copied from 45 | `clojure.repl` with some minor modifications." 46 | [m] 47 | (binding [*out* (StringWriter.)] 48 | (println (str (when-let [ns (:ns m)] (str (ns-name ns) "/")) (:name m))) 49 | (cond 50 | (:forms m) (doseq [f (:forms m)] 51 | (print " ") 52 | (prn f)) 53 | (:arglists m) (prn (:arglists m))) 54 | (if (:special-form m) 55 | (do 56 | (println "Special Form") 57 | (println " " (:doc m)) 58 | (if (contains? m :url) 59 | (when (:url m) 60 | (println (str "\n Please see http://clojure.org/" (:url m)))) 61 | (println (str "\n Please see http://clojure.org/special_forms#" 62 | (:name m))))) 63 | (do 64 | (when (:macro m) 65 | (println "Macro")) 66 | (println " " (:doc m)))) 67 | (str *out*))) 68 | 69 | (defn candidates 70 | "Returns a list of namespace-bound candidates, with namespace being 71 | either the scope (if prefix is scoped), `ns` arg or the namespace 72 | extracted from context if inside `ns` declaration." 73 | [^String prefix, ns context] 74 | (when (var-symbol? prefix) 75 | (let [[scope-name scope ^String prefix] (get-scope-and-prefix prefix ns) 76 | ns-form-namespace (try-get-ns-from-context context) 77 | vars (cond 78 | scope (ns-publics scope) 79 | ns-form-namespace (ns-publics ns-form-namespace) 80 | :else (ns-map ns))] 81 | (for [[var-sym var] vars 82 | :let [var-name (name var-sym) 83 | {:keys [arglists doc] :as var-meta} (meta var)] 84 | :when (dash-matches? prefix var-name)] 85 | (if (= (type var) Class) 86 | {:candidate var-name, :type :class, 87 | :package (when-let [pkg (.getPackage ^Class var)] 88 | ;; Some classes don't have a package 89 | (.getName ^Package pkg))} 90 | 91 | (cond-> {:candidate (if scope 92 | (str scope-name "/" var-name) 93 | var-name) 94 | :type (cond (:macro var-meta) :macro 95 | arglists :function 96 | :else :var) 97 | :ns (str (or (:ns var-meta) ns))} 98 | (and arglists(:arglists *extra-metadata*)) 99 | (assoc :arglists (apply list (map pr-str arglists))) 100 | 101 | (and doc (:doc *extra-metadata*)) 102 | (assoc :doc (generate-docstring var-meta)))))))) 103 | 104 | (defn doc 105 | "Documentation function for this sources' completions." 106 | [symbol-str ns] 107 | (if (var-symbol? symbol-str) 108 | (when-let [var (ns-resolve ns (symbol symbol-str))] 109 | (when (meta var) 110 | (generate-docstring (meta var)))))) 111 | 112 | (defsource ::ns-mappings 113 | :candidates #'candidates 114 | :doc #'doc) 115 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources/resources.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources.resources 2 | "Completion for bundled resource files." 3 | (:require [clojure.java.io :as io] 4 | [compliment.sources :refer [defsource]] 5 | [compliment.utils :as utils]) 6 | (:import java.io.File 7 | java.net.URLConnection)) 8 | 9 | (defn inside-resource-call? 10 | "If context is not nil, check if prefix inside the string in a 11 | clojure.java.io/resource call." 12 | [ctx] 13 | (when (and ctx) 14 | (let [[str call] ctx 15 | fn (first (:form call))] 16 | (and (string? (:form str)) 17 | (list? (:form call)) 18 | (symbol? fn) 19 | (= (name fn) "resource"))))) 20 | 21 | (defn candidates 22 | "Returns list of completions for project resources if within certain context." 23 | [prefix _ context] 24 | (when (inside-resource-call? context) 25 | (for [^String res (utils/project-resources) 26 | :when (.startsWith res prefix)] 27 | {:candidate res 28 | :type :resource}))) 29 | 30 | (defn doc 31 | "Documentation function for project resources." 32 | [resource-name _] 33 | (try (let [^String filename (.getFile (io/resource resource-name))] 34 | (format "File type: %s, size: %d bytes" 35 | (or (URLConnection/guessContentTypeFromName filename) 36 | "application/unknown") 37 | (.length (io/file filename)))) 38 | (catch Exception ex nil))) 39 | 40 | (defsource ::resources 41 | :candidates #'candidates 42 | :doc #'doc) 43 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/sources/special_forms.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.sources.special-forms 2 | "Completion for Clojure's special forms." 3 | (:require [clojure.repl :as repl] 4 | [compliment.sources :refer [defsource]] 5 | [compliment.sources.ns-mappings :as vars])) 6 | 7 | (def ^:private special-forms 8 | (set (map name '[def if do quote var recur throw try catch 9 | monitor-enter monitor-exit new set!]))) 10 | 11 | (defn first-item-in-list? 12 | "If context is not nil, check if prefix is the first item in a list form." 13 | [ctx] 14 | (if ctx 15 | (when-let [expr (first ctx)] 16 | (and (list? (:form expr)) (= (:idx expr) 0))) 17 | true)) 18 | 19 | (defn candidates 20 | "Returns list of completions for special forms." 21 | [prefix _ context] 22 | (when (and (vars/var-symbol? prefix) (first-item-in-list? context)) 23 | (for [form special-forms 24 | :when (vars/dash-matches? prefix form)] 25 | {:candidate form 26 | :type :special-form}))) 27 | 28 | (defn doc 29 | "Documentation function for special forms." 30 | [symbol-str _] 31 | (when (and (vars/var-symbol? symbol-str) (special-forms symbol-str)) 32 | (vars/generate-docstring (#'repl/special-doc (symbol symbol-str))))) 33 | 34 | (defsource ::special-forms 35 | :candidates #'candidates 36 | :doc #'doc) 37 | 38 | (defn literal-candidates 39 | "We define `true`, `false`, and `nil` in a separate source because they are 40 | not context-dependent (don't have to be first items in the list)." 41 | [prefix _ __] 42 | (->> ["true" "false" "nil"] 43 | (filter #(.startsWith ^String % prefix)) 44 | (map (fn [c] {:candidate c, :type :special-form})))) 45 | 46 | (defsource ::literals 47 | :candidates #'literal-candidates 48 | :doc (constantly nil)) 49 | -------------------------------------------------------------------------------- /venom/complete/src/compliment/utils.clj: -------------------------------------------------------------------------------- 1 | (ns compliment.utils 2 | "Functions and utilities for source implementations." 3 | (:import java.io.File java.nio.file.Files 4 | [java.util.jar JarFile JarEntry])) 5 | 6 | (def ^:dynamic *extra-metadata* 7 | "Signals to downstream sources which additional information about completion 8 | candidates they should attach . Should be a set of keywords." 9 | nil) 10 | 11 | (defn fuzzy-matches? 12 | "Tests if symbol matches the prefix when symbol is split into parts on 13 | separator." 14 | [prefix, ^String symbol, separator] 15 | (when (or (.startsWith symbol prefix) (= (first prefix) (first symbol))) 16 | (loop [pre (rest prefix), sym (rest symbol), skipping false] 17 | (cond (empty? pre) true 18 | (empty? sym) false 19 | skipping (if (= (first sym) separator) 20 | (recur (if (= (first pre) separator) 21 | (rest pre) pre) 22 | (rest sym) false) 23 | (recur pre (rest sym) true)) 24 | (= (first pre) (first sym)) (recur (rest pre) (rest sym) false) 25 | :else (recur pre (rest sym) (not= (first sym) separator)))))) 26 | 27 | (defn fuzzy-matches-no-skip? 28 | "Tests if symbol matches the prefix where separator? checks whether character 29 | is a separator. Unlike `fuzzy-matches?` requires separator characters to be 30 | present in prefix." 31 | [prefix, ^String symbol, separator?] 32 | (when (or (.startsWith symbol prefix) (= (first prefix) (first symbol))) 33 | (loop [pre prefix, sym symbol, skipping false] 34 | (cond (empty? pre) true 35 | (empty? sym) false 36 | skipping (if (separator? (first sym)) 37 | (recur pre sym false) 38 | (recur pre (rest sym) true)) 39 | (= (first pre) (first sym)) (recur (rest pre) (rest sym) false) 40 | :else (recur pre (rest sym) true))))) 41 | 42 | (defn resolve-class 43 | "Tries to resolve a classname from the given symbol, or returns nil 44 | if classname can't be resolved." 45 | [ns sym] 46 | (when-let [val (try (ns-resolve ns sym) 47 | (catch ClassNotFoundException ex nil))] 48 | (when (class? val) val))) 49 | 50 | (defn resolve-namespace 51 | "Tries to resolve a namespace from the given symbol, either from a 52 | fully qualified name or an alias in the given namespace." 53 | [sym ns] 54 | (or ((ns-aliases ns) sym) (find-ns sym))) 55 | 56 | (defmacro ^{:doc "Defines a memoized function." 57 | :forms '([name doc-string? [params*] body])} 58 | defmemoized [name & fdecl] 59 | (let [[doc & fdecl] (if (string? (first fdecl)) 60 | [(first fdecl) (rest fdecl)] 61 | ["" fdecl])] 62 | `(def ~name ~doc (memoize (fn ~@fdecl))))) 63 | 64 | (def primitive-cache (atom {})) 65 | 66 | (defmacro cache-last-result 67 | "If cache for `name` is absent, or `key` doesn't match the key in the cache, 68 | calculate `v` and return it. Else return value from cache." 69 | {:style/indent 2} 70 | [name key value] 71 | (let [ksym ()] 72 | `(let [name# ~name 73 | key# ~key 74 | [cached-key# cached-value#] (@primitive-cache name#)] 75 | (if (and (contains? @primitive-cache name#) (= cached-key# key#)) 76 | cached-value# 77 | (let [value# ~value] 78 | (swap! primitive-cache assoc name# [key# value#]) 79 | value#))))) 80 | 81 | (defn flush-caches 82 | "Removes all cached values, forcing functions that depend on 83 | `cache-last-result` to recalculate." 84 | [] 85 | (reset! primitive-cache {})) 86 | 87 | ;; Classpath inspection 88 | 89 | (def android-vm? 90 | "Signifies if the application is running on Android." 91 | (.contains ^String (System/getProperty "java.vendor") "Android")) 92 | 93 | (defn- classpath 94 | "Returns a sequence of File objects of the elements on the classpath." 95 | [] 96 | (if android-vm? 97 | () 98 | (mapcat #(.split (or (System/getProperty %) "") File/pathSeparator) 99 | ["sun.boot.class.path" "java.ext.dirs" "java.class.path" 100 | ;; This is where Boot keeps references to dependencies. 101 | "fake.class.path"]))) 102 | 103 | (defn- symlink? 104 | "Checks if the given file is a symlink." 105 | [^File f] 106 | (Files/isSymbolicLink (.toPath f))) 107 | 108 | (defn- file-seq-nonr 109 | "A tree seq on java.io.Files, doesn't resolve symlinked directories to avoid 110 | infinite sequence resulting from recursive symlinked directories." 111 | [dir] 112 | (tree-seq 113 | (fn [^File f] (and (.isDirectory f) (not (symlink? f)))) 114 | (fn [^File d] (seq (.listFiles d))) 115 | dir)) 116 | 117 | (defn- list-files 118 | "Given a path (either a jar file, directory with classes or directory with 119 | paths) returns all files under that path." 120 | [^String path, scan-jars?] 121 | (cond (.endsWith path "/*") 122 | (for [^File jar (.listFiles (File. path)) 123 | :when (.endsWith ^String (.getName jar) ".jar") 124 | file (list-files (.getPath jar) scan-jars?)] 125 | file) 126 | 127 | (.endsWith path ".jar") 128 | (if scan-jars? 129 | (try (for [^JarEntry entry (enumeration-seq (.entries (JarFile. path))) 130 | :when (not (.isDirectory entry))] 131 | (.getName entry)) 132 | (catch Exception e)) 133 | ()) 134 | 135 | (= path "") () 136 | 137 | :else 138 | (for [^File file (file-seq-nonr (File. path)) 139 | :when (not (.isDirectory file))] 140 | (.replace ^String (.getPath file) path "")))) 141 | 142 | (defn- all-files-on-classpath 143 | "Given a list of files on the classpath, returns the list of all files, 144 | including those located inside jar files." 145 | [classpath] 146 | (cache-last-result ::all-files-on-classpath classpath 147 | (mapcat #(list-files % true) classpath))) 148 | 149 | (defn classes-on-classpath 150 | "Returns a map of all classes that can be located on the classpath. Key 151 | represent the root package of the class, and value is a list of all classes 152 | for that package." 153 | [] 154 | (let [classpath (classpath)] 155 | (cache-last-result ::classes-on-classpath classpath 156 | (->> (for [^String file (all-files-on-classpath classpath) 157 | :when (and (.endsWith file ".class") (not (.contains file "__")) 158 | (not (.contains file "$")))] 159 | (.. (if (.startsWith file File/separator) 160 | (.substring file 1) file) 161 | (replace ".class" "") (replace File/separator "."))) 162 | (group-by #(subs % 0 (max (.indexOf ^String % ".") 0))))))) 163 | 164 | (defn namespaces-on-classpath 165 | "Returns the list of all Clojure namespaces obtained by classpath scanning." 166 | [] 167 | (let [classpath (classpath)] 168 | (cache-last-result ::namespaces-on-classpath classpath 169 | (set (for [^String file (all-files-on-classpath classpath) 170 | :when (and (.endsWith file ".clj") 171 | (not (.startsWith file "META-INF"))) 172 | :let [[_ ^String nsname] (re-matches #"[^\w]?(.+)\.clj" file)] 173 | :when nsname] 174 | (.. nsname (replace File/separator ".") (replace "_" "-"))))))) 175 | 176 | (defn project-resources 177 | "Returns a list of all non-code files in the current project." 178 | [] 179 | (let [classpath (classpath)] 180 | (cache-last-result ::project-resources classpath 181 | (for [path classpath 182 | ^String file (list-files path false) 183 | :when (not (or (empty? file) (.endsWith file ".clj") 184 | (.endsWith file ".jar") (.endsWith file ".class")))] 185 | (if (.startsWith file File/separator) 186 | (.substring file 1) file))))) 187 | -------------------------------------------------------------------------------- /venom/complete/src/vimpire/complete.clj: -------------------------------------------------------------------------------- 1 | ;- 2 | ; Copyright 2009-2017 © Meikel Brandmeyer. 3 | ; All rights reserved. 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in 13 | ; all copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | ; THE SOFTWARE. 22 | 23 | (ns vimpire.complete 24 | (:require 25 | [vimpire.util :as util] 26 | [compliment.core :as c])) 27 | 28 | (defmulti make-completion-item 29 | "Create a completion item for Vim's popup-menu." 30 | (fn [the-thing] 31 | (let [t (:type the-thing)] 32 | (if (= t :var) 33 | (if-let [v (util/safe-ns-resolve (symbol (:ns the-thing)) 34 | (symbol (:candidate the-thing)))] 35 | (cond 36 | (instance? clojure.lang.MultiFn (util/safe-var-get v)) :function 37 | (fn? (util/safe-var-get v)) :function 38 | :else :var) 39 | :var) 40 | t)))) 41 | 42 | (defmethod make-completion-item :namespace 43 | [{:keys [candidate]}] 44 | (let [docs (-> candidate symbol the-ns meta :doc) 45 | info (str " " candidate \newline 46 | (when docs (str \newline docs)))] 47 | {"word" candidate 48 | "kind" "n" 49 | "menu" "" 50 | "info" info})) 51 | 52 | (defmethod make-completion-item :class 53 | [{:keys [candidate]}] 54 | {"word" candidate 55 | "kind" "c" 56 | "menu" "" 57 | "info" ""}) 58 | 59 | (defmethod make-completion-item :method 60 | [{:keys [candidate]}] 61 | {"word" candidate 62 | "kind" "M" 63 | "menu" "" 64 | "info" ""}) 65 | 66 | (defmethod make-completion-item :var 67 | [{:keys [candidate ns]}] 68 | (let [the-var (util/safe-ns-resolve (symbol ns) (symbol candidate)) 69 | info (str " " candidate \newline) 70 | info (if-let [docstring (-> the-var meta :doc)] 71 | (str info \newline " " docstring) 72 | info)] 73 | {"word" candidate 74 | "kind" "v" 75 | "menu" (pr-str (try 76 | (type @the-var) 77 | (catch IllegalStateException _ 78 | ""))) 79 | "info" info})) 80 | 81 | (defn- make-completion-item-fm 82 | [{:keys [candidate ns]} typ] 83 | (let [the-var (util/safe-ns-resolve (symbol ns) (symbol candidate)) 84 | the-fn (util/safe-var-get the-var) 85 | info (str " " candidate \newline) 86 | metadata (meta the-var) 87 | arglists (:arglists metadata) 88 | info (if arglists 89 | (reduce #(str %1 " " (prn-str (cons (symbol candidate) %2))) 90 | (str info \newline) arglists) 91 | info) 92 | info (if-let [docstring (:doc metadata)] 93 | (str info \newline " " docstring) 94 | info)] 95 | {"word" candidate 96 | "kind" typ 97 | "menu" (pr-str arglists) 98 | "info" info})) 99 | 100 | (defmethod make-completion-item :function 101 | [the-thing] 102 | (make-completion-item-fm the-thing "f")) 103 | 104 | (defmethod make-completion-item :macro 105 | [the-thing] 106 | (make-completion-item-fm the-thing "m")) 107 | 108 | (defmethod make-completion-item :default 109 | [{:keys [candidate]}] 110 | {"word" candidate 111 | "kind" "" 112 | "menu" "" 113 | "info" ""}) 114 | 115 | (defn completions 116 | [prefix nspace] 117 | (let [nspace (symbol nspace)] 118 | (mapv make-completion-item (c/completions prefix {:ns nspace})))) 119 | -------------------------------------------------------------------------------- /venom/doc/actions.clj: -------------------------------------------------------------------------------- 1 | #:vimpire 2 | {:doc-lookup 3 | (vimpire.doc/doc-lookup #unrepl/param :nspace #unrepl/param :sym) 4 | 5 | :find-doc 6 | (vimpire.doc/find-doc #unrepl/param :query) 7 | 8 | :javadoc-path 9 | (vimpire.doc/javadoc-path #unrepl/param :nspace #unrepl/param :sym) 10 | 11 | :source-lookup 12 | (vimpire.doc/source-lookup #unrepl/param :nspace #unrepl/param :sym)} 13 | -------------------------------------------------------------------------------- /venom/doc/src/vimpire/doc.clj: -------------------------------------------------------------------------------- 1 | ;- 2 | ; Copyright 2009-2017 © Meikel Brandmeyer. 3 | ; All rights reserved. 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in 13 | ; all copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | ; THE SOFTWARE. 22 | 23 | (ns vimpire.doc 24 | (:require 25 | [vimpire.util :as util] 26 | [clojure.java.io :as io]) 27 | (:import 28 | clojure.lang.RT 29 | java.io.LineNumberReader 30 | java.io.PushbackReader)) 31 | 32 | ; Documentation: 33 | ; Mirror this from clojure 1.3 to allow backwards compatibility. 34 | (def ^:private special-doc-map 35 | '{. {:url "java_interop#dot" 36 | :forms [(.instanceMember instance args*) 37 | (.instanceMember Classname args*) 38 | (Classname/staticMethod args*) 39 | Classname/staticField] 40 | :doc "The instance member form works for both fields and methods. 41 | They all expand into calls to the dot operator at macroexpansion time."} 42 | def {:forms [(def symbol init?)] 43 | :doc "Creates and interns a global var with the name 44 | of symbol in the current namespace (*ns*) or locates such a var if 45 | it already exists. If init is supplied, it is evaluated, and the 46 | root binding of the var is set to the resulting value. If init is 47 | not supplied, the root binding of the var is unaffected."} 48 | do {:forms [(do exprs*)] 49 | :doc "Evaluates the expressions in order and returns the value of 50 | the last. If no expressions are supplied, returns nil."} 51 | if {:forms [(if test then else?)] 52 | :doc "Evaluates test. If not the singular values nil or false, 53 | evaluates and yields then, otherwise, evaluates and yields else. If 54 | else is not supplied it defaults to nil."} 55 | monitor-enter {:forms [(monitor-enter x)] 56 | :doc "Synchronization primitive that should be avoided 57 | in user code. Use the 'locking' macro."} 58 | monitor-exit {:forms [(monitor-exit x)] 59 | :doc "Synchronization primitive that should be avoided 60 | in user code. Use the 'locking' macro."} 61 | new {:forms [(Classname. args*) (new Classname args*)] 62 | :url "java_interop#new" 63 | :doc "The args, if any, are evaluated from left to right, and 64 | passed to the constructor of the class named by Classname. The 65 | constructed object is returned."} 66 | quote {:forms [(quote form)] 67 | :doc "Yields the unevaluated form."} 68 | recur {:forms [(recur exprs*)] 69 | :doc "Evaluates the exprs in order, then, in parallel, rebinds 70 | the bindings of the recursion point to the values of the exprs. 71 | Execution then jumps back to the recursion point, a loop or fn method."} 72 | set! {:forms[(set! var-symbol expr) 73 | (set! (. instance-expr instanceFieldName-symbol) expr) 74 | (set! (. Classname-symbol staticFieldName-symbol) expr)] 75 | :url "vars#set" 76 | :doc "Used to set thread-local-bound vars, Java object instance 77 | fields, and Java class static fields."} 78 | throw {:forms [(throw expr)] 79 | :doc "The expr is evaluated and thrown, therefore it should 80 | yield an instance of some derivee of Throwable."} 81 | try {:forms [(try expr* catch-clause* finally-clause?)] 82 | :doc "catch-clause => (catch classname name expr*) 83 | finally-clause => (finally expr*) 84 | 85 | Catches and handles Java exceptions."} 86 | var {:forms [(var symbol)] 87 | :doc "The symbol must resolve to a var, and the Var object 88 | itself (not its value) is returned. The reader macro #'x expands to (var x)."}}) 89 | 90 | (defn ^:private special-doc 91 | [nspace name-symbol] 92 | (assoc (or (special-doc-map name-symbol) 93 | (meta (ns-resolve nspace name-symbol))) 94 | :name name-symbol 95 | :special-form true)) 96 | 97 | (defn ^:private namespace-doc 98 | [nspace] 99 | (assoc (meta nspace) :name (ns-name nspace))) 100 | 101 | (defn ^:private print-documentation 102 | [m] 103 | (->> ["-------------------------" 104 | (str (when-let [ns (:ns m)] (str (ns-name ns) "/")) (:name m)) 105 | (when-let [forms (:forms m)] 106 | (apply str (interleave (repeat " ") (map prn-str (forms m))))) 107 | (when-let [arglists (:arglists m)] 108 | (prn-str arglists)) 109 | (if (:special-form m) 110 | (str "Special Form\n" 111 | " " (:doc m) \newline 112 | (if (contains? m :url) 113 | (when (:url m) 114 | (str "\n Please see http://clojure.org/" (:url m)))) 115 | (str "\n Please see http://clojure.org/special_forms#" (:name m))) 116 | (str (when (:macro m) "Macro\n") 117 | " " (:doc m) \newline))] 118 | (interpose \newline) 119 | (apply str))) 120 | 121 | (defn doc-lookup 122 | [nspace sym] 123 | (let [nspace (util/resolve-and-load-namespace nspace) 124 | sym (symbol sym)] 125 | (if-let [special-name ('{& fn catch try finally try} sym)] 126 | (print-documentation (special-doc nspace special-name)) 127 | (condp #(%1 %2) sym 128 | special-doc-map :>> (fn [_] 129 | (print-documentation 130 | (special-doc nspace sym))) 131 | find-ns :>> #(print-documentation (namespace-doc %)) 132 | #(ns-resolve nspace %) :>> #(print-documentation (meta %)) 133 | (str "'" sym "' could not be found. Please check the spelling."))))) 134 | 135 | (defn find-doc 136 | [re-string-or-pattern] 137 | (let [re (re-pattern re-string-or-pattern) 138 | ms (concat (mapcat #(sort-by :name (map meta (vals (ns-interns %)))) 139 | (all-ns)) 140 | (map namespace-doc (all-ns)) 141 | (map (partial special-doc "user") (keys special-doc-map))) 142 | sb (StringBuilder.)] 143 | (doseq [m ms 144 | :when (and (:doc m) 145 | (or (re-find (re-matcher re (:doc m))) 146 | (re-find (re-matcher re (str (:name m))))))] 147 | (.append sb (print-documentation m))) 148 | (str sb))) 149 | 150 | (defn javadoc-path 151 | [nspace sym] 152 | (let [nspace (util/resolve-and-load-namespace nspace)] 153 | ) 154 | (-> sym 155 | symbol 156 | (->> (ns-resolve nspace)) 157 | .getName 158 | (.replace \. \/) 159 | (.replace \$ \.) 160 | (str ".html"))) 161 | 162 | (defn source-lookup 163 | [nspace sym] 164 | (let [nspace (util/resolve-and-load-namespace nspace) 165 | the-var (ns-resolve nspace (symbol sym)) 166 | fname (:file (meta the-var)) 167 | file (io/file fname) 168 | strm (if (.isAbsolute file) 169 | (io/input-stream file) 170 | (.getResourceAsStream (RT/baseLoader) fname))] 171 | (when strm 172 | (with-open [rdr (LineNumberReader. (io/reader strm))] 173 | (dotimes [_ (dec (:line (meta the-var)))] (.readLine rdr)) 174 | (let [text (StringBuilder.) 175 | pbr (proxy [PushbackReader] [rdr] 176 | (read [] (let [i (proxy-super read)] 177 | (.append text (char i)) 178 | i)))] 179 | (read (PushbackReader. pbr)) 180 | (str text)))))) 181 | -------------------------------------------------------------------------------- /venom/dynhighlight/actions.clj: -------------------------------------------------------------------------------- 1 | #:vimpire 2 | {:dynamic-highlighting 3 | (vimpire.dynhighlight/dynamic-highlighting #unrepl/param :nspace)} 4 | -------------------------------------------------------------------------------- /venom/dynhighlight/src/vimpire/dynhighlight.clj: -------------------------------------------------------------------------------- 1 | ;- 2 | ; Copyright 2009-2017 © Meikel Brandmeyer. 3 | ; All rights reserved. 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in 13 | ; all copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | ; THE SOFTWARE. 22 | 23 | (ns vimpire.dynhighlight 24 | (:require 25 | [vimpire.util :as util] 26 | [clojure.set :as set])) 27 | 28 | (defn dynamic-highlighting 29 | [nspace] 30 | (let [c-c (the-ns 'clojure.core) 31 | the-space (util/resolve-and-load-namespace nspace) 32 | refers (remove #(= c-c (-> % second meta :ns)) (ns-refers the-space)) 33 | aliases (for [[the-alias the-alias-space] (ns-aliases the-space) 34 | [the-sym the-var] (ns-publics the-alias-space)] 35 | [(symbol (name the-alias) (name the-sym)) the-var]) 36 | namespaces (for [the-namespace (remove #{c-c} (all-ns)) 37 | [the-sym the-var] (ns-publics the-namespace)] 38 | [(symbol (name (ns-name the-namespace)) (name the-sym)) 39 | the-var]) 40 | vars (set (concat refers aliases namespaces)) 41 | macros (set (filter #(-> % second meta :macro) vars)) 42 | vars (set/difference vars macros) 43 | fns (set (filter #(let [v (util/safe-var-get (second %))] 44 | (or (fn? v) 45 | (instance? clojure.lang.MultiFn v))) 46 | vars)) 47 | vars (set/difference vars fns) 48 | strfirst (comp str first)] 49 | {"Func" (mapv strfirst fns) 50 | "Macro" (mapv strfirst macros) 51 | "Variable" (mapv strfirst vars)})) 52 | -------------------------------------------------------------------------------- /venom/macro/actions.clj: -------------------------------------------------------------------------------- 1 | #:vimpire 2 | {:macro-expand 3 | (vimpire.macro/macro-expand #unrepl/param :nspace 4 | #unrepl/param :form 5 | #unrepl/param :one?)} 6 | -------------------------------------------------------------------------------- /venom/macro/src/vimpire/macro.clj: -------------------------------------------------------------------------------- 1 | ;- 2 | ; Copyright 2009-2017 © Meikel Brandmeyer. 3 | ; All rights reserved. 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in 13 | ; all copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | ; THE SOFTWARE. 22 | 23 | (ns vimpire.macro 24 | (:require 25 | [vimpire.util :as util] 26 | [clojure.pprint :as pprint])) 27 | 28 | (defn macro-expand 29 | [nspace form one?] 30 | (let [nspace (util/resolve-and-load-namespace nspace) 31 | expand (if one? 32 | #(macroexpand-1 %) 33 | #(macroexpand %))] 34 | (binding [*ns* nspace] 35 | (with-out-str (-> (read-string form) expand pprint/pprint))))) 36 | -------------------------------------------------------------------------------- /venom/nav/actions.clj: -------------------------------------------------------------------------------- 1 | #:vimpire 2 | {:source-location 3 | (vimpire.nav/source-location #unrepl/param :nspace #unrepl/param :sym)} 4 | -------------------------------------------------------------------------------- /venom/nav/src/vimpire/nav.clj: -------------------------------------------------------------------------------- 1 | ;- 2 | ; Copyright 2009-2017 © Meikel Brandmeyer. 3 | ; All rights reserved. 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in 13 | ; all copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | ; THE SOFTWARE. 22 | 23 | (ns vimpire.nav 24 | (:require 25 | [vimpire.util :as util])) 26 | 27 | ; Source position 28 | (defn source-position 29 | [nspace sym] 30 | (let [nspace (util/resolve-and-load-namespace nspace) 31 | the-var (ns-resolve nspace (symbol sym)) 32 | meta-info (meta the-var) 33 | file (:file meta-info) 34 | line (:line meta-info)] 35 | {:file file :line line})) 36 | -------------------------------------------------------------------------------- /venom/test/actions.clj: -------------------------------------------------------------------------------- 1 | #:vimpire 2 | {:run-tests 3 | (vimpire.test/run-tests #unrepl/param :nspace #unrepl/param :all?)} 4 | -------------------------------------------------------------------------------- /venom/test/src/vimpire/test.clj: -------------------------------------------------------------------------------- 1 | ;- 2 | ; Copyright 2009-2017 © Meikel Brandmeyer. 3 | ; All rights reserved. 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in 13 | ; all copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | ; THE SOFTWARE. 22 | 23 | (ns vimpire.test 24 | (:require 25 | [clojure.test :as test])) 26 | 27 | (defn run-tests 28 | [nspace all?] 29 | (when (not= "user" nspace) 30 | (if all? 31 | (require :reload-all (symbol nspace)) 32 | (require :reload (symbol nspace)))) 33 | (with-out-str 34 | (binding [test/*test-out* *out*] 35 | (test/run-tests (symbol nspace))))) 36 | -------------------------------------------------------------------------------- /venom/util/actions.clj: -------------------------------------------------------------------------------- 1 | #:vimpire 2 | {:namespace-of-file 3 | (vimpire.util/namespace-of-file #unrepl/param :content) 4 | 5 | :check-syntax 6 | (vimpire.util/check-syntax #unrepl/param :nspace #unrepl/param :content)} 7 | -------------------------------------------------------------------------------- /venom/util/src/vimpire/util.clj: -------------------------------------------------------------------------------- 1 | ;- 2 | ; Copyright 2009-2017 © Meikel Brandmeyer. 3 | ; All rights reserved. 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in 13 | ; all copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | ; THE SOFTWARE. 22 | 23 | (ns vimpire.util 24 | (:require 25 | [clojure.pprint :as pprint] 26 | [clojure.stacktrace :as stacktrace]) 27 | (:import 28 | clojure.lang.ISeq 29 | clojure.lang.LineNumberingPushbackReader 30 | clojure.lang.LispReader$ReaderException 31 | java.io.StringReader)) 32 | 33 | ; Common helpers 34 | (defn str-wrap 35 | "Wrap the given string into the given separators." 36 | ([string sep] 37 | (str-wrap string sep sep)) 38 | ([string before after] 39 | (str before string after))) 40 | 41 | (defn str-cat 42 | "Concatenate the given collection to a string, separating the 43 | collection's items with the given separator." 44 | [coll sep] 45 | (apply str (interpose sep coll))) 46 | 47 | (defn safe-ns-resolve 48 | [nspace sym] 49 | (try 50 | (ns-resolve nspace sym) 51 | (catch ClassNotFoundException _ nil))) 52 | 53 | (defn safe-var-get 54 | [the-var] 55 | (when (.isBound the-var) 56 | (var-get the-var))) 57 | 58 | ; Namespace helpers 59 | (defn resolve-and-load-namespace 60 | "Loads and returns the namespace named by the given string or symbol." 61 | [namespace] 62 | ; Special case for user: make sure it always exists for the Repl. 63 | (binding [*ns* *ns*] 64 | (in-ns 'user)) 65 | (let [namespace (if (symbol? namespace) namespace (symbol namespace))] 66 | (try 67 | (the-ns namespace) 68 | (catch Exception _ 69 | (require namespace) 70 | (the-ns namespace))))) 71 | 72 | (defn stream->seq 73 | "Turns a given stream into a seq of Clojure forms read from the stream." 74 | [stream] 75 | (let [eof (Object.) 76 | rdr (fn [] (read stream false eof))] 77 | (take-while #(not= % eof) (repeatedly rdr)))) 78 | 79 | ; Pretty printing. 80 | (defn pretty-print 81 | "Print the given form in a pretty way." 82 | [form] 83 | (pprint/pprint form)) 84 | 85 | (defn pretty-print-code 86 | "Print the given form in a pretty way. 87 | Uses the *code-dispatch* formatting." 88 | [form] 89 | (pprint/with-pprint-dispatch pprint/code-dispatch 90 | (pprint/pprint form))) 91 | 92 | (defn pretty-print-stacktrace 93 | "Print the stacktrace of the given Throwable. Tries clj-stacktrace, 94 | clojure.stacktrace and clojure.contrib.stacktrace in that order. Otherwise 95 | defaults to simple printing." 96 | [e] 97 | (stacktrace/print-stack-trace e)) 98 | 99 | (defn pretty-print-causetrace 100 | "Print the causetrace of the given Throwable. Tries clj-stacktrace, 101 | clojure.stacktrace and clojure.contrib.stacktrace in that order. Otherwise 102 | defaults to simple printing." 103 | [e] 104 | (stacktrace/print-cause-trace e)) 105 | 106 | (defn namespace-of-file 107 | [content] 108 | (let [of-interest '#{in-ns ns clojure.core/in-ns clojure.core/ns} 109 | in-seq (-> content 110 | StringReader. 111 | LineNumberingPushbackReader. 112 | stream->seq) 113 | candidate (first 114 | (drop-while #(or (not (instance? ISeq %)) 115 | (not (contains? of-interest (first %)))) 116 | in-seq))] 117 | (cond 118 | (not (instance? ISeq candidate)) "user" 119 | ('#{ns clojure.core/ns} (first candidate)) (name (second candidate)) 120 | ('#{in-ns clojure.core/in-ns} (first candidate)) (-> candidate 121 | second 122 | second 123 | name)))) 124 | 125 | (defn check-syntax 126 | [nspace content] 127 | (let [nspace (resolve-and-load-namespace nspace)] 128 | (binding [*ns* nspace] 129 | (try 130 | (let [eof (Object.) 131 | rdr (LineNumberingPushbackReader. (StringReader. content))] 132 | (loop [x nil] 133 | (if (identical? x eof) 134 | true 135 | (recur (read rdr false eof))))) 136 | (catch LispReader$ReaderException exc 137 | (let [e (.getCause exc)] 138 | (if (.startsWith (.getMessage e) "EOF while reading") 139 | false 140 | (throw exc)))))))) 141 | --------------------------------------------------------------------------------