Spec-Zone.ru › Ruby 3.2

класс 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]

Лямбда-выражение или 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| ... } Show source
# 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) Show source
# 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| ... } Show source
# 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() Show source
# 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) Show source
# 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 вставляется, если строка не переносится в этом месте.

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

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

current_group() Show source
# 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) Show source
# 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() Show source
# 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) { || ... } Show source
# 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() { || ... } Show source
# 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) { || ... } Show source
# File lib/prettyprint.rb, line 277
def nest(indent)
  @indent += indent
  begin
    yield
  ensure
    @indent -= indent
  end
end

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

text(obj, width=obj.length) Show source
# 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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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