@@ -5,9 +5,8 @@ import android.text.Spannable
55import android.text.SpannableStringBuilder
66import android.util.Log
77import com.swmansion.enriched.EnrichedTextInputView
8- import com.swmansion.enriched.spans.EnrichedBlockQuoteSpan
9- import com.swmansion.enriched.spans.EnrichedCodeBlockSpan
108import com.swmansion.enriched.spans.EnrichedSpans
9+ import com.swmansion.enriched.spans.interfaces.EnrichedSpan
1110import com.swmansion.enriched.utils.getParagraphBounds
1211import com.swmansion.enriched.utils.getSafeSpanBoundaries
1312
@@ -194,16 +193,84 @@ class ParagraphStyles(private val view: EnrichedTextInputView) {
194193 s.setSpan(span, safeStart, safeEnd, Spannable .SPAN_EXCLUSIVE_EXCLUSIVE )
195194 }
196195
196+ private fun handleConflictsDuringNewlineDeletion (s : Editable , style : String , paragraphStart : Int , paragraphEnd : Int ): Boolean {
197+ val spanState = view.spanState ? : return false
198+ val mergingConfig = EnrichedSpans .getMergingConfigForStyle(style, view.htmlStyle) ? : return false
199+ var isConflicting = false
200+ val stylesToCheck = mergingConfig.blockingStyles + mergingConfig.conflictingStyles
201+
202+ for (styleToCheck in stylesToCheck) {
203+ val conflictingType = EnrichedSpans .allSpans[styleToCheck]?.clazz ? : continue
204+
205+ val spans = s.getSpans(paragraphStart, paragraphEnd, conflictingType)
206+ if (spans.isEmpty()) {
207+ continue
208+ }
209+ isConflicting = true
210+
211+ val isParagraphStyle = EnrichedSpans .paragraphSpans[styleToCheck] != null
212+ if (! isParagraphStyle) {
213+ continue
214+ }
215+
216+ for (span in spans) {
217+ extendStyleOnWholeParagraph(s, span as EnrichedSpan , conflictingType, paragraphEnd)
218+ }
219+ }
220+
221+ if (isConflicting) {
222+ val styleStart = spanState.getStart(style) ? : return false
223+ spanState.setStart(style, null )
224+ removeStyle(style, styleStart, paragraphEnd)
225+ return true
226+ }
227+
228+ return false
229+ }
230+
231+
232+ private fun deleteConflictingAndBlockingStyles (s : Editable , style : String , paragraphStart : Int , paragraphEnd : Int ) {
233+ val mergingConfig = EnrichedSpans .getMergingConfigForStyle(style, view.htmlStyle) ? : return
234+ val stylesToCheck = mergingConfig.blockingStyles + mergingConfig.conflictingStyles
235+
236+ for (styleToCheck in stylesToCheck) {
237+ val conflictingType = EnrichedSpans .allSpans[styleToCheck]?.clazz ? : continue
238+
239+ val spans = s.getSpans(paragraphStart, paragraphEnd, conflictingType)
240+ for (span in spans) {
241+ s.removeSpan(span)
242+ }
243+ }
244+ }
245+
246+ private fun <T >extendStyleOnWholeParagraph (s : Editable , span : EnrichedSpan , type : Class <T >, paragraphEnd : Int ) {
247+ val currStyleStart = s.getSpanStart(span)
248+ s.removeSpan(span)
249+ val (safeStart, safeEnd) = s.getSafeSpanBoundaries(currStyleStart, paragraphEnd)
250+ setSpan(s, type, safeStart, safeEnd)
251+ }
252+
197253 fun afterTextChanged (s : Editable , endPosition : Int , previousTextLength : Int ) {
198254 var endCursorPosition = endPosition
199255 val isBackspace = s.length < previousTextLength
200256 val isNewLine = endCursorPosition == 0 || endCursorPosition > 0 && s[endCursorPosition - 1 ] == ' \n '
257+ val spanState = view.spanState ? : return
201258
202259 for ((style, config) in EnrichedSpans .paragraphSpans) {
203- val spanState = view.spanState ? : continue
204260 val styleStart = spanState.getStart(style)
205261
206262 if (styleStart == null ) {
263+ if (isBackspace) {
264+ val (start, end) = s.getParagraphBounds(endCursorPosition)
265+ val spans = s.getSpans(start, end, config.clazz)
266+
267+ for (span in spans) {
268+ // handle conflicts when entering paragraph with some paragraph style applied
269+ deleteConflictingAndBlockingStyles(s, style, start, end)
270+ extendStyleOnWholeParagraph(s, span as EnrichedSpan , config.clazz, end)
271+ }
272+ }
273+
207274 if (config.isContinuous) {
208275 mergeAdjacentStyleSpans(s, endCursorPosition, config.clazz)
209276 }
@@ -218,14 +285,23 @@ class ParagraphStyles(private val view: EnrichedTextInputView) {
218285
219286 if (isBackspace) {
220287 endCursorPosition - = 1
221- view. spanState.setStart(style, null )
288+ spanState.setStart(style, null )
222289 } else {
223290 s.insert(endCursorPosition, " \u200B " )
224291 endCursorPosition + = 1
225292 }
226293 }
227294
228295 var (start, end) = s.getParagraphBounds(styleStart, endCursorPosition)
296+
297+ // handle conflicts when deleting newline from paragraph style (going back to previous line)
298+ if (isBackspace && styleStart != start) {
299+ val isConflicting = handleConflictsDuringNewlineDeletion(s, style, start, end)
300+ if (isConflicting) {
301+ continue
302+ }
303+ }
304+
229305 val isNotEndLineSpan = isSpanEnabledInNextLine(s, end, config.clazz)
230306 val spans = s.getSpans(start, end, config.clazz)
231307
0 commit comments