Smart cell editor - intellisense node (#2232)

Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
Cristine Guadelupe 2023-09-28 16:02:04 +07:00 committed by GitHub
parent 990949e3ad
commit 4a14118b96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 90 additions and 27 deletions

View file

@ -381,12 +381,18 @@ const JSView = {
} else if (message.type === "syncReply") { } else if (message.type === "syncReply") {
this.pongCallbackQueue.push(this.syncCallbackQueue.shift()); this.pongCallbackQueue.push(this.syncCallbackQueue.shift());
this.channel.push("ping", { ref: this.props.ref }); this.channel.push("ping", { ref: this.props.ref });
} else if (message.type == "selectSecret") { } else if (message.type === "selectSecret") {
this.pushEvent("select_secret", { this.pushEvent("select_secret", {
js_view_ref: this.props.ref, js_view_ref: this.props.ref,
preselect_name: message.preselectName, preselect_name: message.preselectName,
options: message.options, options: message.options,
}); });
} else if (message.type === "setSmartCellEditorIntellisenseNode") {
this.pushEvent("set_smart_cell_editor_intellisense_node", {
js_view_ref: this.props.ref,
node: message.node,
cookie: message.cookie,
});
} }
} }
}, },

View file

@ -10,7 +10,17 @@ defmodule Livebook.Notebook.Cell.Smart do
# The available smart cells come from the runtime, therefore they # The available smart cells come from the runtime, therefore they
# are one Livebook's extension points. # are one Livebook's extension points.
defstruct [:id, :source, :chunks, :outputs, :kind, :attrs, :js_view, :editor] defstruct [
:id,
:source,
:chunks,
:outputs,
:kind,
:attrs,
:js_view,
:editor,
:editor_intellisense_node
]
alias Livebook.Utils alias Livebook.Utils
alias Livebook.Notebook.Cell alias Livebook.Notebook.Cell
@ -23,7 +33,8 @@ defmodule Livebook.Notebook.Cell.Smart do
kind: String.t() | nil, kind: String.t() | nil,
attrs: attrs() | :__pruned__, attrs: attrs() | :__pruned__,
js_view: Livebook.Runtime.js_view() | nil, js_view: Livebook.Runtime.js_view() | nil,
editor: Livebook.Runtime.editor() | nil editor: Livebook.Runtime.editor() | nil,
editor_intellisense_node: {String.t(), String.t()} | nil
} }
@type attrs :: map() @type attrs :: map()
@ -41,7 +52,8 @@ defmodule Livebook.Notebook.Cell.Smart do
kind: nil, kind: nil,
attrs: %{}, attrs: %{},
js_view: nil, js_view: nil,
editor: nil editor: nil,
editor_intellisense_node: nil
} }
end end
end end

View file

@ -871,8 +871,14 @@ defprotocol Livebook.Runtime do
The given `parent_locators` identifies a sequence of evaluations The given `parent_locators` identifies a sequence of evaluations
that may be used as the context when resolving the request (if relevant). that may be used as the context when resolving the request (if relevant).
""" """
@spec handle_intellisense(t(), pid(), intellisense_request(), parent_locators()) :: reference() @spec handle_intellisense(
def handle_intellisense(runtime, send_to, request, parent_locators) t(),
pid(),
intellisense_request(),
parent_locators(),
{String.t(), String.t()} | nil
) :: reference()
def handle_intellisense(runtime, send_to, request, parent_locators, node)
@doc """ @doc """
Reads file at the given absolute path within the runtime file system. Reads file at the given absolute path within the runtime file system.

View file

@ -131,8 +131,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Attached do
RuntimeServer.drop_container(runtime.server_pid, container_ref) RuntimeServer.drop_container(runtime.server_pid, container_ref)
end end
def handle_intellisense(runtime, send_to, request, parent_locators) do def handle_intellisense(runtime, send_to, request, parent_locators, node) do
RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, parent_locators) RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, parent_locators, node)
end end
def read_file(runtime, path) do def read_file(runtime, path) do

View file

@ -129,8 +129,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.ElixirStandalone do
RuntimeServer.drop_container(runtime.server_pid, container_ref) RuntimeServer.drop_container(runtime.server_pid, container_ref)
end end
def handle_intellisense(runtime, send_to, request, parent_locators) do def handle_intellisense(runtime, send_to, request, parent_locators, node) do
RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, parent_locators) RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, parent_locators, node)
end end
def read_file(runtime, path) do def read_file(runtime, path) do

