Skip to content

Commit 2d3b78b

Browse files
committed
WIP - add socket repl plugin
1 parent 6476ba0 commit 2d3b78b

File tree

5 files changed

+207
-3
lines changed

5 files changed

+207
-3
lines changed

TODO

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
TODO
2+
[X] Make buffer-get-text-async work, just test it at the repl
3+
[X] Get "eval-code" working
4+
[_] Figure out how to accumulate / display results in vim
5+
[_] Name vim functions correctly
6+
[_] Pass host, port in connect
7+
8+
Goals
9+
-Zero vimscript (plugin written in clojure)
10+
-Simplicity
11+
Ex:
12+
Don't modify behavior of stdio, stacktrace printing
13+
Append results to a "repl" buffer
14+
Code
15+
No nrepl (no request / response)
16+
No middleware
17+
No vimscript
18+
Clojure-only plugin
19+
All results displayed in one place
20+
One connection
21+
-Run repl side-by-side with editor
22+
Does not automatically start a repl
23+
Forces you to explicity connect
24+
One connection only `:call ConnectClojurePlugin(host, port)`
25+
Accessible from vim buffer `:call ShowClojurePluginRepl()`
26+
27+
Questions
28+
Figwheel cljs-repl support
29+
30+
Usage
31+
add command line args to your project, so it starts socket repl
32+
tell the plugin port (and host) to connect to
33+
34+
Debugging notes
35+
vim
36+
rpcrequest(1, ...) <-- use channel 1, w/ tcp
37+
don't do rpcstart
38+
clj
39+
connect! add localhost 7777
40+
vim blocks if you make a synchronous call to clj which makes a sync
41+
call back to vim -- use async!
42+

plugin/socketrepl.vim

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
let s:p_dir = expand('<sfile>:p:h')
2+
let g:is_running = 0
3+
let g:channel = -1
4+
5+
function! StartIfNotRunning()
6+
if g:is_running == 0
7+
echo 'starting plugin...'
8+
"TODO - This is a dirty hack. We should launch things without changing
9+
"the working directory.
10+
exec ':cd ' . s:p_dir
11+
let g:channel = rpcstart('lein', ['run'])
12+
let g:is_running = 1
13+
endif
14+
endfunction
15+
16+
function! EvalConnect()
17+
"call StartIfNotRunning()
18+
let res = rpcrequest(1, 'connect', [])
19+
return res
20+
endfunction
21+
22+
function! EvalBuffer()
23+
"call StartIfNotRunning()
24+
let res = rpcrequest(1, 'eval-buffer', [])
25+
return res
26+
endfunction
27+
28+
function! EvalCode()
29+
"call StartIfNotRunning()
30+
let res = rpcrequest(g:channel, 'eval-code', [])
31+
return res
32+
endfunction
33+
34+
echo 'socket repl plugin loaded!'

project.clj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
:url "http://example.com/FIXME"
44
:license {:name "Eclipse Public License"
55
:url "http://www.eclipse.org/legal/epl-v10.html"}
6-
:dependencies [[org.clojure/clojure "1.6.0"]
6+
:dependencies [[org.clojure/clojure "1.9.0-alpha12"]
77
[org.clojure/core.async "0.1.346.0-17112a-alpha"]
8+
;; TODO: upgrade to this
9+
;; [org.clojure/core.async "0.2.391"]
810
[org.clojure/tools.logging "0.3.1"]
911
[clj-logging-config "1.9.12"]
1012
[clojure-msgpack "1.0.0"]]
1113
:repl-options {:init-ns neovim-client.nvim}
14+
;; TODO: remove this
15+
;;:jvm-opts ["-Dclojure.server.repl={:port 5555 :accept clojure.core.server/repl}"]
16+
:main ^:skip-aot neovim-client.socket-repl-plugin
1217
:target-path "target/%s"
1318
:profiles {:uberjar {:aot :all}})

src/neovim_client/nvim.clj

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(ns neovim-client.nvim
22
(:require [clojure.string :as str]
3-
[neovim-client.message :refer [->request-msg]]
3+
[neovim-client.message :as message :refer [->request-msg]]
44
[neovim-client.rpc :as rpc]))
55

66
;; ***** Public *****
@@ -26,11 +26,27 @@
2626

2727
(defn set-current-line! [line] (send-message! "vim_set_current_line" line))
2828

29-
(defn get-current-buffer [] (send-message! "vim_get_current_buffer"))
29+
(defn get-current-buffer
30+
;; TODO: fix comment
31+
"Returns ??? (object, map, ?) representing the current buffer."
32+
[]
33+
(send-message! "vim_get_current_buffer"))
34+
35+
(declare buffer-get-text)
36+
(defn get-current-buffer-async
37+
"Like `get-current-buffer`, but async."
38+
[f]
39+
(rpc/send-message-async! (->request-msg "vim_get_current_buffer" []) f))
3040

