diff --git a/bin/start b/bin/start index 7f4393a2..38382807 100755 --- a/bin/start +++ b/bin/start @@ -4,4 +4,4 @@ cd "$(dirname "$0")"/.. || exit 1 -elixir --sname "next-ls-$RANDOM" -S mix run --no-halt -e "Application.ensure_all_started(:next_ls)" -- "$@" +iex --sname "next-ls-$RANDOM" -S mix run --no-halt -e "Application.ensure_all_started(:next_ls)" -- "$@" diff --git a/lib/next_ls.ex b/lib/next_ls.ex index 451f79e2..ffc0f20c 100644 --- a/lib/next_ls.ex +++ b/lib/next_ls.ex @@ -2,6 +2,8 @@ defmodule NextLS do @moduledoc false use GenLSP + import NextLS.DB.Query + alias GenLSP.Enumerations.ErrorCodes alias GenLSP.Enumerations.TextDocumentSyncKind alias GenLSP.ErrorResponse @@ -33,11 +35,11 @@ defmodule NextLS do alias GenLSP.Structures.TextDocumentSyncOptions alias GenLSP.Structures.TextEdit alias GenLSP.Structures.WorkspaceFoldersChangeEvent + alias NextLS.DB alias NextLS.Definition alias NextLS.DiagnosticCache alias NextLS.Progress alias NextLS.Runtime - alias NextLS.SymbolTable def start_link(args) do {args, opts} = @@ -120,21 +122,16 @@ defmodule NextLS do def handle_request(%TextDocumentDefinition{params: %{text_document: %{uri: uri}, position: position}}, lsp) do result = - dispatch(lsp.assigns.registry, :symbol_tables, fn entries -> - for {_, %{symbol_table: symbol_table, reference_table: ref_table}} <- entries do - case Definition.fetch( - URI.parse(uri).path, - {position.line + 1, position.character + 1}, - symbol_table, - ref_table - ) do + dispatch(lsp.assigns.registry, :databases, fn entries -> + for {pid, _} <- entries do + case Definition.fetch(URI.parse(uri).path, {position.line + 1, position.character + 1}, pid) do nil -> nil [] -> nil - [{file, line, column} | _] -> + [[_pk, _mod, file, _type, _name, line, column] | _] -> %Location{ uri: "file://#{file}", range: %Range{ @@ -184,10 +181,10 @@ defmodule NextLS do end symbols = - dispatch(lsp.assigns.registry, :symbol_tables, fn entries -> - for {pid, _} <- entries, %SymbolTable.Symbol{} = symbol <- SymbolTable.symbols(pid), filter.(symbol.name) do + dispatch(lsp.assigns.registry, :databases, fn entries -> + for {pid, _} <- entries, symbol <- DB.symbols(pid), filter.(symbol.name) do name = - if symbol.type != :defstruct do + if symbol.type != "defstruct" do "#{symbol.type} #{symbol.name}" else "#{symbol.name}" @@ -201,11 +198,11 @@ defmodule NextLS do range: %Range{ start: %Position{ line: symbol.line - 1, - character: symbol.col - 1 + character: symbol.column - 1 }, end: %Position{ line: symbol.line - 1, - character: symbol.col - 1 + character: symbol.column - 1 } } } @@ -260,10 +257,6 @@ defmodule NextLS do end def handle_request(%Shutdown{}, lsp) do - dispatch(lsp.assigns.registry, :symbol_tables, fn entries -> - for {pid, _} <- entries, do: SymbolTable.close(pid) - end) - {:reply, nil, assign(lsp, exit_code: 0)} end @@ -323,6 +316,7 @@ defmodule NextLS do path: Path.join(working_dir, ".elixir-tools"), name: name, registry: lsp.assigns.registry, + logger: lsp.assigns.logger, runtime: [ task_supervisor: lsp.assigns.runtime_task_supervisor, working_dir: working_dir, @@ -463,16 +457,30 @@ defmodule NextLS do def handle_notification(%WorkspaceDidChangeWatchedFiles{params: %DidChangeWatchedFilesParams{changes: changes}}, lsp) do type = GenLSP.Enumerations.FileChangeType.deleted() - # TODO - # ✅ delete from documents - # ✅ delete all references that occur in this file - # ✅ delete all symbols from that file lsp = for %{type: ^type, uri: uri} <- changes, reduce: lsp do lsp -> - dispatch(lsp.assigns.registry, :symbol_tables, fn entries -> + dispatch(lsp.assigns.registry, :databases, fn entries -> for {pid, _} <- entries do - SymbolTable.remove(pid, uri) + 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) @@ -563,10 +571,10 @@ defmodule NextLS do end end - defp elixir_kind_to_lsp_kind(:defmodule), do: GenLSP.Enumerations.SymbolKind.module() - defp elixir_kind_to_lsp_kind(:defstruct), do: GenLSP.Enumerations.SymbolKind.struct() + defp elixir_kind_to_lsp_kind("defmodule"), do: GenLSP.Enumerations.SymbolKind.module() + defp elixir_kind_to_lsp_kind("defstruct"), do: GenLSP.Enumerations.SymbolKind.struct() - defp elixir_kind_to_lsp_kind(kind) when kind in [:def, :defp, :defmacro, :defmacrop], + defp elixir_kind_to_lsp_kind(kind) when kind in ["def", "defp", "defmacro", "defmacrop"], do: GenLSP.Enumerations.SymbolKind.function() # NOTE: this is only possible because the registry is not partitioned diff --git a/lib/next_ls/db.ex b/lib/next_ls/db.ex new file mode 100644 index 00000000..564598c0 --- /dev/null +++ b/lib/next_ls/db.ex @@ -0,0 +1,193 @@ +defmodule NextLS.DB do + @moduledoc false + use GenServer + + import __MODULE__.Query + + @type query :: String.t() + + def start_link(args) do + GenServer.start_link(__MODULE__, args, Keyword.take(args, [:name])) + end + + @spec query(pid(), query(), list()) :: list() + def query(server, query, args \\ []), do: GenServer.call(server, {:query, query, args}) + + @spec symbols(pid()) :: list(map()) + def symbols(server), do: GenServer.call(server, :symbols) + + @spec insert_symbol(pid(), map()) :: :ok + def insert_symbol(server, payload), do: GenServer.cast(server, {:insert_symbol, payload}) + + @spec insert_reference(pid(), map()) :: :ok + def insert_reference(server, payload), do: GenServer.cast(server, {:insert_reference, payload}) + + def init(args) do + file = Keyword.fetch!(args, :file) + registry = Keyword.fetch!(args, :registry) + logger = Keyword.fetch!(args, :logger) + Registry.register(registry, :databases, %{}) + {:ok, conn} = :esqlite3.open(file) + + NextLS.DB.Schema.init({conn, logger}) + + {:ok, + %{ + conn: conn, + file: file, + logger: logger + }} + end + + def handle_call({:query, query, args}, _from, %{conn: conn} = s) do + rows = __query__({conn, s.logger}, query, args) + + {:reply, rows, s} + end + + def handle_call(:symbols, _from, %{conn: conn} = s) do + rows = + __query__( + {conn, s.logger}, + ~Q""" + SELECT + * + FROM + symbols; + """, + [] + ) + + symbols = + for [_pk, module, file, type, name, line, column] <- rows do + %{ + module: module, + file: file, + type: type, + name: name, + line: line, + column: column + } + end + + {:reply, symbols, s} + end + + def handle_cast({:insert_symbol, symbol}, %{conn: conn} = s) do + %{ + module: mod, + module_line: module_line, + struct: struct, + file: file, + defs: defs + } = symbol + + __query__( + {conn, s.logger}, + ~Q""" + DELETE FROM symbols + WHERE module = ?; + """, + [mod] + ) + + __query__( + {conn, s.logger}, + ~Q""" + INSERT INTO symbols (module, file, type, name, line, 'column') + VALUES (?, ?, ?, ?, ?, ?); + """, + [mod, file, "defmodule", mod, module_line, 1] + ) + + if struct do + {_, _, meta, _} = defs[:__struct__] + + __query__( + {conn, s.logger}, + ~Q""" + INSERT INTO symbols (module, file, type, name, line, 'column') + VALUES (?, ?, ?, ?, ?, ?); + """, + [mod, file, "defstruct", "%#{Macro.to_string(mod)}{}", meta[:line], 1] + ) + end + + for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do + __query__( + {conn, s.logger}, + ~Q""" + INSERT INTO symbols (module, file, type, name, line, 'column') + VALUES (?, ?, ?, ?, ?, ?); + """, + [mod, file, type, name, meta[:line], meta[:column] || 1] + ) + end + + {:noreply, s} + end + + def handle_cast({:insert_reference, reference}, %{conn: conn} = s) do + %{ + meta: meta, + identifier: identifier, + file: file, + type: type, + module: module + } = reference + + col = meta[:column] || 0 + + {{start_line, start_column}, {end_line, end_column}} = + {{meta[:line], col}, + {meta[:line], col + String.length(identifier |> to_string() |> String.replace("Elixir.", ""))}} + + __query__( + {conn, s.logger}, + ~Q""" + DELETE FROM 'references' AS refs + WHERE refs.file = ? + AND (? <= refs.start_line + AND refs.start_line <= ? + AND ? <= refs.start_column + AND refs.start_column <= ?) + OR (? <= refs.end_line + AND refs.end_line <= ? + AND ? <= refs.end_column + AND refs.end_column <= ?); + """, + [file, start_line, end_line, start_column, end_column, start_line, end_line, start_column, end_column] + ) + + __query__( + {conn, s.logger}, + ~Q""" + INSERT INTO 'references' (identifier, arity, file, type, module, start_line, start_column, end_line, end_column) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); + """, + [identifier, reference[:arity], file, type, module, start_line, start_column, end_line, end_column] + ) + + {:noreply, s} + end + + def __query__({conn, logger}, query, args) do + args = Enum.map(args, &cast/1) + + with {:error, _e} <- :esqlite3.q(conn, query, cast(args)) do + error = :esqlite3.error_info(conn).errmsg + NextLS.Logger.error(logger, error) + {:error, error} + end + end + + defp cast(arg) do + cond do + is_atom(arg) and String.starts_with?(to_string(arg), "Elixir.") -> + Macro.to_string(arg) + + true -> + arg + end + end +end diff --git a/lib/next_ls/db/format.ex b/lib/next_ls/db/format.ex new file mode 100644 index 00000000..aa1ff94d --- /dev/null +++ b/lib/next_ls/db/format.ex @@ -0,0 +1,18 @@ +defmodule NextLS.DB.Format do + @moduledoc false + # @behaviour Mix.Tasks.Format + + # @impl Mix.Tasks.Format + # def features(_opts), do: [sigils: [:Q], extensions: []] + + # @impl Mix.Tasks.Format + # def format(input, _formatter_opts, _opts \\ []) do + # path = Path.join(System.tmp_dir!(), "#{System.unique_integer()}-temp.sql") + # File.write!(path, input) + # {result, 0} = System.cmd("pg_format", [path]) + + # File.rm!(path) + + # String.trim(result) <> "\n" + # end +end diff --git a/lib/next_ls/db/query.ex b/lib/next_ls/db/query.ex new file mode 100644 index 00000000..b8aa0aee --- /dev/null +++ b/lib/next_ls/db/query.ex @@ -0,0 +1,6 @@ +defmodule NextLS.DB.Query do + @moduledoc false + defmacro sigil_Q({:<<>>, _, [bin]}, _mods) do + bin + end +end diff --git a/lib/next_ls/db/schema.ex b/lib/next_ls/db/schema.ex new file mode 100644 index 00000000..63ac0530 --- /dev/null +++ b/lib/next_ls/db/schema.ex @@ -0,0 +1,42 @@ +defmodule NextLS.DB.Schema do + @moduledoc false + import NextLS.DB.Query + + alias NextLS.DB + + def init(conn) do + DB.__query__( + conn, + ~Q""" + CREATE TABLE IF NOT EXISTS symbols ( + id integer PRIMARY KEY, + module text NOT NULL, + file text NOT NULL, + type text NOT NULL, + name text NOT NULL, + line integer NOT NULL, + column integer NOT NULL + ); + """, + [] + ) + + DB.__query__( + conn, + ~Q""" + CREATE TABLE IF NOT EXISTS 'references' ( + id integer PRIMARY KEY, + identifier text NOT NULL, + arity integer, + file text NOT NULL, + type text NOT NULL, + module text NOT NULL, + start_line integer NOT NULL, + start_column integer NOT NULL, + end_line integer NOT NULL, + end_column integer NOT NULL) + """, + [] + ) + end +end diff --git a/lib/next_ls/definition.ex b/lib/next_ls/definition.ex index ea663336..bdd94c12 100644 --- a/lib/next_ls/definition.ex +++ b/lib/next_ls/definition.ex @@ -1,48 +1,53 @@ defmodule NextLS.Definition do @moduledoc false - def fetch(file, {line, col}, dets_symbol_table, dets_ref_table) do - ref = - :dets.select( - dets_ref_table, - [ - {{{:"$1", {{:"$2", :"$3"}, {:"$4", :"$5"}}}, :"$6"}, - [ - {:andalso, - {:andalso, {:andalso, {:andalso, {:==, :"$1", file}, {:"=<", :"$2", line}}, {:"=<", :"$3", col}}, - {:"=<", line, :"$4"}}, {:"=<", col, :"$5"}} - ], [:"$6"]} - ] - ) + import NextLS.DB.Query - # :dets.traverse(dets_symbol_table, fn x -> {:continue, x} end) |> dbg - # :dets.traverse(dets_ref_table, fn x -> {:continue, x} end) |> dbg + alias NextLS.DB - # dbg(ref) + def fetch(file, {line, col}, db) do + [[_pk, identifier, _arity, _file, type, module, _start_l, _start_c, _end_l, _end_c]] = + DB.query( + db, + ~Q""" + SELECT + * + FROM + 'references' AS refs + WHERE + refs.file = ? + AND refs.start_line <= ? + AND ? <= refs.end_line + AND refs.start_column <= ? + AND ? <= refs.end_column; + """, + [file, line, line, col, col] + ) query = - case ref do - [%{type: :alias} = ref] -> - [ - {{:_, %{line: :"$3", name: :"$2", file: :"$5", module: :"$1", col: :"$4"}}, - [ - {:andalso, {:==, :"$1", ref.module}, {:==, :"$2", Macro.to_string(ref.module)}} - ], [{{:"$5", :"$3", :"$4"}}]} - ] + ~Q""" + SELECT + * + FROM + symbols + WHERE + symbols.module = ? + AND symbols.name = ?; + """ + + args = + case type do + "alias" -> + [module, module] - [%{type: :function} = ref] -> - [ - {{:_, %{line: :"$3", name: :"$2", file: :"$5", module: :"$1", col: :"$4"}}, - [ - {:andalso, {:==, :"$1", ref.module}, {:==, :"$2", ref.identifier}} - ], [{{:"$5", :"$3", :"$4"}}]} - ] + "function" -> + [module, identifier] _ -> nil end - if query do - :dets.select(dets_symbol_table, query) + if args do + DB.query(db, query, args) else nil end diff --git a/lib/next_ls/runtime/sidecar.ex b/lib/next_ls/runtime/sidecar.ex index 6ab4720b..18ace989 100644 --- a/lib/next_ls/runtime/sidecar.ex +++ b/lib/next_ls/runtime/sidecar.ex @@ -2,24 +2,28 @@ defmodule NextLS.Runtime.Sidecar do @moduledoc false use GenServer - alias NextLS.SymbolTable + alias NextLS.DB def start_link(args) do - GenServer.start_link(__MODULE__, Keyword.take(args, [:symbol_table]), Keyword.take(args, [:name])) + GenServer.start_link(__MODULE__, Keyword.take(args, [:symbol_table, :db]), Keyword.take(args, [:name])) end def init(args) do symbol_table = Keyword.fetch!(args, :symbol_table) - {:ok, %{symbol_table: symbol_table}} + db = Keyword.fetch!(args, :db) + + {:ok, %{symbol_table: symbol_table, db: db}} end def handle_info({:tracer, payload}, state) do - SymbolTable.put_symbols(state.symbol_table, payload) + DB.insert_symbol(state.db, payload) + {:noreply, state} end def handle_info({{:tracer, :reference}, payload}, state) do - SymbolTable.put_reference(state.symbol_table, payload) + DB.insert_reference(state.db, payload) + {:noreply, state} end end diff --git a/lib/next_ls/runtime/supervisor.ex b/lib/next_ls/runtime/supervisor.ex index e2a51025..16404e47 100644 --- a/lib/next_ls/runtime/supervisor.ex +++ b/lib/next_ls/runtime/supervisor.ex @@ -11,18 +11,20 @@ defmodule NextLS.Runtime.Supervisor do def init(init_arg) do name = init_arg[:name] registry = init_arg[:registry] + logger = init_arg[:logger] hidden_folder = init_arg[:path] File.mkdir_p!(hidden_folder) File.write!(Path.join(hidden_folder, ".gitignore"), "*\n") symbol_table_name = :"symbol-table-#{name}" + db_name = :"db-#{name}" sidecar_name = :"sidecar-#{name}" Registry.register(registry, :runtime_supervisors, %{name: name}) children = [ - {NextLS.SymbolTable, workspace: name, path: hidden_folder, registry: registry, name: symbol_table_name}, - {NextLS.Runtime.Sidecar, name: sidecar_name, symbol_table: symbol_table_name}, + {NextLS.DB, logger: logger, file: ~c"#{hidden_folder}/nextls.db", registry: registry, name: db_name}, + {NextLS.Runtime.Sidecar, name: sidecar_name, db: db_name, symbol_table: symbol_table_name}, {NextLS.Runtime, init_arg[:runtime] ++ [name: name, registry: registry, parent: sidecar_name]} ] diff --git a/lib/next_ls/symbol_table.ex b/lib/next_ls/symbol_table.ex deleted file mode 100644 index 09484d38..00000000 --- a/lib/next_ls/symbol_table.ex +++ /dev/null @@ -1,187 +0,0 @@ -defmodule NextLS.SymbolTable do - @moduledoc false - use GenServer - - defmodule Symbol do - @moduledoc false - defstruct [:file, :module, :type, :name, :line, :col] - - @type t :: %__MODULE__{ - file: String.t(), - module: module(), - type: atom(), - name: atom(), - line: integer(), - col: integer() - } - end - - @type uri :: String.t() - - def start_link(args) do - GenServer.start_link(__MODULE__, Keyword.take(args, [:path, :workspace, :registry]), Keyword.take(args, [:name])) - end - - @spec put_symbols(pid() | atom(), list(tuple())) :: :ok - def put_symbols(server, symbols), do: GenServer.cast(server, {:put_symbols, symbols}) - - @spec put_reference(pid() | atom(), map()) :: :ok - def put_reference(server, reference), do: GenServer.cast(server, {:put_reference, reference}) - - @spec remove(pid() | atom(), uri()) :: :ok - def remove(server, uri), do: GenServer.cast(server, {:remove, uri}) - - @spec symbols(pid() | atom()) :: list(struct()) - def symbols(server), do: GenServer.call(server, :symbols) - - @spec symbols(pid() | atom(), String.t()) :: list(struct()) - def symbols(server, file), do: GenServer.call(server, {:symbols, file}) - - @spec close(pid() | atom()) :: :ok | {:error, term()} - def close(server), do: GenServer.call(server, :close) - - def init(args) do - path = Keyword.fetch!(args, :path) - name = Keyword.fetch!(args, :workspace) - registry = Keyword.fetch!(args, :registry) - - {:ok, name} = - :dets.open_file(:"symbol-table-#{name}", - file: path |> Path.join("symbol_table.dets") |> String.to_charlist(), - type: :duplicate_bag - ) - - {:ok, ref_name} = - :dets.open_file(:"reference-table-#{name}", - file: path |> Path.join("reference_table.dets") |> String.to_charlist(), - type: :duplicate_bag - ) - - Registry.register(registry, :symbol_tables, %{symbol_table: name, reference_table: ref_name}) - - {:ok, %{table: name, reference_table: ref_name}} - end - - def handle_call({:symbols, file}, _, state) do - symbols = - case :dets.lookup(state.table, file) do - [{_, symbols} | _rest] -> symbols - _ -> [] - end - - {:reply, symbols, state} - end - - def handle_call(:symbols, _, state) do - symbols = - :dets.foldl( - fn {_key, symbol}, acc -> - if String.match?(to_string(symbol.name), ~r/__.*__/) do - acc - else - [symbol | acc] - end - end, - [], - state.table - ) - - {:reply, symbols, state} - end - - def handle_call(:close, _, state) do - :dets.close(state.table) - :dets.close(state.reference_table) - - {:reply, :ok, state} - end - - def handle_cast({:put_reference, reference}, state) do - %{ - meta: meta, - identifier: identifier, - file: file - } = reference - - col = meta[:column] || 0 - - range = - {{meta[:line], col}, - {meta[:line], col + String.length(identifier |> to_string() |> String.replace("Elixir.", ""))}} - - :dets.insert(state.reference_table, { - {file, range}, - reference - }) - - {:noreply, state} - end - - def handle_cast({:put_symbols, symbols}, state) do - %{ - module: mod, - module_line: module_line, - struct: struct, - file: file, - defs: defs - } = symbols - - :dets.delete(state.table, mod) - - :dets.insert( - state.table, - {mod, - %Symbol{ - module: mod, - file: file, - type: :defmodule, - name: Macro.to_string(mod), - line: module_line, - col: 1 - }} - ) - - if struct do - {_, _, meta, _} = defs[:__struct__] - - :dets.insert( - state.table, - {mod, - %Symbol{ - module: mod, - file: file, - type: :defstruct, - name: "%#{Macro.to_string(mod)}{}", - line: meta[:line], - col: 1 - }} - ) - end - - for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do - :dets.insert( - state.table, - {mod, - %Symbol{ - module: mod, - file: file, - type: type, - name: name, - line: meta[:line], - col: meta[:column] || 1 - }} - ) - end - - {:noreply, state} - end - - def handle_cast({:remove, uri}, %{table: symbol_table, reference_table: reference_table} = state) do - file = URI.parse(uri).path - - :dets.select_delete(symbol_table, [{{:_, %{file: :"$1"}}, [], [{:==, :"$1", file}]}]) - :dets.select_delete(reference_table, [{{{:"$1", :_}, :_}, [], [{:==, :"$1", file}]}]) - - {:noreply, state} - end -end diff --git a/mix.exs b/mix.exs index dd457995..6437fa91 100644 --- a/mix.exs +++ b/mix.exs @@ -35,6 +35,7 @@ defmodule NextLS.MixProject do defp deps do [ {:gen_lsp, "~> 0.5"}, + {:esqlite, "~> 0.8.6"}, {:styler, "~> 0.8", only: :dev}, {:ex_doc, ">= 0.0.0", only: :dev}, {:dialyxir, ">= 0.0.0", only: [:dev, :test], runtime: false} diff --git a/mix.lock b/mix.lock index 4c33c383..8ef09834 100644 --- a/mix.lock +++ b/mix.lock @@ -2,6 +2,7 @@ "dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"}, "earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, + "esqlite": {:hex, :esqlite, "0.8.6", "7852d506eb12bd519c1084f03c467f364f8ddff69227154e5e6df9cc6346fbbf", [:rebar3], [], "hexpm", "607e45f4da42601d8f530979417f57a4cd629ab49085891849302057e68ea188"}, "ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"}, "gen_lsp": {:hex, :gen_lsp, "0.5.0", "463d25c2b81f64b95667e1e6fa9bf2a4ed00896f5e9abe2965bc50edeebae747", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "6d40e2315bd2206cb0e40e93c5d58b28ab15787214fea1066f6561df783ef7c9"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, diff --git a/priv/monkey/_next_ls_private_compiler.ex b/priv/monkey/_next_ls_private_compiler.ex index e50ebbbd..2a1cdfbb 100644 --- a/priv/monkey/_next_ls_private_compiler.ex +++ b/priv/monkey/_next_ls_private_compiler.ex @@ -25,9 +25,7 @@ defmodule NextLSPrivate.Tracer do :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 + def trace({type, meta, module, func, arity}, env) when type in [:remote_function, :remote_macro, :imported_macro] do parent = parent_pid() if type == :remote_macro && meta[:closing][:line] != meta[:line] do diff --git a/test/next_ls/symbol_table_test.exs b/test/next_ls/symbol_table_test.exs deleted file mode 100644 index 0a47ca27..00000000 --- a/test/next_ls/symbol_table_test.exs +++ /dev/null @@ -1,141 +0,0 @@ -defmodule NextLS.SymbolTableTest do - use ExUnit.Case, async: true - - alias NextLS.SymbolTable - - @moduletag :tmp_dir - - setup %{tmp_dir: dir} = cxt do - File.mkdir_p!(dir) - - start_supervised!({Registry, [keys: :duplicate, name: Registry.SymbolTableTest.Registry]}) - # this fails with `{:error, incompatible_arguments}` on CI a lot, and I have no idea why - pid = - try_start_supervised( - {SymbolTable, [path: dir, workspace: cxt.test, registry: Registry.SymbolTableTest.Registry]}, - 10 - ) - - Process.link(pid) - [pid: pid, dir: dir] - end - - defp try_start_supervised(spec, 0) do - start_supervised!(spec) - end - - defp try_start_supervised(spec, num) do - start_supervised!(spec) - rescue - _ -> - Process.sleep(250) - try_start_supervised(spec, num - 1) - end - - test "creates a dets table", %{dir: dir, pid: pid} = cxt do - assert File.exists?(Path.join([dir, "symbol_table.dets"])) - assert File.exists?(Path.join([dir, "reference_table.dets"])) - assert :sys.get_state(pid).table == :"symbol-table-#{cxt.test}" - end - - test "builds the symbol table", %{pid: pid} do - symbols = symbols() - - SymbolTable.put_symbols(pid, symbols) - - assert [ - %SymbolTable.Symbol{ - module: NextLS, - file: "/Users/alice/next_ls/lib/next_ls.ex", - type: :def, - name: :start_link, - line: 45, - col: 1 - }, - %SymbolTable.Symbol{ - module: NextLS, - file: "/Users/alice/next_ls/lib/next_ls.ex", - type: :def, - name: :start_link, - line: 44, - col: 1 - }, - %SymbolTable.Symbol{ - module: NextLS, - file: "/Users/alice/next_ls/lib/next_ls.ex", - type: :defmodule, - name: "NextLS", - line: 1, - col: 1 - } - ] == SymbolTable.symbols(pid) - end - - defp symbols do - %{ - file: "/Users/alice/next_ls/lib/next_ls.ex", - module: NextLS, - module_line: 1, - struct: nil, - defs: [ - __foo__: {:v1, :def, [line: 900], [{[line: 900], [], [], nil}]}, - start_link: - {:v1, :def, [line: 44], - [ - {[line: 44], [{:args, [version: 0, line: 44, column: 18], nil}], [], - {:__block__, [], - [ - {:=, - [ - end_of_expression: [newlines: 2, line: 52, column: 9], - line: 45, - column: 18 - ], - [ - {{:args, [version: 1, line: 45, column: 6], nil}, {:opts, [version: 2, line: 45, column: 12], nil}}, - {{:., [line: 46, column: 14], [Keyword, :split]}, - [closing: [line: 52, column: 8], line: 46, column: 15], - [ - {:args, [version: 0, line: 46, column: 21], nil}, - [:cache, :task_supervisor, :dynamic_supervisor, :extensions, :extension_registry] - ]} - ]}, - {{:., [line: 54, column: 11], [GenLSP, :start_link]}, - [closing: [line: 54, column: 45], line: 54, column: 12], - [ - NextLS, - {:args, [version: 1, line: 54, column: 35], nil}, - {:opts, [version: 2, line: 54, column: 41], nil} - ]} - ]}}, - {[line: 45], [{:args, [version: 0, line: 45, column: 18], nil}], [], - {:__block__, [], - [ - {:=, - [ - end_of_expression: [newlines: 2, line: 52, column: 9], - line: 45, - column: 18 - ], - [ - {{:args, [version: 1, line: 45, column: 6], nil}, {:opts, [version: 2, line: 45, column: 12], nil}}, - {{:., [line: 46, column: 14], [Keyword, :split]}, - [closing: [line: 52, column: 8], line: 46, column: 15], - [ - {:args, [version: 0, line: 46, column: 21], nil}, - [:cache, :task_supervisor, :dynamic_supervisor, :extensions, :extension_registry] - ]} - ]}, - {{:., [line: 54, column: 11], [GenLSP, :start_link]}, - [closing: [line: 54, column: 45], line: 54, column: 12], - [ - NextLS, - {:args, [version: 1, line: 54, column: 35], nil}, - {:opts, [version: 2, line: 54, column: 41], nil} - ]} - ]}} - ]} - ] - } - end -end diff --git a/test/next_ls_test.exs b/test/next_ls_test.exs index bc597ffd..f0a56c54 100644 --- a/test/next_ls_test.exs +++ b/test/next_ls_test.exs @@ -407,15 +407,15 @@ defmodule NextLSTest do "location" => %{ "range" => %{ "start" => %{ - "line" => 3, + "line" => 4, "character" => 0 }, "end" => %{ - "line" => 3, + "line" => 4, "character" => 0 } }, - "uri" => "file://#{cwd}/my_proj/lib/bar.ex" + "uri" => "file://#{cwd}/my_proj/lib/code_action.ex" }, "name" => "def foo" }, @@ -424,15 +424,15 @@ defmodule NextLSTest do "location" => %{ "range" => %{ "start" => %{ - "line" => 4, + "line" => 3, "character" => 0 }, "end" => %{ - "line" => 4, + "line" => 3, "character" => 0 } }, - "uri" => "file://#{cwd}/my_proj/lib/code_action.ex" + "uri" => "file://#{cwd}/my_proj/lib/bar.ex" }, "name" => "def foo" }