mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-09-20 10:05:57 +08:00
Introduce a setup cell (#1075)
* Introduce a setup cell * Don't collapse setup cell when dirty * Collapse fresh setup cell when empty * Reword collapsed setup cell text
This commit is contained in:
parent
6f6b7e3ee0
commit
5476fd001d
|
@ -34,7 +34,7 @@ solely client-side operations.
|
|||
}
|
||||
|
||||
[data-element="session"][data-js-insert-mode]
|
||||
[data-element="cell"][data-type="markdown"][data-js-focused]
|
||||
[data-element="cell"][data-js-focused]
|
||||
[data-element="enable-insert-mode-button"] {
|
||||
@apply hidden;
|
||||
}
|
||||
|
@ -82,12 +82,12 @@ solely client-side operations.
|
|||
@apply opacity-100 pointer-events-auto;
|
||||
}
|
||||
|
||||
[data-element="cell"] [data-element="cell-status"][data-js-changed] {
|
||||
[data-element="cell"][data-js-changed] [data-element="cell-status"] {
|
||||
@apply italic;
|
||||
}
|
||||
|
||||
[data-element="cell"]
|
||||
[data-element="cell-status"]:not([data-js-changed])
|
||||
[data-element="cell"]:not([data-js-changed])
|
||||
[data-element="cell-status"]
|
||||
[data-element="change-indicator"] {
|
||||
@apply invisible;
|
||||
}
|
||||
|
@ -137,6 +137,39 @@ solely client-side operations.
|
|||
@apply hidden;
|
||||
}
|
||||
|
||||
[data-element="session"]:not([data-js-insert-mode])
|
||||
[data-element="cell"][data-type="setup"]:not([data-eval-validity="fresh"]:not([data-js-empty])):not([data-js-changed])
|
||||
[data-element="editor-box"],
|
||||
[data-element="session"]
|
||||
[data-element="cell"][data-type="setup"]:not([data-eval-validity="fresh"]:not([data-js-empty])):not([data-js-changed]):not([data-js-focused])
|
||||
[data-element="editor-box"] {
|
||||
@apply h-0 overflow-hidden;
|
||||
}
|
||||
|
||||
[data-element="session"][data-js-insert-mode]
|
||||
[data-element="cell"][data-type="setup"][data-js-focused]
|
||||
[data-element="enable-insert-mode-button"],
|
||||
[data-element="session"]
|
||||
[data-element="cell"][data-type="setup"][data-eval-validity="fresh"]:not([data-js-empty])
|
||||
[data-element="enable-insert-mode-button"],
|
||||
[data-element="session"]
|
||||
[data-element="cell"][data-type="setup"][data-js-changed]
|
||||
[data-element="enable-insert-mode-button"] {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
[data-element="session"][data-js-insert-mode]
|
||||
[data-element="cell"][data-type="setup"][data-js-focused]
|
||||
[data-element="info-box"],
|
||||
[data-element="session"]
|
||||
[data-element="cell"][data-type="setup"][data-eval-validity="fresh"]:not([data-js-empty])
|
||||
[data-element="info-box"],
|
||||
[data-element="session"]
|
||||
[data-element="cell"][data-type="setup"][data-js-changed]
|
||||
[data-element="info-box"] {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
[data-element="cell"][data-type="smart"]:not([data-js-source-visible])
|
||||
[data-element="show-ui-icon"] {
|
||||
@apply hidden;
|
||||
|
@ -147,17 +180,6 @@ solely client-side operations.
|
|||
@apply hidden;
|
||||
}
|
||||
|
||||
[data-element="cell"][data-type="smart"]:not([data-js-source-visible])
|
||||
[data-element="cell-status-container"] {
|
||||
@apply flex justify-end;
|
||||
}
|
||||
|
||||
[data-element="cell"][data-type="smart"]:not([data-js-source-visible])
|
||||
[data-element="cell-status-container"]
|
||||
> *:first-child {
|
||||
@apply mt-2;
|
||||
}
|
||||
|
||||
[data-element="cell"][data-type="smart"][data-js-source-visible]
|
||||
[data-element="cell-status-container"] {
|
||||
@apply absolute bottom-2 right-2;
|
||||
|
|
|
@ -174,19 +174,23 @@ const Cell = {
|
|||
});
|
||||
|
||||
if (tag === "primary") {
|
||||
const source = liveEditor.getSource();
|
||||
|
||||
this.el.toggleAttribute("data-js-empty", source === "");
|
||||
|
||||
liveEditor.onChange((newSource) => {
|
||||
this.el.toggleAttribute("data-js-empty", newSource === "");
|
||||
});
|
||||
|
||||
// Setup markdown rendering
|
||||
if (this.props.type === "markdown") {
|
||||
const markdownContainer = this.el.querySelector(
|
||||
`[data-element="markdown-container"]`
|
||||
);
|
||||
const markdown = new Markdown(
|
||||
markdownContainer,
|
||||
liveEditor.getSource(),
|
||||
{
|
||||
baseUrl: this.props.sessionPath,
|
||||
emptyText: "Empty markdown cell",
|
||||
}
|
||||
);
|
||||
const markdown = new Markdown(markdownContainer, source, {
|
||||
baseUrl: this.props.sessionPath,
|
||||
emptyText: "Empty markdown cell",
|
||||
});
|
||||
|
||||
liveEditor.onChange((newSource) => {
|
||||
markdown.setContent(newSource);
|
||||
|
@ -259,7 +263,7 @@ const Cell = {
|
|||
const source = this.liveEditors.primary.getSource();
|
||||
const digest = md5Base64(source);
|
||||
const changed = this.props.evaluationDigest !== digest;
|
||||
cellStatus.toggleAttribute("data-js-changed", changed);
|
||||
this.el.toggleAttribute("data-js-changed", changed);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ class LiveEditor {
|
|||
this.language = language;
|
||||
this.intellisense = intellisense;
|
||||
this.readOnly = readOnly;
|
||||
this._onChange = null;
|
||||
this._onBlur = null;
|
||||
this._onCursorSelectionChange = null;
|
||||
this._onChange = [];
|
||||
this._onBlur = [];
|
||||
this._onCursorSelectionChange = [];
|
||||
this._remoteUserByClientPid = {};
|
||||
|
||||
this._mountEditor();
|
||||
|
@ -49,7 +49,7 @@ class LiveEditor {
|
|||
|
||||
this.editorClient.onDelta((delta) => {
|
||||
this.source = delta.applyToString(this.source);
|
||||
this._onChange && this._onChange(this.source);
|
||||
this._onChange.forEach((callback) => callback(this.source));
|
||||
});
|
||||
|
||||
this.editor.onDidFocusEditorWidget(() => {
|
||||
|
@ -58,12 +58,13 @@ class LiveEditor {
|
|||
|
||||
this.editor.onDidBlurEditorWidget(() => {
|
||||
this.editor.updateOptions({ matchBrackets: "never" });
|
||||
this._onBlur && this._onBlur();
|
||||
this._onBlur.forEach((callback) => callback());
|
||||
});
|
||||
|
||||
this.editor.onDidChangeCursorSelection((event) => {
|
||||
this._onCursorSelectionChange &&
|
||||
this._onCursorSelectionChange(event.selection);
|
||||
this._onCursorSelectionChange.forEach((callback) =>
|
||||
callback(event.selection)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -78,21 +79,21 @@ class LiveEditor {
|
|||
* Registers a callback called with a new cell content whenever it changes.
|
||||
*/
|
||||
onChange(callback) {
|
||||
this._onChange = callback;
|
||||
this._onChange.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called with a new cursor selection whenever it changes.
|
||||
*/
|
||||
onCursorSelectionChange(callback) {
|
||||
this._onCursorSelectionChange = callback;
|
||||
this._onCursorSelectionChange.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback called whenever the editor loses focus.
|
||||
*/
|
||||
onBlur(callback) {
|
||||
this._onBlur = callback;
|
||||
this._onBlur.push(callback);
|
||||
}
|
||||
|
||||
focus() {
|
||||
|
|
|
@ -475,11 +475,15 @@ const Session = {
|
|||
* Enters insert mode when a markdown cell is double-clicked.
|
||||
*/
|
||||
handleDocumentDoubleClick(event) {
|
||||
const markdownCell = event.target.closest(
|
||||
`[data-element="cell"][data-type="markdown"]`
|
||||
);
|
||||
const cell = event.target.closest(`[data-element="cell"]`);
|
||||
const type = cell && cell.getAttribute("data-type");
|
||||
|
||||
if (markdownCell && this.focusedId && !this.insertMode) {
|
||||
if (
|
||||
type &&
|
||||
["markdown", "setup"].includes(type) &&
|
||||
this.focusedId &&
|
||||
!this.insertMode
|
||||
) {
|
||||
this.setInsertMode(true);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
* Checks if the given cell type is eligible for evaluation.
|
||||
*/
|
||||
export function isEvaluable(cellType) {
|
||||
return ["code", "smart"].includes(cellType);
|
||||
return ["code", "smart", "setup"].includes(cellType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given cell type has primary editable editor.
|
||||
*/
|
||||
export function isDirectlyEditable(cellType) {
|
||||
return ["markdown", "code"].includes(cellType);
|
||||
return ["markdown", "code", "setup"].includes(cellType);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ defmodule Livebook.LiveMarkdown.Export do
|
|||
end
|
||||
|
||||
defp render_notebook(notebook, ctx) do
|
||||
%{setup_section: %{cells: [setup_cell]}} = notebook
|
||||
|
||||
comments =
|
||||
Enum.map(notebook.leading_comments, fn
|
||||
[line] -> ["<!-- ", line, " -->"]
|
||||
|
@ -51,12 +53,14 @@ defmodule Livebook.LiveMarkdown.Export do
|
|||
end)
|
||||
|
||||
name = ["# ", notebook.name]
|
||||
setup_cell = render_setup_cell(setup_cell, ctx)
|
||||
sections = Enum.map(notebook.sections, &render_section(&1, notebook, ctx))
|
||||
|
||||
metadata = notebook_metadata(notebook)
|
||||
|
||||
notebook_with_metadata =
|
||||
[name | sections]
|
||||
[name, setup_cell | sections]
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.intersperse("\n\n")
|
||||
|> prepend_metadata(metadata)
|
||||
|
||||
|
@ -103,6 +107,9 @@ defmodule Livebook.LiveMarkdown.Export do
|
|||
%{"branch_parent_index" => parent_idx}
|
||||
end
|
||||
|
||||
defp render_setup_cell(%{source: ""}, _ctx), do: nil
|
||||
defp render_setup_cell(cell, ctx), do: render_cell(cell, ctx)
|
||||
|
||||
defp render_cell(%Cell.Markdown{} = cell, _ctx) do
|
||||
metadata = cell_metadata(cell)
|
||||
|
||||
|
|
|
@ -209,10 +209,9 @@ defmodule Livebook.LiveMarkdown.Import do
|
|||
|
||||
defp take_outputs(ast, outputs), do: {outputs, ast}
|
||||
|
||||
# Builds a notebook from the list of elements obtained in the previous step.
|
||||
# Note that the list of elements is reversed:
|
||||
# first we group elements by traversing Earmark AST top-down
|
||||
# and then aggregate elements into data strictures going bottom-up.
|
||||
# Builds a notebook from the list of elements obtained in the
|
||||
# previous step. The elements are in reversed order, because we
|
||||
# want to aggregate them into data structures going bottom-up.
|
||||
defp build_notebook(elems) do
|
||||
build_notebook(elems, _cells = [], _sections = [], _messages = [], _output_counter = 0)
|
||||
end
|
||||
|
@ -281,39 +280,21 @@ defmodule Livebook.LiveMarkdown.Import do
|
|||
build_notebook(elems, cells, sections, messages ++ [warning], output_counter)
|
||||
end
|
||||
|
||||
defp build_notebook(
|
||||
[{:section_name, name} | elems],
|
||||
cells,
|
||||
sections,
|
||||
messages,
|
||||
output_counter
|
||||
) do
|
||||
defp build_notebook([{:section_name, name} | elems], cells, sections, messages, output_counter) do
|
||||
{metadata, elems} = grab_metadata(elems)
|
||||
attrs = section_metadata_to_attrs(metadata)
|
||||
section = %{Notebook.Section.new() | name: name, cells: cells} |> Map.merge(attrs)
|
||||
build_notebook(elems, [], [section | sections], messages, output_counter)
|
||||
end
|
||||
|
||||
# If there are section-less cells, put them in a default one.
|
||||
defp build_notebook(
|
||||
[{:notebook_name, _name} | _] = elems,
|
||||
cells,
|
||||
sections,
|
||||
messages,
|
||||
output_counter
|
||||
)
|
||||
when cells != [] do
|
||||
section = %{Notebook.Section.new() | cells: cells}
|
||||
build_notebook(elems, [], [section | sections], messages, output_counter)
|
||||
end
|
||||
defp build_notebook(elems, cells, sections, messages, output_counter) do
|
||||
# At this point we expect the heading, otherwise we use the default
|
||||
{name, elems} =
|
||||
case elems do
|
||||
[{:notebook_name, name} | elems] -> {name, elems}
|
||||
[] -> {nil, []}
|
||||
end
|
||||
|
||||
# If there are section-less cells, put them in a default one.
|
||||
defp build_notebook([] = elems, cells, sections, messages, output_counter) when cells != [] do
|
||||
section = %{Notebook.Section.new() | cells: cells}
|
||||
build_notebook(elems, [], [section | sections], messages, output_counter)
|
||||
end
|
||||
|
||||
defp build_notebook([{:notebook_name, name} | elems], [], sections, messages, output_counter) do
|
||||
{metadata, elems} = grab_metadata(elems)
|
||||
# If there are any non-metadata comments we keep them
|
||||
{comments, elems} = grab_leading_comments(elems)
|
||||
|
@ -330,24 +311,34 @@ defmodule Livebook.LiveMarkdown.Import do
|
|||
|
||||
attrs = notebook_metadata_to_attrs(metadata)
|
||||
|
||||
# We identify a single leading cell as the setup cell, in any
|
||||
# other case all extra cells are put in a default section
|
||||
{setup_cell, extra_sections} =
|
||||
case cells do
|
||||
[] -> {nil, []}
|
||||
[%Notebook.Cell.Code{} = setup_cell] when name != nil -> {setup_cell, []}
|
||||
extra_cells -> {nil, [%{Notebook.Section.new() | cells: extra_cells}]}
|
||||
end
|
||||
|
||||
notebook =
|
||||
%{
|
||||
Notebook.new()
|
||||
| name: name,
|
||||
sections: sections,
|
||||
| sections: extra_sections ++ sections,
|
||||
leading_comments: comments,
|
||||
output_counter: output_counter
|
||||
}
|
||||
|> maybe_put_name(name)
|
||||
|> maybe_put_setup_cell(setup_cell)
|
||||
|> Map.merge(attrs)
|
||||
|
||||
{notebook, messages}
|
||||
end
|
||||
|
||||
# If there's no explicit notebook heading, use the defaults.
|
||||
defp build_notebook([], [], sections, messages, output_counter) do
|
||||
notebook = %{Notebook.new() | sections: sections, output_counter: output_counter}
|
||||
{notebook, messages}
|
||||
end
|
||||
defp maybe_put_name(notebook, nil), do: notebook
|
||||
defp maybe_put_name(notebook, name), do: %{notebook | name: name}
|
||||
|
||||
defp maybe_put_setup_cell(notebook, nil), do: notebook
|
||||
defp maybe_put_setup_cell(notebook, cell), do: Notebook.put_setup_cell(notebook, cell)
|
||||
|
||||
# Takes optional leading metadata JSON object and returns {metadata, rest}.
|
||||
defp grab_metadata([{:metadata, metadata} | elems]) do
|
||||
|
|
|
@ -16,6 +16,7 @@ defmodule Livebook.Notebook do
|
|||
defstruct [
|
||||
:name,
|
||||
:version,
|
||||
:setup_section,
|
||||
:sections,
|
||||
:leading_comments,
|
||||
:persist_outputs,
|
||||
|
@ -30,6 +31,7 @@ defmodule Livebook.Notebook do
|
|||
@type t :: %__MODULE__{
|
||||
name: String.t(),
|
||||
version: String.t(),
|
||||
setup_section: Section.t(),
|
||||
sections: list(Section.t()),
|
||||
leading_comments: list(list(line :: String.t())),
|
||||
persist_outputs: boolean(),
|
||||
|
@ -47,12 +49,22 @@ defmodule Livebook.Notebook do
|
|||
%__MODULE__{
|
||||
name: "Untitled notebook",
|
||||
version: @version,
|
||||
setup_section: %{Section.new() | id: "setup-section", name: "Setup", cells: []},
|
||||
sections: [],
|
||||
leading_comments: [],
|
||||
persist_outputs: default_persist_outputs(),
|
||||
autosave_interval_s: default_autosave_interval_s(),
|
||||
output_counter: 0
|
||||
}
|
||||
|> put_setup_cell(Cell.new(:code))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets the given cell as the setup cell.
|
||||
"""
|
||||
@spec put_setup_cell(t(), Cell.Code.t()) :: t()
|
||||
def put_setup_cell(notebook, %Cell.Code{} = cell) do
|
||||
put_in(notebook.setup_section.cells, [%{cell | id: "setup"}])
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -79,12 +91,22 @@ defmodule Livebook.Notebook do
|
|||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns all notebook sections, including the implicit ones.
|
||||
"""
|
||||
@spec all_sections(t()) :: list(Section.t())
|
||||
def all_sections(notebook) do
|
||||
get_in(notebook, [access_all_sections()])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Finds notebook section by id.
|
||||
"""
|
||||
@spec fetch_section(t(), Section.id()) :: {:ok, Section.t()} | :error
|
||||
def fetch_section(notebook, section_id) do
|
||||
Enum.find_value(notebook.sections, :error, fn section ->
|
||||
notebook
|
||||
|> all_sections()
|
||||
|> Enum.find_value(:error, fn section ->
|
||||
section.id == section_id && {:ok, section}
|
||||
end)
|
||||
end
|
||||
|
@ -95,7 +117,7 @@ defmodule Livebook.Notebook do
|
|||
@spec fetch_cell_and_section(t(), Cell.id()) :: {:ok, Cell.t(), Section.t()} | :error
|
||||
def fetch_cell_and_section(notebook, cell_id) do
|
||||
for(
|
||||
section <- notebook.sections,
|
||||
section <- all_sections(notebook),
|
||||
cell <- section.cells,
|
||||
cell.id == cell_id,
|
||||
do: {cell, section}
|
||||
|
@ -206,7 +228,7 @@ defmodule Livebook.Notebook do
|
|||
def update_cell(notebook, cell_id, fun) do
|
||||
update_in(
|
||||
notebook,
|
||||
[Access.key(:sections), Access.all(), Access.key(:cells), access_by_id(cell_id)],
|
||||
[access_all_sections(), Access.all(), Access.key(:cells), access_by_id(cell_id)],
|
||||
fun
|
||||
)
|
||||
end
|
||||
|
@ -218,7 +240,7 @@ defmodule Livebook.Notebook do
|
|||
def update_cells(notebook, fun) do
|
||||
update_in(
|
||||
notebook,
|
||||
[Access.key(:sections), Access.all(), Access.key(:cells), Access.all()],
|
||||
[access_all_sections(), Access.all(), Access.key(:cells), Access.all()],
|
||||
fun
|
||||
)
|
||||
end
|
||||
|
@ -229,13 +251,13 @@ defmodule Livebook.Notebook do
|
|||
@spec update_reduce_cells(t(), acc, (Cell.t(), acc -> {Cell.t(), acc})) :: {t(), acc}
|
||||
when acc: term()
|
||||
def update_reduce_cells(notebook, acc, fun) do
|
||||
{sections, acc} =
|
||||
Enum.map_reduce(notebook.sections, acc, fn section, acc ->
|
||||
{[setup_section | sections], acc} =
|
||||
Enum.map_reduce([notebook.setup_section | notebook.sections], acc, fn section, acc ->
|
||||
{cells, acc} = Enum.map_reduce(section.cells, acc, fun)
|
||||
{%{section | cells: cells}, acc}
|
||||
end)
|
||||
|
||||
{%{notebook | sections: sections}, acc}
|
||||
{%{notebook | setup_section: setup_section, sections: sections}, acc}
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -243,7 +265,21 @@ defmodule Livebook.Notebook do
|
|||
"""
|
||||
@spec update_section(t(), Section.id(), (Section.t() -> Section.t())) :: t()
|
||||
def update_section(notebook, section_id, fun) do
|
||||
update_in(notebook, [Access.key(:sections), access_by_id(section_id)], fun)
|
||||
update_in(notebook, [access_all_sections(), access_by_id(section_id)], fun)
|
||||
end
|
||||
|
||||
defp access_all_sections() do
|
||||
fn
|
||||
:get, %__MODULE__{} = notebook, next ->
|
||||
next.([notebook.setup_section | notebook.sections])
|
||||
|
||||
:get_and_update, %__MODULE__{} = notebook, next ->
|
||||
{gets, [setup_section | sections]} = next.([notebook.setup_section | notebook.sections])
|
||||
{gets, %{notebook | setup_section: setup_section, sections: sections}}
|
||||
|
||||
_op, data, _next ->
|
||||
raise "access_all_sections/0 expected %Livebook.Notebook{}, got: #{inspect(data)}"
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -375,7 +411,7 @@ defmodule Livebook.Notebook do
|
|||
"""
|
||||
@spec cells_with_section(t()) :: list({Cell.t(), Section.t()})
|
||||
def cells_with_section(notebook) do
|
||||
for section <- notebook.sections,
|
||||
for section <- all_sections(notebook),
|
||||
cell <- section.cells,
|
||||
do: {cell, section}
|
||||
end
|
||||
|
@ -469,7 +505,8 @@ defmodule Livebook.Notebook do
|
|||
"""
|
||||
@spec cell_dependency_graph(t()) :: Graph.t(Cell.id())
|
||||
def cell_dependency_graph(notebook, opts \\ []) do
|
||||
notebook.sections
|
||||
notebook
|
||||
|> all_sections()
|
||||
|> Enum.reduce(
|
||||
{%{}, nil, %{}},
|
||||
fn section, {graph, prev_regular_section, last_id_by_section} ->
|
||||
|
@ -535,7 +572,9 @@ defmodule Livebook.Notebook do
|
|||
"""
|
||||
@spec find_asset_info(t(), String.t()) :: (asset_info :: map()) | nil
|
||||
def find_asset_info(notebook, hash) do
|
||||
Enum.find_value(notebook.sections, fn section ->
|
||||
notebook
|
||||
|> all_sections()
|
||||
|> Enum.find_value(fn section ->
|
||||
Enum.find_value(section.cells, fn
|
||||
%Cell.Smart{js_view: %{assets: %{hash: ^hash} = assets_info}} ->
|
||||
assets_info
|
||||
|
@ -671,7 +710,7 @@ defmodule Livebook.Notebook do
|
|||
"""
|
||||
@spec find_frame_outputs(t(), String.t()) :: list(Cell.indexed_output())
|
||||
def find_frame_outputs(notebook, frame_ref) do
|
||||
for section <- notebook.sections,
|
||||
for section <- all_sections(notebook),
|
||||
%{outputs: outputs} <- section.cells,
|
||||
output <- outputs,
|
||||
frame_output <- do_find_frame_outputs(output, frame_ref),
|
||||
|
|
|
@ -67,4 +67,13 @@ defmodule Livebook.Notebook.Cell do
|
|||
end
|
||||
|
||||
def find_inputs_in_output(_output), do: []
|
||||
|
||||
@doc """
|
||||
Checks if the given cell is the setup code cell.
|
||||
"""
|
||||
@spec setup?(t()) :: boolean()
|
||||
def setup?(cell)
|
||||
|
||||
def setup?(%Cell.Code{id: "setup"}), do: true
|
||||
def setup?(_cell), do: false
|
||||
end
|
||||
|
|
|
@ -13,10 +13,14 @@ defmodule Livebook.Notebook.Export.Elixir do
|
|||
end
|
||||
|
||||
defp render_notebook(notebook) do
|
||||
%{setup_section: %{cells: [setup_cell]} = setup_section} = notebook
|
||||
|
||||
name = ["# Title: ", notebook.name]
|
||||
setup_cell = render_setup_cell(setup_cell, setup_section)
|
||||
sections = Enum.map(notebook.sections, &render_section(&1, notebook))
|
||||
|
||||
[name | sections]
|
||||
[name, setup_cell | sections]
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.intersperse("\n\n")
|
||||
end
|
||||
|
||||
|
@ -40,6 +44,9 @@ defmodule Livebook.Notebook.Export.Elixir do
|
|||
|> Enum.intersperse("\n\n")
|
||||
end
|
||||
|
||||
defp render_setup_cell(%{source: ""}, _section), do: nil
|
||||
defp render_setup_cell(cell, section), do: render_cell(cell, section)
|
||||
|
||||
defp render_cell(%Cell.Markdown{} = cell, _section) do
|
||||
cell.source
|
||||
|> Livebook.LiveMarkdown.MarkdownHelpers.reformat()
|
||||
|
|
|
@ -221,20 +221,20 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
|
||||
defp initial_section_infos(notebook) do
|
||||
for section <- notebook.sections,
|
||||
for section <- Notebook.all_sections(notebook),
|
||||
into: %{},
|
||||
do: {section.id, new_section_info()}
|
||||
end
|
||||
|
||||
defp initial_cell_infos(notebook) do
|
||||
for section <- notebook.sections,
|
||||
for section <- Notebook.all_sections(notebook),
|
||||
cell <- section.cells,
|
||||
into: %{},
|
||||
do: {cell.id, new_cell_info(cell, %{})}
|
||||
end
|
||||
|
||||
defp initial_input_values(notebook) do
|
||||
for section <- notebook.sections,
|
||||
for section <- Notebook.all_sections(notebook),
|
||||
cell <- section.cells,
|
||||
Cell.evaluable?(cell),
|
||||
output <- cell.outputs,
|
||||
|
@ -372,7 +372,8 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
|
||||
def apply_operation(data, {:delete_cell, _client_pid, id}) do
|
||||
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id) do
|
||||
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id),
|
||||
false <- Cell.setup?(cell) do
|
||||
data
|
||||
|> with_actions()
|
||||
|> delete_cell(cell, section)
|
||||
|
@ -380,6 +381,8 @@ defmodule Livebook.Session.Data do
|
|||
|> update_smart_cell_bases(data)
|
||||
|> set_dirty()
|
||||
|> wrap_ok()
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -400,6 +403,7 @@ defmodule Livebook.Session.Data do
|
|||
|
||||
def apply_operation(data, {:move_cell, _client_pid, id, offset}) do
|
||||
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id),
|
||||
false <- Cell.setup?(cell),
|
||||
true <- offset != 0,
|
||||
true <- can_move_cell_by?(data, cell, section, offset) do
|
||||
data
|
||||
|
@ -1014,7 +1018,8 @@ defmodule Livebook.Session.Data do
|
|||
main_flow_evaluating? = main_flow_evaluating?(data)
|
||||
|
||||
{awaiting_branch_sections, awaiting_regular_sections} =
|
||||
data.notebook.sections
|
||||
data.notebook
|
||||
|> Notebook.all_sections()
|
||||
|> Enum.filter(§ion_awaits_evaluation?(data, &1.id))
|
||||
|> Enum.split_with(& &1.parent_id)
|
||||
|
||||
|
@ -1055,7 +1060,9 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
|
||||
defp main_flow_evaluating?(data) do
|
||||
Enum.any?(data.notebook.sections, fn section ->
|
||||
data.notebook
|
||||
|> Notebook.all_sections()
|
||||
|> Enum.any?(fn section ->
|
||||
section.parent_id == nil and section_evaluating?(data, section.id)
|
||||
end)
|
||||
end
|
||||
|
@ -1066,7 +1073,9 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
|
||||
defp any_section_evaluating?(data) do
|
||||
Enum.any?(data.notebook.sections, fn section ->
|
||||
data.notebook
|
||||
|> Notebook.all_sections()
|
||||
|> Enum.any?(fn section ->
|
||||
section_evaluating?(data, section.id)
|
||||
end)
|
||||
end
|
||||
|
@ -1124,11 +1133,14 @@ defmodule Livebook.Session.Data do
|
|||
|
||||
defp clear_all_evaluation({data, _} = data_actions) do
|
||||
data_actions
|
||||
|> reduce(data.notebook.sections, &clear_section_evaluation/2)
|
||||
|> reduce(Notebook.all_sections(data.notebook), &clear_section_evaluation/2)
|
||||
end
|
||||
|
||||
defp clear_main_evaluation({data, _} = data_actions) do
|
||||
regular_sections = Enum.filter(data.notebook.sections, &(&1.parent_id == nil))
|
||||
regular_sections =
|
||||
data.notebook
|
||||
|> Notebook.all_sections()
|
||||
|> Enum.filter(&(&1.parent_id == nil))
|
||||
|
||||
data_actions
|
||||
|> reduce(regular_sections, &clear_section_evaluation/2)
|
||||
|
@ -1448,7 +1460,7 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
|
||||
defp dead_smart_cells_with_section(data) do
|
||||
for section <- data.notebook.sections,
|
||||
for section <- Notebook.all_sections(data.notebook),
|
||||
%Cell.Smart{} = cell <- section.cells,
|
||||
info = data.cell_infos[cell.id],
|
||||
info.status == :dead,
|
||||
|
|
|
@ -137,7 +137,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
<div class="grow overflow-y-auto relative" data-element="notebook">
|
||||
<div data-element="js-view-iframes" phx-update="ignore" id="js-view-iframes"></div>
|
||||
<div class="w-full max-w-screen-lg px-16 mx-auto py-7" data-element="notebook-content">
|
||||
<div class="flex items-center pb-4 mb-6 space-x-4 border-b border-gray-200"
|
||||
<div class="flex items-center pb-4 mb-2 space-x-4 border-b border-gray-200"
|
||||
data-element="notebook-headline"
|
||||
data-focusable-id="notebook"
|
||||
id="notebook"
|
||||
|
@ -190,7 +190,14 @@ defmodule LivebookWeb.SessionLive do
|
|||
</:content>
|
||||
</.menu>
|
||||
</div>
|
||||
<div class="flex flex-col w-full space-y-16">
|
||||
<div>
|
||||
<.live_component module={LivebookWeb.SessionLive.CellComponent}
|
||||
id={@data_view.setup_cell_view.id}
|
||||
session_id={@session.id}
|
||||
runtime={@data_view.runtime}
|
||||
cell_view={@data_view.setup_cell_view} />
|
||||
</div>
|
||||
<div class="mt-8 flex flex-col w-full space-y-16">
|
||||
<%= if @data_view.section_views == [] do %>
|
||||
<div class="flex justify-center">
|
||||
<button class="button-base button-small"
|
||||
|
@ -715,6 +722,17 @@ defmodule LivebookWeb.SessionLive do
|
|||
end
|
||||
|
||||
def handle_event("queue_cell_evaluation", %{"cell_id" => cell_id}, socket) do
|
||||
data = socket.private.data
|
||||
|
||||
socket =
|
||||
with {:ok, cell, _section} <- Notebook.fetch_cell_and_section(data.notebook, cell_id),
|
||||
true <- Cell.setup?(cell),
|
||||
false <- data.cell_infos[cell.id].eval.validity == :fresh do
|
||||
maybe_restart_runtime(socket)
|
||||
else
|
||||
_ -> socket
|
||||
end
|
||||
|
||||
Session.queue_cell_evaluation(socket.assigns.session.pid, cell_id)
|
||||
|
||||
{:noreply, socket}
|
||||
|
@ -761,21 +779,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
end
|
||||
|
||||
def handle_event("restart_runtime", %{}, socket) do
|
||||
socket =
|
||||
if runtime = socket.private.data.runtime do
|
||||
case Runtime.duplicate(runtime) do
|
||||
{:ok, new_runtime} ->
|
||||
Session.connect_runtime(socket.assigns.session.pid, new_runtime)
|
||||
clear_flash(socket, :error)
|
||||
|
||||
{:error, message} ->
|
||||
put_flash(socket, :error, "Failed to setup runtime - #{message}")
|
||||
end
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
{:noreply, maybe_restart_runtime(socket)}
|
||||
end
|
||||
|
||||
def handle_event("connect_default_runtime", %{}, socket) do
|
||||
|
@ -1312,6 +1316,19 @@ defmodule LivebookWeb.SessionLive do
|
|||
defp autofocus_cell_id(%Notebook{sections: [%{cells: [%{id: id, source: ""}]}]}), do: id
|
||||
defp autofocus_cell_id(_notebook), do: nil
|
||||
|
||||
defp maybe_restart_runtime(%{private: %{data: %{runtime: nil}}} = socket), do: socket
|
||||
|
||||
defp maybe_restart_runtime(%{private: %{data: data}} = socket) do
|
||||
case Runtime.duplicate(data.runtime) do
|
||||
{:ok, new_runtime} ->
|
||||
Session.connect_runtime(socket.assigns.session.pid, new_runtime)
|
||||
clear_flash(socket, :error)
|
||||
|
||||
{:error, message} ->
|
||||
put_flash(socket, :error, "Failed to setup runtime - #{message}")
|
||||
end
|
||||
end
|
||||
|
||||
# Builds view-specific structure of data by cherry-picking
|
||||
# only the relevant attributes.
|
||||
# We then use `@data_view` in the templates and consequently
|
||||
|
@ -1340,6 +1357,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
data.clients_map
|
||||
|> Enum.map(fn {client_pid, user_id} -> {client_pid, data.users_map[user_id]} end)
|
||||
|> Enum.sort_by(fn {_client_pid, user} -> user.name end),
|
||||
setup_cell_view: %{cell_to_view(hd(data.notebook.setup_section.cells), data) | type: :setup},
|
||||
section_views: section_views(data.notebook.sections, data),
|
||||
bin_entries: data.bin_entries
|
||||
}
|
||||
|
@ -1497,7 +1515,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
{:apply_cell_delta, _pid, _cell_id, _tag, _delta, _revision} ->
|
||||
update_dirty_status(data_view, data)
|
||||
|
||||
{:update_smart_cell, _pid, _cell_id, _cell_state, _delta} ->
|
||||
{:update_smart_cell, _pid, _cell_id, _cell_state, _delta, _reevaluate} ->
|
||||
update_dirty_status(data_view, data)
|
||||
|
||||
# For outputs that update existing outputs we send the update directly
|
||||
|
|
|
@ -12,7 +12,9 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
data-focusable-id={@cell_view.id}
|
||||
data-type={@cell_view.type}
|
||||
data-session-path={Routes.session_path(@socket, :page, @session_id)}
|
||||
data-evaluation-digest={get_in(@cell_view, [:eval, :evaluation_digest])}>
|
||||
data-evaluation-digest={get_in(@cell_view, [:eval, :evaluation_digest])}
|
||||
data-eval-validity={get_in(@cell_view, [:eval, :validity])}
|
||||
data-js-empty={empty?(@cell_view.source_view)}>
|
||||
<%= render_cell(assigns) %>
|
||||
</div>
|
||||
"""
|
||||
|
@ -80,7 +82,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
language="elixir"
|
||||
intellisense />
|
||||
<div class="absolute bottom-2 right-2">
|
||||
<.cell_status cell_view={@cell_view} />
|
||||
<.cell_status id={@cell_view.id} cell_view={@cell_view} />
|
||||
</div>
|
||||
</div>
|
||||
<.evaluation_outputs
|
||||
|
@ -92,6 +94,51 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
"""
|
||||
end
|
||||
|
||||
defp render_cell(%{cell_view: %{type: :setup}} = assigns) do
|
||||
~H"""
|
||||
<.cell_actions>
|
||||
<:primary>
|
||||
<.setup_cell_evaluation_button
|
||||
cell_id={@cell_view.id}
|
||||
validity={@cell_view.eval.validity}
|
||||
status={@cell_view.eval.status} />
|
||||
</:primary>
|
||||
<:secondary>
|
||||
<.enable_insert_mode_button />
|
||||
<.cell_link_button cell_id={@cell_view.id} />
|
||||
<.setup_cell_info />
|
||||
</:secondary>
|
||||
</.cell_actions>
|
||||
<.cell_body>
|
||||
<div data-element="info-box">
|
||||
<div class="p-3 flex items-center justify-between border border-gray-200 text-sm text-gray-400 font-medium rounded-lg">
|
||||
<span>Notebook dependencies and setup</span>
|
||||
<.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
|
||||
</div>
|
||||
</div>
|
||||
<div data-element="editor-box">
|
||||
<div class="relative">
|
||||
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent}
|
||||
id={"#{@cell_view.id}-primary"}
|
||||
cell_id={@cell_view.id}
|
||||
tag="primary"
|
||||
source_view={@cell_view.source_view}
|
||||
language="elixir"
|
||||
intellisense />
|
||||
<div class="absolute bottom-2 right-2">
|
||||
<.cell_status id={"#{@cell_view.id}-2"} cell_view={@cell_view} />
|
||||
</div>
|
||||
</div>
|
||||
<.evaluation_outputs
|
||||
cell_view={@cell_view}
|
||||
socket={@socket}
|
||||
session_id={@session_id}
|
||||
runtime={@runtime} />
|
||||
</div>
|
||||
</.cell_body>
|
||||
"""
|
||||
end
|
||||
|
||||
defp render_cell(%{cell_view: %{type: :smart}} = assigns) do
|
||||
~H"""
|
||||
<.cell_actions>
|
||||
|
@ -114,37 +161,41 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
</:secondary>
|
||||
</.cell_actions>
|
||||
<.cell_body>
|
||||
<div class="relative">
|
||||
<div data-element="ui-box">
|
||||
<%= case @cell_view.status do %>
|
||||
<% :started -> %>
|
||||
<div class={"flex #{if(@cell_view.editor && @cell_view.editor.placement == :top, do: "flex-col-reverse", else: "flex-col")}"}>
|
||||
<.live_component module={LivebookWeb.JSViewComponent}
|
||||
id={@cell_view.id}
|
||||
js_view={@cell_view.js_view}
|
||||
session_id={@session_id} />
|
||||
<%= if @cell_view.editor do %>
|
||||
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent}
|
||||
id={"#{@cell_view.id}-secondary"}
|
||||
cell_id={@cell_view.id}
|
||||
tag="secondary"
|
||||
source_view={@cell_view.editor.source_view}
|
||||
language={@cell_view.editor.language} />
|
||||
<% end %>
|
||||
</div>
|
||||
<div data-element="ui-box">
|
||||
<%= case @cell_view.status do %>
|
||||
<% :started -> %>
|
||||
<div class={"flex #{if(@cell_view.editor && @cell_view.editor.placement == :top, do: "flex-col-reverse", else: "flex-col")}"}>
|
||||
<.live_component module={LivebookWeb.JSViewComponent}
|
||||
id={@cell_view.id}
|
||||
js_view={@cell_view.js_view}
|
||||
session_id={@session_id} />
|
||||
<%= if @cell_view.editor do %>
|
||||
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent}
|
||||
id={"#{@cell_view.id}-secondary"}
|
||||
cell_id={@cell_view.id}
|
||||
tag="secondary"
|
||||
source_view={@cell_view.editor.source_view}
|
||||
language={@cell_view.editor.language} />
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% :dead -> %>
|
||||
<div class="info-box">
|
||||
Evaluate and install dependencies to show the contents of this Smart cell.
|
||||
</div>
|
||||
<% :dead -> %>
|
||||
<div class="info-box">
|
||||
Evaluate and install dependencies to show the contents of this Smart cell.
|
||||
</div>
|
||||
|
||||
<% :starting -> %>
|
||||
<div class="delay-200">
|
||||
<.content_skeleton empty={false} />
|
||||
</div>
|
||||
<% end %>
|
||||
<% :starting -> %>
|
||||
<div class="delay-200">
|
||||
<.content_skeleton empty={false} />
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="flex flex-col items-end space-y-2">
|
||||
<div></div>
|
||||
<.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
|
||||
</div>
|
||||
<div data-element="editor-box">
|
||||
</div>
|
||||
<div data-element="editor-box">
|
||||
<div class="relative">
|
||||
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent}
|
||||
id={"#{@cell_view.id}-primary"}
|
||||
cell_id={@cell_view.id}
|
||||
|
@ -153,9 +204,9 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
language="elixir"
|
||||
intellisense
|
||||
read_only />
|
||||
</div>
|
||||
<div data-element="cell-status-container">
|
||||
<.cell_status cell_view={@cell_view} />
|
||||
<div class="absolute bottom-2 right-2">
|
||||
<.cell_status id={"#{@cell_view.id}-2"} cell_view={@cell_view} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<.evaluation_outputs
|
||||
|
@ -168,7 +219,10 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
end
|
||||
|
||||
defp cell_actions(assigns) do
|
||||
assigns = assign_new(assigns, :primary, fn -> [] end)
|
||||
assigns =
|
||||
assigns
|
||||
|> assign_new(:primary, fn -> [] end)
|
||||
|> assign_new(:secondary, fn -> [] end)
|
||||
|
||||
~H"""
|
||||
<div class="mb-1 flex items-center justify-between">
|
||||
|
@ -238,6 +292,35 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
"""
|
||||
end
|
||||
|
||||
defp setup_cell_evaluation_button(%{status: :ready} = assigns) do
|
||||
~H"""
|
||||
<button class="text-gray-600 hover:text-gray-800 focus:text-gray-800 flex space-x-1 items-center"
|
||||
phx-click="queue_cell_evaluation"
|
||||
phx-value-cell_id={@cell_id}>
|
||||
<%= if @validity == :fresh do %>
|
||||
<.remix_icon icon="play-circle-fill" class="text-xl" />
|
||||
<span class="text-sm font-medium">Setup</span>
|
||||
<% else %>
|
||||
<.remix_icon icon="restart-fill" class="text-xl" />
|
||||
<span class="text-sm font-medium">Restart and setup</span>
|
||||
<% end %>
|
||||
</button>
|
||||
"""
|
||||
end
|
||||
|
||||
defp setup_cell_evaluation_button(assigns) do
|
||||
~H"""
|
||||
<button class="text-gray-600 hover:text-gray-800 focus:text-gray-800 flex space-x-1 items-center"
|
||||
phx-click="cancel_cell_evaluation"
|
||||
phx-value-cell_id={@cell_id}>
|
||||
<.remix_icon icon="stop-circle-fill" class="text-xl" />
|
||||
<span class="text-sm font-medium">
|
||||
Stop
|
||||
</span>
|
||||
</button>
|
||||
"""
|
||||
end
|
||||
|
||||
defp enable_insert_mode_button(assigns) do
|
||||
~H"""
|
||||
<span class="tooltip top" data-tooltip="Edit content" data-element="enable-insert-mode-button">
|
||||
|
@ -379,6 +462,23 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
"""
|
||||
end
|
||||
|
||||
defp setup_cell_info(assigns) do
|
||||
~H"""
|
||||
<span class="tooltip top"
|
||||
data-tooltip={
|
||||
~s'''
|
||||
The setup cell includes code that initializes the notebook
|
||||
and should run only once. This is the best place to install
|
||||
dependencies and set global configuration.\
|
||||
'''
|
||||
}>
|
||||
<span class="icon-button">
|
||||
<.remix_icon icon="question-line" class="text-xl" />
|
||||
</span>
|
||||
</span>
|
||||
"""
|
||||
end
|
||||
|
||||
defp evaluation_outputs(assigns) do
|
||||
~H"""
|
||||
<div class="flex flex-col"
|
||||
|
@ -404,7 +504,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
~H"""
|
||||
<.status_indicator circle_class="bg-blue-500" animated_circle_class="bg-blue-400" change_indicator={true}>
|
||||
<span class="font-mono"
|
||||
id={"cell-timer-#{@cell_view.id}"}
|
||||
id={"#{@id}-cell-timer"}
|
||||
phx-hook="Timer"
|
||||
phx-update="ignore"
|
||||
data-start={DateTime.to_iso8601(@cell_view.eval.evaluation_start)}>
|
||||
|
|
|
@ -311,7 +311,7 @@ defmodule Livebook.LiveMarkdown.ExportTest do
|
|||
assert expected_document == document
|
||||
end
|
||||
|
||||
test "formats code in Code cells" do
|
||||
test "formats code in code cells" do
|
||||
notebook = %{
|
||||
Notebook.new()
|
||||
| name: "My Notebook",
|
||||
|
@ -347,7 +347,7 @@ defmodule Livebook.LiveMarkdown.ExportTest do
|
|||
assert expected_document == document
|
||||
end
|
||||
|
||||
test "does not format code in Code cells which have formatting disabled" do
|
||||
test "does not format code in code cells which have formatting disabled" do
|
||||
notebook = %{
|
||||
Notebook.new()
|
||||
| name: "My Notebook",
|
||||
|
@ -1000,6 +1000,32 @@ defmodule Livebook.LiveMarkdown.ExportTest do
|
|||
assert expected_document == document
|
||||
end
|
||||
|
||||
describe "setup cell" do
|
||||
test "includes the leading setup cell when it has content" do
|
||||
notebook =
|
||||
%{
|
||||
Notebook.new()
|
||||
| name: "My Notebook",
|
||||
sections: [%{Notebook.Section.new() | name: "Section 1"}]
|
||||
}
|
||||
|> Notebook.put_setup_cell(%{Notebook.Cell.new(:code) | source: "Mix.install([...])"})
|
||||
|
||||
expected_document = """
|
||||
# My Notebook
|
||||
|
||||
```elixir
|
||||
Mix.install([...])
|
||||
```
|
||||
|
||||
## Section 1
|
||||
"""
|
||||
|
||||
document = Export.notebook_to_livemd(notebook)
|
||||
|
||||
assert expected_document == document
|
||||
end
|
||||
end
|
||||
|
||||
defp spawn_widget_with_data(ref, data) do
|
||||
spawn(fn ->
|
||||
receive do
|
||||
|
|
|
@ -845,11 +845,10 @@ defmodule Livebook.LiveMarkdown.ImportTest do
|
|||
} = notebook
|
||||
end
|
||||
|
||||
test "import notebook with parent section being a branching section itself produces a warning" do
|
||||
test "importing notebook with parent section being a branching section itself produces a warning" do
|
||||
markdown = """
|
||||
# My Notebook
|
||||
|
||||
|
||||
## Section 1
|
||||
|
||||
```elixir
|
||||
|
@ -895,4 +894,57 @@ defmodule Livebook.LiveMarkdown.ImportTest do
|
|||
]
|
||||
} = notebook
|
||||
end
|
||||
|
||||
describe "setup cell" do
|
||||
test "imports a leading setup cell" do
|
||||
markdown = """
|
||||
# My Notebook
|
||||
|
||||
```elixir
|
||||
Mix.install([...])
|
||||
```
|
||||
|
||||
## Section 1
|
||||
"""
|
||||
|
||||
{notebook, []} = Import.notebook_from_livemd(markdown)
|
||||
|
||||
assert %Notebook{
|
||||
name: "My Notebook",
|
||||
setup_section: %{
|
||||
cells: [
|
||||
%Cell.Code{id: "setup", source: "Mix.install([...])"}
|
||||
]
|
||||
},
|
||||
sections: [
|
||||
%Notebook.Section{
|
||||
name: "Section 1",
|
||||
cells: []
|
||||
}
|
||||
]
|
||||
} = notebook
|
||||
end
|
||||
|
||||
test "does not add an implicit section when there is just setup cell" do
|
||||
markdown = """
|
||||
# My Notebook
|
||||
|
||||
```elixir
|
||||
Mix.install([...])
|
||||
```
|
||||
"""
|
||||
|
||||
{notebook, []} = Import.notebook_from_livemd(markdown)
|
||||
|
||||
assert %Notebook{
|
||||
name: "My Notebook",
|
||||
setup_section: %{
|
||||
cells: [
|
||||
%Cell.Code{id: "setup", source: "Mix.install([...])"}
|
||||
]
|
||||
},
|
||||
sections: []
|
||||
} = notebook
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -105,4 +105,28 @@ defmodule Livebook.Notebook.Export.ElixirTest do
|
|||
|
||||
assert expected_document == document
|
||||
end
|
||||
|
||||
describe "setup cell" do
|
||||
test "includes the leading setup cell when it has content" do
|
||||
notebook =
|
||||
%{
|
||||
Notebook.new()
|
||||
| name: "My Notebook",
|
||||
sections: [%{Notebook.Section.new() | name: "Section 1"}]
|
||||
}
|
||||
|> Notebook.put_setup_cell(%{Notebook.Cell.new(:code) | source: "Mix.install([...])"})
|
||||
|
||||
expected_document = """
|
||||
# Title: My Notebook
|
||||
|
||||
Mix.install([...])
|
||||
|
||||
# ── Section 1 ──
|
||||
"""
|
||||
|
||||
document = Export.Elixir.notebook_to_elixir(notebook)
|
||||
|
||||
assert expected_document == document
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -92,7 +92,8 @@ defmodule Livebook.NotebookTest do
|
|||
"c4" => "c3",
|
||||
"c3" => "c2",
|
||||
"c2" => "c1",
|
||||
"c1" => nil
|
||||
"c1" => "setup",
|
||||
"setup" => nil
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -108,7 +109,8 @@ defmodule Livebook.NotebookTest do
|
|||
|
||||
assert Notebook.cell_dependency_graph(notebook) == %{
|
||||
"c2" => "c1",
|
||||
"c1" => nil
|
||||
"c1" => "setup",
|
||||
"setup" => nil
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -150,7 +152,8 @@ defmodule Livebook.NotebookTest do
|
|||
"c4" => "c3",
|
||||
"c3" => "c2",
|
||||
"c2" => "c1",
|
||||
"c1" => nil
|
||||
"c1" => "setup",
|
||||
"setup" => nil
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -183,7 +186,8 @@ defmodule Livebook.NotebookTest do
|
|||
|
||||
assert Notebook.cell_dependency_graph(notebook) == %{
|
||||
"c2" => "c1",
|
||||
"c1" => nil
|
||||
"c1" => "setup",
|
||||
"setup" => nil
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -227,7 +231,8 @@ defmodule Livebook.NotebookTest do
|
|||
"c4" => "c2",
|
||||
"c3" => "c1",
|
||||
"c2" => "c1",
|
||||
"c1" => nil
|
||||
"c1" => "setup",
|
||||
"setup" => nil
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -250,7 +255,8 @@ defmodule Livebook.NotebookTest do
|
|||
assert Notebook.cell_dependency_graph(notebook, cell_filter: &Cell.evaluable?/1) ==
|
||||
%{
|
||||
"c3" => "c1",
|
||||
"c1" => nil
|
||||
"c1" => "setup",
|
||||
"setup" => nil
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,10 +15,11 @@ defmodule Livebook.Session.DataTest do
|
|||
|
||||
describe "new/1" do
|
||||
test "called with no arguments defaults to a blank notebook" do
|
||||
empty_map = %{}
|
||||
|
||||
assert %{notebook: %{sections: []}, cell_infos: ^empty_map, section_infos: ^empty_map} =
|
||||
assert %{notebook: %{sections: []}, cell_infos: cell_infos, section_infos: section_infos} =
|
||||
Data.new()
|
||||
|
||||
assert map_size(cell_infos) == 1
|
||||
assert map_size(section_infos) == 1
|
||||
end
|
||||
|
||||
test "called with a notebook, sets default cell and section infos" do
|
||||
|
@ -221,10 +222,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c3", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
operation = {:set_section_parent, self(), "s2", "s1"}
|
||||
|
@ -250,8 +248,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c3", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c2", "c3"]}
|
||||
])
|
||||
|
||||
operation = {:set_section_parent, self(), "s2", "s1"}
|
||||
|
@ -335,10 +333,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s2", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
operation = {:unset_section_parent, self(), "s2"}
|
||||
|
@ -365,8 +360,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s2", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c2", "c3"]}
|
||||
])
|
||||
|
||||
operation = {:unset_section_parent, self(), "s2"}
|
||||
|
@ -486,15 +481,16 @@ defmodule Livebook.Session.DataTest do
|
|||
])
|
||||
|
||||
operation = {:delete_section, self(), "s1", true}
|
||||
empty_map = %{}
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
notebook: %{
|
||||
sections: []
|
||||
},
|
||||
section_infos: ^empty_map
|
||||
section_infos: section_infos
|
||||
}, []} = Data.apply_operation(data, operation)
|
||||
|
||||
refute Map.has_key?(section_infos, "s1")
|
||||
end
|
||||
|
||||
test "returns error when cell deletion is disabled for the first cell" do
|
||||
|
@ -536,9 +532,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 1, "s2"},
|
||||
{:insert_cell, self(), "s2", 0, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"])
|
||||
])
|
||||
|
||||
operation = {:delete_section, self(), "s2", true}
|
||||
|
@ -571,9 +565,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :code, "c2", %{}},
|
||||
# Evaluate both cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"])
|
||||
])
|
||||
|
||||
operation = {:delete_section, self(), "s1", true}
|
||||
|
@ -596,10 +588,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s2", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
operation = {:delete_section, self(), "s2", false}
|
||||
|
@ -626,10 +615,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s2", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
operation = {:delete_section, self(), "s2", true}
|
||||
|
@ -671,6 +657,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -691,14 +678,13 @@ defmodule Livebook.Session.DataTest do
|
|||
])
|
||||
|
||||
operation = {:delete_cell, self(), "c1"}
|
||||
empty_map = %{}
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
notebook: %{
|
||||
sections: [%{cells: []}]
|
||||
},
|
||||
cell_infos: ^empty_map,
|
||||
cell_infos: cell_infos,
|
||||
bin_entries: [
|
||||
%{
|
||||
cell: %{id: "c1"},
|
||||
|
@ -709,6 +695,8 @@ defmodule Livebook.Session.DataTest do
|
|||
}
|
||||
]
|
||||
}, _actions} = Data.apply_operation(data, operation)
|
||||
|
||||
refute Map.has_key?(cell_infos, "c1")
|
||||
end
|
||||
|
||||
test "unqueues the cell if it's queued for evaluation" do
|
||||
|
@ -718,6 +706,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -737,9 +726,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
# Evaluate both cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"])
|
||||
])
|
||||
|
||||
operation = {:delete_cell, self(), "c1"}
|
||||
|
@ -759,9 +746,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_cell_attributes, self(), "c2", %{reevaluate_automatically: true}},
|
||||
# Evaluate both cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"])
|
||||
])
|
||||
|
||||
operation = {:delete_cell, self(), "c1"}
|
||||
|
@ -780,8 +765,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
# Evaluate the Code cell
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c2"])
|
||||
])
|
||||
|
||||
operation = {:delete_cell, self(), "c1"}
|
||||
|
@ -798,8 +782,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"])
|
||||
])
|
||||
|
||||
operation = {:delete_cell, self(), "c1"}
|
||||
|
@ -831,6 +814,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :smart, "c2", %{kind: "text"}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:set_smart_cell_definitions, self(), [%{kind: "text", name: "Text"}]},
|
||||
{:smart_cell_started, self(), "c2", Delta.new(), %{}, nil},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
|
@ -842,7 +826,8 @@ defmodule Livebook.Session.DataTest do
|
|||
assert {:ok, %{},
|
||||
[
|
||||
{:forget_evaluation, _, _},
|
||||
{:set_smart_cell_base, %{id: "c2"}, %{id: "s1"}, nil}
|
||||
{:set_smart_cell_base, %{id: "c2"}, %{id: "s1"},
|
||||
{%{id: "setup"}, %{id: "setup-section"}}}
|
||||
]} = Data.apply_operation(data, operation)
|
||||
end
|
||||
end
|
||||
|
@ -962,6 +947,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 1, "s2"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -980,11 +966,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 3, :code, "c4", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4"])
|
||||
])
|
||||
|
||||
operation = {:move_cell, self(), "c3", -1}
|
||||
|
@ -1018,11 +1000,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 3, :code, "c4", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4"])
|
||||
])
|
||||
|
||||
operation = {:move_cell, self(), "c2", 1}
|
||||
|
@ -1080,10 +1058,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :code, "c3", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
operation = {:move_cell, self(), "c1", 1}
|
||||
|
@ -1103,8 +1078,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :markdown, "c2", %{}},
|
||||
# Evaluate the Code cell
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"])
|
||||
])
|
||||
|
||||
operation = {:move_cell, self(), "c2", -1}
|
||||
|
@ -1126,6 +1100,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
# Evaluate the Code cell
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -1149,9 +1124,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c3"])
|
||||
])
|
||||
|
||||
operation = {:move_cell, self(), "c1", 1}
|
||||
|
@ -1178,11 +1151,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s2", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4"])
|
||||
])
|
||||
|
||||
operation = {:move_cell, self(), "c2", 1}
|
||||
|
@ -1214,12 +1183,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s4", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4", "c5"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c5", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4", "c5"])
|
||||
])
|
||||
|
||||
operation = {:move_cell, self(), "c2", 1}
|
||||
|
@ -1248,10 +1212,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
{:ok, data_moved, []} = Data.apply_operation(data, {:move_cell, self(), "c2", -1})
|
||||
|
@ -1291,11 +1252,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 1, :code, "c4", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4"])
|
||||
])
|
||||
|
||||
operation = {:move_section, self(), "s2", -1}
|
||||
|
@ -1333,11 +1290,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 1, :code, "c4", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4"])
|
||||
])
|
||||
|
||||
operation = {:move_section, self(), "s1", 1}
|
||||
|
@ -1375,10 +1328,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c3", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
operation = {:move_section, self(), "s1", 1}
|
||||
|
@ -1399,8 +1349,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :markdown, "c2", %{}},
|
||||
# Evaluate the Code cell
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"])
|
||||
])
|
||||
|
||||
operation = {:move_section, self(), "s2", -1}
|
||||
|
@ -1423,6 +1372,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :code, "c2", %{}},
|
||||
# Evaluate the Code cell
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -1450,9 +1400,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s4", 0, :markdown, "c4", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c3"])
|
||||
])
|
||||
|
||||
operation = {:move_section, self(), "s4", -1}
|
||||
|
@ -1480,11 +1428,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s2", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4"])
|
||||
])
|
||||
|
||||
operation = {:move_section, self(), "s2", 1}
|
||||
|
@ -1561,6 +1505,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -1612,7 +1557,8 @@ defmodule Livebook.Session.DataTest do
|
|||
data_after_operations!([
|
||||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()}
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"])
|
||||
])
|
||||
|
||||
operation = {:queue_cells_evaluation, self(), ["c1"]}
|
||||
|
@ -1629,7 +1575,8 @@ defmodule Livebook.Session.DataTest do
|
|||
data_after_operations!([
|
||||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()}
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"])
|
||||
])
|
||||
|
||||
operation = {:queue_cells_evaluation, self(), ["c1"]}
|
||||
|
@ -1645,6 +1592,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -1665,6 +1613,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 1, "s2"},
|
||||
{:insert_cell, self(), "s2", 0, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -1691,12 +1640,9 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 1, :code, "c4", %{}},
|
||||
# Evaluate first 2 cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["setup", "c1", "c2"]),
|
||||
# Evaluate the first cell, so the second becomes stale
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["c1"])
|
||||
])
|
||||
|
||||
# The above leads to:
|
||||
|
@ -1737,7 +1683,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 2, "s3"},
|
||||
{:insert_cell, self(), "s3", 0, :code, "c3", %{}},
|
||||
{:set_section_parent, self(), "s3", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()}
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"])
|
||||
])
|
||||
|
||||
operation = {:queue_cells_evaluation, self(), ["c3"]}
|
||||
|
@ -1770,8 +1717,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c3", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c3"]}
|
||||
])
|
||||
|
||||
operation = {:queue_cells_evaluation, self(), ["c2"]}
|
||||
|
@ -1798,8 +1745,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :code, "c2", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"])
|
||||
])
|
||||
|
||||
operation = {:queue_cells_evaluation, self(), ["c2"]}
|
||||
|
@ -1825,9 +1771,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c4", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"]),
|
||||
{:queue_cells_evaluation, self(), ["c4"]}
|
||||
])
|
||||
|
||||
operation = {:queue_cells_evaluation, self(), ["c3"]}
|
||||
|
@ -1856,8 +1801,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c3", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c2"]}
|
||||
])
|
||||
|
||||
operation = {:queue_cells_evaluation, self(), ["c3"]}
|
||||
|
@ -1877,6 +1822,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -1900,7 +1846,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -1911,7 +1857,7 @@ defmodule Livebook.Session.DataTest do
|
|||
notebook: %{
|
||||
sections: [
|
||||
%{
|
||||
cells: [%{outputs: [{0, {:stdout, "Hello!"}}]}]
|
||||
cells: [%{outputs: [{1, {:stdout, "Hello!"}}]}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1924,8 +1870,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"])
|
||||
])
|
||||
|
||||
operation = {:add_cell_evaluation_output, self(), "c1", {:stdout, "Hello!"}}
|
||||
|
@ -1935,7 +1880,7 @@ defmodule Livebook.Session.DataTest do
|
|||
notebook: %{
|
||||
sections: [
|
||||
%{
|
||||
cells: [%{outputs: [{1, {:stdout, "Hello!"}}, _result]}]
|
||||
cells: [%{outputs: [{2, {:stdout, "Hello!"}}, _result]}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1948,6 +1893,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:set_notebook_attributes, self(), %{persist_outputs: true}},
|
||||
{:mark_as_not_dirty, self()}
|
||||
|
@ -1966,6 +1912,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -1977,7 +1924,7 @@ defmodule Livebook.Session.DataTest do
|
|||
notebook: %{
|
||||
sections: [
|
||||
%{
|
||||
cells: [%{outputs: [{0, {:ok, [1, 2, 3]}}]}]
|
||||
cells: [%{outputs: [{1, {:ok, [1, 2, 3]}}]}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1990,6 +1937,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -2009,9 +1957,9 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
# Evaluate the first cell
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["c1"]),
|
||||
# Start evaluating the second cell
|
||||
{:queue_cells_evaluation, self(), ["c2"]},
|
||||
# Remove the first cell, marking the second as stale
|
||||
|
@ -2033,6 +1981,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -2055,6 +2004,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 1, "s2"},
|
||||
{:insert_cell, self(), "s2", 0, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -2082,10 +2032,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :code, "c3", %{}},
|
||||
# Evaluate all cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"]),
|
||||
# Queue the first cell again
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
@ -2116,11 +2063,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_section_parent, self(), "s4", "s1"},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3", "c4"]),
|
||||
# Queue the second cell again
|
||||
{:queue_cells_evaluation, self(), ["c2"]}
|
||||
])
|
||||
|
@ -2150,10 +2093,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_cell_attributes, self(), "c3", %{reevaluate_automatically: true}},
|
||||
# Evaluate all cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"]),
|
||||
# Queue the first cell again
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
@ -2178,6 +2118,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:set_cell_attributes, self(), "c2", %{reevaluate_automatically: true}},
|
||||
# Evaluate all cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -2201,6 +2142,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
|
@ -2228,6 +2170,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -2250,6 +2193,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:set_notebook_attributes, self(), %{persist_outputs: true}},
|
||||
{:mark_as_not_dirty, self()}
|
||||
|
@ -2269,6 +2213,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
])
|
||||
|
||||
|
@ -2285,6 +2230,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:set_input_value, self(), "i1", "value"},
|
||||
|
@ -2305,6 +2251,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:set_input_value, self(), "i1", "value"},
|
||||
|
@ -2328,6 +2275,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", {:input, input}, @eval_meta},
|
||||
|
@ -2354,6 +2302,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s3", 0, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:set_input_value, self(), "i1", "value"},
|
||||
|
@ -2374,6 +2323,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :smart, "c2", %{kind: "text"}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:set_smart_cell_definitions, self(), [%{kind: "text", name: "Text"}]},
|
||||
{:smart_cell_started, self(), "c2", Delta.new(), %{}, nil},
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
|
@ -2420,6 +2370,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:queue_cells_evaluation, self(), ["c2"]}
|
||||
|
@ -2447,8 +2398,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c2", "c3"]}
|
||||
])
|
||||
|
||||
operation = {:reflect_main_evaluation_failure, self()}
|
||||
|
@ -2476,9 +2427,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 1, :code, "c3", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"]),
|
||||
{:queue_cells_evaluation, self(), ["c3"]}
|
||||
])
|
||||
|
||||
operation = {:reflect_main_evaluation_failure, self()}
|
||||
|
@ -2511,9 +2461,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c4", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"]),
|
||||
{:queue_cells_evaluation, self(), ["c3", "c4"]}
|
||||
])
|
||||
|
||||
operation = {:reflect_evaluation_failure, self(), "s2"}
|
||||
|
@ -2548,8 +2497,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"])
|
||||
])
|
||||
|
||||
operation = {:cancel_cell_evaluation, self(), "c1"}
|
||||
|
@ -2565,8 +2513,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 1, "s2"},
|
||||
{:insert_cell, self(), "s2", 0, :code, "c3", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c2", "c3"]}
|
||||
])
|
||||
|
||||
operation = {:cancel_cell_evaluation, self(), "c2"}
|
||||
|
@ -2592,6 +2540,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -2613,8 +2562,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s3", 0, :code, "c4", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c2", "c3", "c4"]}
|
||||
])
|
||||
|
||||
operation = {:cancel_cell_evaluation, self(), "c2"}
|
||||
|
@ -2642,6 +2591,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]}
|
||||
])
|
||||
|
||||
|
@ -2664,6 +2614,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]}
|
||||
])
|
||||
|
||||
|
@ -2767,6 +2718,7 @@ defmodule Livebook.Session.DataTest do
|
|||
data_after_operations!([
|
||||
{:insert_section, self(), 0, "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:set_smart_cell_definitions, self(), [%{kind: "text", name: "Text"}]},
|
||||
{:insert_cell, self(), "s1", 0, :smart, "c1", %{kind: "text"}},
|
||||
{:smart_cell_started, self(), "c1", Delta.new(), %{}, nil},
|
||||
|
@ -2794,8 +2746,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s2", 0, :code, "c3", %{}},
|
||||
{:set_section_parent, self(), "s2", "s1"},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1"]),
|
||||
{:queue_cells_evaluation, self(), ["c2", "c3"]}
|
||||
])
|
||||
|
||||
operation = {:erase_outputs, self()}
|
||||
|
@ -2822,9 +2774,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :markdown, "c2", %{}},
|
||||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c3"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c3"])
|
||||
])
|
||||
|
||||
operation = {:erase_outputs, self()}
|
||||
|
@ -3332,11 +3282,8 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
# Evaluate cells
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"]),
|
||||
evaluate_cells_operations(["c1"])
|
||||
])
|
||||
|
||||
attrs = %{reevaluate_automatically: true}
|
||||
|
@ -3368,6 +3315,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta}
|
||||
])
|
||||
|
@ -3389,11 +3337,10 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
{:insert_cell, self(), "s1", 3, :code, "c4", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3", "c4"]},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
{:add_cell_evaluation_response, self(), "c4", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["c2", "c3", "c4"]),
|
||||
{:bind_input, self(), "c3", "i1"}
|
||||
])
|
||||
|
||||
|
@ -3428,6 +3375,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
# Second section with evaluating and queued cells
|
||||
{:insert_section, self(), 1, "s2"},
|
||||
|
@ -3459,7 +3407,7 @@ defmodule Livebook.Session.DataTest do
|
|||
data_after_operations!([
|
||||
{:insert_section, self(), 0, "s1"},
|
||||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:queue_cells_evaluation, self(), ["c1"]}
|
||||
{:queue_cells_evaluation, self(), ["setup"]}
|
||||
])
|
||||
|
||||
runtime = NoopRuntime.new()
|
||||
|
@ -3468,13 +3416,13 @@ defmodule Livebook.Session.DataTest do
|
|||
assert {:ok,
|
||||
%{
|
||||
cell_infos: %{
|
||||
"c1" => %{eval: %{status: :evaluating}}
|
||||
"setup" => %{eval: %{status: :evaluating}}
|
||||
},
|
||||
section_infos: %{
|
||||
"s1" => %{evaluating_cell_id: "c1", evaluation_queue: []}
|
||||
"setup-section" => %{evaluating_cell_id: "setup", evaluation_queue: []}
|
||||
}
|
||||
},
|
||||
[{:start_evaluation, %{id: "c1"}, %{id: "s1"}}]} =
|
||||
[{:start_evaluation, %{id: "setup"}, %{id: "setup-section"}}]} =
|
||||
Data.apply_operation(data, operation)
|
||||
end
|
||||
end
|
||||
|
@ -3536,6 +3484,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
{:insert_cell, self(), "s1", 4, :code, "c4", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
evaluate_cells_operations(["setup"]),
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:input, input}, @eval_meta},
|
||||
{:bind_input, self(), "c2", "i1"},
|
||||
|
@ -3557,13 +3506,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:evaluation_started, self(), "c1", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:evaluation_started, self(), "c2", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:evaluation_started, self(), "c3", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"]),
|
||||
# Modify cell 2
|
||||
{:client_join, self(), User.new()},
|
||||
{:apply_cell_delta, self(), "c2", :primary, Delta.new() |> Delta.insert("cats"), 1}
|
||||
|
@ -3579,11 +3522,7 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c3", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c3"]},
|
||||
{:evaluation_started, self(), "c1", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:evaluation_started, self(), "c3", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta},
|
||||
evaluate_cells_operations(["setup", "c1", "c3"]),
|
||||
# Insert a fresh cell between cell 1 and cell 3
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}}
|
||||
])
|
||||
|
@ -3598,15 +3537,9 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 0, :code, "c1", %{}},
|
||||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2"]},
|
||||
{:evaluation_started, self(), "c1", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:evaluation_started, self(), "c2", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
# Reevaluate cell 2
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:evaluation_started, self(), "c1", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2"]),
|
||||
# Reevaluate cell 1
|
||||
evaluate_cells_operations(["c1"])
|
||||
])
|
||||
|
||||
assert Data.cell_ids_for_full_evaluation(data, []) |> Enum.sort() == ["c2"]
|
||||
|
@ -3620,16 +3553,23 @@ defmodule Livebook.Session.DataTest do
|
|||
{:insert_cell, self(), "s1", 1, :code, "c2", %{}},
|
||||
{:insert_cell, self(), "s1", 2, :code, "c3", %{}},
|
||||
{:set_runtime, self(), NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1", "c2", "c3"]},
|
||||
{:evaluation_started, self(), "c1", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
|
||||
{:evaluation_started, self(), "c2", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c2", @eval_resp, @eval_meta},
|
||||
{:evaluation_started, self(), "c3", @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
|
||||
evaluate_cells_operations(["setup", "c1", "c2", "c3"])
|
||||
])
|
||||
|
||||
assert Data.cell_ids_for_full_evaluation(data, ["c2"]) |> Enum.sort() == ["c2", "c3"]
|
||||
end
|
||||
end
|
||||
|
||||
defp evaluate_cells_operations(cell_ids) do
|
||||
[
|
||||
{:queue_cells_evaluation, self(), cell_ids},
|
||||
for(
|
||||
cell_id <- cell_ids,
|
||||
do: [
|
||||
{:evaluation_started, self(), cell_id, @empty_digest},
|
||||
{:add_cell_evaluation_response, self(), cell_id, @eval_resp, @eval_meta}
|
||||
]
|
||||
)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -660,35 +660,10 @@ defmodule Livebook.SessionTest do
|
|||
end
|
||||
|
||||
test "given cell in main flow returns nil if there is no previous cell" do
|
||||
cell1 = %{Cell.new(:markdown) | id: "c1"}
|
||||
section1 = %{Section.new() | id: "s1", cells: [cell1]}
|
||||
|
||||
cell2 = %{Cell.new(:code) | id: "c2"}
|
||||
section2 = %{Section.new() | id: "s2", cells: [cell2]}
|
||||
|
||||
notebook = %{Notebook.new() | sections: [section1, section2]}
|
||||
%{setup_section: %{cells: [setup_cell]} = setup_section} = notebook = Notebook.new()
|
||||
data = Data.new(notebook)
|
||||
|
||||
assert {:main_flow, nil} = Session.find_base_locator(data, cell2, section2)
|
||||
end
|
||||
|
||||
test "given cell in branching section returns nil in that section if there is no previous cell" do
|
||||
cell1 = %{Cell.new(:markdown) | id: "c1"}
|
||||
section1 = %{Section.new() | id: "s1", cells: [cell1]}
|
||||
|
||||
cell2 = %{Cell.new(:code) | id: "c2"}
|
||||
|
||||
section2 = %{
|
||||
Section.new()
|
||||
| id: "s2",
|
||||
parent_id: "s1",
|
||||
cells: [cell2]
|
||||
}
|
||||
|
||||
notebook = %{Notebook.new() | sections: [section1, section2]}
|
||||
data = Data.new(notebook)
|
||||
|
||||
assert {"s2", nil} = Session.find_base_locator(data, cell2, section2)
|
||||
assert {:main_flow, nil} = Session.find_base_locator(data, setup_cell, setup_section)
|
||||
end
|
||||
|
||||
test "when :existing is set ignores fresh and aborted cells" do
|
||||
|
@ -708,6 +683,7 @@ defmodule Livebook.SessionTest do
|
|||
data_after_operations!(data, [
|
||||
{:set_runtime, self(), Livebook.Runtime.NoopRuntime.new()},
|
||||
{:queue_cells_evaluation, self(), ["c1"]},
|
||||
{:add_cell_evaluation_response, self(), "setup", {:ok, nil}, %{evaluation_time_ms: 10}},
|
||||
{:add_cell_evaluation_response, self(), "c1", {:ok, nil}, %{evaluation_time_ms: 10}}
|
||||
])
|
||||
|
||||
|
|
|
@ -114,6 +114,9 @@ defmodule LivebookWeb.SessionLiveTest do
|
|||
end
|
||||
|
||||
test "queueing cell evaluation", %{conn: conn, session: session} do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "sessions:#{session.id}")
|
||||
evaluate_setup(session.pid)
|
||||
|
||||
section_id = insert_section(session.pid)
|
||||
cell_id = insert_text_cell(session.pid, section_id, :code, "Process.sleep(50)")
|
||||
|
||||
|
@ -127,6 +130,19 @@ defmodule LivebookWeb.SessionLiveTest do
|
|||
Session.get_data(session.pid)
|
||||
end
|
||||
|
||||
test "reevaluting the setup cell", %{conn: conn, session: session} do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "sessions:#{session.id}")
|
||||
evaluate_setup(session.pid)
|
||||
|
||||
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
|
||||
|
||||
view
|
||||
|> element(~s{[data-element="session"]})
|
||||
|> render_hook("queue_cell_evaluation", %{"cell_id" => "setup"})
|
||||
|
||||
assert_receive {:operation, {:set_runtime, _pid, %{} = _runtime}}
|
||||
end
|
||||
|
||||
test "cancelling cell evaluation", %{conn: conn, session: session} do
|
||||
section_id = insert_section(session.pid)
|
||||
cell_id = insert_text_cell(session.pid, section_id, :code, "Process.sleep(2000)")
|
||||
|
@ -314,6 +330,9 @@ defmodule LivebookWeb.SessionLiveTest do
|
|||
|
||||
describe "outputs" do
|
||||
test "stdout output update", %{conn: conn, session: session} do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "sessions:#{session.id}")
|
||||
evaluate_setup(session.pid)
|
||||
|
||||
section_id = insert_section(session.pid)
|
||||
cell_id = insert_text_cell(session.pid, section_id, :code)
|
||||
|
||||
|
@ -332,6 +351,9 @@ defmodule LivebookWeb.SessionLiveTest do
|
|||
end
|
||||
|
||||
test "frame output update", %{conn: conn, session: session} do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "sessions:#{session.id}")
|
||||
evaluate_setup(session.pid)
|
||||
|
||||
section_id = insert_section(session.pid)
|
||||
cell_id = insert_text_cell(session.pid, section_id, :code)
|
||||
|
||||
|
@ -897,6 +919,11 @@ defmodule LivebookWeb.SessionLiveTest do
|
|||
cell.id
|
||||
end
|
||||
|
||||
defp evaluate_setup(session_pid) do
|
||||
Session.queue_cell_evaluation(session_pid, "setup")
|
||||
assert_receive {:operation, {:add_cell_evaluation_response, _, "setup", _, _}}
|
||||
end
|
||||
|
||||
defp insert_cell_with_output(session_pid, section_id, output) do
|
||||
code =
|
||||
quote do
|
||||
|
|
|
@ -27,7 +27,9 @@ defmodule Livebook.TestHelpers do
|
|||
Raises if any of the operations results in an error.
|
||||
"""
|
||||
def data_after_operations!(data \\ Data.new(), operations) do
|
||||
Enum.reduce(operations, data, fn operation, data ->
|
||||
operations
|
||||
|> List.flatten()
|
||||
|> Enum.reduce(data, fn operation, data ->
|
||||
case Data.apply_operation(data, operation) do
|
||||
{:ok, data, _action} ->
|
||||
data
|
||||
|
|
Loading…
Reference in a new issue