@@ -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+
2544class 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
374396def 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+
650682def 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+
760843class 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):
779862class 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