Spec-Zone.ru › Elixir 1.15

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

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

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

Типы

t()

Ограничения

is_doc(doc)

Функции

break(string \\ " ")

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

collapse_lines(max)

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

color(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_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.

t()Source

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

is_doc(doc)Source

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_key, opts)Source

@spec color(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(),
  [any()],
  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_doc(docs, folder_fun)Source

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

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

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

format(doc, width)Source

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

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.

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.15.4/Inspect.Algebra.html

Spec-Zone.ru

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