livebook/lib/livebook_web/live/session_live/attached_live.ex
Jonatan Kłosko e2490c0f7f
Implement signature intellisense (#640)
* Remove Code.Fragment backport

* Fix tests compatibility with Elixir 1.13

* Implement signature intellisense

* Don't show signatures on module attributes

* Add tests for calls with do-end block

* Unify spec formatting

* Insert parentheses when completing a function call

* Send all text until cursor in signature request

* Add configuration for completion/signature popups (#693)

* Add editor settings form

* Add configuration for intellisense defaults

* Read fresh settings when editor mounts

* Scope attribute names

* Fix disabled button styling

* Simplify signature box and enable by default

* Split settings into system and user sections

* Update lib/livebook_web/live/settings_live.ex

Co-authored-by: José Valim <jose.valim@dashbit.co>

* Update lib/livebook_web/live/settings_live.ex

Co-authored-by: José Valim <jose.valim@dashbit.co>

Co-authored-by: José Valim <jose.valim@dashbit.co>

* Fix spacing in documentation tests

Co-authored-by: José Valim <jose.valim@dashbit.co>
2021-12-03 21:57:21 +01:00

123 lines
3.8 KiB
Elixir

defmodule LivebookWeb.SessionLive.AttachedLive do
use LivebookWeb, :live_view
alias Livebook.{Session, Runtime, Utils}
@impl true
def mount(_params, %{"session" => session, "current_runtime" => current_runtime}, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(Livebook.PubSub, "sessions:#{session.id}")
end
{:ok,
assign(socket,
session: session,
current_runtime: current_runtime,
error_message: nil,
data: initial_data(current_runtime)
)}
end
@impl true
def render(assigns) do
~H"""
<div class="flex-col space-y-5">
<%= if @error_message do %>
<div class="error-box">
<%= @error_message %>
</div>
<% end %>
<p class="text-gray-700">
Connect the session to an already running node
and evaluate code in the context of that node.
Thanks to this approach you can work with
an arbitrary Elixir runtime.
Make sure to give the node a name and a cookie, for example:
</p>
<div class="text-gray-700 markdown">
<%= if longname = Livebook.Config.longname() do %>
<pre><code>iex --name test@<%= longname %> --cookie mycookie</code></pre>
<% else %>
<pre><code>iex --sname test --cookie mycookie</code></pre>
<% end %>
</div>
<p class="text-gray-700">
Then enter the connection information below:
</p>
<.form let={f} for={:data}
phx-submit="init"
phx-change="validate"
autocomplete="off"
spellcheck="false">
<div class="flex flex-col space-y-4">
<div>
<div class="input-label">Name</div>
<%= text_input f, :name, value: @data["name"], class: "input", placeholder: name_placeholder() %>
</div>
<div>
<div class="input-label">Cookie</div>
<%= text_input f, :cookie, value: @data["cookie"], class: "input", placeholder: "mycookie" %>
</div>
</div>
<button class="mt-5 button-base button-blue"
type="submit"
disabled={not data_valid?(@data)}>
<%= if(matching_runtime?(@current_runtime, @data), do: "Reconnect", else: "Connect") %>
</button>
</.form>
</div>
"""
end
defp matching_runtime?(%Runtime.Attached{} = runtime, data) do
initial_data(runtime) == data
end
defp matching_runtime?(_runtime, _data), do: false
@impl true
def handle_event("validate", %{"data" => data}, socket) do
{:noreply, assign(socket, data: data)}
end
def handle_event("init", %{"data" => data}, socket) do
node = Utils.node_from_name(data["name"])
cookie = String.to_atom(data["cookie"])
case Runtime.Attached.init(node, cookie) do
{:ok, runtime} ->
Session.connect_runtime(socket.assigns.session.pid, runtime)
{:noreply, assign(socket, data: initial_data(runtime), error_message: nil)}
{:error, error} ->
message = runtime_error_to_message(error)
{:noreply, assign(socket, data: data, error_message: message)}
end
end
@impl true
def handle_info({:operation, {:set_runtime, _pid, runtime}}, socket) do
{:noreply, assign(socket, current_runtime: runtime)}
end
def handle_info(_message, socket), do: {:noreply, socket}
defp initial_data(%Runtime.Attached{node: node, cookie: cookie}) do
%{
"name" => Atom.to_string(node),
"cookie" => Atom.to_string(cookie)
}
end
defp initial_data(_runtime), do: %{"name" => "", "cookie" => ""}
defp data_valid?(data) do
data["name"] != "" and data["cookie"] != ""
end
defp name_placeholder do
if longname = Livebook.Config.longname(), do: "test@#{longname}", else: "test"
end
defp runtime_error_to_message(:unreachable), do: "Node unreachable"
end