Add action for clearing evaluation and outputs (#661)

* Move notebook export menu item

* Add action for clearing evaluation and outputs

* Test data operation

* Update wording

* Update wording

* Reorder menu items
This commit is contained in:
Jonatan Kłosko 2021-10-29 16:34:06 +02:00
parent 8a0d218cbe
commit ac1a4a5ffb
6 changed files with 128 additions and 6 deletions

View file

@ -199,6 +199,18 @@ defmodule Livebook.Notebook do
)
end
@doc """
Updates all cells with the given function.
"""
@spec update_cells(t(), (Cell.t() -> Cell.t())) :: t()
def update_cells(notebook, fun) do
update_in(
notebook,
[Access.key(:sections), Access.all(), Access.key(:cells), Access.all()],
fun
)
end
@doc """
Updates section with the given function.
"""

View file

@ -247,6 +247,14 @@ defmodule Livebook.Session do
GenServer.cast(pid, {:cancel_cell_evaluation, self(), cell_id})
end
@doc """
Asynchronously sends erase outputs request to the server.
"""
@spec erase_outputs(pid()) :: :ok
def erase_outputs(pid) do
GenServer.cast(pid, {:erase_outputs, self()})
end
@doc """
Asynchronously sends notebook name update request to the server.
"""
@ -520,6 +528,11 @@ defmodule Livebook.Session do
{:noreply, handle_operation(state, operation)}
end
def handle_cast({:erase_outputs, client_pid}, state) do
operation = {:erase_outputs, client_pid}
{:noreply, handle_operation(state, operation)}
end
def handle_cast({:set_notebook_name, client_pid, name}, state) do
operation = {:set_notebook_name, client_pid, name}
{:noreply, handle_operation(state, operation)}

View file

@ -129,6 +129,7 @@ defmodule Livebook.Session.Data do
| {:reflect_main_evaluation_failure, pid()}
| {:reflect_evaluation_failure, pid(), Section.id()}
| {:cancel_cell_evaluation, pid(), Cell.id()}
| {:erase_outputs, pid()}
| {:set_notebook_name, pid(), String.t()}
| {:set_section_name, pid(), Section.id(), String.t()}
| {:client_join, pid(), User.t()}
@ -459,6 +460,13 @@ defmodule Livebook.Session.Data do
end
end
def apply_operation(data, {:erase_outputs, _client_pid}) do
data
|> with_actions()
|> erase_outputs()
|> wrap_ok()
end
def apply_operation(data, {:set_notebook_name, _client_pid, name}) do
data
|> with_actions()
@ -1053,6 +1061,18 @@ defmodule Livebook.Session.Data do
end
end
defp erase_outputs({data, _} = data_actions) do
data_actions
|> clear_all_evaluation()
|> set!(
notebook:
Notebook.update_cells(data.notebook, fn
%Cell.Elixir{} = cell -> %{cell | outputs: []}
cell -> cell
end)
)
end
defp set_notebook_name({data, _} = data_actions, name) do
data_actions
|> set!(notebook: %{data.notebook | name: name})

View file

