PrettyPrint 클래스

PrettyPrint 클래스

이 클래스는 pretty printing 알고리즘을 구현해요. 그룹화된 구조의 줄바꿈 지점과 보기 좋은 들여쓰기를 찾아내요.

출처: Ruby 3.3 API

본문

기본적으로 이 클래스는 원시 요소가 문자열이고, 문자열의 각 바이트가 가로 폭 1열을 차지한다고 가정해요. 하지만 일부 메서드에 적절한 인자를 주면 다른 상황에서도 쓸 수 있어요:

  • PrettyPrint.new의 newline 객체와 공백 생성 블록
  • PrettyPrint#text, PrettyPrint#breakable의 선택적 width 인자

사용 후보가 몇 가지 있어요:

  • 비례 폰트를 사용한 텍스트 포매팅
  • 바이트 수와 다른 열 수를 가진 멀티바이트 문자
  • 문자열이 아닌 포매팅

버그 (Bugs)

  • 박스 기반 포매팅?
  • 다른(더 나은) 모델/알고리즘?

버그는 bugs.ruby-lang.org에 보고해 주세요.

참고자료 (References)

  • Christian Lindig, Strictly Pretty, March 2000, lindig.github.io/papers/strictly-pretty-2000.pdf
  • Philip Wadler, A prettier printer, March 1998, homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier

저자: Tanaka Akira <[email protected]>

상수 (Constants)

  • VERSION

Attributes

genspace

Integer 하나를 인자로 받아 그 숫자만큼의 공백을 반환하는 lambda 또는 Proc이에요.

기본값은 이래요.

lambda {|n| ' ' * n}

group_queue

pretty print할 스택에 있는 그룹들의 PrettyPrint::GroupQueue예요.

indent

들여쓸 공백의 수예요.

maxwidth

줄이 새 줄로 나뉘기 전의 최대 너비예요.

기본값은 79이고, Integer여야 해요.

newline

output에 새 줄을 추가하기 위해 덧붙여지는 값이에요.

기본값은 "n"이고, String이어야 해요.

output

출력 객체예요.

기본값은 ""이고, << 메서드를 받아들여야 해요.

Public Class Methods

format (output=''.dup, maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n}) { |q| ... }

편의 메서드로, 다음과 같아요.

begin
  q = PrettyPrint.new(output, maxwidth, newline, &genspace)
  ...
  q.flush
  output
end

new (output=''.dup, maxwidth=79, newline="\n", &genspace)

pretty printing용 버퍼를 만들어요.

output은 출력 대상이에요. 지정하지 않으면 ""으로 간주해요. PrettyPrint#text의 첫 인자 obj, PrettyPrint#breakable의 첫 인자 sep, PrettyPrint.new의 첫 인자 newline, 그리고 PrettyPrint.new에 주어진 블록의 결과를 받아들이는 << 메서드를 가져야 해요.

maxwidth는 최대 줄 길이를 지정해요. 지정하지 않으면 79로 간주해요. 다만 긴 줄바꿈 불가 텍스트가 제공되면 실제 출력은 maxwidth를 넘어설 수 있어요.

newline은 줄바꿈에 사용돼요. 지정하지 않으면 "n"이 사용돼요.

블록은 공백을 생성하는 데 사용돼요. 주어지지 않으면 {|width| ' ' * width}가 사용돼요.

singleline_format (output=''.dup, maxwidth=nil, newline=nil, genspace=nil) { |q| ... }

PrettyPrint::format과 비슷하지만 결과에 줄바꿈이 없어요.

maxwidth, newline, genspace는 무시돼요.

블록 안의 breakable 호출은 줄을 끊지 않고 text 호출처럼 취급돼요.

Public Instance Methods

break_outmost_groups ()

버퍼를 maxwidth보다 짧은 줄들로 나눠요.

breakable (sep=' ', width=sep.length)

"필요하면 여기서 줄을 끊어도 돼요"라고 말하는 것과 같아요. 그 지점에서 줄이 끊기지 않으면 width열 너비의 텍스트 sep이 삽입돼요.

sep이 지정되지 않으면 " "이 사용돼요.

width가 지정되지 않으면 sep.length가 사용돼요. sep이 멀티바이트 문자인 경우 등에는 이걸 지정해야 해요.

current_group ()

스택에 가장 최근에 추가된 그룹을 반환해요.

인위적인 예시:

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>

fill_breakable (sep=' ', width=sep.length)

breakable과 비슷하지만 끊을지 말지의 결정이 개별적으로 정해져요.

그룹 아래의 두 fill_breakable은 4가지 결과를 만들 수 있어요: (break,break), (break,non-break), (non-break,break), (non-break,non-break). 두 breakable이 2가지 결과만 만드는 것과 달라요: (break,break), (non-break,non-break).

이 지점에서 줄이 끊기지 않으면 텍스트 sep이 삽입돼요.

sep이 지정되지 않으면 " "이 사용돼요.

width가 지정되지 않으면 sep.length가 사용돼요. sep이 멀티바이트 문자인 경우 등에는 이걸 지정해야 해요.

flush ()

버퍼링된 데이터를 출력해요.

group (indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length) { || ... }

블록에서 추가된 줄바꿈 힌트들을 그룹화해요. 줄바꿈 힌트들은 모두 사용되거나 모두 사용되지 않아요.

indent가 지정되면 이 메서드 호출은 nest(indent) { … }으로 중첩된 것으로 간주돼요.

open_obj가 지정되면 그룹화 전에 text open_obj, open_width가 호출돼요. close_obj가 지정되면 그룹화 후에 text close_obj, close_width가 호출돼요.

group_sub () { || ... }

블록을 받아 한 단계 더 들여쓰기된 새 그룹을 큐에 넣어요.

nest (indent) { || ... }

블록에서 추가된 줄바꿈에 대해 새 줄 이후의 왼쪽 여백을 indent만큼 늘려요.

text (obj, width=obj.length)

objwidth열 너비의 텍스트로 추가해요.

width가 지정되지 않으면 obj.length가 사용돼요.