檢視原始碼 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
代幣的中繼資料,用於關閉代幣、表達式的結尾,以及符號的界定符。請參閱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}
或句點本身。如果提供變數,這可能是遠端呼叫或映射欄位存取。範例包括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}
或句點本身。如果提供變數,則必須是遠端元。範例包括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}
或句點本身。如果提供變數,則必須是遠端呼叫。範例包括Hello.world(
、:hello.world(
、Hello.world
、hello.world(
、hello.world
和@hello.world(
:expr
- 可能為任何表達式。自動完成可能會建議別名、區域變數或變數{:local_or_var, charlist}
- 背景資訊是變數或區域變數(匯入或區域變數)呼叫,例如hello_wor
{:local_arity, charlist}
- 脈絡為區域性 (匯入或區域性) arity,例如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}
- 脈絡為運算子 arity,即運算子後接 /,例如+/
、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 等內顯示建議,這是故意的,因為它有助於 doctest、參考等。
@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 開始。column 必須先於周圍的表達式。例如,表達式 foo
,將會為 column 1、2 和 3 傳回一些東西,但不會傳回 4
foo
^ column 1
foo
^ column 2
foo
^ column 3
foo
^ column 4
傳回的對應包含表達式開始的 column 和表達式結束後的第一個 column。
與 cursor_context/2
類似,此函數也是基於 token,在某些情況下可能不準確。請參閱 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
我們建議查看此函數的測試套件,以取得完整範例和其回傳值的清單。