类 PrettyPrint
此类实现了美化打印算法。它可以为分组的结构查找换行符和合适的缩进。
默认情况下,该类假定原始元素是字符串,并且字符串中的每个字节在宽度上都占单列。但是,通过为某些方法提供合适的参数,它可以用于其他情况
-
PrettyPrint.new的换行符对象和空格生成块 -
PrettyPrint#text的可选宽度参数
有几种可能的用法
-
使用比例字体的文本格式化
-
具有与字节数不同的列数的多字节字符
-
非字符串格式化
缺陷¶ ↑
-
基于盒子的格式化?
-
其他(更好)的模型/算法?
请在 bugs.ruby-lang.org 上报告任何错误
参考¶ ↑
Christian Lindig, Strictly Pretty, 2000 年 3 月, lindig.github.io/papers/strictly-pretty-2000.pdf
Philip Wadler, A prettier printer, 1998 年 3 月, homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
作者¶ ↑
Tanaka Akira <akr@fsij.org>
常量
- VERSION
属性
待美化打印的堆栈中分组的 PrettyPrint::GroupQueue
要缩进的空格数
一行在被分割成换行符之前的最大宽度
默认为 79,应该是一个 Integer
附加到 output 以添加新行的值。
默认为“n”,应该是一个 String
输出对象。
默认为“”,应该接受 << 方法
公共类方法
源代码
# 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
源代码
# 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 是一个输出目标。如果未指定,则假定为“”。它应该有一个 << 方法,该方法接受 PrettyPrint#text 的第一个参数 obj,PrettyPrint#breakable 的第一个参数 sep,PrettyPrint.new 的第一个参数 newline,以及为 PrettyPrint.new 给定的块的结果。
maxwidth 指定最大行长。如果未指定,则假定为 79。但是,如果提供较长的不可中断的文本,则实际输出可能会溢出 maxwidth。
newline 用于换行符。如果未指定,则使用“n”。
该块用于生成空格。如果未给出,则使用 {|width| ‘ ’ * width}。
源代码
# 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。
公共实例方法
源代码
# 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 的行
源代码
# 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 是一个多字节字符时,您将需要指定此值。
源代码
# 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>
源代码
# 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 是一个多字节字符时,您将需要指定此值。
源代码
# File lib/prettyprint.rb, line 290 def flush @buffer.each {|data| @output_width = data.output(@output, @output_width) } @buffer.clear @buffer_width = 0 end
输出缓冲的数据。
源代码
# 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。
源代码
# 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 级的新组。
源代码
# File lib/prettyprint.rb, line 279 def nest(indent) @indent += indent begin yield ensure @indent -= indent end end
对于块中添加的换行符,在换行后使用 indent 增加左边距。
源代码
# 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。