diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt index e3c1fb5dd3..b3c60b69d7 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt @@ -42,6 +42,7 @@ import io.element.android.libraries.textcomposer.model.SuggestionType import io.element.android.libraries.textcomposer.model.aMarkdownTextEditorState import io.element.android.wysiwyg.compose.RichTextEditorStyle import io.element.android.wysiwyg.compose.internal.applyStyleInCompose +import timber.log.Timber @Suppress("ModifierMissing") @Composable @@ -149,8 +150,20 @@ fun MarkdownTextInput( private fun Editable.checkSuggestionNeeded(): Suggestion? { if (this.isEmpty()) return null - val start = Selection.getSelectionStart(this) - val end = Selection.getSelectionEnd(this) + var start = Selection.getSelectionStart(this) + var end = Selection.getSelectionEnd(this) + val range = 0..this.length + + if (start !in range || end !in range) { + Timber.tag("checkSuggestionNeeded").e("Selection indices are out of bounds: start=$start, end=$end, text length=${this.length}") + return null + } + + // Make sure the selection order is correct, if not swap them: sometimes we can get the end before the start + val tempEnd = end + end = maxOf(start, end) + start = minOf(start, tempEnd) + var startOfWord = start while ((startOfWord > 0 || startOfWord == length) && !this[startOfWord - 1].isWhitespace()) { startOfWord-- @@ -161,11 +174,16 @@ private fun Editable.checkSuggestionNeeded(): Suggestion? { // If a mention span already exists we don't need suggestions if (getSpans(startOfWord, startOfWord + 1).isNotEmpty()) return null - return if (firstChar in listOf('@', '#', '/')) { + return if (firstChar in listOf('@', '#', '/', ':')) { var endOfWord = end while (endOfWord < this.length && !this[endOfWord].isWhitespace()) { endOfWord++ } + if (startOfWord + 1 > endOfWord) { + Timber.tag("checkSuggestionNeeded").e("No need to show suggestions for an invalid range (${startOfWord + 1}..$endOfWord)") + return null + } + val text = this.subSequence(startOfWord + 1, endOfWord).toString() val suggestionType = when (firstChar) { '@' -> SuggestionType.Mention