Исходный код 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], []}]}}
Для правильного разбора левой части ->, которая встречается как в анонимных функциях, так и в блоках do-end, параметр trailing fragment должен быть задан вместе с остальным содержимым:
iex> Code.Fragment.container_cursor_to_quoted("fn x", trailing_fragment: " -> :ok end")
{:ok, {:fn, [line: 1], [{:->, [line: 1], [[{:__cursor__, [line: 1], []}], :ok]}]}}
Параметры
: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.:trailing_fragment(начиная с v1.18.0) - остальная часть содержимого после курсора. Это необходимо для правильного завершения анонимных функций и левой части->
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()}
| {:key, charlist()}
| {:capture_arg, 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, ~c"@"}(в отличие от{:module_attribute, ~c""}вcursor_context/2Эта функция никогда не возвращает пустые знаки
{:sigil, ~c""}или пустые структуры{:struct, ~c""}в качестве контекстаЭта функция возвращает ключевые слова как
{:keyword, ~c"do"}Эта функция никогда не возвращает
:expr
Мы рекомендуем ознакомиться с набором тестов этой функции для получения полного списка примеров и их значений возврата.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/Code.Fragment.html