diff --git a/apps/common/lib/lexical/ast/detection/string.ex b/apps/common/lib/lexical/ast/detection/string.ex index abfb51337..b9614e700 100644 --- a/apps/common/lib/lexical/ast/detection/string.ex +++ b/apps/common/lib/lexical/ast/detection/string.ex @@ -41,7 +41,7 @@ defmodule Lexical.Ast.Detection.String do # a string literal defp do_detect({:__block__, _, [literal]} = ast, %Position{} = position) when is_binary(literal) do - case fetch_range(ast, 0, -1) do + case fetch_range(ast) do {:ok, range} -> Range.contains?(range, position) :error -> false end @@ -56,7 +56,7 @@ defmodule Lexical.Ast.Detection.String do # String sigils defp do_detect({sigil, _, _} = ast, %Position{} = position) when sigil in @string_sigils do - case fetch_range(ast, 0, 0) do + case fetch_range(ast) do {:ok, range} -> Range.contains?(range, position) _ -> false end @@ -75,7 +75,7 @@ defmodule Lexical.Ast.Detection.String do |> Keyword.get(:delimiter, "\"") |> String.length() - with {:ok, string_range} <- fetch_range(ast, delimiter_length, -1), + with {:ok, string_range} <- fetch_range(ast, delimiter_length, 0), {:ok, interpolation_ranges} <- collect_interpolation_ranges(interpolations) do Range.contains?(string_range, position) and not Enum.any?(interpolation_ranges, &Range.contains?(&1, position)) @@ -92,7 +92,7 @@ defmodule Lexical.Ast.Detection.String do {ast, :error} {:"::", _, _} = interpolation, {:ok, acc} -> - case fetch_range(interpolation, 1, -1) do + case fetch_range(interpolation, 1, 0) do {:ok, range} -> {interpolation, {:ok, [range | acc]}} diff --git a/apps/remote_control/test/lexical/remote_control/code_intelligence/references_test.exs b/apps/remote_control/test/lexical/remote_control/code_intelligence/references_test.exs index 1d73db782..dd0b66d09 100644 --- a/apps/remote_control/test/lexical/remote_control/code_intelligence/references_test.exs +++ b/apps/remote_control/test/lexical/remote_control/code_intelligence/references_test.exs @@ -59,7 +59,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do def func(x), do: Enum.map(x, & &1 + 1) end / - assert [%Location{} = location] = references(project, "Enum.map|(a, b)", code) + assert [%Location{} = location] = references(project, "Enum.|map(a, b)", code) assert decorate(code, location.range) =~ "def func(x), do: «Enum.map(x, & &1 + 1)»" end @@ -69,7 +69,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do defp func(x), do: Enum.map(x, & &1 + 1) end / - assert [%Location{} = location] = references(project, "Enum.map|(a, b)", code) + assert [%Location{} = location] = references(project, "Enum.|map(a, b)", code) assert decorate(code, location.range) =~ "defp func(x), do: «Enum.map(x, & &1 + 1)»" end @@ -80,7 +80,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do defp func(x), do: E.map(x, & &1 + 1) end / - assert [%Location{} = location] = references(project, "Enum.map|(a, b)", code) + assert [%Location{} = location] = references(project, "Enum.|map(a, b)", code) assert decorate(code, location.range) =~ "defp func(x), do: «E.map(x, & &1 + 1)»" end @@ -91,7 +91,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do defp func(x), do: map(x, & &1 + 1) end / - assert [%Location{} = location] = references(project, "Enum.map|(a, b)", code) + assert [%Location{} = location] = references(project, "Enum.|map(a, b)", code) assert decorate(code, location.range) =~ "defp func(x), do: «map(x, & &1 + 1)»" end @@ -104,7 +104,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do end / - assert [%Location{} = location] = references(project, "Functions.do_map|(a, b)", code) + assert [%Location{} = location] = references(project, "Functions.|do_map(a, b)", code) assert decorate(code, location.range) =~ "def func(x), do: «do_map(x, & &1 + 1)»" end end @@ -118,7 +118,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do end ] - assert [%Location{} = location] = references(project, "ReferencedModule|", code) + assert [%Location{} = location] = references(project, "|ReferencedModule", code) assert decorate(code, location.range) =~ ~s[alias «ReferencedModule»] end @@ -129,7 +129,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do end ] - assert [%Location{} = location] = references(project, "ReferencedModule|", code) + assert [%Location{} = location] = references(project, "|ReferencedModule", code) assert decorate(code, location.range) =~ ~s[@attr «ReferencedModule»] end @@ -138,7 +138,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do some_module = ReferencedModule ] - assert [%Location{} = location] = references(project, "ReferencedModule|", code) + assert [%Location{} = location] = references(project, "|ReferencedModule", code) assert decorate(code, location.range) =~ ~s[some_module = «ReferencedModule»] end @@ -148,7 +148,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do end ] - assert [%Location{} = location] = references(project, "ReferencedModule|", code) + assert [%Location{} = location] = references(project, "|ReferencedModule", code) assert decorate(code, location.range) =~ ~s[def some_fn(«ReferencedModule») do] end @@ -157,7 +157,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do %ReferencedModule{} = something_else ] - assert [%Location{} = location] = references(project, "ReferencedModule|", code) + assert [%Location{} = location] = references(project, "|ReferencedModule", code) assert decorate(code, location.range) =~ ~s[%«ReferencedModule»{} = something_else] end @@ -171,7 +171,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do end ] - assert [location_1, location_2] = references(project, "DefinedModule|", code, true) + assert [location_1, location_2] = references(project, "|DefinedModule", code, true) assert decorate(code, location_1.range) =~ ~s[defmodule «DefinedModule» do] assert decorate(code, location_2.range) =~ ~s[@attr «DefinedModule»] end @@ -185,7 +185,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do end ) - assert [location] = references(project, "%Struct|{}", code, true) + assert [location] = references(project, "%|Struct{}", code, true) assert decorate(code, location.range) =~ "«defstruct [:field]»" end @@ -198,22 +198,23 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do ] selector = ~q( - defmodule Struct do - defstruc|t [:name, :value] - end + defmodule Struct do + defstruc|t [:name, :value] + end ) + assert [location] = references(project, selector, code) assert decorate(code, location.range) =~ "def something(«%Struct{}») do" end test "excludes their definition", %{project: project} do code = ~q( - defmodule Struct do - defstruct [:field] - end + defmodule Struct do + defstruct [:field] + end ) - assert [] = references(project, "%Struct|{}", code) + assert [] = references(project, "%|Struct{}", code) end end @@ -264,7 +265,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do test "are found in a function body", %{project: project} do query = ~S[ def my_fun do - first| = 4 + |first = 4 y = first * 2 z = y * 3 + first end @@ -282,7 +283,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.ReferencesTest do query = ~S[ def my_fun do first = 4 - y = first| * 2 + y = |first * 2 z = y * 3 + first end ] diff --git a/apps/remote_control/test/lexical/remote_control/code_intelligence/variable_test.exs b/apps/remote_control/test/lexical/remote_control/code_intelligence/variable_test.exs index 4ede9e690..8a52bee5b 100644 --- a/apps/remote_control/test/lexical/remote_control/code_intelligence/variable_test.exs +++ b/apps/remote_control/test/lexical/remote_control/code_intelligence/variable_test.exs @@ -1,5 +1,6 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do alias Lexical.Ast + alias Lexical.RemoteControl.CodeIntelligence.Entity alias Lexical.RemoteControl.CodeIntelligence.Variable use ExUnit.Case @@ -11,9 +12,9 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do def find_definition(code) do {position, document} = pop_cursor(code, as: :document) analysis = Ast.analyze(document) - {:ok, {:local_or_var, var_name}} = Ast.cursor_context(analysis, position) + {:ok, {:variable, var_name}, _} = Entity.resolve(analysis, position) - case Variable.definition(analysis, position, List.to_atom(var_name)) do + case Variable.definition(analysis, position, var_name) do {:ok, entry} -> {:ok, entry.range, document} error -> error end @@ -22,11 +23,11 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do def find_references(code, include_definition? \\ false) do {position, document} = pop_cursor(code, as: :document) analysis = Ast.analyze(document) - {:ok, {:local_or_var, var_name}} = Ast.cursor_context(analysis, position) + {:ok, {:variable, var_name}, _} = Entity.resolve(analysis, position) ranges = analysis - |> Variable.references(position, List.to_atom(var_name), include_definition?) + |> Variable.references(position, var_name, include_definition?) |> Enum.map(& &1.range) {:ok, ranges, document} @@ -36,7 +37,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are returned if it is selected" do {:ok, range, doc} = ~q[ - def foo(param|) do + def foo(|param) do param end ] @@ -49,7 +50,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, range, doc} = ~q[ def foo(param) do - param| + |param end ] |> find_definition() @@ -61,7 +62,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, range, doc} = ~q[ def foo(other_param, param) do - param| + |param end ] |> find_definition() @@ -74,7 +75,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do ~q[ def foo(param) do param = param + 1 - param| + |param end ] |> find_definition() @@ -86,7 +87,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, range, doc} = ~q[ def foo(param) do - param = param| + 1 + param = |param + 1 end ] |> find_definition() @@ -97,9 +98,9 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "when there are multiple definitions on one line" do {:ok, range, doc} = ~q[ - param = 3 - foo = param = param + 1 - param| + param = 3 + foo = param = param + 1 + |param ] |> find_definition() @@ -110,7 +111,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, range, doc} = ~q[ %{key: value} = map - value| + |value ] |> find_definition() @@ -125,7 +126,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do def my_fun do foo = 3 if something do - foo| + |foo end end ] @@ -140,7 +141,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do defmodule Parent do x = 3 def fun do - unquote(x|) + unquote(|x) end end ] @@ -155,7 +156,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do x = 3 defmodule Parent do def fun do - unquote(x|) + unquote(|x) end end ] @@ -169,7 +170,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "in a function parameter" do {:ok, [range], doc} = ~q[ - def something(param|) do + def something(|param) do param end ] @@ -181,7 +182,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "can include definitions" do {:ok, [definition, reference], doc} = ~q[ - def something(param|) do + def something(|param) do param end ] @@ -195,9 +196,9 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, [first, second, third], doc} = ~q[ def something(param) do - y = param + 3 - z = param + 4 - param| + y + z + y = param + 3 + z = param + 4 + |param + y + z end ] |> find_references() @@ -210,7 +211,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are found in a function body" do {:ok, [first, second, third, fourth, fifth], doc} = ~q[ - def something(param|) do + def something(|param) do x = param + param + 3 y = param + x z = 10 + param @@ -229,7 +230,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are constrained to their definition function" do {:ok, [range], doc} = ~q[ - def something(param|) do + def something(|param) do param end @@ -245,7 +246,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are visible across blocks" do {:ok, [first, second], doc} = ~q[ - def something(param|) do + def something(|param) do if something() do param + 1 else @@ -265,7 +266,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do def something(param) do if something() do - param| = 3 + |param = 3 param + 1 end param + 1 @@ -279,7 +280,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are found in the head of a case statement" do {:ok, [range], doc} = ~q[ - def something(param|) do + def something(|param) do case param do _ -> :ok end @@ -295,7 +296,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do ~q[ def something(param) do case param do - param| when is_number(param) -> param + 1 + |param when is_number(param) -> param + 1 param -> 0 end end @@ -309,11 +310,11 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are found in a module body" do {:ok, [range], doc} = ~q[ - defmodule Outer do - something| = 3 - def foo(unquote(something)) do + defmodule Outer do + |something = 3 + def foo(unquote(something)) do + end end - end ] |> find_references() @@ -323,13 +324,13 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are found in anonymous function parameters" do {:ok, [first, second], doc} = ~q[ - def outer do - fn param| -> - y = param + 1 - x = param + 2 - x + y + def outer do + fn |param -> + y = param + 1 + x = param + 2 + x + y + end end - end ] |> find_references() @@ -340,11 +341,11 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are found in a pin operator" do {:ok, [ref], doc} = ~q[ - def outer(param|) do - fn ^param -> - nil + def outer(|param) do + fn ^param -> + nil + end end - end ] |> find_references() @@ -354,7 +355,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "are found inside of string interpolation" do {:ok, [ref], doc} = ~S[ - name| = "Stinky" + |name = "Stinky" "#{name} Stinkman" ] |> find_references() @@ -367,7 +368,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "works for variables defined outside of an if while being shadowed" do {:ok, [first, second], doc} = ~q{ - entries| = [1, 2, 3] + |entries = [1, 2, 3] entries = if something() do [4 | entries] @@ -386,14 +387,14 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do ~q" shadowed? = false fn - {:foo, entries|} -> - if shadowed? do - [1, entries] - else + {:foo, |entries} -> + if shadowed? do + [1, entries] + else + entries + end + {:bar, entries} -> entries - end - {:bar, entries} -> - entries end " |> find_references() @@ -408,27 +409,27 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, [], _doc} = ~q[ def something(param) do - other = other = other| = param + other = other = |other = param end - ] + ] |> find_references() end test "in a function body" do {:ok, [], _doc} = ~q[ - def something(param|) do + def something(|param) do param = 3 param end - ] + ] |> find_references() end test "in anonymous function arguments" do {:ok, [], _doc} = ~q[ - def something(param|) do + def something(|param) do fn param -> param + 1 end @@ -442,7 +443,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, [range], doc} = ~q[ def something do - shadow| = 4 + |shadow = 4 if true do shadow = shadow + 1 shadow @@ -458,7 +459,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do {:ok, [range], doc} = ~q[ def something do - shadow| = 4 + |shadow = 4 if true do shadow = :ok shadow @@ -474,7 +475,7 @@ defmodule Lexical.RemoteControl.CodeIntelligence.VariableTest do test "exiting nested blocks" do {:ok, [range], doc} = ~q[ - def something(param| = arg) do + def something(|param = arg) do case arg do param when is_number(n) -> param + 4 diff --git a/projects/lexical_shared/lib/lexical/document/range.ex b/projects/lexical_shared/lib/lexical/document/range.ex index e3ee41194..895396a71 100644 --- a/projects/lexical_shared/lib/lexical/document/range.ex +++ b/projects/lexical_shared/lib/lexical/document/range.ex @@ -42,7 +42,7 @@ defmodule Lexical.Document.Range do cond do position.line == start_pos.line and position.line == end_pos.line -> - position.character >= start_pos.character and position.character <= end_pos.character + position.character >= start_pos.character and position.character < end_pos.character position.line == start_pos.line -> position.character >= start_pos.character diff --git a/projects/lexical_shared/test/lexical/document/range_test.exs b/projects/lexical_shared/test/lexical/document/range_test.exs index 1d4c8860a..6161888e7 100644 --- a/projects/lexical_shared/test/lexical/document/range_test.exs +++ b/projects/lexical_shared/test/lexical/document/range_test.exs @@ -13,7 +13,12 @@ defmodule Lexical.Document.RangeTest do assert Range.contains?(range, position(1, 1)) end - test "excludes the end position" do + test "excludes the end position in a single-line range" do + range = Range.new(position(1, 1), position(1, 10)) + refute Range.contains?(range, position(1, 10)) + end + + test "excludes the end position in a multi-line range" do range = Range.new(position(1, 1), position(2, 1)) refute Range.contains?(range, position(2, 1)) end