Skip to content

Commit af89dd3

Browse files
committed
Use tools.reader for parsing
1 parent 164d83b commit af89dd3

File tree

7 files changed

+88
-27
lines changed

7 files changed

+88
-27
lines changed

deploy-local.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
lein uberjar
2-
cp target/uberjar/socket-repl-plugin-0.1.0-SNAPSHOT-standalone.jar .
2+
cp target/uberjar/socket-repl-plugin-0.1.0-standalone.jar .
33
rm -rf ~/.vim/bundle/clojure-socketrepl.nvim
44
cd ..
55
cp -rf clojure-socketrepl.nvim ~/.vim/bundle

plugin/socketrepl.vim

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ let s:not_ready = "SocketREPL plugin not ready (starting)"
88
function! StartIfNotRunning()
99
if g:is_running == 0
1010
echo 'Starting SocketREPL plugin...'
11-
let jar_file_path = s:p_dir . '/../' . 'socket-repl-plugin-0.1.0-SNAPSHOT-standalone.jar'
11+
let jar_file_path = s:p_dir . '/../' . 'socket-repl-plugin-0.1.0-standalone.jar'
1212
call jobstart(['java', '-jar', jar_file_path], {'rpc': v:true})
1313
let g:is_running = 1
1414
endif

project.clj

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(defproject socket-repl-plugin "0.1.0-SNAPSHOT"
1+
(defproject socket-repl-plugin "0.1.0"
22
:description "FIXME: write description"
33
:url "http://example.com/FIXME"
44
:license {:name "Eclipse Public License"
@@ -7,6 +7,7 @@
77
[org.clojure/tools.logging "0.3.1"]
88
[org.clojure/tools.namespace "0.2.11"]
99
[org.clojure/tools.nrepl "0.2.12"]
10+
[org.clojure/tools.reader "1.0.0-beta4"]
1011
[log4j/log4j "1.2.17" :exclusions [javax.mail/mail
1112
javax.jms/jms
1213
com.sun.jmdk/jmxtools

src/socket_repl/parser.clj

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
(ns socket-repl.parser
2+
(:require
3+
[clojure.string :as string]
4+
[clojure.tools.reader.reader-types :as reader-types]
5+
[clojure.tools.reader :as reader]))
6+
7+
(defn position
8+
"Returns the zero-indexed position in a code string given line and column."
9+
[code-str row col]
10+
(->> code-str
11+
string/split-lines
12+
(take (dec row))
13+
(string/join)
14+
count
15+
(+ col (dec row)) ;; `(dec row)` to include for newlines
16+
dec
17+
(max 0)))
18+
19+
(defn search-start
20+
"Find the place to start reading from. Search backwards from the starting
21+
point, looking for a '[', '{', or '('. If none can be found, search from
22+
the beginning of `code-str`."
23+
[code-str start-row start-col]
24+
(let [openers #{\[ \( \{}
25+
start-position (position code-str start-row start-col)]
26+
(if (contains? openers (nth code-str start-position))
27+
start-position
28+
(let [code-str-prefix (subs code-str 0 start-position)]
29+
(->> openers
30+
(map #(string/last-index-of code-str-prefix %))
31+
(remove nil?)
32+
(apply max 0))))))
33+
34+
(defn read-next
35+
"Reads the next expression from some code. Uses an `indexing-pushback-reader`
36+
to determine how much was read, and return that substring of the original
37+
`code-str`, rather than what was actually read by the reader."
38+
[code-str start-row start-col]
39+
(let [code-str (subs code-str (search-start code-str start-row start-col))
40+
rdr (reader-types/indexing-push-back-reader code-str)]
41+
;; Read a form, but discard it, as we want the original string.
42+
(reader/read rdr)
43+
(subs code-str
44+
0
45+
;; Even though this returns the position *after* the read, this works
46+
;; because subs is end point exclusive.
47+
(position code-str
48+
(reader-types/get-line-number rdr)
49+
(reader-types/get-column-number rdr)))))

src/socket_repl/repl_log.clj

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
[repl-log]
1919
(:input-channel repl-log))
2020

21+
(defn format-input
22+
[s]
23+
s)
24+
2125
(defn start
2226
[{:keys [file input-channel socket-repl nrepl] :as repl-log}]
2327

@@ -34,7 +38,7 @@
3438
(async/thread
3539
(loop []
3640
(when-let [input (async/<!! input-channel)]
37-
(.println print-stream input)
41+
(.println print-stream (#'format-input input))
3842
(.flush print-stream)
3943
(recur))))
4044
(assoc repl-log :print-stream print-stream))))

src/socket_repl/socket_repl_plugin.clj

+3-23
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,11 @@
1313
[neovim-client.message :as message]
1414
[neovim-client.nvim :as nvim]
1515
[socket-repl.nrepl :as nrepl]
16+
[socket-repl.parser :as parser]
1617
[socket-repl.repl-log :as repl-log]
1718
[socket-repl.socket-repl :as socket-repl]
1819
[socket-repl.util :refer [log-start log-stop]]))
1920

20-
(defn position
21-
"Find the position in a code string given line and column."
22-
[code-str [y x]]
23-
(->> code-str
24-
string/split-lines
25-
(take (dec y))
26-
(string/join "\n")
27-
count
28-
(+ (inc x))))
29-
30-
(defn get-form-at
31-
"Returns the enclosing form from a string a code using [row col]
32-
coordinates."
33-
[code-str coords]
34-
(let [pos (position code-str coords)]
35-
(read-string
36-
;; Start at the last index of paren on or before `pos`, read a form.
37-
(subs code-str (if (= \( (.charAt code-str pos))
38-
pos
39-
(.lastIndexOf (subs code-str 0 pos) "("))))))
40-
4121
(defn write-error
4222
"Write a throwable's stack trace to the repl log."
4323
[repl-log throwable]
@@ -135,10 +115,10 @@
135115
(run-command
136116
plugin
137117
(fn [msg]
138-
(let [coords (api-ext/get-cursor-location nvim)
118+
(let [[row col] (api-ext/get-cursor-location nvim)
139119
buffer-text (api-ext/get-current-buffer-text nvim)]
140120
(try
141-
(async/>!! code-channel (get-form-at buffer-text coords))
121+
(async/>!! code-channel (parser/read-next buffer-text row (inc col)))
142122
(catch Throwable t
143123
(log/error t "Error evaluating a form")
144124
(write-error repl-log t)))))))

test/socket_repl/parser_test.clj

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
(ns socket-repl.parser-test
2+
(:require
3+
[clojure.test :as test :refer [deftest is]]
4+
[socket-repl.parser :as parser]))
5+
6+
(deftest parse
7+
(is (= "(println ::Foo)"
8+
(parser/read-next "(println ::Foo)\n(println 'bar)\n::baz"
9+
1 2)))
10+
(is (= "println"
11+
(parser/read-next "println ::Foo)\n(println 'bar)\n::baz"
12+
1 2)))
13+
(is (= "(println 'bar)"
14+
(parser/read-next "println ::Foo)\n(println 'bar)\n::baz"
15+
2 3)))
16+
(is (= "{:foo :bar}"
17+
(parser/read-next (str "println ::Foo)\n(println 'bar)\n::baz blah"
18+
" blah blah\n(doit {:foo :bar} x)")
19+
4 10)))
20+
(is (= "(foo bar)"
21+
(parser/read-next "(foo bar)"
22+
1 1)))
23+
(is (= "(foo bar)"
24+
(parser/read-next "\n\n\n\n(foo bar)"
25+
5 1))))
26+
27+
;(clojure.test/run-tests 'socket-repl.parser-test)

0 commit comments

Comments
 (0)