@@ -26,71 +26,185 @@ internal class LineWrapper(
2626) {
2727 private var closed = false
2828
29- /* * Characters written since the last wrapping space that haven't yet been flushed. */
30- private val buffer = StringBuilder ()
31-
3229 /* * The number of characters since the most recent newline. Includes both out and the buffer. */
3330 private var column = 0
3431
35- /* * -1 if we have no buffering; otherwise the number of spaces to write after wrapping. */
36- private var indentLevel = - 1
32+ private var helper: BufferedLineWrapperHelper = DefaultLineWrapperHelper ()
3733
3834 /* * Emit `s`. This may be buffered to permit line wraps to be inserted. */
3935 fun append (s : String ) {
4036 check(! closed) { " closed" }
4137
42- if (indentLevel != - 1 ) {
38+ if (helper.isBuffering ) {
4339 val nextNewline = s.indexOf(' \n ' )
4440
4541 // If s doesn't cause the current line to cross the limit, buffer it and return. We'll decide
4642 // whether or not we have to wrap it later.
4743 if (nextNewline == - 1 && column + s.length <= columnLimit) {
48- buffer.append (s)
44+ helper.buffer (s)
4945 column + = s.length
5046 return
5147 }
5248
5349 // Wrap if appending s would overflow the current line.
5450 val wrap = nextNewline == - 1 || column + nextNewline > columnLimit
55- flush(wrap)
51+ helper. flush(wrap)
5652 }
5753
58- out .append(s)
54+ helper .append(s)
5955 val lastNewline = s.lastIndexOf(' \n ' )
6056 column = if (lastNewline != - 1 )
6157 s.length - lastNewline - 1 else
6258 column + s.length
6359 }
6460
61+ fun openWrappingGroup () {
62+ check(! closed) { " closed" }
63+
64+ helper = GroupLineWrapperHelper ()
65+ }
66+
6567 /* * Emit either a space or a newline character. */
6668 fun wrappingSpace (indentLevel : Int ) {
6769 check(! closed) { " closed" }
6870
69- if ( this .indentLevel != - 1 ) flush( false )
71+ helper.wrappingSpace(indentLevel )
7072 this .column++
71- this .indentLevel = indentLevel
73+ }
74+
75+ fun closeWrappingGroup (): Boolean {
76+ check(! closed) { " closed" }
77+
78+ val wrapped = helper.close()
79+ helper = DefaultLineWrapperHelper ()
80+ return wrapped
7281 }
7382
7483 /* * Flush any outstanding text and forbid future writes to this line wrapper. */
7584 fun close () {
76- if (indentLevel != - 1 ) flush( false )
85+ helper.close( )
7786 closed = true
7887 }
7988
8089 /* * Write the space followed by any buffered text that follows it. */
81- private fun flush (wrap : Boolean ) {
90+ private fun flush (buffered : String , wrap : Boolean ) {
8291 if (wrap) {
8392 out .append(' \n ' )
84- for (i in 0 until indentLevel) {
93+ for (i in 0 until helper. indentLevel) {
8594 out .append(indent)
8695 }
87- column = indentLevel * indent.length
88- column + = buffer .length
96+ column = helper. indentLevel * indent.length
97+ column + = buffered .length
8998 } else {
9099 out .append(' ' )
91100 }
92- out .append(buffer)
93- buffer.delete(0 , buffer.length)
94- indentLevel = - 1
101+ out .append(buffered)
102+ }
103+
104+ /* *
105+ * Contract for helpers that handle buffering, post-processing and flushing of the input.
106+ */
107+ internal interface BufferedLineWrapperHelper {
108+
109+ val indentLevel: Int
110+
111+ val isBuffering get() = indentLevel != - 1
112+
113+ /* * Append to out, bypassing the buffer */
114+ fun append (s : String ): Appendable
115+
116+ /* * Append to buffer */
117+ fun buffer (s : String ): Appendable
118+
119+ /* *
120+ * Indicates that a new wrapping space occurred in input.
121+ *
122+ * @param indentLevel Indentation level for the new line
123+ */
124+ fun wrappingSpace (indentLevel : Int )
125+
126+ /* *
127+ * Flush any buffered text.
128+ *
129+ * @param wrap `true` if buffer contents should be flushed a on new line
130+ * */
131+ fun flush (wrap : Boolean )
132+
133+ /* *
134+ * Flush and clear the buffer.
135+ *
136+ * @return `true` if input wrapped to new line
137+ */
138+ fun close (): Boolean
139+ }
140+
141+ /* * Flushes the buffer each time the wrapping space is encountered */
142+ internal inner class DefaultLineWrapperHelper : BufferedLineWrapperHelper {
143+
144+ private val buffer = StringBuilder ()
145+
146+ private var _indentLevel = - 1
147+
148+ override val indentLevel get() = _indentLevel
149+
150+ override fun append (s : String ): Appendable = out .append(s)
151+
152+ override fun buffer (s : String ): Appendable = buffer.append(s)
153+
154+ override fun wrappingSpace (indentLevel : Int ) {
155+ if (isBuffering) flush(false )
156+ _indentLevel = indentLevel
157+ }
158+
159+ override fun flush (wrap : Boolean ) {
160+ flush(buffer.toString(), wrap)
161+ buffer.delete(0 , buffer.length)
162+ _indentLevel = - 1
163+ }
164+
165+ override fun close (): Boolean {
166+ if (isBuffering) flush(false )
167+ return false
168+ }
169+ }
170+
171+ /* *
172+ * Holds multiple buffers and only flushes when the group is closed. If wrapping happened within
173+ * a group - each buffer will be flushed on a new line.
174+ */
175+ internal inner class GroupLineWrapperHelper : BufferedLineWrapperHelper {
176+
177+ private val buffer = mutableListOf (StringBuilder ())
178+ private var wrapped = false
179+
180+ private var _indentLevel = - 1
181+
182+ override val indentLevel get() = _indentLevel
183+
184+ override fun append (s : String ): Appendable = buffer.last().append(s)
185+
186+ override fun buffer (s : String ): Appendable = buffer.last().append(s)
187+
188+ override fun wrappingSpace (indentLevel : Int ) {
189+ _indentLevel = indentLevel
190+ buffer + = StringBuilder ()
191+ }
192+
193+ override fun flush (wrap : Boolean ) {
194+ wrapped = wrap
195+ }
196+
197+ override fun close (): Boolean {
198+ if (wrapped) buffer.last().append(' \n ' )
199+ buffer.forEachIndexed { index, segment ->
200+ if (index == 0 && ! wrapped) {
201+ out .append(segment)
202+ } else {
203+ flush(segment.toString(), wrapped)
204+ }
205+ }
206+ _indentLevel = - 1
207+ return wrapped
208+ }
95209 }
96210}
0 commit comments