Spec-Zone.ru › Ruby 3.3

класс PrettyPrint

Родитель:
Объект

Этот класс реализует алгоритм красивой печати. Он находит разрывы строк и хорошие отступы для структурированных данных.

По умолчанию, класс предполагает, что примитивные элементы являются строками, и каждый байт в строках имеет ширину в одну колонку. Но он может быть использован для других ситуаций, передавая подходящие аргументы некоторым методам:

  • объект новой строки и блок генерации пробелов для PrettyPrint.new

  • опциональный аргумент ширины для PrettyPrint#text

  • PrettyPrint#breakable

Существует несколько потенциальных применений:

  • форматирование текста с использованием пропорциональных шрифтов

  • многобайтовые символы, которые имеют количество колонок отличное от количества байтов

  • форматирование нестроковых данных

Ошибки

  • Форматирование на основе блоков?

  • Другая (лучшая) модель/алгоритм?

Сообщайте об ошибках на bugs.ruby-lang.org

Ссылки

Christian Lindig, Строго Красивая печать, Март 2000, lindig.github.io/papers/strictly-pretty-2000.pdf

Philip Wadler, Более красивый принтер, Март 1998, homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier

Автор

Tanaka Akira <akr@fsij.org>

Константы

VERSION

Атрибуты

genspace[R]

Лямбда-функция или Proc, которая принимает один аргумент, целое число Integer, и возвращает соответствующее количество пробелов.

По умолчанию это:

lambda {|n| ' ' * n}
group_queue[R]

PrettyPrint::GroupQueue групп, которые нужно отформатировать.

indent[R]

Количество пробелов для отступа.

maxwidth[R]

Максимальная ширина строки перед её разделением на новую строку.

По умолчанию 79, должно быть целое число Integer.

newline[R]

Значение, добавляемое к output для добавления новой строки.

По умолчанию “n”, должно быть строкой String.

output[R]

Объект вывода.

По умолчанию ”, должен поддерживать метод <<

Публичные методы класса

format(output=''.dup, maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n}) { |q| ... } Показать исходный код
# File lib/prettyprint.rb, line 47
def PrettyPrint.format(output=''.dup, maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n})
  q = PrettyPrint.new(output, maxwidth, newline, &genspace)
  yield q
  q.flush
  output
end

Это вспомогательный метод, эквивалентный следующему:

begin
  q = PrettyPrint.new(output, maxwidth, newline, &genspace)
  ...
  q.flush
  output
end
new(output=''.dup, maxwidth=79, newline="\n", &genspace) Показать исходный код
# File lib/prettyprint.rb, line 84
def initialize(output=''.dup, maxwidth=79, newline="\n", &genspace)
  @output = output
  @maxwidth = maxwidth
  @newline = newline
  @genspace = genspace || lambda {|n| ' ' * n}

  @output_width = 0
  @buffer_width = 0
  @buffer = []

  root_group = Group.new(0)
  @group_stack = [root_group]
  @group_queue = GroupQueue.new(root_group)
  @indent = 0
end

Создает буфер для форматирования вывода.

output — целевой объект вывода. Если не указан, используется ” . Он должен поддерживать метод <<, принимающий первый аргумент obj метода PrettyPrint#text, первый аргумент sep метода PrettyPrint#breakable, первый аргумент newline метода PrettyPrint.new и результат блока для метода PrettyPrint.new.

maxwidth задаёт максимальную длину строки. Если не указано, используется 79. Однако фактические результаты могут превышать maxwidth если предоставлены длинные неразрывные тексты.

newline используется для переносов строк. Если не указано, используется “n”.

Блок используется для генерации пробелов. Если не задано, используется {|width| ‘ ’ * width}.

singleline_format(output=''.dup, maxwidth=nil, newline=nil, genspace=nil) { |q| ... } Показать исходный код
# File lib/prettyprint.rb, line 61
def PrettyPrint.singleline_format(output=''.dup, maxwidth=nil, newline=nil, genspace=nil)
  q = SingleLine.new(output)
  yield q
  output
end

Аналогично PrettyPrint::format, но результат без переносов строк.

maxwidth, newline и genspace игнорируются.

Вызов breakable в блоке не прерывает строку и рассматривается как простой вызов text.

Публичные методы экземпляра

break_outmost_groups() Показать исходный код
# File lib/prettyprint.rb, line 162
def break_outmost_groups
  while @maxwidth < @output_width + @buffer_width
    return unless group = @group_queue.deq
    until group.breakables.empty?
      data = @buffer.shift
      @output_width = data.output(@output, @output_width)
      @buffer_width -= data.width
    end
    while !@buffer.empty? && Text === @buffer.first
      text = @buffer.shift
      @output_width = text.output(@output, @output_width)
      @buffer_width -= text.width
    end
  end
end

Разбивает буфер на строки, которые короче maxwidth.

