From bcb7e2e7433b5488fd3f2bc7170be5028fb56409 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Wed, 14 Feb 2024 13:46:08 -0500 Subject: [PATCH] feat: spitfire (#368) Incorporates experimental usage of the [Spitfire](https://github.com/elixir-tools/spitfire) parser. To enable, the server should be started with `NEXTLS_SPITFIRE_ENABLED=1`. `elixir-tools.nvim` and `elixir-tools.vscode` will have settings to enable this for you. --- .github/workflows/ci.yaml | 58 ++++++++------ .github/workflows/release.yaml | 3 +- .mise.toml | 5 +- flake.nix | 31 +++++++- lib/next_ls/document_symbol.ex | 42 ++++++---- lib/next_ls/helpers/ast_helpers.ex | 84 ++++++++++---------- lib/next_ls/helpers/ast_helpers/variables.ex | 4 +- lib/next_ls/parser.ex | 24 ++++++ lib/next_ls/runtime.ex | 4 +- mix.exs | 1 + mix.lock | 5 +- test/next_ls/autocomplete_test.exs | 12 +-- test/next_ls/definition_test.exs | 24 +++--- test/next_ls/dependency_test.exs | 4 +- test/next_ls/helpers/ast_helpers_test.exs | 33 +++++++- test/next_ls_test.exs | 8 +- 16 files changed, 217 insertions(+), 125 deletions(-) create mode 100644 lib/next_ls/parser.ex diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d7bffc51..85e3e183 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,42 +7,47 @@ on: jobs: tests: runs-on: ${{matrix.os}} - name: Test (${{matrix.os}}) + name: Test (${{matrix.os}}) - spitfire=${{matrix.spitfire}} strategy: matrix: + spitfire: [0, 1] os: - ubuntu-latest - macos-14 steps: - uses: actions/checkout@v4 - - uses: jdx/mise-action@v2 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: actions/cache@v4 with: path: | deps _build - key: ${{ matrix.os }}-mix-${{ hashFiles('**/.mise.toml') }}-${{ hashFiles('**/mix.lock') }} + key: ${{ matrix.os }}-mix-${{ hashFiles('**/flake.nix') }}-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ matrix.os }}-mix-${{ hashFiles('**/.mise.toml') }}- + ${{ matrix.os }}-mix-${{ hashFiles('**/flake.nix') }}- - name: Install Dependencies - run: mix deps.get + run: nix develop --command bash -c 'mix deps.get' - name: Start EPMD - run: epmd -daemon + run: nix develop --command bash -c 'epmd -daemon' - name: Compile env: MIX_ENV: test - run: mix compile + run: nix develop --command bash -c 'mix compile' - name: remove tmp dir run: rm -rf tmp - name: Run Tests - run: elixir --erl '-kernel prevent_overlapping_partitions false' -S mix test --max-cases 1 + env: + NEXTLS_SPITFIRE_ENABLED: ${{ matrix.spitfire }} + run: nix develop --command bash -c "elixir --erl '-kernel prevent_overlapping_partitions false' -S mix test --max-cases 1" formatter: runs-on: ubuntu-latest @@ -50,27 +55,29 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: jdx/mise-action@v2 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main - uses: actions/cache@v4 with: path: | deps _build - key: ${{ runner.os }}-mix-${{ hashFiles('**/.mise.toml') }}-${{ hashFiles('**/mix.lock') }} + key: ${{ runner.os }}-mix-${{ hashFiles('**/flake.nix') }}-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ runner.os }}-mix-${{ hashFiles('**/.mise.toml') }}- + ${{ runner.os }}-mix-${{ hashFiles('**/flake.nix') }}- - name: Install Dependencies - run: mix deps.get + run: nix develop --command bash -c 'mix deps.get' - name: Run Formatter - run: mix format --check-formatted + run: nix develop --command bash -c 'mix format --check-formatted' dialyzer: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: jdx/mise-action@v2 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main # Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones # Cache key based on Elixir & Erlang version (also useful when running in matrix) @@ -78,19 +85,19 @@ jobs: uses: actions/cache/restore@v4 id: plt_cache with: - key: ${{ runner.os }}-mix-${{ hashFiles('**/.mise.toml') }}-${{ hashFiles('**/mix.lock') }} + key: ${{ runner.os }}-mix-${{ hashFiles('**/flake.nix') }}-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ runner.os }}-mix-${{ hashFiles('**/.mise.toml') }}- + ${{ runner.os }}-mix-${{ hashFiles('**/flake.nix') }}- path: | priv/plts - name: Install Dependencies - run: mix deps.get + run: nix develop --command bash -c 'mix deps.get' # Create PLTs if no cache was found - name: Create PLTs if: steps.plt_cache.outputs.cache-hit != 'true' - run: mix dialyzer --plt + run: nix develop --command bash -c 'mix dialyzer --plt' # By default, the GitHub Cache action will only save the cache if all steps in the job succeed, # so we separate the cache restore and save steps in case running dialyzer fails. @@ -99,12 +106,12 @@ jobs: if: steps.plt_cache.outputs.cache-hit != 'true' id: plt_cache_save with: - key: ${{ runner.os }}-mix-${{ hashFiles('**/.mise.toml') }}-${{ hashFiles('**/mix.lock') }} + key: ${{ runner.os }}-mix-${{ hashFiles('**/flake.nix') }}-${{ hashFiles('**/mix.lock') }} path: | priv/plts - name: Run dialyzer - run: mix dialyzer --format github + run: nix develop --command bash -c 'mix dialyzer --format github' release-test: runs-on: ${{matrix.os.name}} @@ -122,23 +129,24 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: jdx/mise-action@v2 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main - uses: actions/cache@v4 with: path: | deps - key: ${{ matrix.os.name }}-mix-prod-${{ hashFiles('**/.mise.toml') }}-${{ hashFiles('**/mix.lock') }} + key: ${{ matrix.os.name }}-mix-prod-${{ hashFiles('**/flake.nix') }}-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ matrix.os.name }}-mix-prod-${{ hashFiles('**/.mise.toml') }}- + ${{ matrix.os.name }}-mix-prod-${{ hashFiles('**/flake.nix') }}- - name: Install Dependencies - run: mix deps.get --only prod + run: nix develop --command bash -c 'mix deps.get --only prod' - name: Release env: MIX_ENV: prod BURRITO_TARGET: ${{ matrix.os.target }} - run: mix release + run: nix develop --command bash -c 'mix release' nix-build: strategy: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c85cc80f..4dade4ab 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -55,7 +55,8 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 - - uses: jdx/mise-action@v2 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main - run: chmod +x "$PWD/bin/7z" - run: brew install 7zip - run: mix local.hex --force diff --git a/.mise.toml b/.mise.toml index ea915b6c..c719901b 100644 --- a/.mise.toml +++ b/.mise.toml @@ -2,7 +2,6 @@ KERL_BUILD_DOCS = "yes" [tools] -erlang = "26.1.2" -elixir = "1.15.7-otp-26" +erlang = "26.2.2" +elixir = "ref:52eaf1456182d5d6cce22a4f5c3f6ec9f4dcbfd9" zig = "0.11.0" - diff --git a/flake.nix b/flake.nix index 15d176b8..d5ca3a6c 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,7 @@ { - inputs = {nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";}; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; nixConfig = { extra-substituters = ["https://elixir-tools.cachix.org"]; @@ -19,10 +21,23 @@ lib.genAttrs (builtins.attrNames burritoExe) (system: let pkgs = nixpkgs.legacyPackages.${system}; beamPackages = pkgs.beam_minimal.packages.erlang_26; - elixir = beamPackages.elixir_1_15; beam = fetchTarball beams.${system}; rawmusl = musls.${system}; musl = lib.optionals nixpkgs.legacyPackages.${system}.stdenv.isLinux (builtins.fetchurl (nixpkgs.lib.attrsets.getAttrs ["url" "sha256"] musls.${system})); + otp = (pkgs.beam.packagesWith beamPackages.erlang).extend (final: prev: { + elixir_1_17 = prev.elixir_1_16.override { + rev = "52eaf1456182d5d6cce22a4f5c3f6ec9f4dcbfd9"; + # You can discover this using Trust On First Use by filling in `lib.fakeHash` + sha256 = "sha256-fOsV+jVIzsa38hQDvAjhUqee36nt8kG6AOpOQJnSZ74="; + version = "1.17.0-dev"; + }; + + elixir = final.elixir_1_17; + # This will get upstreamed into nix-beam-flakes at some point + rebar = prev.rebar.overrideAttrs (_old: {doCheck = false;}); + rebar3 = prev.rebar3.overrideAttrs (_old: {doCheck = false;}); + }); + elixir = otp.elixir; in f {inherit system pkgs beamPackages elixir beam rawmusl musl;}); @@ -100,7 +115,7 @@ src = self.outPath; inherit version elixir; pname = "next-ls-deps"; - hash = "sha256-U5d8DftG0i1c4JiutUentNlRsefFgR4Mfc3eKqnKR3U="; + hash = "sha256-RYPweYD1GD0D6A7ZkrtD3h7arCVimdStcOhrrlHFrnw="; mixEnv = "prod"; }; @@ -149,7 +164,15 @@ beamPackages, elixir, ... - }: { + }: let + aliased_7zz = pkgs.symlinkJoin { + name = "7zz-aliased"; + paths = [pkgs._7zz]; + postBuild = '' + ln -s ${pkgs._7zz}/bin/7zz $out/bin/7z + ''; + }; + in { default = pkgs.mkShell { # The Nix packages provided in the environment packages = [ diff --git a/lib/next_ls/document_symbol.ex b/lib/next_ls/document_symbol.ex index d78e66c9..1fa21cca 100644 --- a/lib/next_ls/document_symbol.ex +++ b/lib/next_ls/document_symbol.ex @@ -5,23 +5,35 @@ defmodule NextLS.DocumentSymbol do alias GenLSP.Structures.Position alias GenLSP.Structures.Range - # we set the literal encoder so that we can know when atoms and strings start and end - # this makes it useful for knowing the exact locations of struct field definitions @spec fetch(text :: String.t()) :: list(DocumentSymbol.t()) def fetch(text) do - text - |> Code.string_to_quoted!( - literal_encoder: fn literal, meta -> - if is_atom(literal) or is_binary(literal) do - {:ok, {:__literal__, meta, [literal]}} - else - {:ok, literal} - end - end, - unescape: false, - token_metadata: true, - columns: true - ) + ast = + case NextLS.Parser.parse( + text, + # we set the literal encoder so that we can know when atoms and strings start and end + # this makes it useful for knowing the exact locations of struct field definitions + literal_encoder: fn literal, meta -> + if is_atom(literal) or is_binary(literal) do + {:ok, {:__literal__, meta, [literal]}} + else + {:ok, literal} + end + end, + unescape: false, + token_metadata: true, + columns: true + ) do + {:error, ast, _errors} -> + ast + + {:error, _} -> + raise "Failed to parse!" + + {:ok, ast} -> + ast + end + + ast |> walker(nil) |> List.wrap() end diff --git a/lib/next_ls/helpers/ast_helpers.ex b/lib/next_ls/helpers/ast_helpers.ex index 0f40759a..109cff13 100644 --- a/lib/next_ls/helpers/ast_helpers.ex +++ b/lib/next_ls/helpers/ast_helpers.ex @@ -72,7 +72,7 @@ defmodule NextLS.ASTHelpers do defp postwalk(ast, acc, _module), do: {ast, acc} defp ast_from_file(file) do - file |> File.read!() |> Code.string_to_quoted!(columns: true) + file |> File.read!() |> NextLS.Parser.parse!(columns: true) end end @@ -99,53 +99,57 @@ defmodule NextLS.ASTHelpers do """ def extract_alias_range(code, {start, stop}, ale) do - lines = - code - |> String.split("\n") - |> Enum.map(&String.split(&1, "")) - |> Enum.slice((start.line - 1)..(stop.line - 1)) - - code = - if start.line == stop.line do - [line] = lines - - line - |> Enum.slice(start.col..stop.col) - |> Enum.join() - else - [first | rest] = lines - first = Enum.drop(first, start.col) - - [last | rest] = Enum.reverse(rest) - - length = Enum.count(last) - last = Enum.drop(last, -(length - stop.col - 1)) - - Enum.map_join([first | Enum.reverse([last | rest])], "\n", &Enum.join(&1, "")) - end - {_, range} = code - |> Code.string_to_quoted!(columns: true, column: start.col, token_metadata: true) - |> Macro.prewalk(nil, fn ast, range -> - range = - case ast do - {:__aliases__, meta, aliases} -> - if ale == List.last(aliases) do - {{meta[:line] + start.line - 1, meta[:column]}, - {meta[:last][:line] + start.line - 1, meta[:last][:column] + String.length(to_string(ale)) - 1}} - else + |> NextLS.Parser.parse!(columns: true, token_metadata: true) + |> Macro.prewalk(nil, fn + ast, nil = range -> + range = + case ast do + {:__aliases__, meta, aliases} -> + if ale == List.last(aliases) do + found_range = + {{meta[:line], meta[:column]}, + {meta[:last][:line], meta[:last][:column] + String.length(to_string(ale)) - 1}} + + if NextLS.ASTHelpers.inside?({{start.line, start.col}, {stop.line, stop.col}}, found_range) do + found_range + else + range + end + else + range + end + + _ -> range - end + end - _ -> - range - end + {ast, range} - {ast, range} + ast, range -> + {ast, range} end) range end end + + def inside?(outer, {{_, _}, {_, _}} = target) do + {{outer_startl, outer_startc}, {outer_endl, outer_endc}} = outer + {target_start, target_end} = target + + Enum.all?([target_start, target_end], fn {line, col} -> + if outer_startl <= line and line <= outer_endl do + cond do + outer_startl < line and line < outer_endl -> true + outer_startl == line and outer_startc <= col -> true + outer_endl == line and col <= outer_endc -> true + true -> false + end + else + false + end + end) + end end diff --git a/lib/next_ls/helpers/ast_helpers/variables.ex b/lib/next_ls/helpers/ast_helpers/variables.ex index f4eb7e80..0d726ef7 100644 --- a/lib/next_ls/helpers/ast_helpers/variables.ex +++ b/lib/next_ls/helpers/ast_helpers/variables.ex @@ -10,7 +10,7 @@ defmodule NextLS.ASTHelpers.Variables do def get_variable_definition(file, position) do file = File.read!(file) - case Code.string_to_quoted(file, columns: true) do + case NextLS.Parser.parse(file, columns: true) do {:ok, ast} -> {_ast, %{vars: vars}} = Macro.traverse( @@ -46,7 +46,7 @@ defmodule NextLS.ASTHelpers.Variables do def list_variable_references(file, position) do file = File.read!(file) - case Code.string_to_quoted(file, columns: true) do + case NextLS.Parser.parse(file, columns: true) do {:ok, ast} -> {_ast, %{vars: vars}} = Macro.traverse( diff --git a/lib/next_ls/parser.ex b/lib/next_ls/parser.ex new file mode 100644 index 00000000..03705676 --- /dev/null +++ b/lib/next_ls/parser.ex @@ -0,0 +1,24 @@ +defmodule NextLS.Parser do + @moduledoc false + def parse!(code, opts \\ []) do + {m, f} = + if System.get_env("NEXTLS_SPITFIRE_ENABLED", "0") == "1" do + {Spitfire, :parse!} + else + {Code, :string_to_quoted!} + end + + apply(m, f, [code, opts]) + end + + def parse(code, opts \\ []) do + {m, f} = + if System.get_env("NEXTLS_SPITFIRE_ENABLED", "0") == "1" do + {Spitfire, :parse} + else + {Code, :string_to_quoted} + end + + apply(m, f, [code, opts]) + end +end diff --git a/lib/next_ls/runtime.ex b/lib/next_ls/runtime.ex index 303e50bb..f53073f3 100644 --- a/lib/next_ls/runtime.ex +++ b/lib/next_ls/runtime.ex @@ -181,8 +181,8 @@ defmodule NextLS.Runtime do end) Task.start_link(fn -> - with {:ok, host} <- :inet.gethostname(), - node <- :"#{sname}@#{host}", + with {:ok, host} = :inet.gethostname(), + node = :"#{sname}@#{host}", true <- connect(node, port, 120) do NextLS.Logger.info(logger, "Connected to node #{node}") diff --git a/mix.exs b/mix.exs index d3fc13fc..7a9b1171 100644 --- a/mix.exs +++ b/mix.exs @@ -62,6 +62,7 @@ defmodule NextLS.MixProject do {:gen_lsp, "~> 0.7"}, {:req, "~> 0.3"}, {:schematic, "~> 0.2"}, + {:spitfire, github: "elixir-tools/spitfire"}, {:opentelemetry, "~> 1.3"}, {:opentelemetry_api, "~> 1.2"}, diff --git a/mix.lock b/mix.lock index c751bcec..957ef1fa 100644 --- a/mix.lock +++ b/mix.lock @@ -10,14 +10,14 @@ "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, "ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"}, "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, - "dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"}, + "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "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"}, "exqlite": {:hex, :exqlite, "0.13.15", "a32c0763915e2b0d7ced9dd8638802d38e9569053f3b28b815bd0faef1cbe6d9", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "4afcc870a33b57781a1e57cd4294eef68815059d26b774c7cd075536b21434b7"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, - "gen_lsp": {:hex, :gen_lsp, "0.7.1", "dd00875a606f05ae5b804360d1212594cc6ff03de2388caa2b40d99103fe1b47", [: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", "3ed9f7d1c4e860f2a3abf0de231e83fa2a59c8b9e5e71a081f15f8c3d739615a"}, + "gen_lsp": {:hex, :gen_lsp, "0.7.2", "42fe4e54bd59f8c956451ab41cfb3c656cc9254c0ea3a6517a1e444acb41080f", [: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", "e9c814dd51376ccee829c7a680206f81c63d6e2b24c885018ecb3e5d3877c3b7"}, "gproc": {:hex, :gproc, "0.9.1", "f1df0364423539cf0b80e8201c8b1839e229e5f9b3ccb944c5834626998f5b8c", [:rebar3], [], "hexpm", "905088e32e72127ed9466f0bac0d8e65704ca5e73ee5a62cb073c3117916d507"}, "grpcbox": {:hex, :grpcbox, "0.17.1", "6e040ab3ef16fe699ffb513b0ef8e2e896da7b18931a1ef817143037c454bcce", [:rebar3], [{:acceptor_pool, "~> 1.0.0", [hex: :acceptor_pool, repo: "hexpm", optional: false]}, {:chatterbox, "~> 0.15.1", [hex: :ts_chatterbox, repo: "hexpm", optional: false]}, {:ctx, "~> 0.6.0", [hex: :ctx, repo: "hexpm", optional: false]}, {:gproc, "~> 0.9.1", [hex: :gproc, repo: "hexpm", optional: false]}], "hexpm", "4a3b5d7111daabc569dc9cbd9b202a3237d81c80bf97212fbc676832cb0ceb17"}, "hpack": {:hex, :hpack_erl, "0.3.0", "2461899cc4ab6a0ef8e970c1661c5fc6a52d3c25580bc6dd204f84ce94669926", [:rebar3], [], "hexpm", "d6137d7079169d8c485c6962dfe261af5b9ef60fbc557344511c1e65e3d95fb0"}, @@ -43,6 +43,7 @@ "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "req": {:hex, :req, "0.4.0", "1c759054dd64ef1b1a0e475c2d2543250d18f08395d3174c371b7746984579ce", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "f53eadc32ebefd3e5d50390356ec3a59ed2b8513f7da8c6c3f2e14040e9fe989"}, "schematic": {:hex, :schematic, "0.2.1", "0b091df94146fd15a0a343d1bd179a6c5a58562527746dadd09477311698dbb1", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0b255d65921e38006138201cd4263fd8bb807d9dfc511074615cd264a571b3b1"}, + "spitfire": {:git, "https://github.com/elixir-tools/spitfire.git", "12a1827821265170a58e40b5ffd2bb785f789d91", []}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "styler": {:hex, :styler, "0.8.1", "f3c0f65023e4bfbf7e7aa752d128b8475fdabfd30f96ee7314b84480cc56e788", [:mix], [], "hexpm", "1aa48d3aa689a639289af3d8254d40e068e98c083d6e5e3d1a695e71a147b344"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, diff --git a/test/next_ls/autocomplete_test.exs b/test/next_ls/autocomplete_test.exs index 9445d41e..bbaad11a 100644 --- a/test/next_ls/autocomplete_test.exs +++ b/test/next_ls/autocomplete_test.exs @@ -656,15 +656,9 @@ defmodule NextLS.AutocompleteTest do assert expand(runtime, ~c"\"#{dir}/d") == {:yes, [%{name: "dir/", kind: :dir}]} assert expand(runtime, ~c"\"#{dir}/dir") == {:yes, [%{name: "dir/", kind: :dir}]} - case :os.type() do - {:unix, :linux} -> - assert expand(runtime, ~c"\"#{dir}/dir/") == - {:yes, [%{name: "file4", kind: :file}, %{name: "file3", kind: :file}]} - - {:unix, :darwin} -> - assert expand(runtime, ~c"\"#{dir}/dir/") == - {:yes, [%{name: "file3", kind: :file}, %{name: "file4", kind: :file}]} - end + assert {:yes, [_, _] = files} = expand(runtime, ~c"\"#{dir}/dir/") + assert %{name: "file4", kind: :file} in files + assert %{name: "file3", kind: :file} in files assert expand(runtime, ~c"\"#{dir}/dir/file") == dir |> Path.join("dir") |> path_autocompletion("file") end diff --git a/test/next_ls/definition_test.exs b/test/next_ls/definition_test.exs index 658bc9de..a5fabfaa 100644 --- a/test/next_ls/definition_test.exs +++ b/test/next_ls/definition_test.exs @@ -79,11 +79,11 @@ defmodule NextLS.DefinitionTest do "range" => %{ "start" => %{ "line" => 7, - "character" => 0 + "character" => 7 }, "end" => %{ "line" => 7, - "character" => 0 + "character" => 7 } }, "uri" => ^uri @@ -114,11 +114,11 @@ defmodule NextLS.DefinitionTest do "range" => %{ "start" => %{ "line" => 1, - "character" => 0 + "character" => 6 }, "end" => %{ "line" => 1, - "character" => 0 + "character" => 6 } }, "uri" => ^uri @@ -150,11 +150,11 @@ defmodule NextLS.DefinitionTest do "range" => %{ "start" => %{ "line" => 1, - "character" => 0 + "character" => 6 }, "end" => %{ "line" => 1, - "character" => 0 + "character" => 6 } }, "uri" => ^uri @@ -243,11 +243,11 @@ defmodule NextLS.DefinitionTest do "range" => %{ "start" => %{ "line" => 4, - "character" => 0 + "character" => 12 }, "end" => %{ "line" => 4, - "character" => 0 + "character" => 12 } }, "uri" => ^uri @@ -278,11 +278,11 @@ defmodule NextLS.DefinitionTest do "range" => %{ "start" => %{ "line" => 1, - "character" => 0 + "character" => 11 }, "end" => %{ "line" => 1, - "character" => 0 + "character" => 11 } }, "uri" => ^uri @@ -313,11 +313,11 @@ defmodule NextLS.DefinitionTest do "range" => %{ "start" => %{ "line" => 1, - "character" => 0 + "character" => 11 }, "end" => %{ "line" => 1, - "character" => 0 + "character" => 11 } }, "uri" => ^uri diff --git a/test/next_ls/dependency_test.exs b/test/next_ls/dependency_test.exs index 10ca2dca..75460df4 100644 --- a/test/next_ls/dependency_test.exs +++ b/test/next_ls/dependency_test.exs @@ -103,11 +103,11 @@ defmodule NextLS.DependencyTest do "range" => %{ "start" => %{ "line" => 1, - "character" => 0 + "character" => 6 }, "end" => %{ "line" => 1, - "character" => 0 + "character" => 6 } }, "uri" => ^uri diff --git a/test/next_ls/helpers/ast_helpers_test.exs b/test/next_ls/helpers/ast_helpers_test.exs index 9183ac79..ca329d46 100644 --- a/test/next_ls/helpers/ast_helpers_test.exs +++ b/test/next_ls/helpers/ast_helpers_test.exs @@ -1,8 +1,32 @@ defmodule NextLS.ASTHelpersTest do use ExUnit.Case, async: true + alias NextLS.ASTHelpers alias NextLS.ASTHelpers.Aliases + describe "inside?/2" do + # example full snippet is the outer + # alias One.Two.{ + # Three, + # Four # this is the target target + # ~~~~ + # } + # alias One.Four + test "completely inside outer range" do + outer = {{1, 1}, {4, 1}} + target = {{3, 3}, {3, 6}} + + assert ASTHelpers.inside?(outer, target) + end + + test "completely outside outer range" do + outer = {{1, 1}, {4, 1}} + target = {{5, 11}, {5, 14}} + + refute ASTHelpers.inside?(outer, target) + end + end + describe "extract_aliases" do test "extracts a normal alias" do code = """ @@ -35,6 +59,7 @@ defmodule NextLS.ASTHelpersTest do test "extract a multi line, multi alias" do code = """ defmodule Foo do + alias One.Four alias One.Two.{ Three, Four @@ -42,11 +67,11 @@ defmodule NextLS.ASTHelpersTest do end """ - start = %{line: 2, col: 3} - stop = %{line: 5, col: 3} + start = %{line: 3, col: 3} + stop = %{line: 6, col: 3} - assert {{3, 5}, {3, 9}} == Aliases.extract_alias_range(code, {start, stop}, :Three) - assert {{4, 5}, {4, 8}} == Aliases.extract_alias_range(code, {start, stop}, :Four) + assert {{4, 5}, {4, 9}} == Aliases.extract_alias_range(code, {start, stop}, :Three) + assert {{5, 5}, {5, 8}} == Aliases.extract_alias_range(code, {start, stop}, :Four) end end end diff --git a/test/next_ls_test.exs b/test/next_ls_test.exs index a32f362b..b7ce8402 100644 --- a/test/next_ls_test.exs +++ b/test/next_ls_test.exs @@ -228,11 +228,11 @@ defmodule NextLSTest do "range" => %{ "start" => %{ "line" => 3, - "character" => 0 + "character" => 6 }, "end" => %{ "line" => 3, - "character" => 0 + "character" => 6 } }, "uri" => "file://#{cwd}/my_proj/lib/bar.ex" @@ -349,8 +349,8 @@ defmodule NextLSTest do "kind" => 12, "location" => %{ "range" => %{ - "end" => %{"character" => 0, "line" => 1}, - "start" => %{"character" => 0, "line" => 1} + "end" => %{"character" => 6, "line" => 1}, + "start" => %{"character" => 6, "line" => 1} }, "uri" => "file://#{cwd}/my_proj/lib/project.ex" },