class PrettyPrint
此类实现了一种漂亮的打印算法。它为分组结构查找换行符和漂亮的缩进。
默认情况下,该类假定基本元素是字符串,并且字符串中的每个字节的宽度为单列。但通过为某些方法提供合适的参数,它也可以用于其他情况。
-
用于
PrettyPrint.new的换行符对象和空格生成块 -
为
PrettyPrint#text提供可选的宽度参数
有几种潜在的用途
-
使用比例字体进行文本格式化
-
多字节字符,其列数与字节数不同
-
非字符串格式化
Bug
-
基于框的格式化?
-
其他(更好的)模型/算法?
请在 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>
Constants
- VERSION
-
版本字符串
属性
堆栈中要漂亮打印的组的 PrettyPrint::GroupQueue
要缩进的空格数
一行的最大宽度,然后将其分成换行符
默认为 79,应为 Integer
添加到 output 以添加新行的值。
默认为“n”,应为 String
输出对象。
默认为空字符串,应接受 << 方法
Public Class Methods
Source
# 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
Source
# 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 是一个输出目标。如果未指定,则假定为空字符串。它应具有接受 PrettyPrint#text 的第一个参数 obj、PrettyPrint#breakable 的第一个参数 sep、PrettyPrint.new 的第一个参数 newline 以及为 PrettyPrint.new 给定的块的结果的 << 方法。
maxwidth 指定最大行长度。如果未指定,则假定为 79。但是,如果提供了很长的不可断行文本,实际输出可能会超出 maxwidth。
newline 用于换行。如果未指定,则使用“n”。
该块用于生成空格。如果未给定,则使用 { |width| ‘ ’ * width }。
Source
# 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 的调用。
Public Instance Methods
Source
# 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 的行
Source
# 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 是多字节字符时,您需要指定此参数。
Source
# 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>
Source
# File lib/prettyprint.rb, line 215 def fill_breakable(sep=' ', width=sep.length) group { breakable sep, width } end
这与 breakable 类似,只是决定是否断开是由单独决定的。
组下的两个 fill_breakable 可能会导致 4 种结果:(break,break)、(break,non-break)、(non-break,break)、(non-break,non-break)。这与 breakable 不同,因为组下的两个 breakable 可能会导致 2 种结果:(break,break)、(non-break,non-break)。
如果此处未断行,则插入文本 sep。
如果未指定 sep,则使用“ ”。
如果未指定 width,则使用 sep.length。例如,当 sep 是多字节字符时,您需要指定此参数。
Source
# File lib/prettyprint.rb, line 291 def flush @buffer.each {|data| @output_width = data.output(@output, @output_width) } @buffer.clear @buffer_width = 0 end
输出缓冲的数据。
Source
# 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。
Source
# 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
接受一个块并排队一个新组,该组进一步缩进 1 个级别。
Source
# File lib/prettyprint.rb, line 280 def nest(indent) @indent += indent begin yield ensure @indent -= indent end end
在换行后增加 indent 的左边距,以换行为块中添加的行。
Source
# 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。