All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.squareup.kotlinpoet.LineWrapper.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.kotlinpoet

import java.io.Closeable

/**
 * Implements soft line wrapping on an appendable. To use, append characters using
 * [LineWrapper.append], which will replace spaces with newlines where necessary. Use
 * [LineWrapper.appendNonWrapping] to append a string that never wraps.
 */
internal class LineWrapper(
  private val out: Appendable,
  private val indent: String,
  private val columnLimit: Int,
) : Closeable {

  private var closed = false

  /**
   * Segments of the current line to be joined by spaces or wraps. Never empty, but contains a lone
   * empty string if no data has been emitted since the last newline.
   */
  private val segments = mutableListOf("")

  /** Number of indents in wraps. -1 if the current line has no wraps. */
  private var indentLevel = -1

  /** Optional prefix that will be prepended to wrapped lines. */
  private var linePrefix = ""

  /** @return whether or not there are pending segments for the current line. */
  val hasPendingSegments get() = segments.size != 1 || segments[0].isNotEmpty()

  /** Emit `s` replacing its spaces with line wraps as necessary. */
  fun append(s: String, indentLevel: Int = -1, linePrefix: String = "") {
    check(!closed) { "closed" }

    var pos = 0
    while (pos < s.length) {
      when (s[pos]) {
        ' ' -> {
          // Each space starts a new empty segment.
          this.indentLevel = indentLevel
          this.linePrefix = linePrefix
          segments += ""
          pos++
        }

        '\n' -> {
          // Each newline emits the current segments.
          newline()
          pos++
        }

        '·' -> {
          // Render · as a non-breaking space.
          segments[segments.size - 1] += " "
          pos++
        }

        else -> {
          var next = s.indexOfAny(SPECIAL_CHARACTERS, pos)
          if (next == -1) next = s.length
          segments[segments.size - 1] += s.substring(pos, next)
          pos = next
        }
      }
    }
  }

  /** Emit `s` leaving spaces as-is. */
  fun appendNonWrapping(s: String) {
    check(!closed) { "closed" }
    require(!s.contains("\n"))

    segments[segments.size - 1] += s
  }

  fun newline() {
    check(!closed) { "closed" }

    emitCurrentLine()
    out.append("\n")
    indentLevel = -1
  }

  /** Flush any outstanding text and forbid future writes to this line wrapper.  */
  override fun close() {
    emitCurrentLine()
    closed = true
  }

  private fun emitCurrentLine() {
    foldUnsafeBreaks()

    var start = 0
    var columnCount = segments[0].length

    for (i in 1.. columnLimit) {
        emitSegmentRange(start, i)
        start = i
        columnCount = segment.length + indent.length * indentLevel
        continue
      }

      columnCount = newColumnCount
    }

    // Print the last run.
    emitSegmentRange(start, segments.size)

    segments.clear()
    segments += ""
  }

  private fun emitSegmentRange(startIndex: Int, endIndex: Int) {
    // If this is a wrapped line we need a newline and an indent.
    if (startIndex > 0) {
      out.append("\n")
      for (i in 0.. 1) i--
      } else {
        i++
      }
    }
  }

  companion object {
    private val UNSAFE_LINE_START = Regex("\\s*[-+].*")
    private val SPECIAL_CHARACTERS = " \n·".toCharArray()
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy