檢視原始碼 Inspect.Algebra (Elixir v1.16.2)

用於建立和處理代數文件的一組函式。

此模組實作了 Christian Lindig 在 「Strictly Pretty」(2000) 中所描述的功能,並加入了一些小功能,例如支援二進位節點和最大化使用水平空間的斷行模式。

iex> Inspect.Algebra.empty()
:doc_nil

iex> "foo"
"foo"

使用此模組中的函式,我們可以將不同的元素串接在一起並呈現它們

iex> doc = Inspect.Algebra.concat(Inspect.Algebra.empty(), "foo")
iex> Inspect.Algebra.format(doc, 80)
["foo"]

函式 nest/2space/2line/2 可協助您將文件組成一個嚴格的結構。不過,當使用 glue/3group/1 等函式時,文件代數才會變得有趣。glue 會在兩個文件之間插入斷行。group 表示必須符合目前行數的文件,否則斷行會呈現為新行。讓我們將兩個文件用斷行黏合在一起,將其分組,然後呈現它

iex> doc = Inspect.Algebra.glue("a", " ", "b")
iex> doc = Inspect.Algebra.group(doc)
iex> Inspect.Algebra.format(doc, 80)
["a", " ", "b"]

請注意,斷行會如實呈現,因為我們尚未達到行數限制。一旦達到,它就會被換行符號取代

iex> doc = Inspect.Algebra.glue(String.duplicate("a", 20), " ", "b")
iex> doc = Inspect.Algebra.group(doc)
iex> Inspect.Algebra.format(doc, 10)
["aaaaaaaaaaaaaaaaaaaa", "\n", "b"]

此模組使用位元組大小來計算剩餘的空間。如果您的文件包含字串,則需要將它們包覆在 string/1 中,然後再依賴 String.length/1 來預先計算文件大小。

最後,此模組也包含與 Elixir 相關的函式,有點類似 Elixir 格式,例如 to_doc/2

實作詳細資料

實作 Inspect.Algebra 的基礎是 Lindig 的 Strictly Pretty 論文,該論文建立在先前的漂亮列印演算法之上,但針對嚴格語言(例如 Elixir)進行調整。論文中的核心概念是使用明確的文件群組,這些群組會呈現為平面(斷行為空白)或斷行(斷行為換行符號)。

此實作提供了兩種斷行::strict:flex。當群組不符合時,所有嚴格斷行都會視為換行符號。不過,彈性斷行會在每次出現時重新評估,且仍可能呈現為平面。請參閱 break/1flex_break/1 以取得更多資訊。

此實作也新增了 force_unfit/1next_break_fits/2,它們提供了對文件調整的更多控制。

摘要

函數

根據給定的 string,傳回一個斷行文件。

壓縮此節點之後的所有換行符號和空白,最多發出 max 個換行符號。

如果 color_key 在選項中具有顏色,則為文件上色。

串接文件清單,傳回新文件。

串接兩個文件實體,傳回新文件。

根據限制和內容,將 collection 包裝在 leftright 中。

傳回用於表示空值的文檔實體。

根據給定的 string,傳回一個彈性斷行文件。

將兩個文件 (doc1doc2) 粘合在一起,在它們之間插入 break_string 給出的 flex_break/1

使用給定的資料夾函數,將文件清單折疊成文件。

強制目前的群組不適合。

根據給定的寬度,格式化給定的文件。

將兩個文件 (doc1doc2) 粘合在一起,在它們之間插入給定的斷行 break_string

傳回包含指定文件 doc 的群組。

強制換行。

在兩個文件之間插入強制換行。

在給定的 level 中,巢狀給定的文件。

將下一個斷行視為適合。

在兩個文件之間插入強制單一空格。

建立由字串表示的文件。

根據 Inspect 協定,將 Elixir 詞彙轉換為代數文件。

防護

類型

@type t() ::
  binary()
  | :doc_line
  | :doc_nil
  | doc_break()
  | doc_collapse()
  | doc_color()
  | doc_cons()
  | doc_fits()
  | doc_force()
  | doc_group()
  | doc_nest()
  | doc_string()
  | doc_limit()

函數

@spec break(binary()) :: doc_break()

根據給定的 string,傳回一個斷行文件。

