Skip to content

Commit

Permalink
fixup! feat(completions): imports, aliases, module attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
mhanberg committed Apr 12, 2024
1 parent 5221e21 commit 6db8fb9
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 95 deletions.
85 changes: 56 additions & 29 deletions lib/next_ls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ defmodule NextLS do
:runtime_task_supervisor,
:dynamic_supervisor,
:extensions,
:registry
:registry,
:bundle_base,
:mix_home
])

GenLSP.start_link(__MODULE__, args, opts)
Expand All @@ -76,6 +78,8 @@ defmodule NextLS do
task_supervisor = Keyword.fetch!(args, :task_supervisor)
runtime_task_supervisor = Keyword.fetch!(args, :runtime_task_supervisor)
dynamic_supervisor = Keyword.fetch!(args, :dynamic_supervisor)
bundle_base = Keyword.get(args, :bundle_base, Path.expand("~/.cache/elixir-tools/nextls"))
mixhome = Keyword.get(args, :mix_home, Path.expand("~/.mix"))

registry = Keyword.fetch!(args, :registry)

Expand All @@ -85,6 +89,8 @@ defmodule NextLS do
cache = Keyword.fetch!(args, :cache)
{:ok, logger} = DynamicSupervisor.start_child(dynamic_supervisor, {NextLS.Logger, lsp: lsp})

NextLS.Runtime.BundledElixir.install(bundle_base, logger, mix_home: mixhome)

