Source Code.Fragment
Этот модуль предоставляет удобные функции для анализа фрагментов текстового кода и извлечения доступной информации, когда это возможно.
Этот модуль следует считать экспериментальным.
Краткое описание
Типы
Функции
- container_cursor_to_quoted(fragment, opts \\ [])
Принимает строку и возвращает выражение с цитированием, содержащее позицию курсора AST внутри родительского выражения.
- cursor_context(fragment, opts \\ [])
Принимает строку и возвращает контекст курсора.
- surround_context(fragment, position, options \\ [])
Принимает строку и возвращает контекст окружающего кода.
Типы
position()Source
@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()}} Принимает строку и возвращает выражение с цитированием с позицией указателя курсора внутри родительского выражения.
Эта функция принимает строку с фрагментом кода 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, '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, чтобы обеспечить завершение имён полей.
Вследствие своей реализации, основанной на токенах, эта функция учитывает только последнюю строку входных данных. Это означает, что она будет предлагать подсказки внутри строк, heredoc и т.д., что является намеренным, поскольку это помогает с 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, '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.16.3/Code.Fragment.html