此換行可以視為換行符號或給定的 字串,具體視所選佈局的 模式 而定。

範例

讓我們透過串接兩個字串並在它們之間換行來建立文件

iex> doc = Inspect.Algebra.concat(["a", Inspect.Algebra.break("\t"), "b"])
iex> Inspect.Algebra.format(doc, 80)
["a", "\t", "b"]

請注意,換行以給定的字串表示,因為我們尚未達到換行限制。一旦達到換行限制,就會以新行取代

iex> break = Inspect.Algebra.break("\t")
iex> doc = Inspect.Algebra.concat([String.duplicate("a", 20), break, "b"])
iex> doc = Inspect.Algebra.group(doc)
iex> Inspect.Algebra.format(doc, 10)
["aaaaaaaaaaaaaaaaaaaa", "\n", "b"]
連結到此函數

collapse_lines(max)

檢視原始碼 (自 1.6.0 起)
@spec collapse_lines(pos_integer()) :: doc_collapse()

壓縮此節點之後的所有換行符號和空白,最多發出 max 個換行符號。

連結到此函數

color(doc, color_key, opts)

檢視原始碼 (自 1.4.0 起)
@spec color(t(), Inspect.Opts.color_key(), Inspect.Opts.t()) :: t()

如果 color_key 在選項中具有顏色,則為文件上色。

@spec concat([t()]) :: t()

串接文件清單,傳回新文件。

範例

iex> doc = Inspect.Algebra.concat(["a", "b", "c"])
iex> Inspect.Algebra.format(doc, 80)
["a", "b", "c"]
@spec concat(t(), t()) :: t()

串接兩個文件實體,傳回新文件。

範例

iex> doc = Inspect.Algebra.concat("hello", "world")
iex> Inspect.Algebra.format(doc, 80)
["hello", "world"]
連結到此函數

container_doc(left, collection, right, inspect_opts, fun, opts \\ [])

檢視原始碼 (自 1.6.0 起)
@spec container_doc(
  t(),
  [any()],
  t(),
  Inspect.Opts.t(),
  (term(), Inspect.Opts.t() -> t()),
  keyword()
) ::
  t()

根據限制和內容,將 collection 包裝在 leftright 中。

它使用給定的 leftright 文件作為外圍,並使用分隔文件 separator 來分隔 docs 中的項目。如果集合中的所有項目都是簡單文件(文字或字串),則此函數會嘗試盡可能將它們放在同一行上。如果它們不是簡單文件,則每行只會顯示一個項目(如果它們不符合)。

會遵循給定 inspect_opts 中的限制,並且在達到限制時,此函數會停止處理並輸出 "..."

選項

  • :separator - 每一份文件之間使用的分隔符號
  • :break - 如果為 :strict,則在每個元素之間始終換行。如果為 :flex,則只在必要時換行。如果為 :maybe,則僅在所有元素都基於文字時選擇 :flex,否則為 :strict

範例

iex> inspect_opts = %Inspect.Opts{limit: :infinity}
iex> fun = fn i, _opts -> to_string(i) end
iex> doc = Inspect.Algebra.container_doc("[", Enum.to_list(1..5), "]", inspect_opts, fun)
iex> Inspect.Algebra.format(doc, 5) |> IO.iodata_to_binary()
"[1,\n 2,\n 3,\n 4,\n 5]"

iex> inspect_opts = %Inspect.Opts{limit: 3}
iex> fun = fn i, _opts -> to_string(i) end
iex> doc = Inspect.Algebra.container_doc("[", Enum.to_list(1..5), "]", inspect_opts, fun)
iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary()
"[1, 2, 3, ...]"

iex> inspect_opts = %Inspect.Opts{limit: 3}
iex> fun = fn i, _opts -> to_string(i) end
iex> opts = [separator: "!"]
iex> doc = Inspect.Algebra.container_doc("[", Enum.to_list(1..5), "]", inspect_opts, fun, opts)
iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary()
"[1! 2! 3! ...]"
@spec empty() :: :doc_nil

傳回用於表示空值的文檔實體。

範例

iex> Inspect.Algebra.empty()
:doc_nil
連結到此函數

flex_break(string \\ " ")

檢視原始碼 (自 1.6.0 起)
@spec flex_break(binary()) :: doc_break()