{:ok,
assign(lsp,
auto_update: Keyword.get(args, :auto_update, false),
Expand Down Expand Up @@ -866,6 +872,18 @@ defmodule NextLS do

parent = self()

elixir_bin_path =
cond do
lsp.assigns.init_opts.elixir_bin_path != nil ->
lsp.assigns.init_opts.elixir_bin_path

lsp.assigns.init_opts.experimental.completions.enable ->
NextLS.Runtime.BundledElixir.binpath()

true ->
"elixir" |> System.find_executable() |> Path.dirname()
end

for %{uri: uri, name: name} <- lsp.assigns.workspace_folders do
token = Progress.token()
Progress.start(lsp, token, "Initializing NextLS runtime for folder #{name}...")
Expand All @@ -887,6 +905,7 @@ defmodule NextLS do
uri: uri,
mix_env: lsp.assigns.init_opts.mix_env,
mix_target: lsp.assigns.init_opts.mix_target,
elixir_bin_path: elixir_bin_path,
on_initialized: fn status ->
if status == :ready do
Progress.stop(lsp, token, "NextLS runtime for folder #{name} has initialized!")
Expand All @@ -912,7 +931,7 @@ defmodule NextLS do
)
end

{:noreply, lsp}
{:noreply, assign(lsp, elixir_bin_path: elixir_bin_path)}
end

def handle_notification(%TextDocumentDidSave{}, %{assigns: %{ready: false}} = lsp) do
Expand Down Expand Up @@ -984,7 +1003,7 @@ defmodule NextLS do
},
lsp
) do
dispatch(lsp.assigns.registry, :runtime_supervisors, fn entries ->
NextLS.Registry.dispatch(lsp.assigns.registry, :runtime_supervisors, fn entries ->
names = Enum.map(entries, fn {_, %{name: name}} -> name end)

for %{name: name, uri: uri} <- added, name not in names do
Expand All @@ -1004,6 +1023,7 @@ defmodule NextLS do
runtime: [
task_supervisor: lsp.assigns.runtime_task_supervisor,
working_dir: working_dir,
elixir_bin_path: lsp.assigns.elixir_bin_path,
uri: uri,
mix_env: lsp.assigns.init_opts.mix_env,
mix_target: lsp.assigns.init_opts.mix_target,
Expand Down Expand Up @@ -1047,47 +1067,51 @@ defmodule NextLS do
lsp =
for %{type: type, uri: uri} <- changes, reduce: lsp do
lsp ->
file = URI.parse(uri).path

cond do
type == GenLSP.Enumerations.FileChangeType.created() ->
with {:ok, text} <- File.read(URI.parse(uri).path) do
with {:ok, text} <- File.read(file) do
put_in(lsp.assigns.documents[uri], String.split(text, "\n"))
else
_ -> lsp
end

type == GenLSP.Enumerations.FileChangeType.changed() ->
with {:ok, text} <- File.read(URI.parse(uri).path) do
with {:ok, text} <- File.read(file) do
put_in(lsp.assigns.documents[uri], String.split(text, "\n"))
else
_ -> lsp
end

type == GenLSP.Enumerations.FileChangeType.deleted() ->
dispatch(lsp.assigns.registry, :databases, fn entries ->
for {pid, _} <- entries do
file = URI.parse(uri).path

NextLS.DB.query(
pid,
~Q"""
DELETE FROM symbols
WHERE symbols.file = ?;
""",
[file]
)

NextLS.DB.query(
pid,
~Q"""
DELETE FROM 'references' AS refs
WHERE refs.file = ?;
""",
[file]
)
end
end)
if not File.exists?(file) do
dispatch(lsp.assigns.registry, :databases, fn entries ->
for {pid, _} <- entries do
NextLS.DB.query(
pid,
~Q"""
DELETE FROM symbols
WHERE symbols.file = ?;
""",
[file]
)

NextLS.DB.query(
pid,
~Q"""
DELETE FROM 'references' AS refs
WHERE refs.file = ?;
""",
[file]
)
end
end)

update_in(lsp.assigns.documents, &Map.drop(&1, [uri]))
update_in(lsp.assigns.documents, &Map.drop(&1, [uri]))
else
lsp
end
end
end

Expand Down Expand Up @@ -1476,6 +1500,7 @@ defmodule NextLS do

defstruct mix_target: "host",
mix_env: "dev",
elixir_bin_path: nil,
experimental: %NextLS.InitOpts.Experimental{},
extensions: %NextLS.InitOpts.Extensions{}

Expand All @@ -1485,6 +1510,8 @@ defmodule NextLS do
schema(__MODULE__, %{
optional(:mix_target) => str(),
optional(:mix_env) => str(),
optional(:mix_env) => str(),
optional(:elixir_bin_path) => str(),
optional(:experimental) =>
schema(NextLS.InitOpts.Experimental, %{
optional(:completions) =>
Expand Down
70 changes: 10 additions & 60 deletions lib/next_ls/runtime.ex
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ defmodule NextLS.Runtime do
db = Keyword.fetch!(opts, :db)
mix_env = Keyword.fetch!(opts, :mix_env)
mix_target = Keyword.fetch!(opts, :mix_target)
elixir_exe = Keyword.get(opts, :elixir_exe, System.find_executable("elixir"))
expand = Keyword.get(opts, :expand, false)
elixir_bin_path = Keyword.get(opts, :elixir_bin_path)

elixir_exe = Path.join(elixir_bin_path, "elixir")

Registry.register(registry, :runtimes, %{name: name, uri: uri, path: working_dir, db: db})
Registry.put_meta(registry, {:expand, self()}, expand)

pid =
cond do
Expand All @@ -132,8 +132,7 @@ defmodule NextLS.Runtime do
path = System.get_env("PATH")
new_path = String.replace(path, bindir <> ":", "")

with dir when is_list(dir) <- :code.priv_dir(:next_ls),
elixir_exe when is_binary(elixir_exe) <- elixir_exe do
with dir when is_list(dir) <- :code.priv_dir(:next_ls) do
exe =
dir
|> Path.join("cmd")
Expand Down Expand Up @@ -236,14 +235,9 @@ defmodule NextLS.Runtime do
:ok
end)

version = :rpc.call(node, System, :version, [])

expander = Version.match?(version, ">= 1.17.0-dev")
NextLS.Logger.info(logger, "version=#{version} expander=#{expander}")

{:ok, _} = :rpc.call(node, :_next_ls_private_compiler, :start, [])

send(me, {:node, node, expander})
send(me, {:node, node})
else
error ->
send(me, {:cancel, error})
Expand All @@ -266,10 +260,7 @@ defmodule NextLS.Runtime do
}}
else
_ ->
NextLS.Logger.error(
logger,
"Either failed to find the private cmd wrapper script or an `elixir`exe on your PATH"
)
NextLS.Logger.error(logger, "Either failed to find the private cmd wrapper script")

{:stop, :failed_to_boot}
end
Expand Down Expand Up @@ -301,28 +292,13 @@ defmodule NextLS.Runtime do
end
end

# if this node is 1.17 or higher, we can just send the expansion request to it
def handle_call({:expand, ast, file}, _from, %{node: node, expander: true} = state) do
def handle_call({:expand, ast, file}, _from, %{node: node} = state) do
NextLS.Logger.info(state.logger, "expanding on the runtime node")
reply = :rpc.call(node, :_next_ls_private_spitfire_env, :expand, [ast, file])
{:reply, {:ok, reply}, state}
end

# else, we need to send it to the bonus runtime
def handle_call({:expand, ast, file}, _from, %{expander: false, name: name} = state) do
NextLS.Logger.info(state.logger, "expanding on the bonus node")

[reply] =
dispatch(state.registry, :bonus_runtimes, fn entries ->
for {pid, %{name: ^name}} <- entries do
NextLS.Runtime.Bonus.call(pid, {:_next_ls_private_spitfire_env, :expand, [ast, file]})
end
end)

{:reply, reply, state}
end

def handle_call({:compile, opts}, _from, %{node: node, name: name} = state) do
def handle_call({:compile, opts}, _from, %{node: node} = state) do
opts =
opts
|> Keyword.put_new(:working_dir, state.working_dir)
Expand All @@ -333,17 +309,6 @@ defmodule NextLS.Runtime do
NextLS.Logger.error(state.logger, "Bad RPC call to node #{node}: #{inspect(error)}")
end

with false <- state.expander,
[{:badrpc, error}] <-
dispatch(state.registry, :bonus_runtimes, fn entries ->
for {pid, %{name: ^name}} <- entries do
:ok = NextLS.Runtime.Bonus.await(pid)
NextLS.Runtime.Bonus.compile(pid, opts)
end
end) do
NextLS.Logger.error(state.logger, "Bad RPC call to node #{node}: #{inspect(error)}")
end

{:reply, :ok, state}
end

Expand Down Expand Up @@ -374,10 +339,10 @@ defmodule NextLS.Runtime do
{:noreply, Map.delete(state, :node)}
end

def handle_info({:node, node, expander}, state) do
def handle_info({:node, node}, state) do
Node.monitor(node, true)
state.on_initialized.(:ready)
{:noreply, state |> Map.put(:node, node) |> Map.put(:expander, expander)}
{:noreply, Map.put(state, :node, node)}
end

def handle_info({:nodedown, node}, %{node: node} = state) do
Expand Down Expand Up @@ -425,19 +390,4 @@ defmodule NextLS.Runtime do
true
end
end

defp dispatch(registry, key, callback) do
ref = make_ref()
me = self()

Registry.dispatch(registry, key, fn entries ->
result = callback.(entries)

send(me, {ref, result})
end)

receive do
{^ref, result} -> result
end
end
end
2 changes: 0 additions & 2 deletions lib/next_ls/runtime/bonus.ex
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,6 @@ defmodule NextLS.Runtime.Bonus do
for bin <- Path.wildcard(Path.join(base, "bin/*")) do
File.chmod(bin, 0o755)
end

# NextLS.Logger.info(logger, "return value of unzip #{inspect(ret)}")
end

bindir = Path.expand("~/.cache/elixir-tools/nextls/elixir/1.17/bin/")
Expand Down
61 changes: 61 additions & 0 deletions lib/next_ls/runtime/bundled_elixir.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
defmodule NextLS.Runtime.BundledElixir do
@moduledoc """
Module to install the bundled Elixir.
The `@version` attribute corresponds to the last digit in the file name of the zip archive, they need to be incremented in lockstep.
"""
@version 1
@base "~/.cache/elixir-tools/nextls"
@dir "elixir/1-17-#{@version}"

def binpath(base \\ @base) do
Path.join([base, @dir, "bin"])
end

def mixpath(base \\ @base) do
Path.join([binpath(base), "mix"])
end

def path(base) do
Path.join([base, @dir])
end

def install(base, logger, opts \\ []) do
mixhome = Keyword.get(opts, :mix_home, Path.expand("~/.mix"))
binpath = binpath(base)

unless File.exists?(binpath) do
extract_path = path(base)
File.mkdir_p!(base)

priv_dir = :code.priv_dir(:next_ls)
bundled_elixir_path = ~c"#{Path.join(priv_dir, "precompiled-1-17-#{@version}.zip")}"

:zip.unzip(bundled_elixir_path, cwd: ~c"#{extract_path}")

for bin <- Path.wildcard(Path.join(binpath, "*")) do
File.chmod(bin, 0o755)
end

new_path = "#{binpath}:#{System.get_env("PATH")}"
mixbin = mixpath(base)

{_, 0} =
System.cmd(mixbin, ["local.rebar", "--force"], env: [{"PATH", new_path}, {"MIX_HOME", mixhome}])

{_, 0} =
System.cmd(mixbin, ["local.hex", "--force"], env: [{"PATH", new_path}, {"MIX_HOME", mixhome}])
end

:ok
rescue
e ->
NextLS.Logger.warning(logger, """
Failed to unzip and install the bundled Elixir archive.
#{Exception.format(:error, e, __STACKTRACE__)}
""")

:error
end
end
3 changes: 1 addition & 2 deletions lib/next_ls/runtime/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ defmodule NextLS.Runtime.Supervisor do
runtime: name,
activity: db_activity},
{NextLS.Runtime,
init_arg[:runtime] ++ [name: name, registry: registry, parent: sidecar_name, lsp_pid: lsp_pid, db: db_name]},
{NextLS.Runtime.Bonus, init_arg[:runtime] ++ [name: name, registry: registry, lsp_pid: lsp_pid]}
init_arg[:runtime] ++ [name: name, registry: registry, parent: sidecar_name, lsp_pid: lsp_pid, db: db_name]}
]

Supervisor.init(children, strategy: :one_for_one)
Expand Down
File renamed without changes.
Loading

0 comments on commit 6db8fb9

Please sign in to comment.