From 81ef356dfc9dd233be588ad408b0a1c4e30e23c0 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Sun, 12 Apr 2020 22:02:42 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=80=20Sync=20code=20folds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Project.toml | 2 +- assets/editor.js | 38 +++++++++++++++++++++++----------- src/notebookserver/Cell.jl | 7 +++++-- src/notebookserver/Client.jl | 5 +++++ src/notebookserver/Notebook.jl | 21 ++++++++++++------- src/webserver/Dynamic.jl | 8 +++++++ 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/Project.toml b/Project.toml index 95ee50d957..038fc8d4a3 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.6.0" +version = "0.6.1" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/assets/editor.js b/assets/editor.js index cd40e50b2f..af94b4cb7f 100644 --- a/assets/editor.js +++ b/assets/editor.js @@ -281,21 +281,18 @@ document.addEventListener("DOMContentLoaded", () => { // EVENT LISTENERS FOR CLICKY THINGS newCellNode.querySelector("celloutput").onclick = (e) => { - // Do not fold if the click event was fired because the user selects text in the output. - if (window.getSelection().isCollapsed) { - // Do not fold if a link was clicked. + var newFolded = newCellNode.classList.contains("code-folded") + if (!newCellNode.querySelector("celloutput").innerHTML || newCellNode.querySelector("celloutput").innerHTML === "
") { + // You may not fold code if the output is empty (it would be confusing) + newFolded = false + } else if (window.getSelection().isCollapsed) { + // Do not fold if the click event was fired because the user selects text in the output. if (e.target.tagName != "A") { - newCellNode.classList.toggle("code-folded") - // Force redraw: - if (!newCellNode.classList.contains("code-folded")) { - editor.refresh() - } + // Do not fold if a link was clicked. + newFolded = !newFolded } } - // You may not fold code if the output is empty (it would be confusing) - if (!newCellNode.querySelector("celloutput").innerHTML || newCellNode.querySelector("celloutput").innerHTML === "
") { - newCellNode.classList.remove("code-folded") - } + requestCodeFoldRemoteCell(uuid, newFolded) } newCellNode.querySelector(".addcell.before").onclick = (e) => { @@ -322,6 +319,16 @@ document.addEventListener("DOMContentLoaded", () => { return newCellNode } + function foldLocalCell(cellNode, newFolded) { + if(newFolded) { + cellNode.classList.add("code-folded") + } else { + cellNode.classList.remove("code-folded") + // Force redraw: + editor.refresh() + } + } + function deleteLocalCell(cellNode) { // TODO: event listeners? gc? uuid = cellNode.id @@ -404,6 +411,10 @@ document.addEventListener("DOMContentLoaded", () => { client.send("deletecell", {}, uuid) } + function requestCodeFoldRemoteCell(uuid, newFolded) { + client.send("foldcell", { folded: newFolded }, uuid) + } + /* SERVER CONNECTION */ @@ -434,6 +445,9 @@ document.addEventListener("DOMContentLoaded", () => { // TODO: catch exception window.localCells[update.cellID].classList.add("running") break + case "cell_folded": + foldLocalCell(window.localCells[update.cellID], message.folded) + break case "notebook_list": // TODO: catch exception updateRemoteNotebooks(message.notebooks) diff --git a/src/notebookserver/Cell.jl b/src/notebookserver/Cell.jl index 618a189453..d20e923bb6 100644 --- a/src/notebookserver/Cell.jl +++ b/src/notebookserver/Cell.jl @@ -7,14 +7,17 @@ mutable struct Cell "because Cells can be reordered, they get a UUID. The JavaScript frontend indexes cells using the UUID." uuid::UUID code::String - parsedcode::Any + output_repr::Union{String, Nothing} error_repr::Union{String, Nothing} repr_mime::MIME runtime::Union{Missing,UInt64} + code_folded::Bool + + parsedcode::Any symstate::SymbolsState module_usings::Set{Expr} end -Cell(uuid, code) = Cell(uuid, code, nothing, nothing, nothing, MIME("text/plain"), missing, SymbolsState(), Set{Expr}()) +Cell(uuid, code) = Cell(uuid, code, nothing, nothing, MIME("text/plain"), missing, false, nothing, SymbolsState(), Set{Expr}()) Cell(code) = Cell(uuid1(), code) \ No newline at end of file diff --git a/src/notebookserver/Client.jl b/src/notebookserver/Client.jl index 02d8ed5d58..07eeea26fb 100644 --- a/src/notebookserver/Client.jl +++ b/src/notebookserver/Client.jl @@ -69,6 +69,11 @@ function clientupdate_cell_running(notebook::Notebook, cell::Cell; initiator::Un Dict(), notebook, cell, initiator) end +function clientupdate_cell_folded(notebook::Notebook, cell::Cell, folded::Bool; initiator::Union{Initiator,Missing}=missing) + return UpdateMessage(:cell_folded, + Dict(:folded => folded), notebook, cell, initiator) +end + function clientupdate_notebook_list(notebooks; initiator::Union{Initiator,Missing}=missing) short_paths = Dict() diff --git a/src/notebookserver/Notebook.jl b/src/notebookserver/Notebook.jl index dfe2ee11fb..941e658fa5 100644 --- a/src/notebookserver/Notebook.jl +++ b/src/notebookserver/Notebook.jl @@ -34,9 +34,10 @@ function selectcell_byuuid(notebook::Notebook, uuid::UUID)::Union{Cell,Nothing} end # We use a creative delimiter to avoid accidental use in code -_uuid_delimiter = "# ╔═╡ " -_order_delimited = "# ○ " -_cell_appendix = "\n\n" +const _uuid_delimiter = "# ╔═╡ " +const _order_delimiter = "# ○ " +const _order_delimiter_folded = "# c " +const _cell_appendix = "\n\n" emptynotebook(path) = Notebook(path, [Cell("")]) emptynotebook() = emptynotebook(tempname() * ".jl") @@ -87,7 +88,8 @@ function save_notebook(io, notebook::Notebook) write(io, _uuid_delimiter * "Cell order:" * "\n") for c in notebook.cells - write(io, _order_delimited * string(c.uuid) * "\n") + delim = c.code_folded ? _order_delimiter_folded : _order_delimiter + write(io, delim * string(c.uuid) * "\n") end end @@ -137,13 +139,16 @@ function load_notebook_nobackup(io, path) ordered_cells = Cell[] while !eof(io) uuid_str = String(readline(io)) - if startswith(uuid_str, _order_delimited) + o, c = startswith(uuid_str, _order_delimiter), startswith(uuid_str, _order_delimiter_folded) + if o || c uuid = let - # Because we support Unicode, this is not just `length(_order_delimited) + 1`. - uuid_index = ncodeunits(_order_delimited) + 1 + # Because we support Unicode, this is not just `length(_order_delimiter) + 1`. + uuid_index = ncodeunits(_order_delimiter) + 1 UUID(uuid_str[uuid_index:end]) end - push!(ordered_cells, collected_cells[uuid]) + next_cell = collected_cells[uuid] + next_cell.code_folded = c + push!(ordered_cells, next_cell) end end diff --git a/src/webserver/Dynamic.jl b/src/webserver/Dynamic.jl index 6263144cf1..ec6bee140b 100644 --- a/src/webserver/Dynamic.jl +++ b/src/webserver/Dynamic.jl @@ -170,6 +170,14 @@ responses[:changecell] = (body, notebook::Notebook, cell::Cell; initiator::Union run_reactive_async!(notebook, cell) end +responses[:foldcell] = (body, notebook::Notebook, cell::Cell; initiator::Union{Initiator, Missing}=missing) -> begin + newfolded = body["folded"] + cell.code_folded = newfolded + save_notebook(notebook) + + putnotebookupdates!(notebook, clientupdate_cell_folded(notebook, cell, newfolded, initiator=initiator)) +end + responses[:run] = (body, notebook::Notebook, cell::Cell; initiator::Union{Initiator, Missing}=missing) -> begin run_reactive_async!(notebook, cell) end