Исходный код Code.Fragment
Этот модуль предоставляет удобные средства для анализа фрагментов текстового кода и извлечения доступной информации, когда это возможно.
Этот модуль следует считать экспериментальным.
Краткое описание
Типы
Функции
- container_cursor_to_quoted(fragment, opts \\ [])
Принимает строку и возвращает выражение с цитированием, содержащее позицию курсора AST в родительском выражении.
- cursor_context(fragment, opts \\ [])
Принимает строку и возвращает контекст курсора.
- surround_context(fragment, position, options \\ [])
Принимает строку и возвращает окружающий контекст.
Типы
position()Исходный код
@type position() :: {line :: pos_integer(), column :: pos_integer()} Функции
container_cursor_to_quoted(fragment, opts \\ [])Source
@spec container_cursor_to_quoted(
List.Chars.t(),
keyword()
) ::
{:ok, Macro.t()}
| {:error, {location :: keyword(), binary() | {binary(), binary()}, binary()}} Принимает строку и возвращает выражение с цитированием с позицией курсора в AST внутри родительского выражения.
Эта функция принимает строку с фрагментом кода Elixir, представляющим положение курсора, и преобразует её в AST с включением специального узла __cursor__(), представляющего положение курсора в контейнере (т. е. родительском элементе).
Например, рассмотрите этот код, который будет передан в качестве входных данных:
max(some_value,
Эта функция вернёт AST, эквивалентный:
max(some_value, __cursor__())
Другими словами, эта функция способна закрыть все открытые скобки и вставить позицию курсора. Другие элементы в позиции курсора, которые не являются родительскими, будут отброшены. Например, если на вход подаётся:
max(some_value, another_val
Будет возвращён тот же AST:
max(some_value, __cursor__())
Аналогично, если подаётся только это:
max(some_va
Тогда возвращается:
max(__cursor__())
Также поддерживаются вызовы без скобок, так как мы предполагаем, что скобки являются неявными.
Кортежи, списки, карты и двоичные данные сохраняют позицию курсора:
max(some_value, [1, 2,
Возвращается следующий AST:
max(some_value, [1, 2, __cursor__()])
Ключевые списки (и блоки do-end) также сохраняются. Следующее:
if(some_value, do: if(some_value, do: :token if(some_value, do: 1 + val
всегда возвращает:
if(some_value, do: __cursor__())
Для многострочных блоков сохраняются все предыдущие строки.
AST, возвращаемый этой функцией, небезопасен для оценки, но его можно анализировать и расширять.
Примеры
Вызов функции:
iex> Code.Fragment.container_cursor_to_quoted("max(some_value, ")
{:ok, {:max, [line: 1], [{:some_value, [line: 1], nil}, {:__cursor__, [line: 1], []}]}}
Контейнеры (например, список):
iex> Code.Fragment.container_cursor_to_quoted("[some, value")
{:ok, [{:some, [line: 1], nil}, {:__cursor__, [line: 1], []}]}
Если выражение завершено, тогда всё выражение отбрасывается и возвращается только родительский элемент:
iex> Code.Fragment.container_cursor_to_quoted("if(is_atom(var)")
{:ok, {:if, [line: 1], [{:__cursor__, [line: 1], []}]}}
это означает, что завершённые выражения сами возвращают только курсор:
iex> Code.Fragment.container_cursor_to_quoted("if(is_atom(var))")
{:ok, {:__cursor__, [line: 1], []}}
Операторы также включаются с версии Elixir v1.15:
iex> Code.Fragment.container_cursor_to_quoted("foo +")
{:ok, {:+, [line: 1], [{:foo, [line: 1], nil}, {:__cursor__, [line: 1], []}]}}
Параметры
:file- имя файла, которое следует сообщить в случае ошибок при разборе. По умолчанию"nofile".:line- начальная строка анализируемой строки. По умолчанию 1.:column- начальный столбец анализируемой строки. По умолчанию 1.:columns- когдаtrue, добавьте ключ:columnк метаданным цитирования. По умолчаниюfalse.:token_metadata- когдаtrue, включает метаданные, связанные с маркерами, в AST выражения, такие как метаданные дляdoиendмаркеров, маркеров закрытия, конца выражений, а также разделителей для сигилов. См.Macro.metadata/0. По умолчаниюfalse.:literal_encoder- функция для кодирования литералов в AST. См. документацию дляCode.string_to_quoted/2для получения дополнительной информации.
cursor_context(fragment, opts \\ [])Source
@spec cursor_context(
List.Chars.t(),
keyword()
) ::
{:alias, charlist()}
| {:alias, inside_alias, charlist()}
| {:dot, inside_dot, charlist()}
| {:dot_arity, inside_dot, charlist()}
| {:dot_call, inside_dot, charlist()}
| :expr
| {:local_or_var, charlist()}
| {:local_arity, charlist()}
| {:local_call, charlist()}
| {:anonymous_call, inside_caller}
| {:module_attribute, charlist()}
| {:operator, charlist()}
| {:operator_arity, charlist()}
| {:operator_call, charlist()}
| :none
| {:sigil, charlist()}
| {:struct, inside_struct}
| {:unquoted_atom, charlist()}
when inside_dot:
{:alias, charlist()}
| {:alias, inside_alias, charlist()}
| {:dot, inside_dot, charlist()}
| {:module_attribute, charlist()}
| {:unquoted_atom, charlist()}
| {:var, charlist()}
| :expr,
inside_alias: {:local_or_var, charlist()} | {:module_attribute, charlist()},
inside_struct:
charlist()
| {:alias, inside_alias, charlist()}
| {:local_or_var, charlist()}
| {:module_attribute, charlist()}
| {:dot, inside_dot, charlist()},
inside_caller: {:var, charlist()} | {:module_attribute, charlist()} Принимает строку и возвращает контекст курсора.
Эта функция принимает строку с фрагментом кода Elixir, представляющим положение курсора, и на основе строки предоставляет контекстную информацию о последнем марке. Возвращаемое значение этой функции может быть использовано для предоставления советов, предложений и функциональности автозаполнения.
Эта функция выполняет свой анализ над маркерами. Это означает, что она не понимает, как конструкции вложены друг в друга. См. раздел «Ограничения» ниже.
Рассмотрите добавление универсального блока при обработке возвращаемого типа этой функции, поскольку в будущих версиях могут быть добавлены новые данные о курсоре.
Примеры
iex> Code.Fragment.cursor_context("")
:expr
iex> Code.Fragment.cursor_context("hello_wor")
{:local_or_var, ~c"hello_wor"}
Возвращаемые значения
{:alias, charlist}- контекст является псевдонимом, потенциально вложенным, таким какHello.WorилиHelloWor{:alias, inside_alias, charlist}- контекст является псевдонимом, потенциально вложенным, гдеinside_aliasявляется выражением{:module_attribute, charlist}или{:local_or_var, charlist}, аcharlist— статической частью. Примерами являются__MODULE__.Submoduleили@hello.Submodule{:dot, inside_dot, charlist}- контекст является точкой, гдеinside_dotявляется либо{:var, charlist},{:alias, charlist},{:module_attribute, charlist},{:unquoted_atom, charlist}, либо самимdot. Если предоставлена переменная, это может быть либо удалённый вызов, либо доступ к полю карты. Примерами являютсяHello.wor,:hello.wor,hello.wor,Hello.nested.wor,hello.nested.wor, и@hello.world. Еслиcharlistпусто, аinside_dotявляется псевдонимом, тогда автозаполнение может быть либо псевдонимом, либо удалённым вызовом.{:dot_arity, inside_dot, charlist}- контекст является арифметической точкой, гдеinside_dotявляется либо{:var, charlist},{:alias, charlist},{:module_attribute, charlist},{:unquoted_atom, charlist}, либо самимdot. Если предоставлена переменная, она должна быть удалённой арифметической точкой. Примерами являютсяHello.world/,:hello.world/,hello.world/2, и@hello.world/2{:dot_call, inside_dot, charlist}- контекст — вызов с точкой. Это означает, что после выражения были добавлены скобки или пробел. гдеinside_dotявляется либо{:var, charlist},{:alias, charlist},{:module_attribute, charlist},{:unquoted_atom, charlist}, либо самимdot. Если предоставлена переменная, она должна быть удалённым вызовом. Примерами являютсяHello.world(,:hello.world(,Hello.world,hello.world(,hello.world, и@hello.world(:expr- может быть любым выражением. Автозаполнение может предложить псевдоним, локальную или переменную{:local_or_var, charlist}- контекст — переменная или локальный вызов (импорт или локальный), такой какhello_wor{:local_arity, charlist}- контекст — локальный арифметический вызов (импорт или локальный), такой какhello_world/{:local_call, charlist}- контекст — локальный вызов (импорт или локальный), такой какhello_world(иhello_world{:anonymous_call, inside_caller}- контекст — анонимный вызов, такой какfun.(и@fun.(.{:module_attribute, charlist}- контекст — атрибут модуля, такой как@hello_wor{:operator, charlist}- контекст — оператор, такой как+или==. Обратите внимание на текстовые операторы, такие какwhen, которые не отображаются как операторы, а скорее как:local_or_var.@никогда не является:operatorи всегда:module_attribute{:operator_arity, charlist}- контекст — арифметический оператор, который является оператором, за которым следует /, таким как+/,not/илиwhen/{:operator_call, charlist}- контекст — вызов оператора, который является оператором, за которым следует пробел, таким какleft +,notилиx when:none- контекст недоступен{:sigil, charlist}- контекст — сигил. Это может быть либо начало сигила, такое как~или~s, либо оператор, начинающийся с~, такой как~>и~>>{:struct, inside_struct}- контекст — структура, такая как%,%URили%URI.inside_structможет быть либоcharlistв случае статического псевдонима, либо выражением{:alias, inside_alias, charlist},{:module_attribute, charlist},{:local_or_var, charlist},{:dot, inside_dot, charlist}{:unquoted_atom, charlist}- контекст — нецитируемый атом. Это может быть любой атом или атом, представляющий модуль
Рекомендуется изучить набор тестов этой функции для получения полного списка примеров и их возвращаемых значений.
Ограничения
Анализ основан на текущем марке, анализируя последнюю строку входных данных. Например, этот код:
iex> Code.Fragment.cursor_context("%URI{")
:expr
возвращает :expr, что предполагает, что можно использовать любую переменную, локальную функцию или псевдоним. Однако, учитывая, что мы находимся внутри структуры, лучшим предложением будет поле структуры. В таких случаях можно использовать container_cursor_to_quoted, которая вернёт контейнер AST, в котором находится курсор. Затем вы можете проанализировать этот AST для завершения имен полей.
Вследствие реализации на основе маркеров, эта функция рассматривает только последнюю строку входных данных. Это означает, что она будет показывать предложения внутри строк, heredocs и т. д., что является преднамеренным, так как это помогает с doctests, ссылками и др.
surround_context(fragment, position, options \\ [])Source
@spec surround_context(List.Chars.t(), position(), keyword()) ::
%{begin: position(), end: position(), context: context} | :none
when context:
{:alias, charlist()}
| {:alias, inside_alias, charlist()}
| {:dot, inside_dot, charlist()}
| {:local_or_var, charlist()}
| {:local_arity, charlist()}
| {:local_call, charlist()}
| {:module_attribute, charlist()}
| {:operator, charlist()}
| {:sigil, charlist()}
| {:struct, inside_struct}
| {:unquoted_atom, charlist()}
| {:keyword, charlist()},
inside_dot:
{:alias, charlist()}
| {:alias, inside_alias, charlist()}
| {:dot, inside_dot, charlist()}
| {:module_attribute, charlist()}
| {:unquoted_atom, charlist()}
| {:var, charlist()}
| :expr,
inside_alias: {:local_or_var, charlist()} | {:module_attribute, charlist()},
inside_struct:
charlist()
| {:alias, inside_alias, charlist()}
| {:local_or_var, charlist()}
| {:module_attribute, charlist()}
| {:dot, inside_dot, charlist()} Принимает строку и возвращает окружающий контекст.
Эта функция получает строку с фрагментом кода Elixir и position. Она возвращает карту, содержащую начало и конец идентификатора вместе с его контекстом, или :none если нет ничего с известным контекстом. Это полезно для предоставления функций наведения курсора и выделения в редакторах.
Разница между cursor_context/2 и surround_context/3 заключается в том, что первая предполагает, что выражение во фрагменте кода неполное. Например, do в cursor_context/2 может быть ключевым словом, переменной или локальным вызовом, в то время как surround_context/3 предполагает, что выражение во фрагменте кода полное, поэтому do всегда будет ключевым словом.
position содержит как line, так и column, оба начинаются с индекса 1. Столбец должен предшествовать окружающему выражению. Например, выражение foo, вернёт что-то для столбцов 1, 2 и 3, но не 4:
foo ^ column 1 foo ^ column 2 foo ^ column 3 foo ^ column 4
Возвращаемая карта содержит столбец, в котором начинается выражение, и первый столбец после окончания выражения.
Подобно cursor_context/2, эта функция также основана на токенах и может быть неточной во всех случаях. Более подробную информацию см. в разделе "Возвращаемые значения" и "Ограничения" в cursor_context/2.
Примеры
iex> Code.Fragment.surround_context("foo", {1, 1})
%{begin: {1, 1}, context: {:local_or_var, ~c"foo"}, end: {1, 4}}
Отличия от cursor_context/2
Поскольку surround_context/3 пытается захватить сложные выражения, у неё есть некоторые отличия от cursor_context/2:
dot_call/dot_arityиoperator_call/operator_arityсводятся кdotиoperatorконтекстам соответственно, так как между ними нет значимых различийС другой стороны, эта функция по-прежнему различает
local_call/local_arityиlocal_or_var, так как последнее может быть локальной переменной или переменной@при отсутствии идентификатора возвращается как{:operator, '@'}(в отличие от{:module_attribute, ''}вcursor_context/2)Эта функция никогда не возвращает пустые сигилы
{:sigil, ''}или пустые структуры{:struct, ''}как контекстЭта функция возвращает ключевые слова как
{:keyword, 'do'}Эта функция никогда не возвращает
:expr
Мы рекомендуем обратиться к набору тестов этой функции для получения полного списка примеров и возвращаемых значений.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/Code.Fragment.html