Skip to content

Commit

Permalink
Add safe analysis api and simplify expand_aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
zachallaun committed Oct 31, 2023
1 parent 1e52f5d commit d8a2c25
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 37 deletions.
71 changes: 36 additions & 35 deletions apps/common/lib/lexical/ast.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ defmodule Lexical.Ast do
"""

alias Future.Code, as: Code
alias Lexical.Ast.Aliases
alias Lexical.Ast.Analysis
alias Lexical.Document
alias Lexical.Document.Edit
Expand Down Expand Up @@ -99,6 +98,34 @@ defmodule Lexical.Ast do
|> Analysis.new(document)
end

@doc """
Attempts to analyze a document at least until `position`.
This can be used to analyze an invalid AST up until, for instance, the
cursor position. The full analysis will be completed if the AST is
valid.
This function is not guaranteed to return a valid analysis.
"""
@spec safe_analyze_to(Document.t() | Analysis.t(), Position.t()) :: Analysis.t()
def safe_analyze_to(%Document{} = document, %Position{} = position) do
with %Analysis{valid?: false} <- analyze(document) do
document
|> fragment(position)
|> Analysis.new(document)
end
end

def safe_analyze_to(%Analysis{valid?: false} = analysis, %Position{} = position) do
analysis.document
|> fragment(position)
|> Analysis.new(analysis.document)
end

def safe_analyze_to(%Analysis{valid?: true} = analysis, _position) do
analysis
end

@doc """
Returns an AST generated from a valid document or string.
"""
Expand Down Expand Up @@ -447,24 +474,15 @@ defmodule Lexical.Ast do
"""
@spec expand_aliases(
alias_segments() | module(),
Document.t(),
Analysis.t() | Document.t(),
Position.t() | {Position.line(), Position.character()}
) ::
{:ok, module()} | :error
def expand_aliases(module_or_segments, %Document{} = document, %Position{} = position) do
with {:ok, quoted} <- fragment(document, position) do
expand_aliases(module_or_segments, document, quoted, position)
end
end

def expand_aliases(module_or_segments, %Document{} = document, {line, column}) do
expand_aliases(module_or_segments, document, Position.new(document, line, column))
end

def expand_aliases(segments, %Analysis{} = analysis, %Position{} = position)
def expand_aliases(segments, analysis_or_document, %Position{} = position)
when is_list(segments) do
with aliases_mapping <- Analysis.aliases_at(analysis, position),
{:ok, resolved} <- resolve_alias(segments, aliases_mapping) do
with %Analysis{valid?: true} = analysis <- safe_analyze_to(analysis_or_document, position),
aliases <- Analysis.aliases_at(analysis, position),
{:ok, resolved} <- resolve_alias(segments, aliases) do
{:ok, Module.concat(resolved)}
else
_ ->
Expand All @@ -476,32 +494,15 @@ defmodule Lexical.Ast do
end
end

@spec expand_aliases(alias_segments() | module(), Document.t(), Macro.t(), Position.t()) ::
{:ok, module()} | :error
def expand_aliases(module, %Document{} = document, quoted_document, %Position{} = position)
def expand_aliases(module, analysis_or_document, %Position{} = position)
when is_atom(module) and not is_nil(module) do
module
|> Module.split()
|> Enum.map(&String.to_atom/1)
|> expand_aliases(document, quoted_document, position)
end

def expand_aliases(segments, %Document{} = document, quoted_document, %Position{} = position)
when is_list(segments) do
with {:ok, aliases_mapping} <- Aliases.at(document, quoted_document, position),
{:ok, resolved} <- resolve_alias(segments, aliases_mapping) do
{:ok, Module.concat(resolved)}
else
_ ->
if Enum.all?(segments, &is_atom/1) do
{:ok, Module.concat(segments)}
else
:error
end
end
|> expand_aliases(analysis_or_document, position)
end

def expand_aliases(empty, _, _, _) when empty in [nil, []] do
def expand_aliases(empty, _, _) when empty in [nil, []] do
Logger.warning("Aliases are #{inspect(empty)}, can't expand them")
:error
end
Expand Down
5 changes: 3 additions & 2 deletions apps/common/lib/lexical/ast/analysis.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Lexical.Ast.Analysis do
alias Lexical.Document.Position
alias Lexical.Document.Range

defstruct [:ast, :document, :parse_error, :scopes]
defstruct [:ast, :document, :parse_error, scopes: [], valid?: true]

@type t :: %__MODULE__{}

Expand All @@ -30,7 +30,8 @@ defmodule Lexical.Ast.Analysis do
def new(error, document) do
%__MODULE__{
document: document,
parse_error: error
parse_error: error,
valid?: false
}
end

Expand Down

0 comments on commit d8a2c25

Please sign in to comment.