breakable(sep=' ', width=sep.length) Показать исходный код
# File lib/prettyprint.rb, line 226
def breakable(sep=' ', width=sep.length)
  group = @group_stack.last
  if group.break?
    flush
    @output << @newline
    @output << @genspace.call(@indent)
    @output_width = @indent
    @buffer_width = 0
  else
    @buffer << Breakable.new(sep, width, self)
    @buffer_width += width
    break_outmost_groups
  end
end

Указывает, что здесь можно переносить строку при необходимости, и текст width-символьной ширины sep вставляется, если строка не разбита в этом месте.

Если sep не указано, используется “ ”.

Если width не указано, используется sep.length. Вам нужно будет указать это, если sep является многобайтовым символом, например.

current_group() Показать исходный код
# File lib/prettyprint.rb, line 157
def current_group
  @group_stack.last
end

Возвращает последнюю добавленную в стек группу.

Искусственный пример:

out = ""
=> ""
q = PrettyPrint.new(out)
=> #<PrettyPrint:0x82f85c0 @output="", @maxwidth=79, @newline="\n", @genspace=#<Proc:0x82f8368@/home/vbatts/.rvm/rubies/ruby-head/lib/ruby/2.0.0/prettyprint.rb:82 (lambda)>, @output_width=0, @buffer_width=0, @buffer=[], @group_stack=[#<PrettyPrint::Group:0x82f8138 @depth=0, @breakables=[], @break=false>], @group_queue=#<PrettyPrint::GroupQueue:0x82fb7c0 @queue=[[#<PrettyPrint::Group:0x82f8138 @depth=0, @breakables=[], @break=false>]]>, @indent=0>
q.group {
  q.text q.current_group.inspect
  q.text q.newline
  q.group(q.current_group.depth + 1) {
    q.text q.current_group.inspect
    q.text q.newline
    q.group(q.current_group.depth + 1) {
      q.text q.current_group.inspect
      q.text q.newline
      q.group(q.current_group.depth + 1) {
        q.text q.current_group.inspect
        q.text q.newline
      }
    }
  }
}
=> 284
 puts out
#<PrettyPrint::Group:0x8354758 @depth=1, @breakables=[], @break=false>
#<PrettyPrint::Group:0x8354550 @depth=2, @breakables=[], @break=false>
#<PrettyPrint::Group:0x83541cc @depth=3, @breakables=[], @break=false>
#<PrettyPrint::Group:0x8347e54 @depth=4, @breakables=[], @break=false>
fill_breakable(sep=' ', width=sep.length) Показать исходный код
# File lib/prettyprint.rb, line 214
def fill_breakable(sep=' ', width=sep.length)
  group { breakable sep, width }
end

Аналогично breakable, за исключением того, что решение о переносе строки определяется индивидуально.

Два fill_breakable внутри группы могут привести к 4 результатам: (перенос,перенос), (перенос,не перенос), (не перенос,перенос), (не перенос,не перенос). Это отличается от breakable, так как два breakable внутри группы могут привести к 2 результатам: (перенос,перенос), (не перенос,не перенос).

Текст sep вставляется, если строка не разбита в этом месте.

Если sep не указано, используется “ ”.

Если width не указано, используется sep.length. Вам нужно будет указать это, если sep является многобайтовым символом, например.

flush() Показать исходный код
# File lib/prettyprint.rb, line 290
def flush
  @buffer.each {|data|
    @output_width = data.output(@output, @output_width)
  }
  @buffer.clear
  @buffer_width = 0
end

Выводит данные из буфера.

group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length) { || ... } Показать исходный код
# File lib/prettyprint.rb, line 251
def group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length)
  text open_obj, open_width
  group_sub {
    nest(indent) {
      yield
    }
  }
  text close_obj, close_width
end

Группирует подсказки переноса строки, добавленные в блоке. Все подсказки переноса строки будут использованы или нет.

Если indent указано, вызов метода рассматривается как вложенный вызовом nest(indent) { … }.

Если open_obj указано, text open_obj, open_width вызывается до группировки. Если close_obj указано, text close_obj, close_width вызывается после группировки.

group_sub() { || ... } Показать исходный код
# File lib/prettyprint.rb, line 262
def group_sub
  group = Group.new(@group_stack.last.depth + 1)
  @group_stack.push group
  @group_queue.enq group
  begin
    yield
  ensure
    @group_stack.pop
    if group.breakables.empty?
      @group_queue.delete group
    end
  end
end

Принимает блок и добавляет новую группу, отступающую на 1 уровень.

nest(indent) { || ... } Показать исходный код
# File lib/prettyprint.rb, line 279
def nest(indent)
  @indent += indent
  begin
    yield
  ensure
    @indent -= indent
  end
end

Увеличивает отступ после новой строки с indent для переносов строк, добавленных в блоке.

text(obj, width=obj.length) Показать исходный код
# File lib/prettyprint.rb, line 182
def text(obj, width=obj.length)
  if @buffer.empty?
    @output << obj
    @output_width += width
  else
    text = @buffer.last
    unless Text === text
      text = Text.new
      @buffer << text
    end
    text.add(obj, width)
    @buffer_width += width
    break_outmost_groups
  end
end

Добавляет obj как текст шириной width столбцов.

Если width не указано, используется obj.length.

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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