@@ -85,7 +85,7 @@ class IndentFormatter(
8585 // write out anything we've read before this embed block
8686 result.append(prefixWithIndent(lineContent.joinToString(" " ), nesting.size))
8787 // write out the lines of the embed content, indenting the whole block appropriately
88- result.append(indentEmbedContent (token, embedContentIndent))
88+ result.append(prefixWithIndent (token.value , embedContentIndent, true ))
8989 tokenIndex++
9090 // write the rest of the trailing content from this line
9191 while (tokenIndex < line.size) {
@@ -200,58 +200,84 @@ class IndentFormatter(
200200 /* *
201201 * Prefixes the given [content] with an indent computed from [nestingLevel]
202202 */
203- private fun prefixWithIndent (content : String , nestingLevel : Int ): String {
204- return indentType.indentString.repeat(nestingLevel) + content
203+ private fun prefixWithIndent (content : String , nestingLevel : Int , keepTrailingIndent : Boolean = false): String {
204+ val indent = indentType.indentString.repeat(nestingLevel)
205+ val lines = content.split(' \n ' )
206+
207+ return lines.mapIndexed { index, line ->
208+ /* *
209+ * If [content] ends in a newline, the next line does not belong to it,
210+ * so don't indent it unless our caller demands it
211+ */
212+ if (! keepTrailingIndent && index == lines.lastIndex && line.isEmpty() && content.endsWith(' \n ' )) {
213+ line
214+ } else {
215+ indent + line
216+ }
217+ }.joinToString(" \n " )
205218 }
206219
207220 /* *
208221 * Split the given [tokens] into lines of tokens corresponding to the lines of the original source that was tokenized.
209- * Note: trims leading whitespace tokens/lines
222+ * Note: trims leading whitespace tokens/lines, and may gather multiple lines of underlying source in one logical
223+ * "token line" when appropriate
210224 */
211225 private fun splitTokenLines (tokens : List <Token >): MutableList <List <Token >> {
212- val lines = mutableListOf<List <Token >>()
226+ val tokenLines = mutableListOf<List <Token >>()
213227 var currentLine = mutableListOf<Token >()
214228
215229 // Skip any leading whitespace tokens
216230 var startIndex = 0
217231 while (startIndex < tokens.size && tokens[startIndex].tokenType == WHITESPACE ) {
218232 startIndex++
219233 }
234+
235+ var index = startIndex
236+ while (index < tokens.size) {
237+ val token = tokens[index]
220238
221- for (token in tokens.subList(startIndex, tokens.size)) {
222239 if (token.tokenType == WHITESPACE && token.lexeme.text.contains(' \n ' )) {
223240 currentLine.add(token.copy(lexeme = token.lexeme.copy(text = " \n " )))
224- lines .add(currentLine)
241+ tokenLines .add(currentLine)
225242
226243 var numAdditionalNewLines = token.lexeme.text.count { it == ' \n ' } - 1
227244 while (numAdditionalNewLines > 0 ) {
228- lines .add(mutableListOf (token.copy(lexeme = token.lexeme.copy(text = " \n " ))))
245+ tokenLines .add(mutableListOf (token.copy(lexeme = token.lexeme.copy(text = " \n " ))))
229246 numAdditionalNewLines--
230247 }
231248 currentLine = mutableListOf ()
249+
250+ /* *
251+ * If we see a comment starting a newline, we gather all its lines and prefix the next
252+ * "tokenLine" with it so it gets consistently indented with the content it precedes/comments
253+ */
254+ val nextToken = tokens.getOrNull(index + 1 )
255+ if (nextToken?.tokenType == COMMENT ) {
256+ var commentToken: Token = nextToken
257+ while (commentToken.tokenType == COMMENT
258+ || (commentToken.tokenType == WHITESPACE && commentToken.lexeme.text.contains(' \n ' ))) {
259+ index++
260+ if (commentToken.tokenType == WHITESPACE ) {
261+ val numNewlines = commentToken.lexeme.text.count { it == ' \n ' }
262+ repeat (numNewlines) {
263+ currentLine.add(commentToken.copy(lexeme = commentToken.lexeme.copy(text = " \n " )))
264+ }
265+ } else {
266+ currentLine.add(commentToken)
267+ }
268+ commentToken = tokens.getOrNull(index + 1 ) ? : break
269+ }
270+ }
232271 } else {
233272 currentLine.add(token)
234273 }
274+
275+ index++
235276 }
236277
237- lines .add(currentLine)
278+ tokenLines .add(currentLine)
238279
239- return lines
240- }
241-
242- /* *
243- * Prepend an appropriate indent to the lines in an [EMBED_CONTENT] token
244- */
245- private fun indentEmbedContent (
246- token : Token ,
247- nestingLevel : Int
248- ): String {
249- val indent = indentType.indentString.repeat(nestingLevel)
250- val lines = token.value.split(' \n ' )
251-
252- return lines.joinToString(" \n " ) { line ->
253- indent + line
254- }
280+ return tokenLines
255281 }
256282}
257283
0 commit comments