defmodule NextLSPrivate.Tracer do @moduledoc false def trace(:start, _env) do :ok end def trace({:alias_reference, meta, module}, env) do parent = parent_pid() alias_map = Map.new(env.aliases, fn {alias, mod} -> {mod, alias} end) Process.send( parent, {{:tracer, :reference}, %{ meta: meta, identifier: Map.get(alias_map, module, module), file: env.file, type: :alias, module: module }}, [] ) :ok end def trace({type, meta, module, func, arity}, env) when type in [:remote_function, :remote_macro, :imported_macro] and module not in [:elixir_def, :elixir_utils, Kernel, Enum] do parent = parent_pid() if type == :remote_macro && meta[:closing][:line] != meta[:line] do # this is the case that a macro is getting expanded from inside # another macro expansion :noop else Process.send( parent, {{:tracer, :reference}, %{ meta: meta, identifier: func, arity: arity, file: env.file, type: :function, module: module }}, [] ) end :ok end def trace({type, meta, func, arity}, env) when type in [:local_function, :local_macro] do parent = parent_pid() Process.send( parent, {{:tracer, :reference}, %{ meta: meta, identifier: func, arity: arity, file: env.file, type: :function, module: env.module }}, [] ) :ok end def trace({:on_module, bytecode, _}, env) do parent = parent_pid() defs = Module.definitions_in(env.module) defs = for {name, arity} = _def <- defs do {name, Module.get_definition(env.module, {name, arity})} end {:ok, {_, [{~c"Dbgi", bin}]}} = :beam_lib.chunks(bytecode, [~c"Dbgi"]) {:debug_info_v1, _, {_, %{line: line, struct: struct}, _}} = :erlang.binary_to_term(bin) Process.send( parent, {:tracer, %{ file: env.file, module: env.module, module_line: line, struct: struct, defs: defs }}, [] ) :ok end def trace(_event, _env) do :ok end defp parent_pid do "NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term() end end defmodule :_next_ls_private_compiler do @moduledoc false def compile do # keep stdout on this node Process.group_leader(self(), Process.whereis(:user)) Mix.Task.clear() # load the paths for deps and compile them # will noop if they are already compiled # The mix cli basically runs this before any mix task # we have to rerun because we already ran a mix task # (mix run), which called this, but we also passed # --no-compile, so nothing was compiled, but the # task was not re-enabled it seems Mix.Task.rerun("deps.loadpaths") Mix.Task.rerun("compile", ["--no-protocol-consolidation", "--return-errors", "--tracer", "NextLSPrivate.Tracer"]) rescue e -> {:error, e} end end