@@ -58,10 +58,13 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
5858 }
5959
6060 fun afterTextChanged (s : Editable , endCursorPosition : Int ) {
61- val result = getWordAtIndex(s, endCursorPosition) ? : return
61+ val currentWord = getWordAtIndex(s, endCursorPosition) ? : return
62+ afterTextChangedLinks(currentWord)
6263
63- afterTextChangedLinks(result)
64- afterTextChangedMentions(result)
64+ val mentionText = getMentionTextAtIndex(s, endCursorPosition)
65+ if (mentionText != null ) {
66+ afterTextChangedMentions(currentWord, mentionText)
67+ }
6568 }
6669
6770 fun detectAllLinks () {
@@ -85,7 +88,34 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
8588 }
8689 }
8790
88- private fun getWordAtIndex (s : Editable , index : Int ): Triple <String , Int , Int >? {
91+ // Returns the mention string (max two words) along with its start and end indices
92+ private fun getMentionTextAtIndex (s : Editable , index : Int ): TextRange ? {
93+ if (index < 0 ) return null
94+
95+ var start = index
96+ var end = index
97+ var whitespaceReached = false
98+
99+ while (start > 0 ) {
100+ if (Character .isWhitespace(s[start - 1 ])) {
101+ // One whitespace is allowed for mentions (to cover two-word mentions)
102+ if (whitespaceReached) break
103+ whitespaceReached = true
104+ }
105+
106+ start--
107+ }
108+
109+ while (end < s.length && ! Character .isWhitespace(s[end])) {
110+ end++
111+ }
112+
113+ val result = s.subSequence(start, end).toString()
114+
115+ return TextRange (result, start, end)
116+ }
117+
118+ private fun getWordAtIndex (s : Editable , index : Int ): TextRange ? {
89119 if (index < 0 ) return null
90120
91121 var start = index
@@ -101,7 +131,7 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
101131
102132 val result = s.subSequence(start, end).toString()
103133
104- return Triple (result, start, end)
134+ return TextRange (result, start, end)
105135 }
106136
107137 private fun canLinkBeApplied (): Boolean {
@@ -120,7 +150,7 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
120150 return true
121151 }
122152
123- private fun afterTextChangedLinks (result : Triple < String , Int , Int > ) {
153+ private fun afterTextChangedLinks (result : TextRange ) {
124154 // Do not detect link if it's applied manually
125155 if (isSettingLinkSpan || ! canLinkBeApplied()) return
126156
@@ -141,33 +171,35 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
141171 }
142172 }
143173
144- private fun afterTextChangedMentions (result : Triple < String , Int , Int > ) {
174+ private fun afterTextChangedMentions (currentWord : TextRange , mentionText : TextRange ) {
145175 val mentionHandler = view.mentionHandler ? : return
146176 val spannable = view.text as Spannable
147- val (word, start, end) = result
148177
149178 val indicatorsPattern = mentionIndicators.joinToString(" |" ) { Regex .escape(it) }
150- val mentionIndicatorRegex = Regex (" ^($indicatorsPattern )" )
151- val mentionRegex= Regex (" ^($indicatorsPattern )\\ w*" )
179+ val mentionIndicatorRegex = Regex (" ($indicatorsPattern )" )
152180
153- val spans = spannable.getSpans(start, end, EnrichedMentionSpan ::class .java)
181+ val spans = spannable.getSpans(currentWord. start, currentWord. end, EnrichedMentionSpan ::class .java)
154182 for (span in spans) {
155183 spannable.removeSpan(span)
156184 }
157185
158- if (mentionRegex.matches(word)) {
159- val indicator = mentionIndicatorRegex.find(word)?.value ? : " "
160- val text = word.replaceFirst(indicator, " " )
186+ val match = mentionIndicatorRegex.find(mentionText.text)
187+ if (match == null ) {
188+ mentionHandler.endMention()
189+ return
190+ }
161191
162- // Means we are starting mention
163- if (text.isEmpty()) {
164- mentionStart = start
165- }
192+ val indicator = match.value
193+ val indicatorIndexInWord = match.range.first
194+ val textStart = indicatorIndexInWord + indicator.length
195+ val text = if (textStart <= mentionText.text.length) mentionText.text.substring(textStart) else " "
166196
167- mentionHandler.onMention (indicator, text)
168- } else {
169- mentionHandler.endMention()
197+ // Means we are starting mention (indicator present but no text after it )
198+ if (text.isEmpty()) {
199+ mentionStart = mentionText.start + indicatorIndexInWord
170200 }
201+
202+ mentionHandler.onMention(indicator, text)
171203 }
172204
173205 fun setImageSpan (src : String ) {
@@ -245,4 +277,8 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
245277 val spannable = view.text as Spannable
246278 return removeSpansForRange(spannable, start, end, config.clazz)
247279 }
280+
281+ companion object {
282+ data class TextRange (val text : String , val start : Int , val end : Int )
283+ }
248284}
0 commit comments