檢視原始碼 Code.Fragment (Elixir v1.16.2)

此模組提供便利功能,用於分析文字程式碼片段,並在可能時擷取可用資訊。

此模組應視為實驗性質。

摘要

函式

接收字串,並傳回引號包住的運算式,其游標 AST 位置位於其父運算式內。

接收字串,並傳回游標內容。

接收字串,並傳回周圍內容。

類型

@type position() :: {line :: pos_integer(), column :: pos_integer()}

函式

連結至此函式

container_cursor_to_quoted(fragment, opts \\ [])

檢視原始碼 (自 1.13.0 起)
@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 中包含與代幣相關的中繼資料,例如 doend 代幣的中繼資料,用於關閉代幣、表達式的結尾,以及符號的界定符。請參閱 Macro.metadata/0。預設為 false

  • :literal_encoder - 在 AST 中編碼文字的函數。請參閱 Code.string_to_quoted/2 的文件以取得更多資訊。

連結至此函式

cursor_context(fragment, opts \\ [])

檢視原始碼 (自 1.13.0 起)
@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.WorHelloWor

  • {: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.worhello.worHello.nested.worhello.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.worldhello.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 +notx when

  • :none - 沒有可能的脈絡

  • {:sigil, charlist} - 脈絡為 sigil。它可能是 sigil 的開頭,例如 ~~s,或以 ~ 開頭的運算子,例如 ~>~>>

  • {:struct, inside_struct} - 脈絡為結構,例如 %%UR%URIinside_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、參考等。

連結至此函式

surround_context(fragment, position, options \\ [])

檢視原始碼 (自 1.13.0 起)
@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/2surround_context/3 之間的差異在於,前者假設程式碼片段中的表達式不完整。例如,cursor_context/2 中的 do 可能是一個關鍵字、變數或區域呼叫,而 surround_context/3 假設程式碼片段中的表達式是完整的,因此 do 永遠會是一個關鍵字。

position 同時包含 linecolumn,兩者都從索引 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_arityoperator_call/operator_arity 分別被壓縮成 dotoperator 內容,因為它們之間沒有任何有意義的區別

  • 另一方面,此函數仍然區分 local_call/local_aritylocal_or_var,因為後者可以是區域或變數

  • @ 後面沒有任何識別碼時,會傳回 {:operator, '@'} (與 cursor_context/2 中的 {:module_attribute, ''} 相反

  • 此函數永遠不會傳回空的 sigil {:sigil, ''} 或空的結構 {:struct, ''} 作為內容

  • 此函數將關鍵字傳回為 {:keyword, 'do'}

  • 此函數永遠不會傳回 :expr

我們建議查看此函數的測試套件,以取得完整範例和其回傳值的清單。