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

commonMain.com.jakewharton.crossword.text.kt Maven / Gradle / Ivy

The newest version!
package com.jakewharton.crossword

import kotlin.DeprecationLevel.ERROR

private val ansiColorEscape = Regex("""\u001B\[\d+(;\d+)*m""")

fun CharSequence.visualIndex(index: Int): Int {
  var remaining = index
  forEachVisualCharacter {
    if (remaining == 0) {
      return it
    }
    remaining--
  }
  if (remaining == 0) {
    return length
  }
  throw IndexOutOfBoundsException()
}

val CharSequence.visualWidth: Int get() {
  var count = 0
  forEachVisualCharacter {
    count++
  }
  return count
}

private inline fun CharSequence.forEachVisualCharacter(block: (index: Int) -> Unit) {
  var index = 0

  // These values will force a code path that searches for the first real match below.
  var nextMatchStart = 0
  var nextMatchEnd = -1

  val length = length
  while (index < length) {
    if (index == nextMatchStart) {
      // Jump over ANSI control sequence.
      index = nextMatchEnd + 1

      // Find the next ANSI control sequence, if any.
      val match = ansiColorEscape.find(this, index)
      if (match != null) {
        nextMatchStart = match.range.first
        nextMatchEnd = match.range.last
      } else {
        // No future matches. Ensure we never take this conditional again.
        nextMatchStart = length
      }

      // Restart loop since there may be successive ANSI sequences.
      continue
    }

    block(index)

    val code = this[index].code
    index++

    // Check for a surrogate pair which render as a single visual glyph.
    if (code.isHighSurrogate() && index < length && this[index].code.isLowSurrogate()) {
      index++
    }

    // Consume combining diacritics which render on the preceding code point.
    while (index < length && this[index].code.isCombiningDiacritical()) {
      index++
    }
  }
}

@Suppress("NOTHING_TO_INLINE")
private inline fun Int.isLowSurrogate(): Boolean = this in 0xDC00..0xDFFF
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.isHighSurrogate(): Boolean = this in 0xD800..0xDBFF
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.isCombiningDiacritical(): Boolean = this in 0x0300..0x036F




© 2015 - 2024 Weber Informatics LLC | Privacy Policy