查看源代码 Code.Fragment (Elixir v1.16.2)
此模块提供用于分析文本代码片段和尽可能提取可用信息的便利功能。
此模块应被视为实验性。
摘要
类型
@type position() :: {line :: pos_integer(), column :: pos_integer()}
函数
@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
令牌的元数据,用于关闭令牌、表达式结束以及用于 sigil 的分隔符。请参阅Macro.metadata/0
。默认值为false
。:literal_encoder
- 用于在 AST 中编码字面量的函数。有关更多信息,请参阅Code.string_to_quoted/2
的文档。
@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}
- 上下文是一个 sigil。它可以是 sigil 的开头,例如~
或~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 等内部的建议,这是故意的,因为它有助于文档测试、引用等。
@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
之间的区别在于,前者假设代码片段中的表达式不完整。例如,在 cursor_context/2
中的 do
可以是关键字、变量或本地调用,而 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, '@'}
返回(与cursor_context/2
中的{:module_attribute, ''}
相反)。此函数永远不会将空的 sigil
{:sigil, ''}
或空的结构体{:struct, ''}
作为上下文返回。此函数将关键字作为
{:keyword, 'do'}
返回。此函数永远不会返回
:expr
。
我们建议查看此函数的测试套件,以获取完整的示例列表及其返回值。