3141
(defn get-buffers [] (send-message! "vim_get_buffers"))
3242

3343
(defn get-buffer-line-count [buffer] (send-message! "buffer_line_count" buffer))
44+
(defn get-buffer-line-count-async
45+
"Like `get-buffer-line-count but async.
46+
47+
`f` callback function invoked with the buffer line count as an argument."
48+
[buffer f]
49+
(rpc/send-message-async! (->request-msg "buffer_line_count" [buffer]) f))
3450

3551
(defn buffer-set-line!
3652
[buffer line-num line]
@@ -52,6 +68,11 @@
5268
(rpc/send-message-async! (->request-msg "buffer_get_line"
5369
[buffer line-num]) f))
5470

71+
(defn buffer-get-lines-async
72+
[buffer start end f]
73+
(rpc/send-message-async! (->request-msg "buffer_get_line_slice"
74+
[buffer start end true true]) f))
75+
5576
;; ***** Experimental *****
5677

5778
(defn buffer-update-lines!
@@ -68,6 +89,12 @@
6889
lines (map (partial buffer-get-line buffer) (range buf-size))]
6990
(str/join "\n" lines)))
7091

92+
(defn get-current-buffer-text-async
93+
"Convenience function to get the current buffer's text asynchronously."
94+
[f]
95+
(get-current-buffer-async
96+
(fn [buffer] (buffer-get-lines-async buffer 0 -1 f))))
97+
7198
;; TODO - make it wipe out text in the buffer after the last line of new text.
7299
(defn buffer-set-text!
73100
[buffer text]
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(ns neovim-client.socket-repl-plugin
2+
"A plugin which connects to a running socket repl and sends output back to
3+
Neovim."
4+
(:require
5+
[clojure.java.io :as io]
6+
[clojure.core.async :as async :refer [go go-loop >! <!]]
7+
[neovim-client.nvim :as nvim])
8+
(:import
9+
(java.net Socket)
10+
(java.io PrintStream)))
11+
12+
(defn connection
13+
[host port]
14+
(let [socket (java.net.Socket. "localhost" 5555)]
15+
{:host host
16+
:port port
17+
:out (-> socket
18+
io/output-stream
19+
PrintStream.)
20+
:in (io/reader socket)}))
21+
22+
(defn eval*
23+
[{:keys [:out]} code-string]
24+
(.println out code-string)
25+
(.flush out))
26+
27+
(def current-connection (atom nil))
28+
29+
(defn connect!
30+
[host port handler]
31+
(let [conn (connection host port)
32+
chan (async/chan 10)]
33+
(reset! current-connection (assoc conn
34+
:handler handler
35+
:chan chan))
36+
37+
;; input producer
38+
(go-loop []
39+
(when-let [line (.readLine (:in conn))]
40+
(>! chan line)
41+
(recur)))
42+
43+
;; input consumer
44+
(go-loop []
45+
(when-let [x (<! chan)]
46+
(handler x)
47+
(recur))))
48+
"success")
49+
50+
(defn eval*!
51+
[code-string]
52+
(eval* @current-connection code-string))
53+
54+
(defn -main
55+
[& args]
56+
;; TODO: remove params for STDIO
57+
(nvim/connect! "localhost" 7777)
58+
59+
(nvim/register-method!
60+
"connect"
61+
(fn [msg]
62+
;; TODO: Get host/port from message
63+
(connect! "localhost" "5555"
64+
(fn [x]
65+
(nvim/run-command-async!
66+
;; TODO: Actually, append to a buffer?
67+
(format ":echo '%s'" (pr-str x))
68+
(fn [_] nil))))))
69+
70+
(nvim/register-method!
71+
"eval-code"
72+
(fn [msg]
73+
;; TODO: Get the cursor location, find current form
74+
))
75+
76+
(nvim/register-method!
77+
"eval-buffer"
78+
(fn [msg]
79+
(nvim/get-current-buffer-text-async
80+
(fn [[x & _]]
81+
;; TODO: send code being executed
82+
;; back to vim, to show what actually
83+
;; happened in the repl buffer
84+
(eval*! x)))))
85+
86+
;; TODO: When do we disconnect?
87+
(comment
88+
(dotimes [n 60]
89+
(if (= 0 (mod n 10))
90+
(nvim/run-command! (str ":echo 'plugin alive for " n " seconds.'")))
91+
(Thread/sleep 1000))
92+
93+
;; Let nvim know we're shutting down.
94+
(nvim/run-command! ":let g:is_running=0")
95+
(nvim/run-command! ":echo 'plugin stopping.'")))
96+

0 commit comments

Comments
 (0)