класс PrettyPrint
Этот класс реализует алгоритм красивой печати. Он находит переводы строк и хорошие отступы для группированных структур.
По умолчанию класс предполагает, что примитивные элементы являются строками, и каждый байт в строках имеет ширину в одну колонку. Но его можно использовать и в других ситуациях, передав соответствующие аргументы для некоторых методов:
Существует несколько возможных применений:
-
форматирование текста с использованием пропорциональных шрифтов
-
многобайтовые символы, у которых количество колонок отличается от количества байтов
-
форматирование, не связанное со строками
Ошибки
-
Форматирование на основе прямоугольников?
-
Другая (лучшая) модель/алгоритм?
Сообщите об ошибках на сайте bugs.ruby-lang.org
Ссылки
Christian Lindig, Строго красивая печать, март 2000, www.st.cs.uni-sb.de/~lindig/papers/#pretty
Philip Wadler, Более красивый принтер, март 1998, homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
Автор
Tanaka Akira <akr@fsij.org>
Атрибуты
Лямбда или Proc, принимающая один аргумент типа Fixnum и возвращающая соответствующее количество пробелов.
По умолчанию это:
lambda {|n| ' ' * n}
Очередь PrettyPrint::GroupQueue групп, которые нужно отформатировать.
Количество пробелов для отступа.
Максимальная ширина строки, после которой она разбивается на новую строку.
По умолчанию 79, должно быть целым числом (Fixnum).
Значение, добавляемое к output для добавления новой строки.
По умолчанию “n”, должно быть строкой (String).
Объект вывода.
По умолчанию '', должен принимать метод <<.
Методы публичного класса
# File lib/prettyprint.rb, line 44
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
# File lib/prettyprint.rb, line 81
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 из #text, первый аргумент sep из #breakable, первый аргумент newline из ::new и результат блока, переданного в ::new.
maxwidth задаёт максимальную длину строки. Если не указан, используется 79. Однако фактический вывод может превысить maxwidth при использовании длинных неразрывных текстов.
newline используется для разрыва строк. Если не указан, используется “n”.
Блок используется для генерации пробелов. Если не указан, используется {|width| ' ' * width}.
# File lib/prettyprint.rb, line 58 def PrettyPrint.singleline_format(output=''.dup, maxwidth=nil, newline=nil, genspace=nil) q = SingleLine.new(output) yield q output end
Аналогично ::format, но без разрывов строк.
maxwidth, newline и genspace игнорируются.
Вызов breakable в блоке не разрывает строку и обрабатывается как простой вызов text.
Методы публичного экземпляра
# File lib/prettyprint.rb, line 159
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.
# File lib/prettyprint.rb, line 223
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 — многобайтовый символ.
# File lib/prettyprint.rb, line 154 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> # File lib/prettyprint.rb, line 211
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 — многобайтовый символ.
# File lib/prettyprint.rb, line 287
def flush
@buffer.each {|data|
@output_width = data.output(@output, @output_width)
}
@buffer.clear
@buffer_width = 0
end Выводит данные из буфера.
# File lib/prettyprint.rb, line 248
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 после группировки.
# File lib/prettyprint.rb, line 259
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 уровень.
# File lib/prettyprint.rb, line 276
def nest(indent)
@indent += indent
begin
yield
ensure
@indent -= indent
end
end Увеличивает левый отступ после новой строки с помощью indent для разрывов строк, добавленных в блоке.
# File lib/prettyprint.rb, line 179
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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.