Skip to content

Commit

Permalink
improvement: support fragments when finding DSL options
Browse files Browse the repository at this point in the history
fix: support latest elixir_ls changes

unfortunately, we're going to have a failing CI  until elixir-lsp/elixir_sense#296
is merged
  • Loading branch information
zachdaniel committed Sep 14, 2024
1 parent 87634f9 commit 8c0c2e9
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 20 deletions.
13 changes: 9 additions & 4 deletions lib/spark/elixir_sense/aliases.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ defmodule Spark.ElixirSense.Types do

alias ElixirSense.Core.Introspection

if Code.ensure_loaded?(ElixirLS.LanguageServer.Plugins.Util) do
@util ElixirLS.LanguageServer.Plugins.Util
else
@util ElixirSense.Plugins.Util
cond do
Code.ensure_loaded?(ElixirSense.Providers.Plugins.Util) ->
@util ElixirSense.Providers.Plugins.Util

Code.ensure_loaded?(ElixirLS.LanguageServer.Plugins.Util) ->
@util ElixirLS.LanguageServer.Plugins.Util

true ->
@util ElixirSense.Plugins.Util
end

if Code.ensure_loaded?(ElixirLS.Utils.Matcher) do
Expand Down
11 changes: 11 additions & 0 deletions lib/spark/elixir_sense/behaviour_setter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Spark.ElixirSense.BehaviourSetter do
@moduledoc false
# This module serves as a workaround for changes in ElixirLS behavior implementation.
# It allows us to reference a non-existent behavior without triggering compile-time warnings.

defmacro __before_compile__(_env) do
quote do
Module.put_attribute(__MODULE__, :behaviour, ElixirLS.LanguageServer.Plugin)
end
end
end
21 changes: 17 additions & 4 deletions lib/spark/elixir_sense/entity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ defmodule Spark.ElixirSense.Entity do
alias ElixirSense.Core.Introspection
alias ElixirSense.Providers.Suggestion.Complete

if Code.ensure_loaded?(ElixirLS.LanguageServer.Plugins.Util) do
@util ElixirLS.LanguageServer.Plugins.Util
else
@util ElixirSense.Plugins.Util
cond do
Code.ensure_loaded?(ElixirSense.Providers.Plugins.Util) ->
@util ElixirSense.Providers.Plugins.Util

Code.ensure_loaded?(ElixirLS.LanguageServer.Plugins.Util) ->
@util ElixirLS.LanguageServer.Plugins.Util

true ->
@util ElixirSense.Plugins.Util
end

def find_entities(type, hint) do
Expand All @@ -31,6 +36,14 @@ defmodule Spark.ElixirSense.Entity do
builtins =
if builtins && !String.contains?(hint, ".") && lowercase_string?(hint) do
cond do
Code.ensure_loaded?(ElixirSense.Providers.Completion.CompletionEngine) ->
apply(ElixirSense.Providers.Completion.CompletionEngine, :complete, [
to_string("#{inspect(builtins)}.#{hint}"),
apply(ElixirSense.Core.State.Env, :__struct__, []),
apply(ElixirSense.Core.Metadata, :__struct__, []),
0
])

