diff --git a/Project.toml b/Project.toml index f36f5e567b..caf74feb9c 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ name = "Pluto" uuid = "c3e4b0f8-55cb-11ea-2926-15256bba5781" license = "MIT" authors = ["Fons van der Plas ", "Mikołaj Bochenski "] -version = "0.8.5" +version = "0.8.6" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/assets/client.js b/assets/client.js index 20d829434c..22b39525f6 100644 --- a/assets/client.js +++ b/assets/client.js @@ -135,7 +135,7 @@ class PlutoConnection { } this.psocket.onopen = () => { this.sendreceive("connect", {}).then(u => { - this.plutoCONFIG = u.message.CONFIG + this.plutoENV = u.message.ENV if(this.notebookID && !u.message.notebookExists){ // https://github.com/fonsp/Pluto.jl/issues/55 document.location.href = "./" diff --git a/assets/editor.js b/assets/editor.js index 33d975ed42..578739030f 100644 --- a/assets/editor.js +++ b/assets/editor.js @@ -233,7 +233,7 @@ function updateLocalCellOutput(cellNode, msg) { // convert LaTeX to svg try{ - MathJax.typeset() + MathJax.typeset([containerNode]) } catch(err) { console.info("Failed to typeset TeX:") console.info(err) @@ -553,7 +553,7 @@ function onUpdate(update, byMe) { window.customPlutoListeners = {} function onEstablishConnection() { - const runAll = client.plutoCONFIG["PLUTO_RUN_NOTEBOOK_ON_LOAD"] == "true" + const runAll = client.plutoENV["PLUTO_RUN_NOTEBOOK_ON_LOAD"] == "true" // on socket success client.send("getallnotebooks", {}) diff --git a/src/Pluto.jl b/src/Pluto.jl index ef369d6cc7..3761f009fe 100644 --- a/src/Pluto.jl +++ b/src/Pluto.jl @@ -4,10 +4,12 @@ export Notebook, Cell, run import Pkg const PKG_ROOT_DIR = normpath(joinpath(@__DIR__, "..")) +include_dependency(joinpath(PKG_ROOT_DIR, "Project.toml")) const PLUTO_VERSION = VersionNumber(Pkg.TOML.parsefile(joinpath(PKG_ROOT_DIR, "Project.toml"))["version"]) const PLUTO_VERSION_STR = 'v' * string(PLUTO_VERSION) const JULIA_VERSION_STR = 'v' * string(VERSION) -const CONFIG = Dict( +const ENV_DEFAULTS = Dict( + "PLUTO_WORKSPACE_USE_DISTRIBUTED" => "true", "PLUTO_RUN_NOTEBOOK_ON_LOAD" => "true", "PLUTO_ROOT_URL" => "/", ) diff --git a/src/notebookserver/Notebook.jl b/src/notebookserver/Notebook.jl index 7fbeae8491..77b10e1be6 100644 --- a/src/notebookserver/Notebook.jl +++ b/src/notebookserver/Notebook.jl @@ -153,7 +153,7 @@ function load_notebook(path::String) update_caches!(loaded, loaded.cells) save_notebook(loaded) # Clear symstates if autorun is disabled. Otherwise running a single cell for the first time will also run downstream cells. - if CONFIG["PLUTO_RUN_NOTEBOOK_ON_LOAD"] != "true" + if ENV["PLUTO_RUN_NOTEBOOK_ON_LOAD"] != "true" for cell in loaded.cells cell.symstate = SymbolsState() end diff --git a/src/react/WorkspaceManager.jl b/src/react/WorkspaceManager.jl index bae4054808..ea2e207e51 100644 --- a/src/react/WorkspaceManager.jl +++ b/src/react/WorkspaceManager.jl @@ -16,19 +16,6 @@ Workspace(workspace_pid::Integer, module_name::Symbol) = let Workspace(workspace_pid, module_name, t) end -"Should future workspaces be created on a separate process (`true`) or on the same one (`false`)? - -Only workspaces on a separate process can be stopped during execution. Windows currently supports `true` only partially: you can't stop cells on Windows." -function set_default_distributed(val::Bool) - global default_distributed = val -end - -function reset_default_distributed() - global default_distributed = Sys.iswindows() ? false : true -end - -reset_default_distributed() - "These expressions get executed whenever a new workspace is created." const workspace_preamble = [ :(using Markdown, Main.PlutoRunner), @@ -45,8 +32,10 @@ moduleworkspace_count = 0 workspaces = Dict{UUID,Workspace}() -"Create a workspace for the notebook, optionally in a separate process." -function make_workspace(notebook::Notebook, new_process = default_distributed)::Workspace +"""Create a workspace for the notebook, optionally in a separate process. + +`new_process`: Should future workspaces be created on a separate process (`true`) or on the same one (`false`)? Only workspaces on a separate process can be stopped during execution. Windows currently supports `true` only partially: you can't stop cells on Windows. _Defaults to `ENV["PLUTO_WORKSPACE_USE_DISTRIBUTED"]`_""" +function make_workspace(notebook::Notebook, new_process = (ENV["PLUTO_WORKSPACE_USE_DISTRIBUTED"] == "true"))::Workspace pid = if new_process create_workspaceprocess() else @@ -178,10 +167,10 @@ function eval_fetch_in_workspace(workspace::Workspace, expr::Any, ends_with_semi @assert ex.pid == workspace.workspace_pid @assert ex.captured.ex isa InterruptException - return (output_formatted = PlutoRunner.format_output(InterruptException()), errored = true, interrupted = true, runtime=missing, declared_assignments=Set{Symbol}(), declared_references=Set{Symbol}()) + return (output_formatted = PlutoRunner.format_output(InterruptException()), errored = true, interrupted = true, runtime=missing) catch assertionerr showerror(stderr, exs) - return (output_formatted = PlutoRunner.format_output(exs), errored = true, interrupted = true, runtime=missing, declared_assignments=Set{Symbol}(), declared_references=Set{Symbol}()) + return (output_formatted = PlutoRunner.format_output(exs), errored = true, interrupted = true, runtime=missing) end end @@ -243,7 +232,7 @@ function kill_workspace(workspace::Workspace) return false end if workspace.workspace_pid == Distributed.myid() - @warn "Cells in this workspace can't be stopped, because it is not running in a separate workspace. Use `set_default_distributed` to control whether future workspaces are generated in a separate process." + @warn """Cells in this workspace can't be stopped, because it is not running in a separate workspace. Use `ENV["PLUTO_WORKSPACE_USE_DISTRIBUTED"]` to control whether future workspaces are generated in a separate process.""" end # You can force kill a julia process by pressing Ctrl+C five times 🙃 diff --git a/src/runner/PlutoRunner.jl b/src/runner/PlutoRunner.jl index 98df7805a7..dab3b5e929 100644 --- a/src/runner/PlutoRunner.jl +++ b/src/runner/PlutoRunner.jl @@ -229,14 +229,17 @@ struct 🥔 end const struct_showmethod = which(show, (IO, 🥔)) const struct_showmethod_mime = which(show, (IO, MIME"text/plain", 🥔)) -function show_richest(io::IO, @nospecialize(x); onlyhtml::Bool=false) - mime = first(filter(m->Base.invokelatest(showable, m, x), allmimes)) +function show_richest(io::IO, @nospecialize(x); onlyhtml::Bool=false)::MIME + mime = allmimes[findfirst(m -> Base.invokelatest(showable, m, x), allmimes)] t = typeof(x) - if mime isa MIME"text/plain" && + isstruct = + mime isa MIME"text/plain" && t isa DataType && which(show, (IO, MIME"text/plain", t)) === struct_showmethod_mime && which(show, (IO, t)) === struct_showmethod + + if isstruct # we have a struct with no specialised show function # we display it as an interactive tree show_struct(io, x) diff --git a/src/webserver/Dynamic.jl b/src/webserver/Dynamic.jl index a2a3093a5d..c6cbf36b22 100644 --- a/src/webserver/Dynamic.jl +++ b/src/webserver/Dynamic.jl @@ -102,7 +102,7 @@ end responses[:connect] = (body, notebook=nothing; initiator::Union{Initiator, Missing}=missing) -> begin putclientupdates!(initiator, UpdateMessage(:👋, Dict( :notebookExists => (notebook != nothing), - :CONFIG => CONFIG, + :ENV => filter(p -> startswith(p.first, "PLUTO"), ENV), ), nothing, nothing, initiator)) end diff --git a/src/webserver/Static.jl b/src/webserver/Static.jl index 4abc117ed8..d3d26aefcf 100644 --- a/src/webserver/Static.jl +++ b/src/webserver/Static.jl @@ -37,13 +37,12 @@ const PLUTOROUTER = HTTP.Router() function notebook_redirect(notebook) response = HTTP.Response(302, "") - push!(response.headers, "Location" => CONFIG["PLUTO_ROOT_URL"] * "edit?uuid=" * string(notebook.uuid)) + push!(response.headers, "Location" => ENV["PLUTO_ROOT_URL"] * "edit?uuid=" * string(notebook.uuid)) return response end function serve_sample(req::HTTP.Request) - uri=HTTP.URI(req.target) - + uri = HTTP.URI(req.target) path = split(uri.path, "sample/")[2] try nb = load_notebook_nobackup(joinpath(PKG_ROOT_DIR, "sample", path)) @@ -57,13 +56,12 @@ function serve_sample(req::HTTP.Request) end function serve_openfile(req::HTTP.Request) - uri=HTTP.URI(req.target) + uri = HTTP.URI(req.target) query = HTTP.URIs.unescapeuri(replace(uri.query, '+' => ' ')) if length(query) > 5 path = tamepath(query[6:end]) - - if(isfile(path)) + if (isfile(path)) try for nb in values(notebooks) if realpath(nb.path) == realpath(path) @@ -75,11 +73,9 @@ function serve_openfile(req::HTTP.Request) save_notebook(nb) notebooks[nb.uuid] = nb return notebook_redirect(nb) - catch e return HTTP.Response(500, "Failed to load notebook:\n\n$(e)\n\nGo back") end - # return HTTP.Response(200, """Path: $(path)""") else return HTTP.Response(404, "Can't find a file here.\n\nGo back") end diff --git a/src/webserver/WebServer.jl b/src/webserver/WebServer.jl index 47e8124966..1379e3afe1 100644 --- a/src/webserver/WebServer.jl +++ b/src/webserver/WebServer.jl @@ -10,6 +10,12 @@ function endswith(vec::Vector{T}, suffix::Vector{T}) where T liv >= lis && (view(vec, (liv-lis + 1):liv) == suffix) end +function set_ENV_defaults() + for pair in ENV_DEFAULTS + haskey(ENV, pair.first) || setindex!(ENV, pair.second, pair.first) + end +end + const connectedclients = Dict{Symbol,Client}() const notebooks = Dict{UUID,Notebook}() @@ -117,6 +123,7 @@ const MSG_DELIM = "IUUQ.km jt ejggjdvmu vhi" # riddle me this, Julius This will start the static HTTP server and a WebSocket server. Pluto Notebooks will be started dynamically (by user input).""" function run(host, port::Integer; launchbrowser::Bool = false) + set_ENV_defaults() hostIP = parse(Sockets.IPAddr, host) serversocket = Sockets.listen(hostIP, UInt16(port)) servertask = @async HTTP.serve(hostIP, UInt16(port), stream = true, server = serversocket) do http::HTTP.Stream @@ -206,12 +213,12 @@ function run(host, port::Integer; launchbrowser::Bool = false) end end - address = if CONFIG["PLUTO_ROOT_URL"] == "/" + address = if ENV["PLUTO_ROOT_URL"] == "/" hostPretty = (hostStr = string(hostIP)) == "127.0.0.1" ? "localhost" : hostStr portPretty = Int(port) "http://$(hostPretty):$(portPretty)/" else - CONFIG["PLUTO_ROOT_URL"] + ENV["PLUTO_ROOT_URL"] end println("Go to $address to start writing ~ have fun!") println() diff --git a/test/React.jl b/test/React.jl index 6ae604733a..b977a081a7 100644 --- a/test/React.jl +++ b/test/React.jl @@ -1,14 +1,9 @@ using Test using Pluto import Pluto: Notebook, Client, run_reactive!, Cell, WorkspaceManager +import Distributed -# to_test = [WorkspaceManager.ModuleWorkspace] -# if Sys.iswindows() -# println("Can't test ProcessWorkspace on Windows") -# else -# push!(to_test, WorkspaceManager.ProcessWorkspace) -# end -WorkspaceManager.set_default_distributed(false) +ENV["PLUTO_WORKSPACE_USE_DISTRIBUTED"] = "false" @testset "Reactivity" begin fakeclient = Client(:fake, nothing) @@ -17,7 +12,7 @@ WorkspaceManager.set_default_distributed(false) @testset "Basic $(parallel ? "distributed" : "single-process")" for parallel in [false, true] - WorkspaceManager.set_default_distributed(parallel) + ENV["PLUTO_WORKSPACE_USE_DISTRIBUTED"] = string(parallel) notebook = Notebook([ Cell("x = 1"), @@ -30,11 +25,13 @@ WorkspaceManager.set_default_distributed(false) g(a,b) = y end"""), Cell("g(6) + g(6,6)"), + + Cell("import Distributed"), + Cell("Distributed.myid()"), ]) fakeclient.connected_notebook = notebook @test !haskey(WorkspaceManager.workspaces, notebook.uuid) - # @test WorkspaceManager.get_workspace(notebook) isa method run_reactive!(notebook, notebook.cells[1:2]) @test notebook.cells[1].output_repr == notebook.cells[2].output_repr @@ -72,10 +69,13 @@ WorkspaceManager.set_default_distributed(false) run_reactive!(notebook, notebook.cells[1]) @test notebook.cells[6].output_repr == "3" + run_reactive!(notebook, notebook.cells[7:8]) + @test xor(parallel, notebook.cells[8].output_repr == string(Distributed.myid())) + WorkspaceManager.unmake_workspace(notebook) end - WorkspaceManager.set_default_distributed(false) + ENV["PLUTO_WORKSPACE_USE_DISTRIBUTED"] = "false" # https://github.com/fonsp/Pluto.jl/issues/32 @@ -623,4 +623,4 @@ WorkspaceManager.set_default_distributed(false) end end -WorkspaceManager.reset_default_distributed() +Pluto.set_ENV_defaults() diff --git a/test/helpers.jl b/test/helpers.jl index 6b56057461..7f1a9436ac 100644 --- a/test/helpers.jl +++ b/test/helpers.jl @@ -1,6 +1,6 @@ +import Pluto import Pluto.ExploreExpression: SymbolsState, compute_symbolreferences - "Calls `ExploreExpression.compute_symbolreferences` on the given `expr` and test the found SymbolsState against a given one, with convient syntax. # Example @@ -105,4 +105,6 @@ function num_backups_in(dir::AbstractString) count(readdir(dir)) do fn occursin("backup", fn) end -end \ No newline at end of file +end + +Pluto.set_ENV_defaults() \ No newline at end of file