Skip to content

Commit 90ee27d

Browse files
authored
Merge pull request #77 from sainadh-d/shadow-cljs
shadow-cljs Support
2 parents 5454a8b + 328d051 commit 90ee27d

File tree

3 files changed

+105
-9
lines changed

3 files changed

+105
-9
lines changed

Default.sublime-commands

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
"caption": "Clojure Sublimed: Connect",
44
"command": "clojure_sublimed_connect"
55
},
6+
{
7+
"caption": "Clojure Sublimed: Connect shadow-cljs",
8+
"command": "clojure_sublimed_connect_shadow_cljs"
9+
},
610
{
711
"caption": "Clojure Sublimed: Disconnect",
812
"command": "clojure_sublimed_disconnect"

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,18 @@ Differences from [Tutkain](https://tutkain.flowthing.me/):
107107

108108
Important! Make sure you switched your syntax to `Clojure (Sublimed)`.
109109

110+
For Clojure apps
111+
110112
1. Run nREPL server.
111113
2. Run `Clojure Sublimed: Connect` command.
112114

115+
For Shadow-cljs apps
116+
117+
1. Run `shadow-cljs watch app`. (This starts a HTTP server and an nREPL)
118+
2. If you are building a web-app, open the http-server url (from step 1) in the browser. This connects the shadow server to JS runtime.
119+
3. Run `Clojure Sublimed: Connect shadow-cljs` command from sublime's command palette.
120+
121+
113122
### Evaluating code from buffer
114123

115124
From here you have three options:

package.py

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,25 @@ def format_time_taken(time_taken):
2222
else:
2323
return f"({'{:.2f}'.format(elapsed * 1000)} ms)"
2424

25+
26+
def get_middleware_opts(conn):
27+
"""Returns middleware options to send to nREPL as a dict.
28+
Currently only Clojure profile supports middleware.
29+
"""
30+
if conn and conn.profile == Profile.CLOJURE:
31+
return {
32+
"nrepl.middleware.caught/caught": f"{ns}.middleware/print-root-trace",
33+
"nrepl.middleware.print/print": f"{ns}.middleware/pprint",
34+
"nrepl.middleware.print/quota": 4096
35+
}
36+
return {}
37+
38+
39+
class Profile:
40+
CLOJURE = 'clojure'
41+
SHADOW_CLJS = 'shadow-cljs'
42+
43+
2544
class Eval:
2645
# class
2746
next_id: int = 10
@@ -170,6 +189,8 @@ class Connection:
170189
last_view: sublime.View
171190
session: str
172191
eval_in_session: bool
192+
profile: Profile
193+
cljs_build: str
173194

174195
def __init__(self):
175196
self.host = 'localhost'
@@ -180,6 +201,8 @@ def __init__(self):
180201
self.last_view = window.active_view() if (window := sublime.active_window()) else None
181202
self.session = None
182203
self.eval_in_session = None
204+
self.profile = None
205+
self.cljs_build = None
183206

184207
def set_status(self, status):
185208
self.status = status
@@ -363,17 +386,16 @@ def eval_msg(view, region, msg):
363386
progress_thread.wake()
364387
eval.msg = {k: v for k, v in msg.items() if v}
365388
eval.msg["id"] = eval.id
366-
eval.msg["nrepl.middleware.caught/caught"] = f"{ns}.middleware/print-root-trace"
367-
eval.msg["nrepl.middleware.print/print"] = f"{ns}.middleware/pprint"
368-
eval.msg["nrepl.middleware.print/quota"] = 1024
369389
eval.msg["session"] = conn.session
390+
eval.msg.update(get_middleware_opts(conn))
391+
370392
conn.add_eval(eval)
371393
conn.send(eval.msg)
372394
eval.update("pending", progress_thread.phase())
373395

374396
def eval(view, region, code=None):
375397
(line, column) = view.rowcol_utf16(region.begin())
376-
msg = {"op": "eval" if conn.eval_in_session else "clone-eval-close",
398+
msg = {"op": "eval" if (conn.profile == Profile.SHADOW_CLJS or conn.eval_in_session) else "clone-eval-close",
377399
"code": view.substr(region) if code is None else code,
378400
"ns": namespace(view, region.begin()) or 'user',
379401
"line": line,
@@ -450,9 +472,8 @@ def run(self, code):
450472
eval.msg = {"op": "eval",
451473
"id": eval.id,
452474
"ns": ns,
453-
"code": code,
454-
"nrepl.middleware.caught/caught": f"{ns}.middleware/print-root-trace",
455-
"nrepl.middleware.print/quota": 300}
475+
"code": code}
476+
eval.msg.update(get_middleware_opts(conn))
456477
conn.add_eval(eval)
457478
conn.send(eval.msg)
458479
eval.update("pending", progress_thread.phase())
@@ -647,8 +668,34 @@ def read(self, n):
647668
self.pos = end
648669
return self.buffer[begin:end]
649670

671+
672+
def get_shadow_repl_init_cmd(build):
673+
"""Returns the command to initialise shadow-repl."""
674+
if build == "node-repl":
675+
return "(shadow.cljs.devtools.api/node-repl)"
676+
elif build == "browser-repl":
677+
return "(shadow.cljs.devtools.api/browser-repl)"
678+
else:
679+
return f"(shadow.cljs.devtools.api/repl {build})"
680+
681+
650682
def handle_connect(msg):
651683

684+
if conn.profile == Profile.SHADOW_CLJS:
685+
if 1 == msg.get("id") and "new-session" in msg:
686+
# Once we have the connnection to shadow's nrepl, we will
687+
# tell shadow to watch the cljs build provided by the user.
688+
conn.session = msg["new-session"]
689+
conn.send({"op": "eval",
690+
"session": conn.session,
691+
"code": get_shadow_repl_init_cmd(conn.cljs_build),
692+
"id": 2})
693+
return True
694+
695+
elif 2 == msg.get("id") and msg.get("status") == ["done"]:
696+
conn.set_status(f"🌕 {conn.host}:{conn.port}")
697+
return True
698+
652699
if 1 == msg.get("id") and "new-session" in msg:
653700
conn.session = msg["new-session"]
654701
conn.send({"op": "load-file",
@@ -712,9 +759,11 @@ def read_loop():
712759
pass
713760
conn.disconnect()
714761

715-
def connect(host, port):
762+
def connect(host, port, profile=Profile.CLOJURE, cljs_build=None):
716763
conn.host = host
717764
conn.port = port
765+
conn.profile = profile
766+
conn.cljs_build = cljs_build
718767
try:
719768
conn.socket = socket.create_connection((host, port))
720769
conn.reader = threading.Thread(daemon=True, target=read_loop)
@@ -757,6 +806,40 @@ def validate(self, text):
757806
port = int(port)
758807
return 0 <= port and port <= 65536
759808

809+
class ClojureSublimedShadowCljsBuildInputHandler(sublime_plugin.TextInputHandler):
810+
def initial_text(self):
811+
return ':app'
812+
813+
def preview(self, text):
814+
return sublime.Html("""
815+
<html>
816+
<body>
817+
Provide the cljs build for shadow to watch.
818+
<br>
819+
Valid options are <b>node-repl</b>, <b>browser-repl</b> or the build defined in shadow-cljs.edn / project.clj
820+
For more info check <a href="https://shadow-cljs.github.io/docs/UsersGuide.html#_repl_2"> Shadow Documentation </a>
821+
</body>
822+
</html>
823+
""")
824+
825+
def next_input(self, args):
826+
return ClojureSublimedHostPortInputHandler()
827+
828+
829+
class ClojureSublimedConnectShadowCljsCommand(sublime_plugin.ApplicationCommand):
830+
831+
def run(self, clojure_sublimed_shadow_cljs_build, clojure_sublimed_host_port=''):
832+
host, port = clojure_sublimed_host_port.strip().split(':')
833+
port = int(port)
834+
connect(host, port, Profile.SHADOW_CLJS, clojure_sublimed_shadow_cljs_build)
835+
836+
def input(self, args):
837+
if 'clojure_sublimed_shadow_cljs_build' not in args:
838+
return ClojureSublimedShadowCljsBuildInputHandler()
839+
840+
def is_enabled(self):
841+
return conn.socket == None
842+
760843
class ClojureSublimedConnectCommand(sublime_plugin.ApplicationCommand):
761844
def run(self, clojure_sublimed_host_port):
762845
host, port = clojure_sublimed_host_port.strip().split(':')
@@ -779,7 +862,7 @@ def is_enabled(self):
779862
class ClojureSublimedReconnectCommand(sublime_plugin.ApplicationCommand):
780863
def run(self):
781864
conn.disconnect()
782-
connect(conn.host, conn.port)
865+
connect(conn.host, conn.port, conn.profile, conn.cljs_build)
783866

784867
def is_enabled(self):
785868
return conn.socket != None

0 commit comments

Comments
 (0)