Spec-Zone.ru › Ruby 3.4

класс PrettyPrint

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

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

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

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

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

  • PrettyPrint#breakable

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

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

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

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

Ошибки

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

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

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

Ссылки

Christian Lindig, Strictly Pretty, Март 2000, lindig.github.io/papers/strictly-pretty-2000.pdf

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

Автор

Tanaka Akira <akr@fsij.org>

Константы

VERSION

Атрибуты

genspace [R]

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

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

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–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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