Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

with_js_link with stronger invalidation #2780

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/evaluation/Run.jl
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ function run_reactive_core!(
new_errable = keys(new_order.errable)
to_delete_vars = union!(to_delete_vars, defined_variables(new_topology, new_errable)...)
to_delete_funcs = union!(to_delete_funcs, defined_functions(new_topology, new_errable)...)

cells_to_macro_invalidate = Set{UUID}(c.cell_id for c in cells_with_deleted_macros(old_topology, new_topology))
cells_to_js_link_invalidate = Set{UUID}(c.cell_id for c in union!(Set{Cell}(), to_run, new_errable, indirectly_deactivated))

module_imports_to_move = reduce(all_cells(new_topology); init=Set{Expr}()) do module_imports_to_move, c
c ∈ to_run && return module_imports_to_move
Expand All @@ -156,7 +157,7 @@ function run_reactive_core!(

if will_run_code(notebook)
to_delete_funcs_simple = Set{Tuple{UUID,Tuple{Vararg{Symbol}}}}((id, name.parts) for (id,name) in to_delete_funcs)
deletion_hook((session, notebook), old_workspace_name, nothing, to_delete_vars, to_delete_funcs_simple, module_imports_to_move, cells_to_macro_invalidate; to_run) # `deletion_hook` defaults to `WorkspaceManager.move_vars`
deletion_hook((session, notebook), old_workspace_name, nothing, to_delete_vars, to_delete_funcs_simple, module_imports_to_move, cells_to_macro_invalidate, cells_to_js_link_invalidate; to_run) # `deletion_hook` defaults to `WorkspaceManager.move_vars`
end

foreach(v -> delete!(notebook.bonds, v), to_delete_vars)
Expand Down
3 changes: 2 additions & 1 deletion src/evaluation/RunBonds.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function set_bond_values_reactive(;
bond_value_pairs = zip(syms_to_set, new_values)

syms_to_set_set = Set{Symbol}(syms_to_set)
function custom_deletion_hook((session, notebook)::Tuple{ServerSession,Notebook}, old_workspace_name, new_workspace_name, to_delete_vars::Set{Symbol}, methods_to_delete, module_imports_to_move, cells_to_macro_invalidate; to_run)
function custom_deletion_hook((session, notebook)::Tuple{ServerSession,Notebook}, old_workspace_name, new_workspace_name, to_delete_vars::Set{Symbol}, methods_to_delete, module_imports_to_move, cells_to_macro_invalidate, cells_to_js_link_invalidate; to_run)
to_delete_vars = union(to_delete_vars, syms_to_set_set) # also delete the bound symbols
WorkspaceManager.move_vars(
(session, notebook),
Expand All @@ -51,6 +51,7 @@ function set_bond_values_reactive(;
methods_to_delete,
module_imports_to_move,
cells_to_macro_invalidate,
cells_to_js_link_invalidate,
syms_to_set_set,
)
set_bond_value_pairs!(session, notebook, zip(syms_to_set, new_values))
Expand Down
10 changes: 7 additions & 3 deletions src/evaluation/WorkspaceManager.jl
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ function move_vars(
methods_to_delete::Set{Tuple{UUID,Tuple{Vararg{Symbol}}}},
module_imports_to_move::Set{Expr},
cells_to_macro_invalidate::Set{UUID},
cells_to_js_link_invalidate::Set{UUID},
keep_registered::Set{Symbol}=Set{Symbol}();
kwargs...
)
Expand All @@ -570,6 +571,7 @@ function move_vars(
$methods_to_delete,
$module_imports_to_move,
$cells_to_macro_invalidate,
$cells_to_js_link_invalidate,
$keep_registered,
)
end)
Expand All @@ -580,16 +582,18 @@ function move_vars(
to_delete::Set{Symbol},
methods_to_delete::Set{Tuple{UUID,Tuple{Vararg{Symbol}}}},
module_imports_to_move::Set{Expr},
cells_to_macro_invalidate::Set{UUID};
cells_to_macro_invalidate::Set{UUID},
cells_to_js_link_invalidate::Set{UUID};
kwargs...
)
move_vars(
session_notebook,
bump_workspace_module(session_notebook)...,
to_delete,
to_delete,
methods_to_delete,
module_imports_to_move,
cells_to_macro_invalidate;
cells_to_macro_invalidate,
cells_to_js_link_invalidate;
kwargs...
)
end
Expand Down
7 changes: 6 additions & 1 deletion src/runner/PlutoRunner/src/PlutoRunner.jl
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ function move_vars(
methods_to_delete::Set{Tuple{UUID,Tuple{Vararg{Symbol}}}},
module_imports_to_move::Set{Expr},
cells_to_macro_invalidate::Set{UUID},
cells_to_js_link_invalidate::Set{UUID},
keep_registered::Set{Symbol},
)
old_workspace = getfield(Main, old_workspace_name)
Expand All @@ -704,7 +705,8 @@ function move_vars(
for cell_id in cells_to_macro_invalidate
delete!(cell_expanded_exprs, cell_id)
end

foreach(unregister_js_link, cells_to_js_link_invalidate)

# TODO: delete
Core.eval(new_workspace, :(import ..($(old_workspace_name))))

Expand Down Expand Up @@ -2577,6 +2579,9 @@ end
function unregister_js_link(cell_id::UUID)
# cancel old links
old_links = get!(() -> Dict{String,JSLink}(), cell_js_links, cell_id)
for (name, link) in old_links
link.cancelled_ref[] = true
end
for (name, link) in old_links
c = link.on_cancellation
c === nothing || c()
Expand Down
4 changes: 3 additions & 1 deletion src/webserver/Dynamic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ responses[:request_js_link_response] = function response_request_js_link_respons
require_notebook(🙋)
@assert will_run_code(🙋.notebook)

withtoken(🙋.notebook.executetoken) do
Threads.@spawn try
result = WorkspaceManager.eval_fetch_in_workspace(
(🙋.session, 🙋.notebook),
quote
Expand All @@ -544,6 +544,8 @@ responses[:request_js_link_response] = function response_request_js_link_respons
)

putclientupdates!(🙋.session, 🙋.initiator, UpdateMessage(:🐤, result, nothing, nothing, 🙋.initiator))
catch ex
@error "Error in request_js_link_response" exception=(ex, stacktrace(catch_backtrace()))
end
end

Expand Down
8 changes: 4 additions & 4 deletions test/frontend/__tests__/javascript_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ describe("JavaScript API", () => {
page,
`# ╔═╡ 90cfa9a0-114d-49bf-8dea-e97d58fa2442
html"""<script>
const div = document.createElement("div")
const div = document.createElement("find-me")
div.innerHTML = "${expected}"
return div;
</script>"""
`
)
await runAllChanged(page)
await waitForPlutoToCalmDown(page, { polling: 100 })
const initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output`, expected)
const initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output find-me`, expected)
expect(initialLastCellContent).toBe(expected)
})

Expand All @@ -69,7 +69,7 @@ describe("JavaScript API", () => {
)
await runAllChanged(page)
await waitForPlutoToCalmDown(page, { polling: 100 })
let initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output`, expected)
let initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output span`, expected)
expect(initialLastCellContent).toBe(expected)

await paste(
Expand All @@ -84,7 +84,7 @@ describe("JavaScript API", () => {
)
await runAllChanged(page)
await waitForPlutoToCalmDown(page, { polling: 100 })
initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output`, expected)
initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output span`, expected)
expect(initialLastCellContent).toBe(expected)
})

Expand Down
2 changes: 1 addition & 1 deletion test/frontend/__tests__/slide_controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("slideControls", () => {
await importNotebook(page, "slides.jl", { permissionToRunCode: false })
const plutoCellIds = await getCellIds(page)
const content = await waitForContent(page, `pluto-cell[id="${plutoCellIds[1]}"] pluto-output`)
expect(content).toBe("Slide 2")
expect(content).toBe("Slide 2\n")

const slide_1_title = await page.$(`pluto-cell[id="${plutoCellIds[0]}"] pluto-output h1`)
const slide_2_title = await page.$(`pluto-cell[id="${plutoCellIds[1]}"] pluto-output h1`)
Expand Down
4 changes: 2 additions & 2 deletions test/frontend/__tests__/wind_directions.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ describe("wind_directions", () => {
).toBe(expected)
}

await expect_chosen_directions('chosen_directions_copy\n"North"')
await expect_chosen_directions('chosen_directions_copyString1"North"')

expect(await page.evaluate((sel) => document.querySelector(sel).checked, checkbox_selector(0))).toBe(true)

await page.click(checkbox_selector(2))
await waitForPlutoToCalmDown(page)

await expect_chosen_directions('chosen_directions_copy\n"North"\n"South"')
await expect_chosen_directions('chosen_directions_copyString1"North"2"South"')

expect(await page.evaluate((sel) => document.querySelector(sel).checked, checkbox_selector(0))).toBe(true)
expect(await page.evaluate((sel) => document.querySelector(sel).checked, checkbox_selector(1))).toBe(false)
Expand Down
58 changes: 50 additions & 8 deletions test/frontend/__tests__/with_js_link.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import puppeteer from "puppeteer"
import { saveScreenshot, createPage, waitForContentToBecome, getTextContent } from "../helpers/common"
import { importNotebook, getPlutoUrl, shutdownCurrentNotebook, setupPlutoBrowser, getLogs, getLogSelector } from "../helpers/pluto"
import {
importNotebook,
getPlutoUrl,
shutdownCurrentNotebook,
setupPlutoBrowser,
getLogs,
getLogSelector,
writeSingleLineInPlutoInput,
runAllChanged,
waitForPlutoToCalmDown,
} from "../helpers/pluto"

describe("with_js_link", () => {
/**
Expand Down Expand Up @@ -137,16 +147,48 @@ describe("with_js_link", () => {

await page.waitForTimeout(4000)

// NOT
// they dont run in parallel so right now only cc1 should be finished
expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1")
expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("C2")
// expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1")
// expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("C2")

await expect_ev_output("c1", "CC1")
await expect_ev_output("c2", "CC2")
// await expect_ev_output("c1", "CC1")
// await expect_ev_output("c2", "CC2")

// NOT
// they should run in parallel: after 4 seconds both should be finished
// expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1")
// expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("CC2")
expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1")
expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("CC2")
})

const expect_jslog = async (expected) => {
expect(await waitForContentToBecome(page, "#checkme", expected)).toBe(expected)
}
it("js errors", async () => {
await waitForPlutoToCalmDown(page)
await page.waitForTimeout(100)
await expect_jslog("hello!")
await page.click("#jslogbtn")
await page.waitForTimeout(500)
await page.click("#jslogbtn")
await page.waitForTimeout(100)

// We clicked twice, but sometimes it only registers one click for some reason. I don't care, so let's check for either.
let prefix = await Promise.race([
waitForContentToBecome(page, "#checkme", "hello!clickyay KRATJE"),
waitForContentToBecome(page, "#checkme", "hello!clickclickyay KRATJEyay KRATJE"),
])

const yolotriggerid = "8782cc14-eb1a-48a8-a114-2f71f77be275"
await page.click(`pluto-cell[id="${yolotriggerid}"] pluto-output input[type="button"]`)
await expect_jslog(`${prefix}hello!`)
await page.click("#jslogbtn")
await expect_jslog(`${prefix}hello!clicknee exception in Julia callback:ErrorException("bad")`)

await page.click("#jslogbtn")
await page.waitForTimeout(500)

await page.click(`pluto-cell[id="${yolotriggerid}"] .runcell`)

await expect_jslog(`${prefix}hello!clicknee exception in Julia callback:ErrorException("bad")clickhello!nee link not found`)
})
})
2 changes: 1 addition & 1 deletion test/frontend/fixtures/slides.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
### A Pluto.jl notebook ###
# v0.11.14
# v0.19.40

using Markdown
using InteractiveUtils
Expand Down
Loading
Loading