mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-09-20 18:15:56 +08:00
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:
parent
8a0d218cbe
commit
ac1a4a5ffb
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue