Spec-Zone.ru › Elixir 1.18

Исходный код Inspect.Algebra

Набор функций для создания и обработки документов алгебры.

Этот модуль реализует функциональность, описанную в «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/2, space/2 и line/2 помогут вам организовать документ в жёсткую структуру. Однако алгебра документов становится интересной при использовании функций, таких как glue/3 и group/1. Склеивание вставляет разрыв между двумя документами. Группа указывает документ, который должен поместиться на текущей строке, в противном случае разрывы отображаются как новые строки. Давайте склеим два документа с разрывом, сгруппируем их и отобразим:

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 основана на статье «Strictly Pretty» Линдига, которая основана на предыдущих алгоритмах красивой печати, но адаптирована для строгих языков, таких как Elixir. Основная идея в статье — использование явных групп документов, которые отображаются как плоские (разрывы как пробелы) или как разрывы (разрывы как новые строки).

Эта реализация предоставляет два типа разрывов: :strict и :flex. Когда группа не помещается, все строгие разрывы обрабатываются как новые строки. Гибкие разрывы, однако, переоцениваются при каждом появлении и могут по-прежнему отображаться плоскими. Для получения дополнительной информации см. break/1 и flex_break/1.

Эта реализация также добавляет force_unfit/1 и next_break_fits/2, которые обеспечивают больший контроль над подгонкой документа.

Краткое описание

Ограничения

is_doc(doc)

Типы

t()

Функции

break(string \\ " ")

Возвращает документ разрыва, основанный на заданном string.

collapse_lines(max)

Сжимает любые новые строки и пробелы после этого узла, выводя до max новых строк.

color(doc, color)

Окрашивает документ заданным цветом (перед самим документом).

color(doc, key, opts) устаревший
color_doc(doc, color_key, opts)

Окрашивает документ, если у color_key есть цвет в опциях.

concat(docs)

Объединяет список документов, возвращая новый документ.

concat(doc1, doc2)

Объединяет два сущности документов, возвращая новый документ.

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

Оборачивает collection в left и right в соответствии с лимитом и содержимым.

empty()

Возвращает сущность документа, используемую для представления пустоты.

flex_break(string \\ " ")

Возвращает документ гибкого разрыва, основанный на заданном string.

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

Склеивает два документа (doc1 и doc2) вставляя flex_break/1, заданный break_string, между ними.

fold(docs, folder_fun)

Складывает список документов в документ с помощью заданной функции складывания.

fold_doc(docs, folder_fun) устаревший
force_unfit(doc)

Принудительно делает текущую группу непригодной.

format(doc, width)

Форматирует заданный документ для заданной ширины.

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

Склеивает два документа (doc1 и doc2) вставляя заданный разрыв break_string между ними.

group(doc, mode \\ :self)

Возвращает группу, содержащую указанный документ doc.

line()

Обязательный разрыв строки.

line(doc1, doc2)

Вставляет обязательный разрыв строки между двумя документами.

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

Вкладывает данный документ на заданном level.

next_break_fits(doc, mode \\ :enabled)

Рассматривает следующий разрыв как подходящий.

no_limit(doc)

Отключить любой предел отображения при отображении данного документа.

space(doc1, doc2)

Вставляет обязательный одиночный пробел между двумя документами.

string(string)

Создает документ, представленный строкой.

to_doc(term, opts)

Преобразует термин Elixir в документ алгебры в соответствии с протоколом Inspect.

Ограничения

is_doc(doc)Исходный код

Типы

t()Исходный код

@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()

Функции

break(string \\ " ")Source

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

Возвращает документ разрыва, основанный на заданном string.

Этот разрыв может быть отображен как перевод строки или как заданный string, в зависимости от mode выбранного макета.

Примеры

Давайте создадим документ, конкатенируя две строки с разрывом между ними:

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)Source

@spec collapse_lines(pos_integer()) :: doc_collapse()

Сжимает любые новые строки и пробелы, следующие за этим узлом, выводимые до max новых строк.

color(doc, color)Source

@spec color(t(), binary()) :: t()

Окрашивает документ заданным цветом (перед самим документом).

color(doc, key, opts)Source

Эта функция устарела. Используйте color_doc/3 вместо неё.

color_doc(doc, color_key, opts)Source

@spec color_doc(t(), Inspect.Opts.color_key(), Inspect.Opts.t()) :: t()

Окрашивает документ, если color_key имеет цвет в параметрах.

concat(docs)Source

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

Конкатенирует список документов, возвращая новый документ.

Примеры

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

concat(doc1, doc2)Source

@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 \\ [])Source

@spec container_doc(
  t(),
  [term()],
  t(),
  Inspect.Opts.t(),
  (term(), Inspect.Opts.t() -> t()),
  keyword()
) ::
  t()

Оборачивает collection в left и right в соответствии с лимитом и содержимым.

Использует заданные left и right документы в качестве окружающих и 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! ...]"

empty()Source

@spec empty() :: :doc_nil

Возвращает сущность документа, используемую для представления пустоты.

Примеры

iex> Inspect.Algebra.empty()
:doc_nil

flex_break(string \\ " ")Source

@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)Source

@spec flex_glue(t(), binary(), t()) :: t()

Склеивает два документа (doc1 и doc2) с вставкой flex_break/1, заданного break_string между ними.

Эта функция используется container_doc/6 и другими функциями для максимального количества элементов на одной строке.

fold(docs, folder_fun)Source

@spec fold([t()], (t(), t() -> t())) :: t()

Складывает список документов в документ, используя заданную функцию складывания.

Список документов складывается «справа»; в этом функция похожа на List.foldr/3, за исключением того, что она не ожидает начального аккумулятора и использует последний элемент docs в качестве начального аккумулятора.

Примеры

iex> docs = ["A", "B", "C"]
iex> docs =
...>   Inspect.Algebra.fold(docs, fn doc, acc ->
...>     Inspect.Algebra.concat([doc, "!", acc])
...>   end)
iex> Inspect.Algebra.format(docs, 80)
["A", "!", "B", "!", "C"]

fold_doc(docs, folder_fun)Source

Эта функция устарела. Используйте fold/2 вместо неё.

force_unfit(doc)Source

@spec force_unfit(t()) :: doc_force()

Принудительно делает текущую группу непригодной.

format(doc, width)Source

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

Форматирует заданный документ для заданной ширины.

Принимает максимальную ширину и документ для печати в качестве аргументов и возвращает представление данных ввода-вывода лучшей компоновки документа для помещения в заданную ширину.

Документ начинается плоским (без разрывов) до тех пор, пока не будет найдена группа.

Примеры

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)Source

@spec glue(t(), binary(), t()) :: t()

Склеивает два документа (doc1 и doc2) с вставкой заданного разрыва 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)Source

@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"]

line()Source

@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"]

line(doc1, doc2)Source

@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)Source

@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)Source

@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,
  ...
})

no_limit(doc)Source

@spec no_limit(t()) :: t()

Отключить любые ограничения рендеринга во время рендеринга заданного документа.

Примеры

iex> doc = Inspect.Algebra.glue("hello", "world") |> Inspect.Algebra.group()
iex> Inspect.Algebra.format(doc, 10)
["hello", "\n", "world"]
iex> doc = Inspect.Algebra.no_limit(doc)
iex> Inspect.Algebra.format(doc, 10)
["hello", " ", "world"]

space(doc1, doc2)Source

@spec space(t(), t()) :: t()

Вставляет обязательный одиночный пробел между двумя документами.

Примеры

iex> doc = Inspect.Algebra.space("Hughes", "Wadler")
iex> Inspect.Algebra.format(doc, 5)
["Hughes", " ", "Wadler"]

string(string)Source

@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"]

to_doc(term, opts)Source

@spec to_doc(any(), Inspect.Opts.t()) :: t()

Преобразует термин Elixir в документ алгебры в соответствии с протоколом Inspect.

Скачать версию ePub

Создано с помощью ExDoc (v0.36.1) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/Inspect.Algebra.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API