diff --git a/apps/language_server/lib/language_server/providers/declaration/locator.ex b/apps/language_server/lib/language_server/providers/declaration/locator.ex index 942bdf421..dd7057ca5 100644 --- a/apps/language_server/lib/language_server/providers/declaration/locator.ex +++ b/apps/language_server/lib/language_server/providers/declaration/locator.ex @@ -15,8 +15,9 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do alias ElixirLS.LanguageServer.Location alias ElixirSense.Core.Parser alias ElixirSense.Core.State.{ModFunInfo, SpecInfo} - + require ElixirSense.Core.Introspection, as: Introspection + alias ElixirLS.LanguageServer.Providers.LocatorUtils @doc """ Finds the declaration (callback or protocol definition) for the function under the cursor. @@ -28,26 +29,23 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do Returns either a single `%Location{}` or a list of locations if multiple declarations are found. """ def declaration(code, line, column, options \\ []) do - case NormalizedCode.Fragment.surround_context(code, {line, column}) do - :none -> + case LocatorUtils.build(code, line, column, options) do + nil -> nil - context -> - metadata = - Keyword.get_lazy(options, :metadata, fn -> - Parser.parse_string(code, true, false, {line, column}) - end) - - env = Metadata.get_cursor_env(metadata, {line, column}, {context.begin, context.end}) - find(context, env, metadata) + info -> + find(info) end end @doc false - def find(context, %State.Env{module: module} = env, metadata) do - binding_env = Binding.from_env(env, metadata, context.begin) - - type = SurroundContext.to_binding(context.context, module) + def find(%{ + context: context, + env: %State.Env{module: module} = env, + metadata: metadata, + binding_env: binding_env, + type: type + }) do case type do nil -> diff --git a/apps/language_server/lib/language_server/providers/definition/locator.ex b/apps/language_server/lib/language_server/providers/definition/locator.ex index a0aecc8f4..cd97bd613 100644 --- a/apps/language_server/lib/language_server/providers/definition/locator.ex +++ b/apps/language_server/lib/language_server/providers/definition/locator.ex @@ -25,47 +25,29 @@ defmodule ElixirLS.LanguageServer.Providers.Definition.Locator do alias ElixirLS.LanguageServer.Plugins.Phoenix.Scope alias ElixirSense.Core.Normalized.Code, as: NormalizedCode + alias ElixirLS.LanguageServer.Providers.LocatorUtils def definition(code, line, column, options \\ []) do - case NormalizedCode.Fragment.surround_context(code, {line, column}) do - :none -> + case LocatorUtils.build(code, line, column, options) do + nil -> nil - context -> - metadata = - Keyword.get_lazy(options, :metadata, fn -> - Parser.parse_string(code, true, false, {line, column}) - end) - - env = Metadata.get_cursor_env(metadata, {line, column}, {context.begin, context.end}) - - find( - context, - env, - metadata - ) + info -> + find(info) end end @doc """ Finds out where a module, function, macro or variable was defined. """ - @spec find( - any(), - State.Env.t(), - Metadata.t() - ) :: %Location{} | nil - def find( - context, - %State.Env{ - module: module, - attributes: attributes - } = env, - metadata - ) do - binding_env = Binding.from_env(env, metadata, context.begin) - - type = SurroundContext.to_binding(context.context, module) + @spec find(LocatorUtils.t()) :: %Location{} | nil + def find(%{ + context: context, + env: %State.Env{module: module, attributes: attributes} = env, + metadata: metadata, + binding_env: binding_env, + type: type + }) do case type do nil -> diff --git a/apps/language_server/lib/language_server/providers/hover/docs.ex b/apps/language_server/lib/language_server/providers/hover/docs.ex index dd161ad4d..c6e3de9e1 100644 --- a/apps/language_server/lib/language_server/providers/hover/docs.ex +++ b/apps/language_server/lib/language_server/providers/hover/docs.ex @@ -20,6 +20,7 @@ defmodule ElixirLS.LanguageServer.Providers.Hover.Docs do alias ElixirSense.Core.TypeInfo alias ElixirSense.Core.Parser alias ElixirSense.Core.Source + alias ElixirLS.LanguageServer.Providers.LocatorUtils @type markdown :: String.t() @@ -71,20 +72,12 @@ defmodule ElixirLS.LanguageServer.Providers.Hover.Docs do |> Kernel.--([:exception, :message]) def docs(code, line, column, options \\ []) do - case NormalizedCode.Fragment.surround_context(code, {line, column}) do - :none -> + case LocatorUtils.build(code, line, column, options) do + nil -> nil - %{begin: begin_pos, end: end_pos} = context -> - metadata = - Keyword.get_lazy(options, :metadata, fn -> - Parser.parse_string(code, true, false, {line, column}) - end) - - env = - Metadata.get_cursor_env(metadata, {line, column}, {begin_pos, end_pos}) - - case all(context, env, metadata) do + %{context: %{begin: begin_pos, end: end_pos}} = info -> + case all(info) do [] -> nil @@ -100,16 +93,15 @@ defmodule ElixirLS.LanguageServer.Providers.Hover.Docs do end end - defp all( - context, - %State.Env{ + defp all(%{ + context: context, + env: %State.Env{ module: module } = env, - metadata - ) do - binding_env = Binding.from_env(env, metadata, context.begin) - - type = SurroundContext.to_binding(context.context, module) + metadata: metadata, + binding_env: binding_env, + type: type + }) do case type do nil -> diff --git a/apps/language_server/lib/language_server/providers/implementation/locator.ex b/apps/language_server/lib/language_server/providers/implementation/locator.ex index cc15b57a9..55d0f40b5 100644 --- a/apps/language_server/lib/language_server/providers/implementation/locator.ex +++ b/apps/language_server/lib/language_server/providers/implementation/locator.ex @@ -19,48 +19,31 @@ defmodule ElixirLS.LanguageServer.Providers.Implementation.Locator do alias ElixirLS.LanguageServer.Location alias ElixirSense.Core.Parser alias ElixirSense.Core.Normalized.Code, as: NormalizedCode + alias ElixirLS.LanguageServer.Providers.LocatorUtils require ElixirSense.Core.Introspection, as: Introspection def implementations(code, line, column, options \\ []) do - case NormalizedCode.Fragment.surround_context(code, {line, column}) do - :none -> + case LocatorUtils.build(code, line, column, options) do + nil -> [] - context -> - metadata = - Keyword.get_lazy(options, :metadata, fn -> - Parser.parse_string(code, true, false, {line, column}) - end) - - env = Metadata.get_cursor_env(metadata, {line, column}, {context.begin, context.end}) - - find( - context, - env, - metadata - ) + info -> + find(info) end end @doc """ Finds out where a callback, protocol or delegate was implemented. """ - @spec find( - any(), - State.Env.t(), - Metadata.t() - ) :: [%Location{}] - def find( - context, - %State.Env{ - module: module - } = env, - metadata - ) do - binding_env = Binding.from_env(env, metadata, context.begin) - - type = SurroundContext.to_binding(context.context, module) + @spec find(LocatorUtils.t()) :: [%Location{}] + def find(%{ + context: context, + env: %State.Env{module: module} = env, + metadata: metadata, + binding_env: binding_env, + type: type + }) do case type do nil -> diff --git a/apps/language_server/lib/language_server/providers/locator_utils.ex b/apps/language_server/lib/language_server/providers/locator_utils.ex new file mode 100644 index 000000000..160dd1a16 --- /dev/null +++ b/apps/language_server/lib/language_server/providers/locator_utils.ex @@ -0,0 +1,40 @@ +defmodule ElixirLS.LanguageServer.Providers.LocatorUtils do + @moduledoc """ + Helper for providers resolving symbols at a given cursor position. + """ + + alias ElixirSense.Core.{Binding, Metadata, Parser, SurroundContext} + alias ElixirSense.Core.Normalized.Code, as: NormalizedCode + + @type t :: %{ + context: map(), + env: Metadata.t(), + metadata: Metadata.t(), + binding_env: any(), + type: any() + } + + @spec build(String.t(), pos_integer, pos_integer, keyword()) :: t | nil + def build(code, line, column, options \\ []) do + case NormalizedCode.Fragment.surround_context(code, {line, column}) do + :none -> + nil + + context -> + metadata = + Keyword.get_lazy(options, :metadata, fn -> + Parser.parse_string(code, true, false, {line, column}) + end) + + env = Metadata.get_cursor_env(metadata, {line, column}, {context.begin, context.end}) + + %{ + context: context, + env: env, + metadata: metadata, + binding_env: Binding.from_env(env, metadata, context.begin), + type: SurroundContext.to_binding(context.context, env.module) + } + end + end +end diff --git a/apps/language_server/lib/language_server/providers/references/locator.ex b/apps/language_server/lib/language_server/providers/references/locator.ex index 13973f750..8b5f1ce3c 100644 --- a/apps/language_server/lib/language_server/providers/references/locator.ex +++ b/apps/language_server/lib/language_server/providers/references/locator.ex @@ -19,37 +19,16 @@ defmodule ElixirLS.LanguageServer.Providers.References.Locator do alias ElixirSense.Core.SurroundContext alias ElixirSense.Core.Parser alias ElixirSense.Core.Source + alias ElixirLS.LanguageServer.Providers.LocatorUtils def references(code, line, column, trace, options \\ []) do - case NormalizedCode.Fragment.surround_context(code, {line, column}) do - :none -> + case LocatorUtils.build(code, line, column, options) do + nil -> [] - context -> - metadata = - Keyword.get_lazy(options, :metadata, fn -> - Parser.parse_string(code, true, false, {line, column}) - end) - - # if context is var try to find env by scope_id - # find scopes that contain this variable - - env = - %State.Env{ - module: module - } = - Metadata.get_cursor_env(metadata, {line, column}, {context.begin, context.end}) - - # find last env of current module - attributes = get_attributes(metadata, module) - - find( - context, - env, - attributes, - metadata, - trace - ) + info -> + attributes = get_attributes(info.metadata, info.env.module) + find(Map.put(info, :attributes, attributes), trace) |> Enum.uniq() end end @@ -73,23 +52,21 @@ defmodule ElixirLS.LanguageServer.Providers.References.Locator do range: range } - def find( - context, - %State.Env{ + def find(%{ + context: context, + env: %State.Env{ aliases: aliases, module: module } = env, - attributes, - %Metadata{ + metadata: %Metadata{ mods_funs_to_positions: mods_funs, calls: calls, types: metadata_types } = metadata, - trace - ) do - binding_env = Binding.from_env(env, metadata, context.begin) - - type = SurroundContext.to_binding(context.context, module) + binding_env: binding_env, + type: type, + attributes: attributes + }, trace) do refs_for_mod_fun = fn {mod, function} -> actual =