Code.ensure_loaded?(ElixirLS.Utils.CompletionEngine) ->
apply(ElixirLS.Utils.CompletionEngine, :complete, [
to_string("#{inspect(builtins)}.#{hint}"),
Expand Down
15 changes: 14 additions & 1 deletion lib/spark/elixir_sense/plugin.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ defmodule Spark.ElixirSense.Plugin do

@matcher ElixirSense.Providers.Suggestion.Matcher

@before_compile Spark.ElixirSense.BehaviourSetter

case Code.ensure_compiled(ElixirSense.Plugin) do
{:module, _} ->
@behaviour ElixirSense.Plugin
Expand All @@ -18,6 +20,17 @@ defmodule Spark.ElixirSense.Plugin do
:ok
end

case Code.ensure_compiled(ElixirSense.Providers.Plugin) do
{:module, _} ->
@behaviour ElixirSense.Providers.Plugin
@behaviour ElixirSense.Providers.Completion.GenericReducer
@generic_reducer ElixirSense.Providers.Completion.GenericReducer
@matcher ElixirSense.Providers.Utils.Matcher

_ ->
:ok
end

case Code.ensure_compiled(ElixirLS.LanguageServer.Plugin) do
{:module, _} ->
@behaviour ElixirLS.LanguageServer.Plugin
Expand Down Expand Up @@ -60,7 +73,7 @@ defmodule Spark.ElixirSense.Plugin do
suggestions
end
rescue
_ ->
_e ->
:ignore
end

Expand Down
81 changes: 78 additions & 3 deletions lib/spark/igniter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,50 @@ defmodule Spark.Igniter do

{:ok, {igniter, _source, zipper}} = Igniter.Code.Module.find_module(igniter, module)

{igniter, do_get_option(zipper, path)}
zipper =
case Igniter.Code.Common.move_to_do_block(zipper) do
{:ok, zipper} -> zipper
_ -> zipper
end

zipper
|> search_modules(module)
|> Enum.reduce_while({igniter, :error}, fn search_module, {igniter, :error} ->
{igniter, zipper} =
if search_module == module do
{igniter, {:ok, zipper}}
else
with {:ok, {igniter, _source, zipper}} <-
Igniter.Code.Module.find_module(igniter, search_module),
{:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper) do
{igniter, {:ok, zipper}}
else
{:error, igniter} ->
{igniter, :error}

_ ->
{igniter, :error}
end
end

with {:ok, zipper} <- zipper,
{:ok, value} <- do_get_option(zipper, path) do
{:halt, {igniter, {:ok, value}}}
else
_ -> {:cont, {igniter, :error}}
end
end)
end

defp do_get_option(zipper, [{:option, name}]) do
with {:ok, zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, name, 1),
{:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 0) do
zipper = Igniter.Code.Common.maybe_move_to_single_child_block(zipper)

case Igniter.Code.Common.expand_literal(zipper) do
{:ok, value} -> value
:error -> zipper.node
{:ok, value} -> {:ok, value}
:error -> {:ok, zipper.node}
end
else
_ -> :error
Expand Down Expand Up @@ -350,4 +384,45 @@ defmodule Spark.Igniter do
end
end)
end

# sobelow_skip ["RCE.CodeModule"]
defp search_modules(zipper, base) do
with {:ok, zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:use,
2,
fn zipper ->
with {:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 1),
{:ok, _} <- Igniter.Code.Keyword.get_key(zipper, :fragments) do
true
else
_ ->
false
end
end
),
{:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 1),
{:ok, zipper} <- Igniter.Code.Keyword.get_key(zipper, :fragments) do
evaled =
try do
case Igniter.Code.Common.expand_literal(zipper) do
{:ok, value} -> value
:error -> Code.eval_quoted(zipper.node)
end
rescue
_e ->
[]
end

if is_list(evaled) do
Enum.uniq([base | Enum.filter(evaled, &is_atom/1)])
else
[base]
end
else
_ ->
[base]
end
end
end
7 changes: 5 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ defmodule Spark.MixProject do
{:sourceror, "~> 1.2"},
# in 3.x, make this dependency optional
{:jason, "~> 1.4"},
{:igniter, "~> 0.2 and >= 0.2.6"},
{:igniter, "~> 0.2 and >= 0.3.36"},
# Dev/Test dependencies
{:benchee, "~> 1.3", only: [:dev, :test]},
{:eflame, "~> 1.0", only: [:dev, :test], runtime: false},
Expand All @@ -112,7 +112,10 @@ defmodule Spark.MixProject do
{:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:git_ops, "~> 2.5", only: [:dev, :test]},
{:mix_test_watch, "~> 1.0", only: [:dev, :test], runtime: false},
{:elixir_sense, github: "elixir-lsp/elixir_sense", only: [:test, :dev, :docs]},
{:elixir_sense,
github: "elixir-lsp/elixir_sense",
only: [:test, :docs],
ref: "4adfc55dc6902f56e6d070e14fba768a0ff05bd3"},
{:mix_audit, ">= 0.0.0", only: [:dev, :test], runtime: false}
]
end
Expand Down
8 changes: 2 additions & 6 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,27 @@
"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.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"eflame": {:hex, :eflame, "1.0.1", "0664d287e39eef3c413749254b3af5f4f8b00be71c1af67d325331c4890be0fc", [:mix], [], "hexpm", "e0b08854a66f9013129de0b008488f3411ae9b69b902187837f994d7a99cf04e"},
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "653ef79ba9b1abab05c576e3406af3def7f5f86e", []},
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "4adfc55dc6902f56e6d070e14fba768a0ff05bd3", [ref: "4adfc55dc6902f56e6d070e14fba768a0ff05bd3"]},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"},
"ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
"git_ops": {:hex, :git_ops, "2.6.1", "cc7799a68c26cf814d6d1a5121415b4f5bf813de200908f930b27a2f1fe9dad5", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "ce62d07e41fe993ec22c35d5edb11cf333a21ddaead6f5d9868fcb607d42039e"},
"glob_ex": {:hex, :glob_ex, "0.1.8", "f7ef872877ca2ae7a792ab1f9ff73d9c16bf46ecb028603a8a3c5283016adc07", [:mix], [], "hexpm", "9e39d01729419a60a937c9260a43981440c43aa4cadd1fa6672fecd58241c464"},
"igniter": {:hex, :igniter, "0.3.24", "791a91650ffab9d66b9a3011c66491f767577ad55c363f820cc188554207ee6f", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "2e1d336534c6129bae0db043fae650303b96974c0488c290191d6d4c61ec9a9f"},
"inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
"igniter": {:hex, :igniter, "0.3.36", "7dffb41e8c25dac3de8a0947c4973dc3db3cd25f394fd87ac334fe18a725f291", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "5a493222cbf4e3cf0106cd090c93a1f61fa4df958b7d00e03a836e8a67a4bab2"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
"mix_audit": {:hex, :mix_audit, "2.1.3", "c70983d5cab5dca923f9a6efe559abfb4ec3f8e87762f02bab00fa4106d17eda", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "8c3987100b23099aea2f2df0af4d296701efd031affb08d0746b2be9e35988ec"},
"mix_test_watch": {:hex, :mix_test_watch, "1.2.0", "1f9acd9e1104f62f280e30fc2243ae5e6d8ddc2f7f4dc9bceb454b9a41c82b42", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "278dc955c20b3fb9a3168b5c2493c2e5cffad133548d307e0a50c7f2cfbf34f6"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"owl": {:hex, :owl, "0.11.0", "2cd46185d330aa2400f1c8c3cddf8d2ff6320baeff23321d1810e58127082cae", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "73f5783f0e963cc04a061be717a0dbb3e49ae0c4bfd55fb4b78ece8d33a65efe"},
"rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"},
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
"sourceror": {:hex, :sourceror, "1.6.0", "9907884e1449a4bd7dbaabe95088ed4d9a09c3c791fb0103964e6316bc9448a7", [:mix], [], "hexpm", "e90aef8c82dacf32c89c8ef83d1416fc343cd3e5556773eeffd2c1e3f991f699"},
"spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"ucwidth": {:hex, :ucwidth, "0.2.0", "1f0a440f541d895dff142275b96355f7e91e15bca525d4a0cc788ea51f0e3441", [:mix], [], "hexpm", "c1efd1798b8eeb11fb2bec3cafa3dd9c0c3647bee020543f0340b996177355bf"},
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
}
39 changes: 39 additions & 0 deletions test/igniter_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Spark.IgniterTest do
use ExUnit.Case

import Igniter.Test

test "options are found in DSLs" do
assert {_igniter, {:ok, Bar.Baz}} =
test_project()
|> Igniter.Code.Module.create_module(TedDansen, """
use Spark.Test.Contact
contact do
module(Bar.Baz)
end
""")
|> Spark.Igniter.get_option(TedDansen, [:contact, :module])
end

test "options are found in fragments" do
assert {_igniter, {:ok, "foobar"}} =
test_project()
|> Igniter.Code.Module.create_module(TedDansenFragment, """
@moduledoc false
use Spark.Dsl.Fragment, of: Spark.Test.Contact
address do
street("foobar")
end
""")
|> Igniter.Code.Module.create_module(TedDansen, """
use Spark.Test.Contact, fragments: [TedDansenFragment]
contact do
module(Bar.Baz)
end
""")
|> Spark.Igniter.get_option(TedDansen, [:address, :street])
end
end

0 comments on commit 8c0c2e9

Please sign in to comment.