View file

@ -93,8 +93,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Embedded do
RuntimeServer.drop_container(runtime.server_pid, container_ref) RuntimeServer.drop_container(runtime.server_pid, container_ref)
end end
def handle_intellisense(runtime, send_to, request, parent_locators) do def handle_intellisense(runtime, send_to, request, parent_locators, node) do
RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, parent_locators) RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, parent_locators, node)
end end
def read_file(runtime, path) do def read_file(runtime, path) do

View file

@ -129,11 +129,12 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
pid(), pid(),
pid(), pid(),
Runtime.intellisense_request(), Runtime.intellisense_request(),
Runtime.Runtime.parent_locators() Runtime.Runtime.parent_locators(),
{String.t(), String.t()} | nil
) :: reference() ) :: reference()
def handle_intellisense(pid, send_to, request, parent_locators) do def handle_intellisense(pid, send_to, request, parent_locators, node) do
ref = make_ref() ref = make_ref()
GenServer.cast(pid, {:handle_intellisense, send_to, ref, request, parent_locators}) GenServer.cast(pid, {:handle_intellisense, send_to, ref, request, parent_locators, node})
ref ref
end end
@ -497,7 +498,10 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
{:noreply, state} {:noreply, state}
end end
def handle_cast({:handle_intellisense, send_to, ref, request, parent_locators}, state) do def handle_cast(
{:handle_intellisense, send_to, ref, request, parent_locators, node},
state
) do
{container_ref, parent_evaluation_refs} = {container_ref, parent_evaluation_refs} =
case parent_locators do case parent_locators do
[] -> [] ->
@ -525,7 +529,8 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
end end
Task.Supervisor.start_child(state.task_supervisor, fn -> Task.Supervisor.start_child(state.task_supervisor, fn ->
response = Livebook.Intellisense.handle_request(request, intellisense_context, node()) node = intellisense_node(node)
response = Livebook.Intellisense.handle_request(request, intellisense_context, node)
send(send_to, {:runtime_intellisense_response, ref, request, response}) send(send_to, {:runtime_intellisense_response, ref, request, response})
end) end)
@ -907,4 +912,12 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
name = elem(dependency.dep, 0) name = elem(dependency.dep, 0)
Application.spec(name) != nil Application.spec(name) != nil
end end
defp intellisense_node({node, cookie}) do
{node, cookie} = {String.to_atom(node), String.to_atom(cookie)}
Node.set_cookie(node, cookie)
if Node.connect(node), do: node, else: node()
end
defp intellisense_node(_node), do: node()
end end

View file

@ -1475,7 +1475,9 @@ defmodule LivebookWeb.SessionLive do
with {:ok, cell, _section} <- Notebook.fetch_cell_and_section(data.notebook, cell_id) do with {:ok, cell, _section} <- Notebook.fetch_cell_and_section(data.notebook, cell_id) do
if Runtime.connected?(data.runtime) do if Runtime.connected?(data.runtime) do
parent_locators = Session.parent_locators_for_cell(data, cell) parent_locators = Session.parent_locators_for_cell(data, cell)
ref = Runtime.handle_intellisense(data.runtime, self(), request, parent_locators) node = intellisense_node(cell)
ref = Runtime.handle_intellisense(data.runtime, self(), request, parent_locators, node)
{:reply, %{"ref" => inspect(ref)}, socket} {:reply, %{"ref" => inspect(ref)}, socket}
else else
@ -1701,6 +1703,23 @@ defmodule LivebookWeb.SessionLive do
push_patch(socket, to: ~p"/sessions/#{socket.assigns.session.id}/settings/custom-view")} push_patch(socket, to: ~p"/sessions/#{socket.assigns.session.id}/settings/custom-view")}
end end
def handle_event(
"set_smart_cell_editor_intellisense_node",
%{"js_view_ref" => cell_id, "node" => node, "cookie" => cookie},
socket
) do
node =
if is_binary(node) and node =~ "@" and is_binary(cookie) and cookie != "" do
{node, cookie}
end
Session.set_cell_attributes(socket.assigns.session.pid, cell_id, %{
editor_intellisense_node: node
})
{:noreply, socket}
end
@impl true @impl true
def handle_info({:operation, operation}, socket) do def handle_info({:operation, operation}, socket) do
{:noreply, handle_operation(socket, operation)} {:noreply, handle_operation(socket, operation)}
@ -2909,4 +2928,7 @@ defmodule LivebookWeb.SessionLive do
defp app_status_color(%{execution: :executed}), do: "bg-green-bright-400" defp app_status_color(%{execution: :executed}), do: "bg-green-bright-400"
defp app_status_color(%{execution: :error}), do: "bg-red-400" defp app_status_color(%{execution: :error}), do: "bg-red-400"
defp app_status_color(%{execution: :interrupted}), do: "bg-gray-400" defp app_status_color(%{execution: :interrupted}), do: "bg-gray-400"
defp intellisense_node(%Cell.Smart{editor_intellisense_node: node_cookie}), do: node_cookie
defp intellisense_node(_), do: nil
end end

