diff --git a/assets/js/session/index.js b/assets/js/session/index.js
index d6e722b6d..69c8143ae 100644
--- a/assets/js/session/index.js
+++ b/assets/js/session/index.js
@@ -60,6 +60,8 @@ const Session = {
this.pushEvent("queue_section_cells_evaluation", {});
} else if (keyBuffer.tryMatch(["e", "j"])) {
this.pushEvent("queue_child_cells_evaluation", {});
+ } else if (keyBuffer.tryMatch(["e", "x"])) {
+ this.pushEvent("cancel_focused_cell_evaluation", {});
} else if (keyBuffer.tryMatch(["?"])) {
this.pushEvent("show_shortcuts", {});
} else if (key === "i") {
diff --git a/lib/live_book_web/live/cell_component.ex b/lib/live_book_web/live/cell_component.ex
index 0205ee936..737279e94 100644
--- a/lib/live_book_web/live/cell_component.ex
+++ b/lib/live_book_web/live/cell_component.ex
@@ -44,9 +44,15 @@ defmodule LiveBookWeb.CellComponent do
~L"""
<%= if @focused do %>
-
+ <%= if @cell_info.evaluation_status == :ready do %>
+
+ <% else %>
+
+ <% end %>
diff --git a/lib/live_book_web/live/icons.ex b/lib/live_book_web/live/icons.ex
index ededc949d..5cb6557db 100644
--- a/lib/live_book_web/live/icons.ex
+++ b/lib/live_book_web/live/icons.ex
@@ -138,6 +138,17 @@ defmodule LiveBookWeb.Icons do
"""
end
+ def svg(:stop, attrs) do
+ assigns = %{attrs: heroicon_svg_attrs(attrs)}
+
+ ~L"""
+ <%= tag(:svg, @attrs) %>
+
+
+
+ """
+ end
+
# https://heroicons.com
defp heroicon_svg_attrs(attrs) do
heroicon_svg_attrs = [
diff --git a/lib/live_book_web/live/session_live.ex b/lib/live_book_web/live/session_live.ex
index 0162aafd5..892632e70 100644
--- a/lib/live_book_web/live/session_live.ex
+++ b/lib/live_book_web/live/session_live.ex
@@ -348,6 +348,14 @@ defmodule LiveBookWeb.SessionLive do
{:noreply, socket}
end
+ def handle_event("cancel_focused_cell_evaluation", %{}, socket) do
+ if socket.assigns.focused_cell_id do
+ Session.cancel_cell_evaluation(socket.assigns.session_id, socket.assigns.focused_cell_id)
+ end
+
+ {:noreply, socket}
+ end
+
def handle_event("show_shortcuts", %{}, socket) do
{:noreply,
push_patch(socket, to: Routes.session_path(socket, :shortcuts, socket.assigns.session_id))}
diff --git a/lib/live_book_web/live/session_live/shortcuts_component.ex b/lib/live_book_web/live/session_live/shortcuts_component.ex
index cf3405aaf..6b9925a94 100644
--- a/lib/live_book_web/live/session_live/shortcuts_component.ex
+++ b/lib/live_book_web/live/session_live/shortcuts_component.ex
@@ -18,7 +18,8 @@ defmodule LiveBookWeb.SessionLive.ShortcutsComponent do
%{seq: "dd", desc: "Delete cell"},
%{seq: "ee", desc: "Evaluate cell"},
%{seq: "es", desc: "Evaluate section"},
- %{seq: "ej", desc: "Evaluate cells below"}
+ %{seq: "ej", desc: "Evaluate cells below"},
+ %{seq: "ex", desc: "Cancel cell evaluation"}
]
}
diff --git a/test/live_book_web/live/session_live_test.exs b/test/live_book_web/live/session_live_test.exs
index 664fca9e0..3dc7a65d1 100644
--- a/test/live_book_web/live/session_live_test.exs
+++ b/test/live_book_web/live/session_live_test.exs
@@ -108,6 +108,26 @@ defmodule LiveBookWeb.SessionLiveTest do
Session.get_data(session_id)
end
+ test "cancelling focused cell evaluation", %{conn: conn, session_id: session_id} do
+ section_id = insert_section(session_id)
+ cell_id = insert_cell(session_id, section_id, :elixir, "Process.sleep(2000)")
+
+ {:ok, view, _} = live(conn, "/sessions/#{session_id}")
+
+ focus_cell(view, cell_id)
+
+ view
+ |> element("#session")
+ |> render_hook("queue_focused_cell_evaluation", %{})
+
+ view
+ |> element("#session")
+ |> render_hook("cancel_focused_cell_evaluation", %{})
+
+ assert %{cell_infos: %{^cell_id => %{evaluation_status: :ready}}} =
+ Session.get_data(session_id)
+ end
+
test "inserting a cell below the focused cell", %{conn: conn, session_id: session_id} do
section_id = insert_section(session_id)
cell_id = insert_cell(session_id, section_id, :elixir)