类 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 <[email protected]>
常量
- VERSION
属性
要进行漂亮打印的堆栈中组的 PrettyPrint::GroupQueue
缩进的空格数
一行文本的最大宽度,超过该宽度就会换行
默认值为 79,应该是一个 Integer
添加到 output
中用于添加新行的值。
默认值为 “\n”,应该是一个 String
输出对象。
默认值为 “”,应该接受 << 方法
公共类方法
这是一个便捷方法,等同于以下代码
begin q = PrettyPrint.new(output, maxwidth, newline, &genspace) ... q.flush output end
# 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
创建一个用于美化打印的缓冲区。
output
是一个输出目标。如果未指定,则默认为 “”。它应该具有一个 << 方法,该方法接受 PrettyPrint#text
的第一个参数 obj
,PrettyPrint#breakable
的第一个参数 sep
,PrettyPrint.new
的第一个参数 newline
,以及 PrettyPrint.new
给定代码块的结果。
maxwidth
指定最大行长。如果未指定,则默认为 79。但是,如果提供长不可分割的文本,实际输出可能会超过 maxwidth
。
newline
用于换行。如果未指定,则使用 “\n”。
该代码块用于生成空格。如果未给出,则使用 {|width| ‘ ’ * width}。
# 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
这类似于 PrettyPrint::format
,但结果没有换行。
maxwidth
、newline
和 genspace
被忽略。
代码块中对 breakable
的调用不会换行,而是被视为对 text
的调用。
# 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
公共实例方法
将缓冲区分解成比 maxwidth
短的行
# 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
这表示“如果需要,您可以在此处换行”,如果在该点没有换行,则会插入一个 width
列的文本 sep
。
如果未指定 sep
,则使用 “ ”。
如果未指定width
,则使用sep.length
。例如,当sep
是多字节字符时,您将需要指定它。
# 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
返回最近添加到堆栈的组。
人为的例子
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 157 def current_group @group_stack.last end
这类似于breakable
,只是决定是否换行是单独决定的。
一个组下的两个fill_breakable
可能会导致4种结果:(换行,换行),(换行,不换行),(不换行,换行),(不换行,不换行)。这与breakable
不同,因为一个组下的两个breakable
可能会导致2种结果:(换行,换行),(不换行,不换行)。
如果在此处未换行,则插入文本sep
。
如果未指定 sep
,则使用 “ ”。
如果未指定width
,则使用sep.length
。例如,当sep
是多字节字符时,您将需要指定它。
# File lib/prettyprint.rb, line 214 def fill_breakable(sep=' ', width=sep.length) group { breakable sep, width } end
输出缓冲数据。
# File lib/prettyprint.rb, line 290 def flush @buffer.each {|data| @output_width = data.output(@output, @output_width) } @buffer.clear @buffer_width = 0 end
对块中添加的换行提示进行分组。所有换行提示都将被使用或不使用。
如果指定了indent
,则方法调用将被视为嵌套在nest(indent) { … } 中。
如果指定了open_obj
,则在分组之前调用text open_obj, open_width
。如果指定了close_obj
,则在分组之后调用text close_obj, close_width
。
# 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
接受一个块,并排队一个新的组,该组的缩进级别比当前级别高一级。
# 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
在块中添加的换行符之后,使用indent
增加左边的空白。
# File lib/prettyprint.rb, line 279 def nest(indent) @indent += indent begin yield ensure @indent -= indent end end
这将obj
作为width
列的文本添加到宽度中。
如果未指定width
,则使用obj.length。
# 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