-
Notifications
You must be signed in to change notification settings - Fork 10
/
ClojureHelpers.py
169 lines (148 loc) · 8.46 KB
/
ClojureHelpers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import re, sublime, sublime_plugin, sublimerepl, text_transfer
from sublimerepl import manager
REFRESH_NAMESPACES_CMD = "(let [r 'user/reset] (if (find-var r) ((resolve r)) (clojure.tools.namespace.repl/refresh :after r)))"
def selected_text(s):
v = s.view
parts = [v.substr(region) for region in v.sel()]
return "".join(parts)
def repl_external_id(s):
return s.view.scope_name(0).split(" ")[0].split(".", 1)[1]
# Allows running an arbitrary command in the REPL but not in the current namespace
# Example key binding:
# { "keys": ["alt+super+r"], "command": "run_command_in_repl", "args": {"command": "(user/reset)"}},
# This will run (user/reset) in the repl
class RunCommandInReplCommand(text_transfer.ReplTransferCurrent):
def run(self, edit, command, refresh_namespaces=False):
if refresh_namespaces:
self.view.window().run_command("refresh_namespaces_in_repl", {"clean": False})
external_id = self.repl_external_id()
for rv in manager.find_repl(external_id):
command += rv.repl.cmd_postfix
rv.append_input_text(command)
rv.adjust_end()
rv.repl.write(command)
break
else:
sublime.error_message("Cannot find REPL for '{}'".format(external_id))
# Refreshes all the namespaces in the REPL using clojure.tools.namespace
class RefreshNamespacesInReplCommand(RunCommandInReplCommand):
def run(self, edit, clean):
if clean:
refresh_command = "(clojure.tools.namespace.repl/clear) " + REFRESH_NAMESPACES_CMD
else:
refresh_command = REFRESH_NAMESPACES_CMD
super( RefreshNamespacesInReplCommand, self ).run(edit, refresh_command)
# Allows running an arbitrary command in the REPL in the current namespace
# Example key binding:
# { "keys": ["alt+super+r"], "command": "run_command_in_namespace_in_repl", "args": {"command": "(foo)"}},
# This will run (foo) in the repl in the current namespace.
class RunCommandInNamespaceInReplCommand(text_transfer.ReplSend):
def run(self, edit, command, refresh_namespaces=False):
if refresh_namespaces:
self.view.window().run_command("refresh_namespaces_in_repl", {"clean": False})
external_id = repl_external_id(self)
super( RunCommandInNamespaceInReplCommand, self ).run(edit, external_id, command)
class TestSelectedVarInReplCommand(text_transfer.ReplSend):
def run(self, edit, refresh_namespaces=False):
if refresh_namespaces:
self.view.window().run_command("refresh_namespaces_in_repl", {"clean": False})
selected = selected_text(self)
text = "(do (test-vars [#'" + selected +"]) (println \"tested " + selected +"\"))"
external_id = repl_external_id(self)
super(TestSelectedVarInReplCommand, self).run(edit, external_id, text)
# Allows running a function specified in arguments on the current text selected in the repl.
# Example key binding:
# { "keys": ["alt+super+d"], "command": "run_on_selection_in_repl", "args": {"function": "clojure.repl/doc"}},
# Would run (clojure.repl/doc <selected_text>) in the repl in the current namespace
class RunOnSelectionInReplCommand(text_transfer.ReplSend):
def run(self, edit, function):
text = "(" + function + " " + selected_text(self) +")"
external_id = repl_external_id(self)
super( RunOnSelectionInReplCommand, self ).run(edit, external_id, text)
# Opens the file containing the currently selected var or namespace in the REPL. If the file is located
# inside of a jar file it will decompress the jar file then open it. It will first check to see if a
# jar file has already been decompressed once to avoid doing it multiple times for the same library.
# Assumes that the Sublime command line alias "subl" can be used to invoke sublime.
class OpenFileContainingVarCommand(text_transfer.ReplSend):
def run(self, edit):
text = """(let [var-sym 'THE_VAR
the-var (or (some->> (find-ns var-sym)
clojure.repl/dir-fn
first
name
(str (name var-sym) "/")
symbol)
var-sym)
{:keys [file line]} (meta (eval `(var ~the-var)))
file-path (.getPath (.getResource (clojure.lang.RT/baseLoader) file))]
(if-let [[_
jar-path
partial-jar-path
within-file-path] (re-find #"file:(.+/\.m2/repository/(.+\.jar))!/(.+)" file-path)]
;; The file is in a jar file in the maven repo. We'll decompress the jar file then open it
(let [decompressed-path (str (System/getProperty "user.home")
"/.lein/tmp-sublime-jars/"
partial-jar-path)
decompressed-file-path (str decompressed-path "/" within-file-path)
decompressed-path-dir (clojure.java.io/file decompressed-path)]
(when-not (.exists decompressed-path-dir)
(println "decompressing" jar-path "to" decompressed-path)
(.mkdirs decompressed-path-dir)
(clojure.java.shell/sh "unzip" jar-path "-d" decompressed-path))
(println "Opening file" decompressed-file-path)
(clojure.java.shell/sh "subl" (str decompressed-file-path ":" line))
nil)
(do
(println "Opening file" file-path)
(clojure.java.shell/sh "subl" (str file-path ":" line))
nil)))""".replace("THE_VAR", selected_text(self))
external_id = repl_external_id(self)
super( OpenFileContainingVarCommand, self ).run(edit, external_id, text)
# Lists all the vars in the selected namespace or namespace alias
class ListVarsInSelectedNsCommand(text_transfer.ReplSend):
def run(self, edit):
text = """(let [selected-symbol 'THE_NS
selected-ns (get (ns-aliases *ns*) selected-symbol selected-symbol)]
(println "\nVars in" (str selected-ns ":"))
(println "------------------------------")
(doseq [s (clojure.repl/dir-fn selected-ns)]
(println s))
(println "------------------------------"))""".replace("THE_NS", selected_text(self))
external_id = repl_external_id(self)
super( ListVarsInSelectedNsCommand, self ).run(edit, external_id, text)
# Lists all the vars with their documentation in the selected namespace or namespace alias
class ListVarsWithDocsInSelectedNsCommand(text_transfer.ReplSend):
def run(self, edit):
text = """(let [selected-symbol 'THE_NS
selected-ns (get (ns-aliases *ns*) selected-symbol selected-symbol)]
(println (str "\n" selected-ns ":"))
(println "" (:doc (meta (the-ns selected-ns))))
(doseq [s (clojure.repl/dir-fn selected-ns) :let [m (-> (str selected-ns "/" s) symbol find-var meta)]]
(println "---------------------------")
(println (:name m))
(cond
(:forms m) (doseq [f (:forms m)]
(print " ")
(prn f))
(:arglists m) (prn (:arglists m)))
(println " " (:doc m)))
(println "------------------------------"))""".replace("THE_NS", selected_text(self))
external_id = repl_external_id(self)
super( ListVarsWithDocsInSelectedNsCommand, self ).run(edit, external_id, text)
# Loads the current file in the REPL by telling the REPL to load it using the complete path
# This is much faster than the built in sublime repl command which copies the entire file into the
# REPL.
class LoadFileInReplCommand(text_transfer.ReplTransferCurrent):
def run(self, edit):
form = "(load-file \"" + self.view.file_name() +"\")"
self.view.window().run_command("run_command_in_repl", {"command": form})
# Writes the selected text to a temporary file then tells the REPL to load that file.
# This is much faster than copying every character to the REPL individually which echos everything.
class LoadSelectionInReplCommand(text_transfer.ReplSend):
def run(self, edit):
f = open("/tmp/selection.clj", "w")
f.write(selected_text(self))
f.close()
form = "(load-file \"" + f.name +"\")"
external_id = repl_external_id(self)
super( LoadSelectionInReplCommand, self ).run(edit, external_id, form)