View file

@ -143,10 +143,10 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do
end end
end end
describe "handle_intellisense/5 given completion request" do describe "handle_intellisense/6 given completion request" do
test "provides basic completion when no evaluation reference is given", %{pid: pid} do test "provides basic completion when no evaluation reference is given", %{pid: pid} do
request = {:completion, "System.ver"} request = {:completion, "System.ver"}
ref = RuntimeServer.handle_intellisense(pid, self(), request, []) ref = RuntimeServer.handle_intellisense(pid, self(), request, [], nil)
assert_receive {:runtime_intellisense_response, ^ref, ^request, assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{items: [%{label: "version/0"}]}} %{items: [%{label: "version/0"}]}}
@ -162,33 +162,37 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do
assert_receive {:runtime_evaluation_response, :e1, _, %{evaluation_time_ms: _time_ms}} assert_receive {:runtime_evaluation_response, :e1, _, %{evaluation_time_ms: _time_ms}}
request = {:completion, "num"} request = {:completion, "num"}
ref = RuntimeServer.handle_intellisense(pid, self(), request, [{:c1, :e1}])
ref =
RuntimeServer.handle_intellisense(pid, self(), request, [{:c1, :e1}], nil)
assert_receive {:runtime_intellisense_response, ^ref, ^request, assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{items: [%{label: "number"}]}} %{items: [%{label: "number"}]}}
request = {:completion, "ANSI.brigh"} request = {:completion, "ANSI.brigh"}
ref = RuntimeServer.handle_intellisense(pid, self(), request, [{:c1, :e1}])
ref =
RuntimeServer.handle_intellisense(pid, self(), request, [{:c1, :e1}], nil)
assert_receive {:runtime_intellisense_response, ^ref, ^request, assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{items: [%{label: "bright/0"}]}} %{items: [%{label: "bright/0"}]}}
end end
end end
describe "handle_intellisense/5 given details request" do describe "handle_intellisense/6 given details request" do
test "responds with identifier details", %{pid: pid} do test "responds with identifier details", %{pid: pid} do
request = {:details, "System.version", 10} request = {:details, "System.version", 10}
ref = RuntimeServer.handle_intellisense(pid, self(), request, []) ref = RuntimeServer.handle_intellisense(pid, self(), request, [], nil)
assert_receive {:runtime_intellisense_response, ^ref, ^request, assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{range: %{from: 1, to: 15}, contents: [_]}} %{range: %{from: 1, to: 15}, contents: [_]}}
end end
end end
describe "handle_intellisense/5 given format request" do describe "handle_intellisense/6 given format request" do
test "responds with a formatted code", %{pid: pid} do test "responds with a formatted code", %{pid: pid} do
request = {:format, "System.version"} request = {:format, "System.version"}
ref = RuntimeServer.handle_intellisense(pid, self(), request, []) ref = RuntimeServer.handle_intellisense(pid, self(), request, [], nil)
assert_receive {:runtime_intellisense_response, ^ref, ^request, %{code: "System.version()"}} assert_receive {:runtime_intellisense_response, ^ref, ^request, %{code: "System.version()"}}
end end

View file

@ -22,7 +22,7 @@ defmodule Livebook.Runtime.NoopRuntime do
def evaluate_code(_, _, _, _, _, _ \\ []), do: :ok def evaluate_code(_, _, _, _, _, _ \\ []), do: :ok
def forget_evaluation(_, _), do: :ok def forget_evaluation(_, _), do: :ok
def drop_container(_, _), do: :ok def drop_container(_, _), do: :ok
def handle_intellisense(_, _, _, _), do: make_ref() def handle_intellisense(_, _, _, _, _), do: make_ref()
def read_file(_, path) do def read_file(_, path) do
case File.read(path) do case File.read(path) do