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
取得區塊並排入一個新的群組,該群組的縮排再深入 1 個層級。
# 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