@ -202,6 +202,16 @@ defmodule LivebookWeb.SessionLive do
<.remix_icon icon="more-2-fill" class="text-xl" />
</button>
<div class="menu" data-content>
<%= live_patch to: Routes.session_path(@socket, :export, @session.id, "livemd"),
class: "menu__item text-gray-500" do %>
<.remix_icon icon="download-2-line" />
<span class="font-medium">Export</span>
<% end %>
<button class="text-gray-500 menu__item"
phx-click="erase_outputs">
<.remix_icon icon="eraser-fill" />
<span class="font-medium">Erase outputs</span>
</button>
<button class="text-gray-500 menu__item"
phx-click="fork_session">
<.remix_icon icon="git-branch-line" />
@ -213,11 +223,6 @@ defmodule LivebookWeb.SessionLive do
<.remix_icon icon="dashboard-2-line" />
<span class="font-medium">See on Dashboard</span>
</a>
<%= live_patch to: Routes.session_path(@socket, :export, @session.id, "livemd"),
class: "menu__item text-gray-500" do %>
<.remix_icon icon="download-2-line" />
<span class="font-medium">Export</span>
<% end %>
<%= live_patch to: Routes.home_path(@socket, :close_session, @session.id),
class: "menu__item text-red-600" do %>
<.remix_icon icon="close-circle-line" />
@ -697,6 +702,11 @@ defmodule LivebookWeb.SessionLive do
{:noreply, create_session(socket, notebook: notebook, copy_images_from: images_dir)}
end
def handle_event("erase_outputs", %{}, socket) do
Session.erase_outputs(socket.assigns.session.pid)
{:noreply, socket}
end
def handle_event("location_report", report, socket) do
Phoenix.PubSub.broadcast_from(
Livebook.PubSub,

View file

@ -13,7 +13,7 @@
<div class="shadow-custom-1 max-w-2xl flex items-center space-x-3 rounded-lg px-4 py-2 border-l-4 rounded-l-none border-yellow-300 bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-500 cursor-pointer" role="alert"
phx-click="lv:clear-flash"
phx-value-key="warning">
<.remix_icon icon="error-warning-line" class="text-2xl text-yellow-400" />
<.remix_icon icon="alert-line" class="text-2xl text-yellow-400" />
<span class="whitespace-pre-wrap"><%= live_flash(@flash, :warning) %></span>
</div>
<% end %>

View file

@ -2515,6 +2515,73 @@ defmodule Livebook.Session.DataTest do
end
end
describe "apply_operation/2 given :erase_outputs" do
test "clears all sections evaluation and queues" do
data =
data_after_operations!([
{:insert_section, self(), 0, "s1"},
{:insert_cell, self(), "s1", 0, :elixir, "c1"},
{:insert_cell, self(), "s1", 1, :elixir, "c2"},
{:insert_section, self(), 1, "s2"},
{:insert_cell, self(), "s2", 0, :elixir, "c3"},
{:set_section_parent, self(), "s2", "s1"},
{:set_runtime, self(), NoopRuntime.new()},
{:queue_cell_evaluation, self(), "c1"},
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
{:queue_cell_evaluation, self(), "c2"},
{:queue_cell_evaluation, self(), "c3"}
])
operation = {:erase_outputs, self()}
assert {:ok,
%{
cell_infos: %{
"c1" => %{validity_status: :aborted, evaluation_status: :ready},
"c2" => %{validity_status: :aborted, evaluation_status: :ready},
"c3" => %{validity_status: :fresh, evaluation_status: :ready}
},
section_infos: %{
"s1" => %{evaluating_cell_id: nil, evaluation_queue: []},
"s2" => %{evaluating_cell_id: nil, evaluation_queue: []}
}
}, _actions} = Data.apply_operation(data, operation)
end
test "removes elixir cell outputs" do
data =
data_after_operations!([
{:insert_section, self(), 0, "s1"},
{:insert_cell, self(), "s1", 0, :elixir, "c1"},
{:insert_cell, self(), "s1", 1, :markdown, "c2"},
{:insert_cell, self(), "s1", 2, :elixir, "c3"},
{:set_runtime, self(), NoopRuntime.new()},
{:queue_cell_evaluation, self(), "c1"},
{:add_cell_evaluation_response, self(), "c1", @eval_resp, @eval_meta},
{:queue_cell_evaluation, self(), "c3"},
{:add_cell_evaluation_response, self(), "c3", @eval_resp, @eval_meta}
])
operation = {:erase_outputs, self()}
assert {:ok,
%{
notebook: %{
sections: [
%{
id: "s1",
cells: [
%{id: "c1", outputs: []},
%{id: "c2"},
%{id: "c3", outputs: []}
]
}
]
}
}, _actions} = Data.apply_operation(data, operation)
end
end
describe "apply_operation/2 given :set_notebook_name" do
test "updates notebook name with the given string" do
data = Data.new()