класс PrettyPrint
Этот класс реализует алгоритм красивой печати. Он находит разрывы строк и хорошие отступы для группируемых структур.
По умолчанию класс предполагает, что примитивные элементы являются строками, а каждый байт в строках имеет ширину одной колонки. Но его можно использовать для других ситуаций, передавая подходящие аргументы для некоторых методов:
-
объект новой строки и блок генерации пробелов для
PrettyPrint.new -
необязательный аргумент ширины для
PrettyPrint#text
Существует несколько возможных применений:
-
форматирование текста с использованием пропорциональных шрифтов
-
многобайтовые символы, у которых количество колонок отличается от количества байтов
-
форматирование, не использующее строки
Ошибки
-
Форматирование на основе блоков?
-
Другая (лучшая) модель/алгоритм?
Сообщайте об ошибках на 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>
Атрибуты
Lambda или Proc, принимающий один аргумент, целое число, и возвращающий соответствующее количество пробелов.
По умолчанию это:
lambda {|n| ' ' * n}
PrettyPrint::GroupQueue групп в стеке, которые нужно отформатировать.
Количество пробелов для отступа.
Максимальная ширина строки перед её разбиением на новую строку.
По умолчанию 79, и должно быть целое число.Integer
Значение, которое добавляется к output для добавления новой строки.
По умолчанию “n”, и должно быть String
Объект вывода.
По умолчанию ”, и должен поддерживать метод <<
Публичные методы класса
# 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
# 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} используется, если он не задан.
# 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.
Публичные методы экземпляра
# 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.
# 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 является многобайтовым символом, например.
# 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> # 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 является многобайтовым символом, например.
# File lib/prettyprint.rb, line 288
def flush
@buffer.each {|data|
@output_width = data.output(@output, @output_width)
}
@buffer.clear
@buffer_width = 0
end Выводит данные из буфера.
# 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 вызывается после группировки.
# 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 Принимает блок и добавляет новую группу с отступом на один уровень ниже.
# File lib/prettyprint.rb, line 277
def nest(indent)
@indent += indent
begin
yield
ensure
@indent -= indent
end
end Увеличивает левый отступ после перевода строки с indent для перевода строк, добавленных в блок.
# 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.