mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-01-26 08:49:50 +08:00
Improve "changed" indicator and batch evaluation shortcuts (#766)
* Make cell status italic when content changed * Add Ctrl+Shift+Enter for evaluating all cells * Improve the behaviour of evaluating all cells * Fix typo
This commit is contained in:
parent
812f753a37
commit
89ea67861f
11 changed files with 417 additions and 276 deletions
|
@ -81,7 +81,13 @@ solely client-side operations.
|
|||
@apply opacity-100 pointer-events-auto;
|
||||
}
|
||||
|
||||
[data-element="cell"] [data-element="change-indicator"]:not([data-js-shown]) {
|
||||
[data-element="cell"] [data-element="cell-status"][data-js-changed] {
|
||||
@apply italic;
|
||||
}
|
||||
|
||||
[data-element="cell"]
|
||||
[data-element="cell-status"]:not([data-js-changed])
|
||||
[data-element="change-indicator"] {
|
||||
@apply invisible;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,15 +66,18 @@ const Cell = {
|
|||
this.state.evaluationDigest = evaluation_digest;
|
||||
|
||||
const updateChangeIndicator = () => {
|
||||
const indicator = this.el.querySelector(
|
||||
`[data-element="change-indicator"]`
|
||||
const cellStatus = this.el.querySelector(
|
||||
`[data-element="cell-status"]`
|
||||
);
|
||||
const indicator =
|
||||
cellStatus &&
|
||||
cellStatus.querySelector(`[data-element="change-indicator"]`);
|
||||
|
||||
if (indicator) {
|
||||
const source = this.state.liveEditor.getSource();
|
||||
const digest = md5Base64(source);
|
||||
const changed = this.state.evaluationDigest !== digest;
|
||||
indicator.toggleAttribute("data-js-shown", changed);
|
||||
cellStatus.toggleAttribute("data-js-changed", changed);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -297,6 +297,7 @@ function handleDocumentKeyDown(hook, event) {
|
|||
|
||||
const cmd = isMacOS() ? event.metaKey : event.ctrlKey;
|
||||
const alt = event.altKey;
|
||||
const shift = event.shiftKey;
|
||||
const key = event.key;
|
||||
const keyBuffer = hook.state.keyBuffer;
|
||||
|
||||
|
@ -321,7 +322,10 @@ function handleDocumentKeyDown(hook, event) {
|
|||
if (!monacoInputOpen && !completionBoxOpen && !signatureDetailsOpen) {
|
||||
escapeInsertMode(hook);
|
||||
}
|
||||
} else if (cmd && key === "Enter" && !alt) {
|
||||
} else if (cmd && shift && !alt && key === "Enter") {
|
||||
cancelEvent(event);
|
||||
queueFullCellsEvaluation(hook, true);
|
||||
} else if (cmd && !alt && key === "Enter") {
|
||||
cancelEvent(event);
|
||||
if (hook.state.focusedCellType === "elixir") {
|
||||
queueFocusedCellEvaluation(hook);
|
||||
|
@ -350,12 +354,17 @@ function handleDocumentKeyDown(hook, event) {
|
|||
saveNotebook(hook);
|
||||
} else if (keyBuffer.tryMatch(["d", "d"])) {
|
||||
deleteFocusedCell(hook);
|
||||
} else if (keyBuffer.tryMatch(["e", "e"]) || (cmd && key === "Enter")) {
|
||||
} else if (cmd && shift && !alt && key === "Enter") {
|
||||
queueFullCellsEvaluation(hook, true);
|
||||
} else if (keyBuffer.tryMatch(["e", "a"])) {
|
||||
queueFullCellsEvaluation(hook, false);
|
||||
} else if (
|
||||
keyBuffer.tryMatch(["e", "e"]) ||
|
||||
(cmd && !alt && key === "Enter")
|
||||
) {
|
||||
if (hook.state.focusedCellType === "elixir") {
|
||||
queueFocusedCellEvaluation(hook);
|
||||
}
|
||||
} else if (keyBuffer.tryMatch(["e", "a"])) {
|
||||
queueAllCellsEvaluation(hook);
|
||||
} else if (keyBuffer.tryMatch(["e", "s"])) {
|
||||
queueFocusedSectionEvaluation(hook);
|
||||
} else if (keyBuffer.tryMatch(["s", "s"])) {
|
||||
|
@ -667,8 +676,15 @@ function queueFocusedCellEvaluation(hook) {
|
|||
}
|
||||
}
|
||||
|
||||
function queueAllCellsEvaluation(hook) {
|
||||
hook.pushEvent("queue_all_cells_evaluation", {});
|
||||
function queueFullCellsEvaluation(hook, includeFocused) {
|
||||
const forcedCellIds =
|
||||
includeFocused && hook.state.focusedId && isCell(hook.state.focusedId)
|
||||
? [hook.state.focusedId]
|
||||
: [];
|
||||
|
||||
hook.pushEvent("queue_full_evaluation", {
|
||||
forced_cell_ids: forcedCellIds,
|
||||
});
|
||||
}
|
||||
|
||||
function queueFocusedSectionEvaluation(hook) {
|
||||
|
@ -676,7 +692,7 @@ function queueFocusedSectionEvaluation(hook) {
|
|||
const sectionId = getSectionIdByFocusableId(hook.state.focusedId);
|
||||
|
||||
if (sectionId) {
|
||||
hook.pushEvent("queue_section_cells_evaluation", {
|
||||
hook.pushEvent("queue_section_evaluation", {
|
||||
section_id: sectionId,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -416,6 +416,23 @@ defmodule Livebook.Notebook do
|
|||
|> Enum.filter(fn {cell, _} -> MapSet.member?(child_cell_ids, cell.id) end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the list with the given parent cells and all of
|
||||
their child cells.
|
||||
|
||||
The cells are not ordered in any secific way.
|
||||
"""
|
||||
@spec cell_ids_with_children(t(), list(Cell.id())) :: list(Cell.id())
|
||||
def cell_ids_with_children(data, parent_cell_ids) do
|
||||
graph = cell_dependency_graph(data.notebook)
|
||||
|
||||
for parent_id <- parent_cell_ids,
|
||||
leaf_id <- Graph.leaves(graph),
|
||||
cell_id <- Graph.find_path(graph, leaf_id, parent_id),
|
||||
uniq: true,
|
||||
do: cell_id
|
||||
end
|
||||
|
||||
@doc """
|
||||
Computes cell dependency graph.
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends notebook attributes update to the server.
|
||||
Sends notebook attributes update to the server.
|
||||
"""
|
||||
@spec set_notebook_attributes(pid(), map()) :: :ok
|
||||
def set_notebook_attributes(pid, attrs) do
|
||||
|
@ -156,7 +156,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends section insertion request to the server.
|
||||
Sends section insertion request to the server.
|
||||
"""
|
||||
@spec insert_section(pid(), non_neg_integer()) :: :ok
|
||||
def insert_section(pid, index) do
|
||||
|
@ -164,7 +164,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends section insertion request to the server.
|
||||
Sends section insertion request to the server.
|
||||
"""
|
||||
@spec insert_section_into(pid(), Section.id(), non_neg_integer()) :: :ok
|
||||
def insert_section_into(pid, section_id, index) do
|
||||
|
@ -172,7 +172,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends parent update request to the server.
|
||||
Sends parent update request to the server.
|
||||
"""
|
||||
@spec set_section_parent(pid(), Section.id(), Section.id()) :: :ok
|
||||
def set_section_parent(pid, section_id, parent_id) do
|
||||
|
@ -180,7 +180,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends parent update request to the server.
|
||||
Sends parent update request to the server.
|
||||
"""
|
||||
@spec unset_section_parent(pid(), Section.id()) :: :ok
|
||||
def unset_section_parent(pid, section_id) do
|
||||
|
@ -188,7 +188,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends cell insertion request to the server.
|
||||
Sends cell insertion request to the server.
|
||||
"""
|
||||
@spec insert_cell(pid(), Section.id(), non_neg_integer(), Cell.type()) :: :ok
|
||||
def insert_cell(pid, section_id, index, type) do
|
||||
|
@ -196,7 +196,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends section deletion request to the server.
|
||||
Sends section deletion request to the server.
|
||||
"""
|
||||
@spec delete_section(pid(), Section.id(), boolean()) :: :ok
|
||||
def delete_section(pid, section_id, delete_cells) do
|
||||
|
@ -204,7 +204,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends cell deletion request to the server.
|
||||
Sends cell deletion request to the server.
|
||||
"""
|
||||
@spec delete_cell(pid(), Cell.id()) :: :ok
|
||||
def delete_cell(pid, cell_id) do
|
||||
|
@ -212,7 +212,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends cell restoration request to the server.
|
||||
Sends cell restoration request to the server.
|
||||
"""
|
||||
@spec restore_cell(pid(), Cell.id()) :: :ok
|
||||
def restore_cell(pid, cell_id) do
|
||||
|
@ -220,7 +220,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends cell move request to the server.
|
||||
Sends cell move request to the server.
|
||||
"""
|
||||
@spec move_cell(pid(), Cell.id(), integer()) :: :ok
|
||||
def move_cell(pid, cell_id, offset) do
|
||||
|
@ -228,7 +228,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends section move request to the server.
|
||||
Sends section move request to the server.
|
||||
"""
|
||||
@spec move_section(pid(), Section.id(), integer()) :: :ok
|
||||
def move_section(pid, section_id, offset) do
|
||||
|
@ -236,7 +236,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends cell evaluation request to the server.
|
||||
Sends cell evaluation request to the server.
|
||||
"""
|
||||
@spec queue_cell_evaluation(pid(), Cell.id()) :: :ok
|
||||
def queue_cell_evaluation(pid, cell_id) do
|
||||
|
@ -244,7 +244,34 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends cell evaluation cancellation request to the server.
|
||||
Sends section evaluation request to the server.
|
||||
"""
|
||||
@spec queue_section_evaluation(pid(), Section.id()) :: :ok
|
||||
def queue_section_evaluation(pid, section_id) do
|
||||
GenServer.cast(pid, {:queue_section_evaluation, self(), section_id})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sends input bound cells evaluation request to the server.
|
||||
"""
|
||||
@spec queue_bound_cells_evaluation(pid(), Data.input_id()) :: :ok
|
||||
def queue_bound_cells_evaluation(pid, input_id) do
|
||||
GenServer.cast(pid, {:queue_bound_cells_evaluation, self(), input_id})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sends full evaluation request to the server.
|
||||
|
||||
All outdated (new/stale/changed) cells, as well as cells given
|
||||
as `forced_cell_ids` are scheduled for evaluation.
|
||||
"""
|
||||
@spec queue_full_evaluation(pid(), list(Cell.id())) :: :ok
|
||||
def queue_full_evaluation(pid, forced_cell_ids) do
|
||||
GenServer.cast(pid, {:queue_full_evaluation, self(), forced_cell_ids})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sends cell evaluation cancellation request to the server.
|
||||
"""
|
||||
@spec cancel_cell_evaluation(pid(), Cell.id()) :: :ok
|
||||
def cancel_cell_evaluation(pid, cell_id) do
|
||||
|
@ -252,7 +279,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends erase outputs request to the server.
|
||||
Sends erase outputs request to the server.
|
||||
"""
|
||||
@spec erase_outputs(pid()) :: :ok
|
||||
def erase_outputs(pid) do
|
||||
|
@ -260,7 +287,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends notebook name update request to the server.
|
||||
Sends notebook name update request to the server.
|
||||
"""
|
||||
@spec set_notebook_name(pid(), String.t()) :: :ok
|
||||
def set_notebook_name(pid, name) do
|
||||
|
@ -268,7 +295,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends section name update request to the server.
|
||||
Sends section name update request to the server.
|
||||
"""
|
||||
@spec set_section_name(pid(), Section.id(), String.t()) :: :ok
|
||||
def set_section_name(pid, section_id, name) do
|
||||
|
@ -276,7 +303,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends a cell delta to apply to the server.
|
||||
Sends a cell delta to apply to the server.
|
||||
"""
|
||||
@spec apply_cell_delta(pid(), Cell.id(), Delta.t(), Data.cell_revision()) :: :ok
|
||||
def apply_cell_delta(pid, cell_id, delta, revision) do
|
||||
|
@ -284,7 +311,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously informs at what revision the given client is.
|
||||
Informs at what revision the given client is.
|
||||
|
||||
This helps to remove old deltas that are no longer necessary.
|
||||
"""
|
||||
|
@ -294,7 +321,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends a cell attributes update to the server.
|
||||
Sends a cell attributes update to the server.
|
||||
"""
|
||||
@spec set_cell_attributes(pid(), Cell.id(), map()) :: :ok
|
||||
def set_cell_attributes(pid, cell_id, attrs) do
|
||||
|
@ -302,15 +329,15 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends a input value update to the server.
|
||||
Sends a input value update to the server.
|
||||
"""
|
||||
@spec set_input_value(pid(), Session.input_id(), term()) :: :ok
|
||||
@spec set_input_value(pid(), Data.input_id(), term()) :: :ok
|
||||
def set_input_value(pid, input_id, value) do
|
||||
GenServer.cast(pid, {:set_input_value, self(), input_id, value})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously connects to the given runtime.
|
||||
Connects to the given runtime.
|
||||
|
||||
Note that this results in initializing the corresponding remote node
|
||||
with modules and processes required for evaluation.
|
||||
|
@ -321,7 +348,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously disconnects from the current runtime.
|
||||
Disconnects from the current runtime.
|
||||
|
||||
Note that this results in clearing the evaluation state.
|
||||
"""
|
||||
|
@ -331,7 +358,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends file location update request to the server.
|
||||
Sends file location update request to the server.
|
||||
"""
|
||||
@spec set_file(pid(), FileSystem.File.t() | nil) :: :ok
|
||||
def set_file(pid, file) do
|
||||
|
@ -339,7 +366,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends save request to the server.
|
||||
Sends save request to the server.
|
||||
|
||||
If there's a file set and the notebook changed since the last save,
|
||||
it will be persisted to said file.
|
||||
|
@ -360,7 +387,7 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Asynchronously sends a close request to the server.
|
||||
Sends a close request to the server.
|
||||
|
||||
This results in saving the file and broadcasting
|
||||
a :closed message to the session topic.
|
||||
|
@ -533,7 +560,35 @@ defmodule Livebook.Session do
|
|||
end
|
||||
|
||||
def handle_cast({:queue_cell_evaluation, client_pid, cell_id}, state) do
|
||||
operation = {:queue_cell_evaluation, client_pid, cell_id}
|
||||
operation = {:queue_cells_evaluation, client_pid, [cell_id]}
|
||||
{:noreply, handle_operation(state, operation)}
|
||||
end
|
||||
|
||||
def handle_cast({:queue_section_evaluation, client_pid, section_id}, state) do
|
||||
case Notebook.fetch_section(state.data.notebook, section_id) do
|
||||
{:ok, section} ->
|
||||
cell_ids = for cell <- section.cells, is_struct(cell, Cell.Elixir), do: cell.id
|
||||
operation = {:queue_cells_evaluation, client_pid, cell_ids}
|
||||
{:noreply, handle_operation(state, operation)}
|
||||
|
||||
:error ->
|
||||
{:noreply, state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_cast({:queue_bound_cells_evaluation, client_pid, input_id}, state) do
|
||||
cell_ids =
|
||||
for {bound_cell, _} <- Data.bound_cells_with_section(state.data, input_id),
|
||||
do: bound_cell.id
|
||||
|
||||
operation = {:queue_cells_evaluation, client_pid, cell_ids}
|
||||
{:noreply, handle_operation(state, operation)}
|
||||
end
|
||||
|
||||
def handle_cast({:queue_full_evaluation, client_pid, forced_cell_ids}, state) do
|
||||
cell_ids = Data.cell_ids_for_full_evaluation(state.data, forced_cell_ids)
|
||||
|
||||
operation = {:queue_cells_evaluation, client_pid, cell_ids}
|
||||
{:noreply, handle_operation(state, operation)}
|
||||
end
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ defmodule Livebook.Session.Data do
|
|||
| {:restore_cell, pid(), Cell.id()}
|
||||
| {:move_cell, pid(), Cell.id(), offset :: integer()}
|
||||
| {:move_section, pid(), Section.id(), offset :: integer()}
|
||||
| {:queue_cell_evaluation, pid(), Cell.id()}
|
||||
| {:queue_cells_evaluation, pid(), list(Cell.id())}
|
||||
| {:evaluation_started, pid(), Cell.id(), binary()}
|
||||
| {:add_cell_evaluation_output, pid(), Cell.id(), term()}
|
||||
| {:add_cell_evaluation_response, pid(), Cell.id(), term(), metadata :: map()}
|
||||
|
@ -379,20 +379,28 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
end
|
||||
|
||||
def apply_operation(data, {:queue_cell_evaluation, _client_pid, id}) do
|
||||
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id),
|
||||
%Cell.Elixir{} <- cell,
|
||||
:ready <- data.cell_infos[cell.id].evaluation_status do
|
||||
data
|
||||
|> with_actions()
|
||||
|> queue_prerequisite_cells_evaluation(cell)
|
||||
|> queue_cell_evaluation(cell, section)
|
||||
def apply_operation(data, {:queue_cells_evaluation, _client_pid, cell_ids}) do
|
||||
cells_with_section =
|
||||
data.notebook
|
||||
|> Notebook.elixir_cells_with_section()
|
||||
|> Enum.filter(fn {cell, _section} ->
|
||||
info = data.cell_infos[cell.id]
|
||||
cell.id in cell_ids and info.evaluation_status == :ready
|
||||
end)
|
||||
|
||||
if cell_ids != [] and length(cell_ids) == length(cells_with_section) do
|
||||
cells_with_section
|
||||
|> Enum.reduce(with_actions(data), fn {cell, section}, data_actions ->
|
||||
data_actions
|
||||
|> queue_prerequisite_cells_evaluation(cell)
|
||||
|> queue_cell_evaluation(cell, section)
|
||||
end)
|
||||
|> maybe_start_runtime(data)
|
||||
|> maybe_evaluate_queued()
|
||||
|> compute_snapshots_and_validity()
|
||||
|> wrap_ok()
|
||||
else
|
||||
_ -> :error
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1490,4 +1498,40 @@ defmodule Livebook.Session.Data do
|
|||
|> queue_cell_evaluation(cell, section)
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if the given cell is outdated.
|
||||
|
||||
A cell is considered outdated if its new/fresh or its content
|
||||
has changed since the last evaluation.
|
||||
"""
|
||||
@spec cell_outdated?(t(), Cell.t()) :: boolean()
|
||||
def cell_outdated?(data, cell) do
|
||||
info = data.cell_infos[cell.id]
|
||||
digest = :erlang.md5(cell.source)
|
||||
info.validity_status != :evaluated or info.evaluation_digest != digest
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the list of cell ids for full evaluation.
|
||||
|
||||
The list includes all outdated cells, cells in `forced_cell_ids`
|
||||
and all of their child cells.
|
||||
"""
|
||||
@spec cell_ids_for_full_evaluation(t(), list(Cell.id())) :: list(Cell.id())
|
||||
def cell_ids_for_full_evaluation(data, forced_cell_ids) do
|
||||
elixir_cells_with_section = Notebook.elixir_cells_with_section(data.notebook)
|
||||
|
||||
evaluable_cell_ids =
|
||||
for {cell, _} <- elixir_cells_with_section,
|
||||
cell_outdated?(data, cell) or cell.id in forced_cell_ids,
|
||||
uniq: true,
|
||||
do: cell.id
|
||||
|
||||
cell_ids = Notebook.cell_ids_with_children(data, evaluable_cell_ids)
|
||||
|
||||
for {cell, _} <- elixir_cells_with_section,
|
||||
cell.id in cell_ids,
|
||||
do: cell.id
|
||||
end
|
||||
end
|
||||
|
|
|
@ -665,26 +665,18 @@ defmodule LivebookWeb.SessionLive do
|
|||
|
||||
def handle_event("queue_cell_evaluation", %{"cell_id" => cell_id}, socket) do
|
||||
Session.queue_cell_evaluation(socket.assigns.session.pid, cell_id)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("queue_section_cells_evaluation", %{"section_id" => section_id}, socket) do
|
||||
with {:ok, section} <- Notebook.fetch_section(socket.private.data.notebook, section_id) do
|
||||
for cell <- section.cells, is_struct(cell, Cell.Elixir) do
|
||||
Session.queue_cell_evaluation(socket.assigns.session.pid, cell.id)
|
||||
end
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("queue_all_cells_evaluation", _params, socket) do
|
||||
data = socket.private.data
|
||||
def handle_event("queue_section_evaluation", %{"section_id" => section_id}, socket) do
|
||||
Session.queue_section_evaluation(socket.assigns.session.pid, section_id)
|
||||
|
||||
for {cell, _} <- Notebook.elixir_cells_with_section(data.notebook),
|
||||
data.cell_infos[cell.id].validity_status != :evaluated do
|
||||
Session.queue_cell_evaluation(socket.assigns.session.pid, cell.id)
|
||||
end
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("queue_full_evaluation", %{"forced_cell_ids" => forced_cell_ids}, socket) do
|
||||
Session.queue_full_evaluation(socket.assigns.session.pid, forced_cell_ids)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
@ -917,9 +909,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
end
|
||||
|
||||
def handle_info({:queue_bound_cells_evaluation, input_id}, socket) do
|
||||
for {bound_cell, _} <- Session.Data.bound_cells_with_section(socket.private.data, input_id) do
|
||||
Session.queue_cell_evaluation(socket.assigns.session.pid, bound_cell.id)
|
||||
end
|
||||
Session.queue_bound_cells_evaluation(socket.assigns.session.pid, input_id)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
|
|
@ -309,7 +309,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
|
|||
~H"""
|
||||
<div class={"#{if(@tooltip, do: "tooltip")} bottom distant-medium"} data-tooltip={@tooltip}>
|
||||
<div class="flex items-center space-x-1">
|
||||
<div class="flex text-xs text-gray-400">
|
||||
<div class="flex text-xs text-gray-400" data-element="cell-status">
|
||||
<%= render_slot(@inner_block) %>
|
||||
<%= if @change_indicator do %>
|
||||
<span data-element="change-indicator">*</span>
|
||||
|
|
|
@ -101,7 +101,7 @@ defmodule LivebookWeb.SessionLive.ShortcutsComponent do
|
|||
%{seq: ["d", "d"], desc: "Delete cell", basic: true},
|
||||
%{seq: ["e", "e"], desc: "Evaluate cell"},
|
||||
%{seq: ["e", "s"], desc: "Evaluate section"},
|
||||
%{seq: ["e", "a"], desc: "Evaluate all stale/new cells", basic: true},
|
||||
%{seq: ["e", "a"], desc: "Evaluate all outdated cells", basic: true},
|
||||
%{seq: ["e", "x"], desc: "Cancel cell evaluation"},
|
||||
%{seq: ["s", "s"], desc: "Toggle sections panel"},
|
||||
%{seq: ["s", "u"], desc: "Toggle users panel"},
|
||||
|
@ -117,6 +117,13 @@ defmodule LivebookWeb.SessionLive.ShortcutsComponent do
|
|||
desc: "Evaluate cell in either mode",
|
||||
basic: true
|
||||
},
|
||||
%{
|
||||
seq: ["ctrl", "shift", "↵"],
|
||||
seq_mac: ["⌘", "⇧", "↵"],
|
||||
press_all: true,
|
||||
desc: "Evaluate current and all outdated cells",
|
||||
basic: true
|
||||
},
|
||||
%{
|
||||
seq: ["ctrl", "s"],
|
||||
seq_mac: ["⌘", "s"],
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -90,7 +90,7 @@ defmodule Livebook.SessionTest do
|
|||
|
||||
Session.queue_cell_evaluation(session.pid, cell_id)
|
||||
|
||||
assert_receive {:operation, {:queue_cell_evaluation, ^pid, ^cell_id}}
|
||||
assert_receive {:operation, {:queue_cells_evaluation, ^pid, [^cell_id]}}
|
||||
|
||||
assert_receive {:operation,
|
||||
{:add_cell_evaluation_response, _, ^cell_id, _,
|
||||
|
|
Loading…
Reference in a new issue