11# ###
22@info " COMMAND LINE ARGUMENTS"
33
4- asset_output_dir, vscode_proxy_root_raw, port_str, secret, pluto_launch_params = if isempty (ARGS )
4+ asset_output_dir, port_str, secret, pluto_launch_params = if isempty (ARGS )
55 @warn " No arguments given, using development defaults!"
66 mktempdir (cleanup= false ), " " , " 4653" , " asdf" , " {}"
77else
88 ARGS
99end
1010port = parse (Int, port_str)
11- vscode_proxy_root = let s = vscode_proxy_root_raw
12- if isempty (s) || endswith (s, " /" )
13- s
14- else
15- s * " /"
16- end
17- end
1811
1912
2013# ###
2114@info " PLUTO SETUP"
22-
15+ using Base64
2316import Pkg
2417Pkg. instantiate ()
2518
2619import JSON
2720using Suppressor
21+ using UUIDs
2822
2923import Pluto
3024
25+ #= These are the function which document how we communicate, through STDIN
26+ # with the extension =#
27+ function getNextSTDINCommand ()
28+ new_command_str_raw = readuntil (stdin , ' \0 ' )
29+ new_command_str = strip (new_command_str_raw, [' \0 ' ])
30+ JSON. parse (new_command_str)
31+ end
32+
33+ function sendSTDERRCommand (name:: String , payload:: String )
34+ io = IOBuffer ()
35+ io64 = Base64EncodePipe (io)
36+ print (io64, payload)
37+ close (io64)
38+ @info " Command: [[Notebook=$(name) ]] ## $(String (take! (io))) ###"
39+ end
40+
41+ # This is the definition of type piracy
42+ @Base . kwdef struct PlutoExtensionSessionData
43+ textRepresentations:: Dict{String, String}
44+ notebooks:: Dict{String, Pluto.Notebook}
45+ session:: Pluto.ServerSession
46+ session_options:: Any
47+ jlfilesroot:: String
48+ end
49+
50+ # We spin up Pluto from here.
3151pluto_server_options = Pluto. Configuration. from_flat_kwargs (;
3252 port= port,
3353 launch_browser= false ,
@@ -42,17 +62,37 @@ pluto_server_session = Pluto.ServerSession(;
4262 options= pluto_server_options,
4363)
4464
65+ extensionData = PlutoExtensionSessionData (
66+ Dict (),
67+ Dict (),
68+ pluto_server_session,
69+ pluto_server_options,
70+ joinpath (asset_output_dir, " jlfiles/" )
71+ )
4572
46- # ##
47- @info " OPEN NOTEBOOK "
48-
49-
73+ function whenNotebookUpdates (jlfile, newString)
74+ filename = splitpath (jlfile)[ end ]
75+ sendSTDERRCommand (filename, newString)
76+ end
5077
78+ # This is the definition of Type Piracy 😇
79+ function Pluto. save_notebook (notebook:: Pluto.Notebook )
80+ oldRepr = get (extensionData. textRepresentations, notebook. path, " " )
81+ newRepr = sprint () do io
82+ Pluto. save_notebook (io, notebook)
83+ end
84+ if newRepr != oldRepr
85+ extensionData. textRepresentations[notebook. path] = newRepr
86+ whenNotebookUpdates (notebook. path, newRepr)
87+ end
88+ end
5189
90+ # ##
91+ @info " OPEN NOTEBOOK"
5292
5393# ###
5494
55- function generate_output (nb:: Pluto.Notebook , filename:: String , frontend_params:: Dict = Dict ())
95+ function generate_output (nb:: Pluto.Notebook , filename:: String , vscode_proxy_root :: String , frontend_params:: Dict = Dict ())
5696 @info " GENERATING HTML FOR BESPOKE EDITOR" string (nb. notebook_id)
5797 new_editor_contents = Pluto. generate_html (;
5898 pluto_cdn_root = vscode_proxy_root,
@@ -67,56 +107,90 @@ function generate_output(nb::Pluto.Notebook, filename::String, frontend_params::
67107end
68108
69109
70- copy_assets () = cp (Pluto. project_relative_path (" frontend" ), asset_output_dir; force= true )
110+ function copy_assets ()
111+ mkpath (asset_output_dir)
112+ src = Pluto. project_relative_path (" frontend" )
113+ dest = asset_output_dir
114+ for f in readdir (src)
115+ cp (joinpath (src, f), joinpath (dest, f); force= true )
116+ end
117+ end
71118
72119copy_assets ()
73-
74-
75- try
76- import BetterFileWatching
77- @async try
78- BetterFileWatching. watch_folder (Pluto. project_relative_path (" frontend" )) do event
79- @info " Pluto asset changed!"
80- copy_assets ()
120+ mkpath (extensionData. jlfilesroot)
121+
122+ try # # Note: This is to assist with co-developing Pluto & this Extension
123+ # # In a production setting it's not necessary to watch pluto folder for updates
124+ import BetterFileWatching
125+ @async try
126+ BetterFileWatching. watch_folder (Pluto. project_relative_path (" frontend" )) do event
127+ @info " Pluto asset changed!"
128+ # It's not safe to remove the folder
129+ # because we reuse HTML files
130+ copy_assets ()
131+ mkpath (joinpath (asset_output_dir, " jlfiles/" ))
132+ end
133+ catch e
134+ showerror (stderr , e, catch_backtrace ())
81135 end
82- catch e
83- showerror (stderr , e, catch_backtrace ())
84- end
85- @info " Watching Pluto folder for changes!"
136+ @info " Watching Pluto folder for changes!"
86137catch end
87138
88-
89-
90-
91139command_task = Pluto. @asynclog while true
92-
93- new_command_str_raw = readuntil (stdin , ' \0 ' )
94- new_command_str = strip (new_command_str_raw, [' \0 ' ])
95- new_command = JSON. parse (new_command_str)
140+ filenbmap = extensionData. notebooks
141+ new_command = getNextSTDINCommand ()
96142
97143 @info " New command received!" new_command
98144
99145 type = get (new_command, " type" , " " )
100146 detail = get (new_command, " detail" , Dict ())
101147
102- if type == " new"
148+
149+ if type == " open"
103150 editor_html_filename = detail[" editor_html_filename" ]
104- nb = Pluto. SessionActions. new (pluto_server_session)
151+ vscode_proxy_root = let
152+ s = get (detail, " vscode_proxy_root" , " not given" )
153+ if isempty (s) || endswith (s, " /" )
154+ s
155+ else
156+ s * " /"
157+ end
158+ end
159+ frontend_params = get (detail, " frontend_params" , Dict ())
105160
106- generate_output (nb, editor_html_filename)
107- elseif type == " open"
108- editor_html_filename = detail[" editor_html_filename" ]
109- nb = Pluto. SessionActions. open (pluto_server_session, detail[" path" ])
110161
111- generate_output (nb, editor_html_filename)
162+ jlpath = joinpath (extensionData. jlfilesroot, detail[" jlfile" ])
163+ extensionData. textRepresentations[detail[" jlfile" ]] = detail[" text" ]
164+ open (jlpath, " w" ) do f
165+ write (f, detail[" text" ])
166+ end
167+ nb = Pluto. SessionActions. open (pluto_server_session, jlpath; notebook_id= UUID (detail[" notebook_id" ]))
168+ filenbmap[detail[" jlfile" ]] = nb
169+ generate_output (nb, editor_html_filename, vscode_proxy_root, frontend_params)
170+
171+ elseif type == " update"
172+ nb = filenbmap[detail[" jlfile" ]]
173+ jlpath = joinpath (extensionData. jlfilesroot, detail[" jlfile" ])
174+ open (jlpath, " w" ) do f
175+ write (f, detail[" text" ])
176+ end
177+ Pluto. update_from_file (pluto_server_session, nb)
178+ extensionData. textRepresentations[detail[" jlfile" ]] = detail[" text" ]
179+
180+ elseif type == " shutdown"
181+ nb = get (filenbmap, detail[" jlfile" ], nothing );
182+ ! isnothing (nb) && Pluto. SessionActions. shutdown (
183+ pluto_server_session,
184+ nb,
185+ keep_in_session= false
186+ )
187+
112188 else
113189 @error " Message of this type not recognised. " type
114190 end
115191
116192end
117193
118-
119-
120194# ###
121195@info " RUNNING PLUTO SERVER..."
122196@info " MESSAGE TO EXTENSION: READY FOR COMMANDS"
0 commit comments