2021-03-04 05:56:28 +08:00
|
|
|
defmodule LivebookWeb.SessionLive.ShortcutsComponent do
|
|
|
|
use LivebookWeb, :live_component
|
2021-02-18 20:14:09 +08:00
|
|
|
|
|
|
|
@shortcuts %{
|
|
|
|
insert_mode: [
|
2021-07-01 21:02:56 +08:00
|
|
|
%{seq: ["esc"], desc: "Switch back to navigation mode", basic: true},
|
|
|
|
%{seq: ["tab"], desc: "Autocomplete expression when applicable", basic: true},
|
2021-05-24 21:45:28 +08:00
|
|
|
%{
|
|
|
|
seq: ["ctrl", "␣"],
|
2021-06-16 19:31:53 +08:00
|
|
|
press_all: true,
|
2021-07-01 21:02:56 +08:00
|
|
|
desc: "Show completion list, use twice for details",
|
|
|
|
basic: true
|
2021-07-01 17:50:04 +08:00
|
|
|
},
|
2021-12-04 04:57:21 +08:00
|
|
|
%{
|
|
|
|
seq: ["ctrl", "shift", "␣"],
|
|
|
|
seq_mac: ["⌘", "⇧", "␣"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Show signature help"
|
|
|
|
},
|
2021-07-01 17:50:04 +08:00
|
|
|
%{
|
|
|
|
seq: ["ctrl", "shift", "i"],
|
|
|
|
seq_mac: ["⇧", "⌥", "f"],
|
|
|
|
seq_windows: ["shift", "alt", "f"],
|
|
|
|
press_all: true,
|
2021-07-01 21:02:56 +08:00
|
|
|
desc: "Format Elixir code",
|
|
|
|
basic: true
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
seq: ["ctrl", "/"],
|
|
|
|
seq_mac: ["⌘", "/"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Toggle lines comment"
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
seq: ["ctrl", "shift", "k"],
|
|
|
|
seq_mac: ["⌘", "⇧", "k"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Delete lines"
|
|
|
|
},
|
|
|
|
%{
|
2021-12-12 07:10:15 +08:00
|
|
|
seq: ["ctrl", "]"],
|
|
|
|
seq_mac: ["⌘", "]"],
|
2021-07-01 21:02:56 +08:00
|
|
|
press_all: true,
|
|
|
|
desc: "Indent lines"
|
|
|
|
},
|
|
|
|
%{
|
2021-12-12 07:10:15 +08:00
|
|
|
seq: ["ctrl", "["],
|
|
|
|
seq_mac: ["⌘", "["],
|
2021-07-01 21:02:56 +08:00
|
|
|
press_all: true,
|
|
|
|
desc: "Outdent lines"
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
seq: ["ctrl", "h"],
|
|
|
|
seq_mac: ["⌘", "⌥", "f"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Replace"
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
seq: ["alt", "↑"],
|
|
|
|
seq_mac: ["⌥", "↑"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Move lines up"
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
seq: ["alt", "↓"],
|
|
|
|
seq_mac: ["⌥", "↓"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Move lines down"
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
seq: ["ctrl", "←"],
|
|
|
|
seq_mac: ["⌥", "←"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Cursor skip word left"
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
seq: ["ctrl", "→"],
|
|
|
|
seq_mac: ["⌥", "→"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Cursor skip word right"
|
2021-05-24 21:45:28 +08:00
|
|
|
}
|
2021-02-18 20:14:09 +08:00
|
|
|
],
|
|
|
|
navigation_mode: [
|
2021-07-01 21:02:56 +08:00
|
|
|
%{seq: ["?"], desc: "Open this help modal", basic: true},
|
|
|
|
%{seq: ["j"], desc: "Focus next cell", basic: true},
|
|
|
|
%{seq: ["k"], desc: "Focus previous cell", basic: true},
|
2021-03-12 18:57:01 +08:00
|
|
|
%{seq: ["J"], desc: "Move cell down"},
|
|
|
|
%{seq: ["K"], desc: "Move cell up"},
|
2021-07-01 21:02:56 +08:00
|
|
|
%{seq: ["i"], desc: "Switch to insert mode", basic: true},
|
|
|
|
%{seq: ["n"], desc: "Insert Elixir cell below", basic: true},
|
|
|
|
%{seq: ["m"], desc: "Insert Markdown cell below", basic: true},
|
2021-03-12 18:57:01 +08:00
|
|
|
%{seq: ["N"], desc: "Insert Elixir cell above"},
|
|
|
|
%{seq: ["M"], desc: "Insert Markdown cell above"},
|
2021-07-01 21:02:56 +08:00
|
|
|
%{seq: ["d", "d"], desc: "Delete cell", basic: true},
|
2021-06-16 19:31:53 +08:00
|
|
|
%{seq: ["e", "e"], desc: "Evaluate cell"},
|
|
|
|
%{seq: ["e", "s"], desc: "Evaluate section"},
|
2021-12-08 02:14:32 +08:00
|
|
|
%{seq: ["e", "a"], desc: "Evaluate all outdated cells", basic: true},
|
2021-06-16 19:31:53 +08:00
|
|
|
%{seq: ["e", "x"], desc: "Cancel cell evaluation"},
|
|
|
|
%{seq: ["s", "s"], desc: "Toggle sections panel"},
|
|
|
|
%{seq: ["s", "u"], desc: "Toggle users panel"},
|
2021-06-30 23:48:27 +08:00
|
|
|
%{seq: ["s", "r"], desc: "Show runtime settings"},
|
2021-07-01 19:23:07 +08:00
|
|
|
%{seq: ["s", "b"], desc: "Show bin"},
|
|
|
|
%{seq: ["0", "0"], desc: "Restart current runtime"}
|
2021-04-22 05:02:09 +08:00
|
|
|
],
|
|
|
|
universal: [
|
2021-07-01 21:02:56 +08:00
|
|
|
%{
|
|
|
|
seq: ["ctrl", "↵"],
|
|
|
|
seq_mac: ["⌘", "↵"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Evaluate cell in either mode",
|
|
|
|
basic: true
|
|
|
|
},
|
2021-12-08 02:14:32 +08:00
|
|
|
%{
|
|
|
|
seq: ["ctrl", "shift", "↵"],
|
|
|
|
seq_mac: ["⌘", "⇧", "↵"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Evaluate current and all outdated cells",
|
|
|
|
basic: true
|
|
|
|
},
|
2021-07-01 21:02:56 +08:00
|
|
|
%{
|
|
|
|
seq: ["ctrl", "s"],
|
|
|
|
seq_mac: ["⌘", "s"],
|
|
|
|
press_all: true,
|
|
|
|
desc: "Save notebook",
|
|
|
|
basic: true
|
|
|
|
}
|
2021-02-18 20:14:09 +08:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def mount(socket) do
|
2021-07-01 21:02:56 +08:00
|
|
|
{:ok, assign(socket, shortcuts: @shortcuts, basic: false)}
|
2021-02-18 20:14:09 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def render(assigns) do
|
2021-07-07 20:32:49 +08:00
|
|
|
~H"""
|
2021-04-28 20:28:28 +08:00
|
|
|
<div class="p-6 flex flex-col space-y-3">
|
2021-03-12 18:57:01 +08:00
|
|
|
<h3 class="text-2xl font-semibold text-gray-800">
|
2021-02-18 20:14:09 +08:00
|
|
|
Keyboard shortcuts
|
|
|
|
</h3>
|
2021-03-12 18:57:01 +08:00
|
|
|
<p class="text-gray-700">
|
2021-03-04 05:56:28 +08:00
|
|
|
Livebook highly embraces keyboard navigation to improve your productivity.
|
2021-02-18 20:14:09 +08:00
|
|
|
It operates in one of two modes similarly to the Vim text editor.
|
|
|
|
In <span class="font-semibold">navigation mode</span> you move around
|
|
|
|
the notebook and execute commands, whereas in the <span class="font-semibold">insert mode</span>
|
|
|
|
you have editor focus and directly modify the given cell content.
|
|
|
|
</p>
|
2021-12-11 08:57:54 +08:00
|
|
|
<div class="flex">
|
|
|
|
<form class="mt-4" phx-change="settings" onsubmit="return false;" phx-target={@myself}>
|
2021-07-07 20:32:49 +08:00
|
|
|
<.switch_checkbox
|
|
|
|
name="basic"
|
|
|
|
label="Basic view (essential shortcuts only)"
|
|
|
|
checked={@basic} />
|
2021-07-01 21:02:56 +08:00
|
|
|
</form>
|
|
|
|
</div>
|
2021-12-11 08:57:54 +08:00
|
|
|
<.shortcuts_section title="Navigation mode" shortcuts={@shortcuts.navigation_mode} basic={@basic} platform={@platform} />
|
|
|
|
<.shortcuts_section title="Insert mode" shortcuts={@shortcuts.insert_mode} basic={@basic} platform={@platform} />
|
|
|
|
<.shortcuts_section title="Universal" shortcuts={@shortcuts.universal} basic={@basic} platform={@platform} />
|
2021-02-18 20:14:09 +08:00
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2021-07-07 20:32:49 +08:00
|
|
|
defp shortcuts_section(assigns) do
|
2021-07-01 21:02:56 +08:00
|
|
|
shortcuts =
|
2021-07-07 20:32:49 +08:00
|
|
|
if assigns.basic do
|
|
|
|
Enum.filter(assigns.shortcuts, & &1[:basic])
|
2021-07-01 21:02:56 +08:00
|
|
|
else
|
2021-07-07 20:32:49 +08:00
|
|
|
assigns.shortcuts
|
2021-07-01 21:02:56 +08:00
|
|
|
end
|
|
|
|
|
2021-02-18 20:14:09 +08:00
|
|
|
{left, right} = split_in_half(shortcuts)
|
2021-07-07 20:32:49 +08:00
|
|
|
assigns = assign(assigns, left: left, right: right)
|
2021-02-18 20:14:09 +08:00
|
|
|
|
2021-07-07 20:32:49 +08:00
|
|
|
~H"""
|
2021-04-04 18:42:46 +08:00
|
|
|
<h3 class="text-lg font-medium text-gray-900 pt-4">
|
2021-02-18 20:14:09 +08:00
|
|
|
<%= @title %>
|
|
|
|
</h3>
|
2021-07-01 21:02:56 +08:00
|
|
|
<div class="mt-2 flex flex-col lg:flex-row lg:space-x-4">
|
2021-12-30 05:06:19 +08:00
|
|
|
<div class="lg:grow">
|
2021-07-07 20:32:49 +08:00
|
|
|
<.shortcuts_section_table shortcuts={@left} platform={@platform} />
|
2021-02-18 20:14:09 +08:00
|
|
|
</div>
|
2021-07-01 21:02:56 +08:00
|
|
|
<div class="lg:w-1/2">
|
2021-07-07 20:32:49 +08:00
|
|
|
<.shortcuts_section_table shortcuts={@right} platform={@platform} />
|
2021-02-18 20:14:09 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2021-07-07 20:32:49 +08:00
|
|
|
defp shortcuts_section_table(assigns) do
|
|
|
|
~H"""
|
2021-02-18 20:14:09 +08:00
|
|
|
<table>
|
|
|
|
<tbody>
|
|
|
|
<%= for shortcut <- @shortcuts do %>
|
|
|
|
<tr>
|
2021-03-12 18:57:01 +08:00
|
|
|
<td class="py-2 pr-3">
|
2021-07-07 20:32:49 +08:00
|
|
|
<.shortcut shortcut={shortcut} platform={@platform} />
|
2021-02-18 20:14:09 +08:00
|
|
|
</td>
|
2021-07-01 21:02:56 +08:00
|
|
|
<td class="lg:whitespace-nowrap">
|
2021-02-18 20:14:09 +08:00
|
|
|
<%= shortcut.desc %>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<% end %>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2021-07-07 20:32:49 +08:00
|
|
|
defp shortcut(%{shortcut: shortcut, platform: platform}) do
|
2021-07-01 17:50:04 +08:00
|
|
|
seq = shortcut[:"seq_#{platform}"] || shortcut.seq
|
2021-06-16 19:31:53 +08:00
|
|
|
press_all = Map.get(shortcut, :press_all, false)
|
2021-03-12 18:57:01 +08:00
|
|
|
|
2021-06-16 19:31:53 +08:00
|
|
|
joiner =
|
|
|
|
if press_all do
|
2021-07-07 20:32:49 +08:00
|
|
|
assigns = %{}
|
|
|
|
|
|
|
|
~H"""
|
|
|
|
<.remix_icon icon="add-line" class="text-lg text-gray-600" />
|
|
|
|
"""
|
2021-06-16 19:31:53 +08:00
|
|
|
end
|
2021-03-12 18:57:01 +08:00
|
|
|
|
2021-06-16 19:31:53 +08:00
|
|
|
elements = Enum.map_intersperse(seq, joiner, &content_tag("kbd", &1))
|
2021-07-07 20:32:49 +08:00
|
|
|
|
2021-03-12 18:57:01 +08:00
|
|
|
assigns = %{elements: elements}
|
|
|
|
|
2021-07-07 20:32:49 +08:00
|
|
|
~H"""
|
2021-06-16 19:31:53 +08:00
|
|
|
<div class="flex space-x-1 items-center markdown">
|
2021-03-12 18:57:01 +08:00
|
|
|
<%= for element <- @elements do %>
|
|
|
|
<%= element %>
|
|
|
|
<% end %>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2021-02-18 20:14:09 +08:00
|
|
|
defp split_in_half(list) do
|
|
|
|
half_idx = list |> length() |> Kernel.+(1) |> div(2)
|
|
|
|
Enum.split(list, half_idx)
|
|
|
|
end
|
2021-07-01 21:02:56 +08:00
|
|
|
|
|
|
|
@impl true
|
2021-07-27 01:59:52 +08:00
|
|
|
def handle_event("settings", %{"basic" => basic}, socket) do
|
|
|
|
basic? = basic == "true"
|
2021-07-01 21:02:56 +08:00
|
|
|
{:noreply, assign(socket, :basic, basic?)}
|
|
|
|
end
|
2021-02-18 20:14:09 +08:00
|
|
|
end
|