Spec-Zone.ru › Ruby 3

класс PrettyPrint

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

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

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

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

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

  • PrettyPrint#breakable

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

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

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

  • форматирование, не использующее строки

Ошибки

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

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

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

Ссылки

Christian Lindig, Strictly Pretty, Март 2000, www.st.cs.uni-sb.de/~lindig/papers/#pretty

Philip Wadler, A prettier printer, Март 1998, homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier

Автор

Tanaka Akira <akr@fsij.org>

Атрибуты

genspace[R]

Lambda или 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 45
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 82
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 59
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 160
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 224
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 не указано, используется “ ”.

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

current_group() Показать исходный код
# File lib/prettyprint.rb, line 155
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 212
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 288
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 249
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 260
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 277
def nest(indent)
  @indent += indent
  begin
    yield
  ensure
    @indent -= indent
  end
end

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

text(obj, width=obj.length) Показать исходный код
# File lib/prettyprint.rb, line 180
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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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