-
Notifications
You must be signed in to change notification settings - Fork 0
/
linewriter.rb
137 lines (97 loc) · 2.33 KB
/
linewriter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class LineWriter
class BaseFormatter
def initialize(arr)
@arr = arr
@sep = 0
end
def trim
@sep.times { @arr.pop }
@sep = 0
end
def <<(val)
str = val.to_s
str.rstrip!
relay str, is_first: @arr.none?
end
def relay(str, is_first:)
if str.strip.empty?
@sep += 1
else
@sep = 0
end
@arr << str
self
end
def sep(spacing, and_merge: true, unless_first: true)
return self if unless_first and @arr.none?
spacing -= @sep if and_merge
spacing.times { @arr << "" }
@sep += spacing
self
end
end
class Formatter
attr_reader :spacing, :indent
def initialize(with_spacing: 0, with_indent: "", child_of: nil)
@spacing = with_spacing
@indent = with_indent
@parent = child_of
@first = true
end
def <<(val)
sep @spacing
str = val.to_s
str.rstrip!
relay str, is_first: @first
end
def relay(str, is_first:)
sep @spacing if is_first
str.prepend(@indent) if !str.empty?
@first = false if @first
@parent.relay str, is_first: is_first
self
end
def sep(spacing = 1, and_merge: true, unless_first: true)
return self if unless_first and @first
@parent.sep spacing, and_merge: and_merge, unless_first: unless_first
self
end
def group
sep @spacing
@first = true
end
def close; @parent = nil end
end
def initialize()
@lines = []
@base_fmt = BaseFormatter.new(@lines)
@stack = [@base_fmt]
end
def self.lines(with_spacing: 0, with_indent: "")
l = LineWriter.new()
l.fmt with_spacing: with_spacing, with_indent: with_indent do
yield l
end
l.to_s
end
def curr; @stack.last end
def peek; @lines.last end
def fmt(with_spacing: 0, with_indent: "")
@stack.push Formatter.new(
with_spacing: with_spacing,
with_indent: with_indent,
child_of: curr
)
yield
curr.close
@stack.pop
end
def group; curr.group; self end
def trim; @base_fmt.trim; self end
def <<(val) curr << val; self end
def sep(spacing = 1, and_merge: true, unless_first: true)
curr.sep spacing, and_merge: and_merge, unless_first: unless_first
self
end
def to_s; "#{@lines.join("\n")}" end
end