Repository: JetBrains/compose-multiplatform
File: examples/codeviewer/common/src/jvmMain/kotlin/org/jetbrains/codeviewer/platform/JvmFile.kt (Kotlin)

Code before:
        val lineStartPositions = IntList()

        var size by mutableStateOf(0)

        val refreshJob = scope.launch {
            delay(100)
            size = lineStartPositions.size
            while (true) {
                delay(1000)
                size = lineStartPositions.size
            }
        }

        scope.launch(Dispatchers.IO) {
            readLinePositions(lineStartPositions)
            refreshJob.cancel()
            size = lineStartPositions.size
        }

        return object : TextLines {
            override val size get() = size

            override fun get(index: Int): String {
                val startPosition = lineStartPositions[index]
                var endPosition = if (index + 1 < size) lineStartPositions[index + 1] else byteBufferSize
                endPosition-- // Skip \n
                if (byteBuffer[endPosition].toInt().toChar() == '\r') {
                    endPosition-- // Skip \r
                }
                // Only JDK since 13 has slice() method we need, so do ugly for now.
                byteBuffer.position(startPosition)
                val slice = byteBuffer.slice()
                slice.limit(endPosition - startPosition)
                return StandardCharsets.UTF_8.decode(slice).toString()
            }
        }
    }
}

private fun java.io.File.readLinePositions(
    starts: IntList
) {
    require(length() <= Int.MAX_VALUE) {
        "Files with size over ${Int.MAX_VALUE} aren't supported"
    }

    val averageLineLength = 200
    starts.clear(length().toInt() / averageLineLength)

    try {
        FileInputStream(this@readLinePositions).use {

Review comment (suggestion):
It fixes the example, but we can have \n and \r in the user code as well, and Compose should behave well if text have them.

Code after:
        val byteBuffer = RandomAccessFile(this@toProjectFile, "r").use { file ->
            byteBufferSize = file.length().toInt()
            file.channel.map(FileChannel.MapMode.READ_ONLY, 0, file.length())
        }

        val lineStartPositions = IntList()
        var size by mutableStateOf(0)

        // In case of big files, update number of lines periodically
        val refreshJob = scope.launch {
            delay(100)
            size = lineStartPositions.size
            while (isActive) {
                delay(1000)
                size = lineStartPositions.size
            }
        }

        // Find indexes where lines starts in background
        scope.launch(Dispatchers.IO) {
            readLinePositions(lineStartPositions)
            refreshJob.cancel()
            size = lineStartPositions.size
        }

        return object : TextLines {
            override val size get() = size

            override fun get(index: Int): String {
                val position = lineRange(index)
                val slice = byteBuffer.slice(position.first, position.last - position.first)
                return StandardCharsets.UTF_8.decode(slice).toString()
            }

            private fun lineRange(index: Int): IntRange {
                val startPosition = lineStartPositions[index]
                val nextLineIndex = index + 1
                var endPosition = if (nextLineIndex < size) lineStartPositions[nextLineIndex] else byteBufferSize

                // Remove line endings from the range
                while (endPosition > startPosition) {
                    val lastSymbol = byteBuffer[endPosition - 1]
                    when (lastSymbol.toInt().toChar()) {
                        '\n', '\r' -> endPosition--
                        else -> break
                    }
                }
                return startPosition..endPosition
            }
        }
    }