根據給定的 string,傳回一個彈性斷行文件。

彈性換行仍會導致群組換行,例如 break/1,但會在文件呈現時重新評估。

例如,取一個群組文件,表示為 [1, 2, 3],其中每個逗號後的空格都是一個換行。當上述文件不適合單行時,所有換行都會啟用,導致文件呈現為

[1,
 2,
 3]

然而,如果使用彈性換行,則每一個換行在呈現時都會重新評估,因此文件可能會呈現為

[1, 2,
 3]

因此得名「彈性」。它們在文件調整時更具彈性。另一方面,它們的成本更高,因為需要重新評估每個換行。

此函式由 container_doc/6 和朋友使用,以取得同一行上的最大項目數。

連結到此函數

flex_glue(doc1, break_string \\ " ", doc2)

檢視原始碼 (自 1.6.0 起)
@spec flex_glue(t(), binary(), t()) :: t()

將兩個文件 (doc1doc2) 粘合在一起,在它們之間插入 break_string 給出的 flex_break/1

此函式由 container_doc/6 和朋友使用,以取得同一行上的最大項目數。

連結到此函數

fold_doc(docs, folder_fun)

檢視原始碼
@spec fold_doc([t()], (t(), t() -> t())) :: t()

使用給定的資料夾函數,將文件清單折疊成文件。

文件清單「從右邊」折疊;在於此,此函式類似於 List.foldr/3,但它不預期初始累加器,並使用 docs 的最後元素作為初始累加器。

範例

iex> docs = ["A", "B", "C"]
iex> docs =
...>   Inspect.Algebra.fold_doc(docs, fn doc, acc ->
...>     Inspect.Algebra.concat([doc, "!", acc])
...>   end)
iex> Inspect.Algebra.format(docs, 80)
["A", "!", "B", "!", "C"]
連結到此函數

force_unfit(doc)

檢視原始碼 (自 1.6.0 起)
@spec force_unfit(t()) :: doc_force()

強制目前的群組不適合。

@spec format(t(), non_neg_integer() | :infinity) :: iodata()

根據給定的寬度,格式化給定的文件。

取得最大寬度和文件以列印作為其引數,並傳回文件的最佳版面配置的 IO 資料表示法,以符合給定的寬度。

文件開始為平面(沒有換行),直到找到一個群組。

範例

iex> doc = Inspect.Algebra.glue("hello", " ", "world")
iex> doc = Inspect.Algebra.group(doc)
iex> doc |> Inspect.Algebra.format(30) |> IO.iodata_to_binary()
"hello world"
iex> doc |> Inspect.Algebra.format(10) |> IO.iodata_to_binary()
"hello\nworld"
連結到此函數

glue(doc1, break_string \\ " ", doc2)

檢視原始碼
@spec glue(t(), binary(), t()) :: t()

將兩個文件 (doc1doc2) 粘合在一起,在它們之間插入給定的斷行 break_string

有關如何插入換行的更多資訊,請參閱 break/1

範例

iex> doc = Inspect.Algebra.glue("hello", "world")
iex> Inspect.Algebra.format(doc, 80)
["hello", " ", "world"]

iex> doc = Inspect.Algebra.glue("hello", "\t", "world")
iex> Inspect.Algebra.format(doc, 80)
["hello", "\t", "world"]
連結到此函數

group(doc, mode \\ :self)

檢視原始碼
@spec group(t(), :self | :inherit) :: doc_group()

傳回包含指定文件 doc 的群組。

群組中的文件會盡可能地一起呈現,以發揮呈現器的最佳能力。

群組模式也可以設定為 :inherit,這表示如果父群組也已換行,它會自動換行。

範例

iex> doc =
...>   Inspect.Algebra.group(
...>     Inspect.Algebra.concat(
...>       Inspect.Algebra.group(
...>         Inspect.Algebra.concat(
...>           "Hello,",
...>           Inspect.Algebra.concat(
...>             Inspect.Algebra.break(),
...>             "A"
...>           )
...>         )
...>       ),
...>       Inspect.Algebra.concat(
...>         Inspect.Algebra.break(),
...>         "B"
...>       )
...>     )
...>   )
iex> Inspect.Algebra.format(doc, 80)
["Hello,", " ", "A", " ", "B"]
iex> Inspect.Algebra.format(doc, 6)
["Hello,", "\n", "A", "\n", "B"]
@spec line() :: t()

強制換行。

如果群組中的所有列都符合,則包含換行符號的群組將符合。

範例

iex> doc =
...>   Inspect.Algebra.concat(
...>     Inspect.Algebra.concat(
...>       "Hughes",
...>       Inspect.Algebra.line()
...>     ),
...>     "Wadler"
...>   )
iex> Inspect.Algebra.format(doc, 80)
["Hughes", "\n", "Wadler"]
@spec line(t(), t()) :: t()

在兩個文件之間插入強制換行。

請參閱 line/0

範例

iex> doc = Inspect.Algebra.line("Hughes", "Wadler")
iex> Inspect.Algebra.format(doc, 80)
["Hughes", "\n", "Wadler"]
連結到此函數

nest(doc, level, mode \\ :always)

檢視原始碼
@spec nest(t(), non_neg_integer() | :cursor | :reset, :always | :break) ::
  doc_nest() | t()

在給定的 level 中,巢狀給定的文件。

如果 level 是整數,則為每當發生換行符號時附加的縮排。如果層級是 :cursor,則文件中的「游標」目前位置會成為巢狀結構。如果層級是 :reset,則會重設為 0。

mode 可以是 :always,表示巢狀結構總是會發生,或 :break,表示巢狀結構只會發生在已中斷的群組內。

範例

iex> doc = Inspect.Algebra.nest(Inspect.Algebra.glue("hello", "world"), 5)
iex> doc = Inspect.Algebra.group(doc)
iex> Inspect.Algebra.format(doc, 5)
["hello", "\n     ", "world"]
連結到此函數

next_break_fits(doc, mode \\ :enabled)

檢視原始碼 (自 1.6.0 起)
@spec next_break_fits(t(), :enabled | :disabled) :: doc_fits()

將下一個斷行視為適合。

mode 可以是 :enabled:disabled。當為 :enabled 時,它會在找到下一個中斷後立即將文件視為符合,有效地取消中斷。它也會忽略在尋找下一個中斷時出現的任何 force_unfit/1

當停用時,它會像平常一樣運作,並且會忽略任何後續 next_break_fits/2 指令。

範例

Elixir 的程式碼格式化器會使用此功能,以避免在某些特定位置中斷程式碼。例如,考慮以下程式碼

some_function_call(%{..., key: value, ...})

現在假設這段程式碼不符合其列。程式碼格式化器會在 () 內以及在 %{} 內加入中斷。因此,文件會中斷如下

some_function_call(
  %{
    ...,
    key: value,
    ...
  }
)

格式化器會將表示映射的代數文件包裝在 next_break_fits/1 中,因此程式碼會格式化如下

some_function_call(%{
  ...,
  key: value,
  ...
})
@spec space(t(), t()) :: t()

在兩個文件之間插入強制單一空格。

範例

iex> doc = Inspect.Algebra.space("Hughes", "Wadler")
iex> Inspect.Algebra.format(doc, 5)
["Hughes", " ", "Wadler"]
連結到此函數

string(string)

檢視原始碼 (自 1.6.0 起)
@spec string(String.t()) :: doc_string()

建立由字串表示的文件。

雖然 Inspect.Algebra 接受二進位資料作為文件,但會根據二進位資料大小進行計算。另一方面,string 文件會根據文件大小中的字元數進行衡量。

範例

以下文件有 10 個位元組,因此在沒有斷行的情況下,無法格式化為寬度 9

iex> doc = Inspect.Algebra.glue("olá", " ", "mundo")
iex> doc = Inspect.Algebra.group(doc)
iex> Inspect.Algebra.format(doc, 9)
["olá", "\n", "mundo"]

但是,如果我們使用 string,則會使用字串長度,而不是位元組大小,正確地符合

iex> string = Inspect.Algebra.string("olá")
iex> doc = Inspect.Algebra.glue(string, " ", "mundo")
iex> doc = Inspect.Algebra.group(doc)
iex> Inspect.Algebra.format(doc, 9)
["olá", " ", "mundo"]
@spec to_doc(any(), Inspect.Opts.t()) :: t()

根據 Inspect 協定,將 Elixir 詞彙轉換為代數文件。