mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
fix: support new versions of ecto's struct fields
fix: fixes for elixir_sense plugin
This commit is contained in:
parent
951eb256fc
commit
2986838a19
5 changed files with 577 additions and 49 deletions
|
@ -358,12 +358,13 @@ if Code.ensure_loaded?(ElixirSense.Plugin) do
|
|||
Enum.flat_map(
|
||||
extensions,
|
||||
fn extension ->
|
||||
if :erlang.function_exported(extension, :sections, 0) do
|
||||
try do
|
||||
Enum.filter(extension.sections(), fn section ->
|
||||
Matcher.match?(to_string(section.name), hint)
|
||||
end)
|
||||
else
|
||||
[]
|
||||
rescue
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
)
|
||||
|
@ -373,7 +374,7 @@ if Code.ensure_loaded?(ElixirSense.Plugin) do
|
|||
Enum.flat_map(
|
||||
extensions,
|
||||
fn extension ->
|
||||
if :erlang.function_exported(extension, :sections, 0) do
|
||||
try do
|
||||
Enum.flat_map(extension.sections(), fn section ->
|
||||
if section.name == first do
|
||||
do_find_constructors(section, rest, hint, type)
|
||||
|
@ -381,8 +382,9 @@ if Code.ensure_loaded?(ElixirSense.Plugin) do
|
|||
[]
|
||||
end
|
||||
end)
|
||||
else
|
||||
[]
|
||||
rescue
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
)
|
||||
|
|
|
@ -32,7 +32,7 @@ if Code.ensure_loaded?(ElixirSense.Plugin) do
|
|||
end
|
||||
|
||||
custom =
|
||||
for module <- module_store.by_behaviour[behaviour],
|
||||
for module <- module_store.by_behaviour[behaviour] || [],
|
||||
mod_str = inspect(module),
|
||||
!String.starts_with?(mod_str, "Ash."),
|
||||
Util.match_module?(mod_str, hint) do
|
||||
|
|
|
@ -407,20 +407,24 @@ defmodule Ash.Filter do
|
|||
@doc "Whether or not a given template contains an actor reference"
|
||||
def template_references_actor?({:_actor, _}), do: true
|
||||
|
||||
def template_references_actor?(filter) when is_list(filter) do
|
||||
Enum.any?(filter, &template_references_actor?/1)
|
||||
def template_references_actor?(%BooleanExpression{op: :and, left: left, right: right}) do
|
||||
template_references_actor?(left) || template_references_actor?(right)
|
||||
end
|
||||
|
||||
def template_references_actor?(filter) when is_map(filter) do
|
||||
filter
|
||||
|> Map.delete(:__struct__)
|
||||
|> Enum.any?(fn {key, value} ->
|
||||
template_references_actor?(key) || template_references_actor?(value)
|
||||
end)
|
||||
def template_references_actor?(%Not{expression: expression}) do
|
||||
template_references_actor?(expression)
|
||||
end
|
||||
|
||||
def template_references_actor?(tuple) when is_tuple(tuple) do
|
||||
Enum.any?(Tuple.to_list(tuple), &template_references_actor?/1)
|
||||
def template_references_actor?(%{left: left, right: right}) do
|
||||
template_references_actor?(left) || template_references_actor?(right)
|
||||
end
|
||||
|
||||
def template_references_actor?(%{arguments: args}) do
|
||||
Enum.any?(args, &template_references_actor?/1)
|
||||
end
|
||||
|
||||
def template_references_actor?(%Ash.Query.Call{args: args}) do
|
||||
Enum.any?(args, &template_references_actor?/1)
|
||||
end
|
||||
|
||||
def template_references_actor?(_), do: false
|
||||
|
|
|
@ -14,9 +14,19 @@ defmodule Ash.Schema do
|
|||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
struct_fields_name =
|
||||
if Module.get_attribute(__MODULE__, :struct_fields) do
|
||||
:struct_fields
|
||||
else
|
||||
:ecto_struct_fields
|
||||
end
|
||||
|
||||
for relationship <- Ash.Resource.Info.relationships(__MODULE__) do
|
||||
@struct_fields {relationship.name,
|
||||
%Ash.NotLoaded{type: :relationship, field: relationship.name}}
|
||||
Module.put_attribute(
|
||||
__MODULE__,
|
||||
struct_fields_name,
|
||||
{relationship.name, %Ash.NotLoaded{type: :relationship, field: relationship.name}}
|
||||
)
|
||||
end
|
||||
|
||||
for attribute <- Ash.Resource.Info.attributes(__MODULE__) do
|
||||
|
@ -46,13 +56,11 @@ defmodule Ash.Schema do
|
|||
|
||||
field(aggregate.name, Ash.Type.ecto_type(type), virtual: true)
|
||||
|
||||
struct_fields = Keyword.delete(@struct_fields, aggregate.name)
|
||||
Module.delete_attribute(__MODULE__, :struct_fields)
|
||||
Module.register_attribute(__MODULE__, :struct_fields, accumulate: true)
|
||||
Enum.each(struct_fields, &Module.put_attribute(__MODULE__, :struct_fields, &1))
|
||||
|
||||
@struct_fields {aggregate.name,
|
||||
%Ash.NotLoaded{type: :aggregate, field: aggregate.name}}
|
||||
Module.put_attribute(
|
||||
__MODULE__,
|
||||
struct_fields_name,
|
||||
{aggregate.name, %Ash.NotLoaded{type: :aggregate, field: aggregate.name}}
|
||||
)
|
||||
end
|
||||
|
||||
for calculation <- Ash.Resource.Info.calculations(__MODULE__) do
|
||||
|
@ -60,14 +68,17 @@ defmodule Ash.Schema do
|
|||
|
||||
field(calculation.name, Ash.Type.ecto_type(calculation.type), virtual: true)
|
||||
|
||||
struct_fields = Keyword.delete(@struct_fields, calculation.name)
|
||||
Module.delete_attribute(__MODULE__, :struct_fields)
|
||||
Module.register_attribute(__MODULE__, :struct_fields, accumulate: true)
|
||||
Enum.each(struct_fields, &Module.put_attribute(__MODULE__, :struct_fields, &1))
|
||||
|
||||
@struct_fields {calculation.name,
|
||||
%Ash.NotLoaded{type: :calculation, field: calculation.name}}
|
||||
Module.put_attribute(
|
||||
__MODULE__,
|
||||
struct_fields_name,
|
||||
{calculation.name, %Ash.NotLoaded{type: :calculation, field: calculation.name}}
|
||||
)
|
||||
end
|
||||
|
||||
struct_fields = Keyword.new(Module.get_attribute(__MODULE__, struct_fields_name))
|
||||
Module.delete_attribute(__MODULE__, struct_fields_name)
|
||||
Module.register_attribute(__MODULE__, struct_fields_name, accumulate: true)
|
||||
Enum.each(struct_fields, &Module.put_attribute(__MODULE__, struct_fields_name, &1))
|
||||
end
|
||||
end
|
||||
else
|
||||
|
@ -77,9 +88,19 @@ defmodule Ash.Schema do
|
|||
@primary_key false
|
||||
|
||||
schema Ash.DataLayer.source(__MODULE__) do
|
||||
struct_fields_name =
|
||||
if Module.get_attribute(__MODULE__, :struct_fields) do
|
||||
:struct_fields
|
||||
else
|
||||
:ecto_struct_fields
|
||||
end
|
||||
|
||||
for relationship <- Ash.Resource.Info.relationships(__MODULE__) do
|
||||
@struct_fields {relationship.name,
|
||||
%Ash.NotLoaded{type: :relationship, field: relationship.name}}
|
||||
Module.put_attribute(
|
||||
__MODULE__,
|
||||
struct_fields_name,
|
||||
{relationship.name, %Ash.NotLoaded{type: :relationship, field: relationship.name}}
|
||||
)
|
||||
end
|
||||
|
||||
for attribute <- Ash.Resource.Info.attributes(__MODULE__) do
|
||||
|
@ -109,13 +130,11 @@ defmodule Ash.Schema do
|
|||
|
||||
field(aggregate.name, Ash.Type.ecto_type(type), virtual: true)
|
||||
|
||||
struct_fields = Keyword.delete(@struct_fields, aggregate.name)
|
||||
Module.delete_attribute(__MODULE__, :struct_fields)
|
||||
Module.register_attribute(__MODULE__, :struct_fields, accumulate: true)
|
||||
Enum.each(struct_fields, &Module.put_attribute(__MODULE__, :struct_fields, &1))
|
||||
|
||||
@struct_fields {aggregate.name,
|
||||
%Ash.NotLoaded{type: :aggregate, field: aggregate.name}}
|
||||
Module.put_attribute(
|
||||
__MODULE__,
|
||||
struct_fields_name,
|
||||
{aggregate.name, %Ash.NotLoaded{type: :aggregate, field: aggregate.name}}
|
||||
)
|
||||
end
|
||||
|
||||
for calculation <- Ash.Resource.Info.calculations(__MODULE__) do
|
||||
|
@ -123,14 +142,17 @@ defmodule Ash.Schema do
|
|||
|
||||
field(calculation.name, Ash.Type.ecto_type(calculation.type), virtual: true)
|
||||
|
||||
struct_fields = Keyword.delete(@struct_fields, calculation.name)
|
||||
Module.delete_attribute(__MODULE__, :struct_fields)
|
||||
Module.register_attribute(__MODULE__, :struct_fields, accumulate: true)
|
||||
Enum.each(struct_fields, &Module.put_attribute(__MODULE__, :struct_fields, &1))
|
||||
|
||||
@struct_fields {calculation.name,
|
||||
%Ash.NotLoaded{type: :calculation, field: calculation.name}}
|
||||
Module.put_attribute(
|
||||
__MODULE__,
|
||||
struct_fields_name,
|
||||
{calculation.name, %Ash.NotLoaded{type: :calculation, field: calculation.name}}
|
||||
)
|
||||
end
|
||||
|
||||
struct_fields = Keyword.new(Module.get_attribute(__MODULE__, struct_fields_name))
|
||||
Module.delete_attribute(__MODULE__, struct_fields_name)
|
||||
Module.register_attribute(__MODULE__, struct_fields_name, accumulate: true)
|
||||
Enum.each(struct_fields, &Module.put_attribute(__MODULE__, struct_fields_name, &1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
500
test/elixir_sense/plugin_test.exs
Normal file
500
test/elixir_sense/plugin_test.exs
Normal file
|
@ -0,0 +1,500 @@
|
|||
defmodule Ash.ElixirSense.PluginTest do
|
||||
use ExUnit.Case
|
||||
|
||||
def cursors(text) do
|
||||
{_, cursors} =
|
||||
ElixirSense.Core.Source.walk_text(text, {false, []}, fn
|
||||
"#", rest, _, _, {_comment?, cursors} ->
|
||||
{rest, {true, cursors}}
|
||||
|
||||
"\n", rest, _, _, {_comment?, cursors} ->
|
||||
{rest, {false, cursors}}
|
||||
|
||||
"^", rest, line, col, {true, cursors} ->
|
||||
{rest, {true, [%{line: line - 1, col: col} | cursors]}}
|
||||
|
||||
_, rest, _, _, acc ->
|
||||
{rest, acc}
|
||||
end)
|
||||
|
||||
Enum.reverse(cursors)
|
||||
end
|
||||
|
||||
def suggestions(buffer, cursor) do
|
||||
ElixirSense.suggestions(buffer, cursor.line, cursor.col)
|
||||
end
|
||||
|
||||
def suggestions(buffer, cursor, type) do
|
||||
suggestions(buffer, cursor)
|
||||
|> Enum.filter(fn s -> s.type == type end)
|
||||
end
|
||||
|
||||
def suggestions_by_kind(buffer, cursor, kind) do
|
||||
suggestions(buffer, cursor)
|
||||
|> Enum.filter(fn s -> s[:kind] == kind end)
|
||||
end
|
||||
|
||||
test "suggesting DSL items" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource
|
||||
|
||||
attrib
|
||||
# ^
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
detail: "Dsl Section",
|
||||
documentation: doc,
|
||||
kind: :function,
|
||||
label: "attributes",
|
||||
snippet: "attributes do\n $0\nend",
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
|
||||
assert doc =~ "attributes"
|
||||
end
|
||||
|
||||
test "suggesting DSL options in a section" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
@moduledoc false
|
||||
use Ash.Resource
|
||||
|
||||
resource do
|
||||
descri
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
detail: "Option",
|
||||
documentation:
|
||||
"A human readable description of the resource, to be used in generated documentation",
|
||||
kind: :function,
|
||||
label: "description",
|
||||
snippet: "description \"$0\"",
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting available sections" do
|
||||
buffer = """
|
||||
defmodule do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
# ^
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
labels =
|
||||
result |> Enum.filter(&Map.has_key?(&1, :label)) |> Enum.map(& &1.label) |> Enum.sort()
|
||||
|
||||
assert labels == [
|
||||
"actions",
|
||||
"aggregates",
|
||||
"attributes",
|
||||
"calculations",
|
||||
"changes",
|
||||
"code_interface",
|
||||
"identities",
|
||||
"multitenancy",
|
||||
"preparations",
|
||||
"pub_sub",
|
||||
"relationships",
|
||||
"resource",
|
||||
"validations"
|
||||
]
|
||||
end
|
||||
|
||||
test "suggesting available nested sections" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
actions do
|
||||
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
labels = result |> Enum.map(& &1.label) |> Enum.sort()
|
||||
|
||||
assert labels == ["create", "defaults", "destroy", "primary_actions?", "read", "update"]
|
||||
end
|
||||
|
||||
test "suggesting available options and entities" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
actions do
|
||||
create do
|
||||
|
||||
# ^
|
||||
end
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
labels = result |> Enum.map(& &1.label) |> Enum.sort()
|
||||
|
||||
assert labels == [
|
||||
"accept",
|
||||
"allow_nil_input",
|
||||
"argument",
|
||||
"change",
|
||||
"description",
|
||||
"error_handler",
|
||||
"manual?",
|
||||
"metadata",
|
||||
"name",
|
||||
"primary?",
|
||||
"reject",
|
||||
"require_attributes",
|
||||
"validate"
|
||||
]
|
||||
end
|
||||
|
||||
test "suggesting available options when in keyword format" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
attributes do
|
||||
attribute :name, :type, all
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
detail: "Option",
|
||||
kind: :function,
|
||||
label: "always_select?",
|
||||
snippet: "always_select?: true",
|
||||
type: :generic
|
||||
},
|
||||
%{
|
||||
detail: "Option",
|
||||
kind: :function,
|
||||
label: "allow_nil?",
|
||||
snippet: "allow_nil?: false",
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting available values when in keyword format" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
attributes do
|
||||
attribute :name, :type, allow_nil?:
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
documentation: "true",
|
||||
insert_text: "true",
|
||||
kind: :type_parameter,
|
||||
label: "boolean",
|
||||
priority: 0,
|
||||
snippet: "true",
|
||||
type: :generic
|
||||
},
|
||||
%{
|
||||
documentation: "false",
|
||||
insert_text: "false",
|
||||
kind: :type_parameter,
|
||||
label: "boolean",
|
||||
priority: 0,
|
||||
snippet: "false",
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting available values in section" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
attributes do
|
||||
attribute :name, :type do
|
||||
allow_nil?
|
||||
# ^
|
||||
end
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
documentation: "true",
|
||||
insert_text: "true",
|
||||
kind: :type_parameter,
|
||||
label: "boolean",
|
||||
priority: 0,
|
||||
snippet: "true",
|
||||
type: :generic
|
||||
},
|
||||
%{
|
||||
documentation: "false",
|
||||
insert_text: "false",
|
||||
kind: :type_parameter,
|
||||
label: "boolean",
|
||||
priority: 0,
|
||||
snippet: "false",
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting values in args with no hint" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
pub_sub do
|
||||
publish_all
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
documentation: ":create",
|
||||
insert_text: ":create",
|
||||
kind: :type_parameter,
|
||||
label: "type",
|
||||
priority: 0,
|
||||
snippet: ":create",
|
||||
type: :generic
|
||||
},
|
||||
%{
|
||||
documentation: ":update",
|
||||
insert_text: ":update",
|
||||
kind: :type_parameter,
|
||||
label: "type",
|
||||
priority: 0,
|
||||
snippet: ":update",
|
||||
type: :generic
|
||||
},
|
||||
%{
|
||||
documentation: ":destroy",
|
||||
insert_text: ":destroy",
|
||||
kind: :type_parameter,
|
||||
label: "type",
|
||||
priority: 0,
|
||||
snippet: ":destroy",
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting values in args with hint" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
pub_sub do
|
||||
publish_all :crea
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
documentation: ":create",
|
||||
insert_text: ":create",
|
||||
kind: :type_parameter,
|
||||
label: "type",
|
||||
priority: 0,
|
||||
snippet: ":create",
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting ash builtin types" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
attributes do
|
||||
attribute :name, :str
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
detail: "Ash type",
|
||||
insert_text: "string",
|
||||
kind: :type_parameter,
|
||||
label: ":string",
|
||||
priority: 0,
|
||||
snippet: nil,
|
||||
type: :generic
|
||||
},
|
||||
%{
|
||||
detail: "Ash type",
|
||||
insert_text: "ci_string",
|
||||
kind: :type_parameter,
|
||||
label: ":ci_string",
|
||||
priority: 0,
|
||||
snippet: nil,
|
||||
type: :generic
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting builtin types for behaviours" do
|
||||
buffer = """
|
||||
defmodule MyResource do
|
||||
use Ash.Resource,
|
||||
extensions: [Ash.Notifier.PubSub]
|
||||
|
||||
actions do
|
||||
create :create do
|
||||
change manage_relati
|
||||
# ^
|
||||
end
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert [
|
||||
%{
|
||||
args: "argument, relationship_name \\\\ nil, opts",
|
||||
name: "manage_relationship",
|
||||
args_list: ["argument", "relationship_name \\\\ nil", "opts"],
|
||||
arity: 2,
|
||||
def_arity: 3,
|
||||
metadata: %{defaults: 1},
|
||||
origin: "Ash.Resource.Change.Builtins",
|
||||
snippet: nil,
|
||||
spec: "",
|
||||
summary:
|
||||
"Calls `Ash.Changeset.manage_relationship/4` with the changeset and relationship provided, using the value provided for the named argument",
|
||||
type: :function,
|
||||
visibility: :public
|
||||
},
|
||||
%{
|
||||
args: "argument, relationship_name \\\\ nil, opts",
|
||||
args_list: ["argument", "relationship_name \\\\ nil", "opts"],
|
||||
arity: 3,
|
||||
def_arity: 3,
|
||||
metadata: %{defaults: 1},
|
||||
name: "manage_relationship",
|
||||
origin: "Ash.Resource.Change.Builtins",
|
||||
snippet: nil,
|
||||
spec: "",
|
||||
summary:
|
||||
"Calls `Ash.Changeset.manage_relationship/4` with the changeset and relationship provided, using the value provided for the named argument",
|
||||
type: :function,
|
||||
visibility: :public
|
||||
}
|
||||
] = result
|
||||
end
|
||||
|
||||
test "suggesting available sections from single extension types" do
|
||||
buffer = """
|
||||
defmodule do
|
||||
use Ash.Resource,
|
||||
data_layer: Ash.DataLayer.Ets
|
||||
|
||||
ets do
|
||||
|
||||
# ^
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
[cursor] = cursors(buffer)
|
||||
|
||||
result = suggestions(buffer, cursor)
|
||||
|
||||
assert result == [
|
||||
%{
|
||||
detail: "Option",
|
||||
documentation: nil,
|
||||
kind: :function,
|
||||
label: "private?",
|
||||
snippet: "private? true",
|
||||
type: :generic
|
||||
}
|
||||
]
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue