Spec-Zone.ru › Ruby 4.0

класс PrettyPrint

Родитель:
Object

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

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

  • объект новой строки и блок генерации пробелов для 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, принимающий один аргумент типа 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 48
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 85
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 62
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 163
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 227
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 158
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 215
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 291
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 252
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 263
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

Принимает блок и ставит в очередь новую группу с отступом на один уровень глубже.

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

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

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

Spec-Zone.ru

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