MohamedRejeb / compose-rich-editor

A Rich text editor library for both Jetpack Compose and Compose Multiplatform, fully customizable, supports HTML and Markdown.
https://mohamedrejeb.github.io/compose-rich-editor/
Apache License 2.0
1.17k stars 76 forks source link

Small bug with replaceTextRange #401

Closed abarrafo closed 3 weeks ago

abarrafo commented 1 month ago

I have a spell checker running on top of RichTextEditor. When I do a replaceTextRange triggered from a popup, and, the cursor is not near the the replacement, it removes text incorrectly as you run it through a remove and add process that is selection dependent.

replaceTextRange should be selection independent:

    public fun addTextAtIndex(
        index: Int,
        text: String,
    ) {
        require(index >= 0) {
            "The index must be non-negative."
        }

        require(index <= textFieldValue.text.length) {
            "The index must be within the text bounds. " +
                    "The text length is ${textFieldValue.text.length}, " +
                    "but the index is $index."
        }

        val beforeText = textFieldValue.text.substring(0, index)
        val afterText = textFieldValue.text.substring(selection.max) // This causes the bug. 
        val newText = "$beforeText$text$afterText"

        onTextFieldValueChange(
            newTextFieldValue = textFieldValue.copy(
                text = newText,
                selection = TextRange(index + text.length),
            )
        )
    }

You may consider doing replaceTextRange like this?

    public fun replaceTextRange(
    textRange: TextRange,
    text: String
) {
    require(textRange.min >= 0) {
        "The start index must be non-negative."
    }

    require(textRange.max <= textFieldValue.text.length) {
        "The end index must be within the text bounds. " +
                "The text length is ${textFieldValue.text.length}, " +
                "but the end index is ${textRange.max}."
    }

    // Perform the replacement in a single step
    val beforeText = textFieldValue.text.substring(0, textRange.min)
    val afterText = textFieldValue.text.substring(textRange.max)
    val newText = beforeText + text + afterText

    onTextFieldValueChange(
        newTextFieldValue = textFieldValue.copy(
            text = newText,
            selection = TextRange(textRange.min + text.length),
        )
    )
}

You can see in this video demo of the issue. When I click to have the spelling/grammer corrected at the top (first word in this blurb), since the cursor is at the bottom of the editor after the replace it splits the word Bizarre onto different lines, and makes things a bit wonky.

https://github.com/user-attachments/assets/5d43df22-ac04-472a-bb0a-9bf53a063d64

MohamedRejeb commented 3 weeks ago

Thanks for reporting this issue, I'm checking your PR.