Skip to content

Commit 072979d

Browse files
authored
Merge pull request #408 from wordpress-mobile/issue/403-fixing-list-styling-of-empty-line
Issue/403 fixing list styling of empty line
2 parents 837bfc5 + b6788f9 commit 072979d

File tree

4 files changed

+147
-23
lines changed

4 files changed

+147
-23
lines changed

aztec/src/main/kotlin/org/wordpress/aztec/formatting/BlockFormatter.kt

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
8585
}
8686
}
8787
}
88-
else -> { }
88+
else -> {
89+
}
8990
}
9091
}
9192

@@ -130,8 +131,11 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
130131
var end = originalEnd
131132

132133
//if splitting block set a range that would be excluded from it
133-
val boundsOfSelectedText = if (ignoreLineBounds) IntRange(start, end)
134-
else getSelectedTextBounds(editableText, start, end)
134+
val boundsOfSelectedText = if (ignoreLineBounds) {
135+
IntRange(start, end)
136+
} else {
137+
getSelectedTextBounds(editableText, start, end)
138+
}
135139

136140
var startOfBounds = boundsOfSelectedText.start
137141
var endOfBounds = boundsOfSelectedText.endInclusive
@@ -182,7 +186,16 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
182186
}
183187

184188
spanTypes.forEach { spanType ->
185-
val spans = editableText.getSpans(start, end, spanType)
189+
// when removing style from multiple selected lines, if the last selected line is empty
190+
// or at the end of editor the selection wont include the trailing newline/EOB marker
191+
// that will leave us with orphan <li> tag, so we need to shift index to the right
192+
val hasLingeringEmptyListItem = spanType.isAssignableFrom(AztecListItemSpan::class.java)
193+
&& editableText.length > end
194+
&& (editableText[end] == '\n' || editableText[end] == Constants.END_OF_BUFFER_MARKER)
195+
196+
val endModifier = if (hasLingeringEmptyListItem) 1 else 0
197+
198+
val spans = editableText.getSpans(start, end + endModifier, spanType)
186199
spans.forEach { span ->
187200

188201
val spanStart = editableText.getSpanStart(span)
@@ -299,7 +312,9 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
299312
}
300313
} else {
301314
if (indexOfLastLineBreak == -1) {
302-
indexOfFirstLineBreak = editable.lastIndexOf("\n", selectionStart) + 1
315+
indexOfFirstLineBreak = if (selectionStart == 0) 0 else {
316+
editable.lastIndexOf("\n", selectionStart) + 1
317+
}
303318
} else {
304319
indexOfFirstLineBreak = editable.lastIndexOf("\n", selectionStart)
305320
}
@@ -342,7 +357,9 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
342357
if (numberOfLines == numberOfLinesWithSpanApplied) {
343358
removeBlockStyle(blockElementType)
344359
} else {
345-
applyBlock(makeBlockSpan(blockElementType, nestingLevel), startOfBlock + 1,
360+
//if block starts with newline do not move index to the right
361+
val startOfBlockModifier = if (startOfBlock >= 0 && editableText[startOfBlock] == '\n') 0 else 1
362+
applyBlock(makeBlockSpan(blockElementType, nestingLevel), startOfBlock + startOfBlockModifier,
346363
(if (endOfBlock == editableText.length) endOfBlock else endOfBlock + 1))
347364
}
348365
} else {
@@ -412,20 +429,29 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
412429

413430
private fun applyListBlock(listSpan: AztecListSpan, start: Int, end: Int) {
414431
BlockHandler.set(editableText, listSpan, start, end)
432+
//special case for styling single empty lines
433+
if (end - start == 1 && (editableText[end - 1] == '\n' || editableText[end - 1] == Constants.END_OF_BUFFER_MARKER)) {
434+
ListItemHandler.newListItem(editableText, start, end, listSpan.nestingLevel + 1)
435+
} else {
436+
//there is always something at the end (newline or EOB), so we shift end index to the left
437+
//to avoid empty lines
438+
val listContent = editableText.substring(start, end - 1)
415439

416-
val lines = TextUtils.split(editableText.substring(start, end), "\n")
417-
for (i in lines.indices) {
418-
val lineLength = lines[i].length
440+
val lines = TextUtils.split(listContent, "\n")
441+
for (i in lines.indices) {
442+
val lineLength = lines[i].length
419443

420-
val lineStart = (0..i - 1).sumBy { lines[it].length + 1 }
421-
val lineEnd = (lineStart + lineLength).let {
422-
if ((start + it) != editableText.length) it + 1 else it // include the newline or not
423-
}
444+
val lineStart = (0..i - 1).sumBy { lines[it].length + 1 }
424445

425-
if (lineLength == 0) continue
446+
val lineEnd = (lineStart + lineLength).let {
447+
if ((start + it) != editableText.length) it + 1 else it // include the newline or not
448+
}
426449

427-
ListItemHandler.newListItem(editableText, start + lineStart, start + lineEnd, listSpan.nestingLevel + 1)
450+
ListItemHandler.newListItem(editableText, start + lineStart, start + lineEnd, listSpan.nestingLevel + 1)
451+
}
428452
}
453+
454+
429455
}
430456

431457
private fun applyHeadingBlock(headingSpan: AztecHeadingSpan, start: Int, end: Int) {
@@ -624,6 +650,7 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
624650
return false
625651
}
626652

653+
627654
fun containsOtherHeadings(textFormat: ITextFormat, selStart: Int = selectionStart, selEnd: Int = selectionEnd): Boolean {
628655
arrayOf(AztecTextFormat.FORMAT_HEADING_1,
629656
AztecTextFormat.FORMAT_HEADING_2,
@@ -632,12 +659,12 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
632659
AztecTextFormat.FORMAT_HEADING_5,
633660
AztecTextFormat.FORMAT_HEADING_6,
634661
AztecTextFormat.FORMAT_PREFORMAT)
635-
.filter { it != textFormat }
636-
.forEach {
637-
if (containsHeading(it, selStart, selEnd)) {
638-
return true
662+
.filter { it != textFormat }
663+
.forEach {
664+
if (containsHeading(it, selStart, selEnd)) {
665+
return true
666+
}
639667
}
640-
}
641668

642669
return false
643670
}
@@ -651,7 +678,7 @@ class BlockFormatter(editor: AztecText, val listStyle: ListStyle, val quoteStyle
651678
AztecTextFormat.FORMAT_HEADING_5,
652679
AztecTextFormat.FORMAT_HEADING_6,
653680
AztecTextFormat.FORMAT_PREFORMAT)
654-
.filter { it != textFormat }
681+
.filter { it != textFormat }
655682

656683
return containsHeading(textFormat, selStart, selEnd) && otherHeadings.none { containsHeading(it, selStart, selEnd) }
657684
}

aztec/src/main/kotlin/org/wordpress/aztec/handlers/ListItemHandler.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ class ListItemHandler : BlockHandler<AztecListItemSpan>(AztecListItemSpan::class
1818
override fun handleNewlineAtEmptyLineAtBlockEnd() {
1919
val parent = IAztecNestable.getParent(text, block)
2020

21-
if (parent == null) {
21+
if (parent == null || (parent.end == 0 && parent.start == 0)) {
2222
// no parent (or parent has already adjusted its bounds away from this list item so,
23-
// just remove ourselves and bail
23+
// just remove ourselves and bail
2424
block.remove()
2525
return
2626
}

aztec/src/test/kotlin/org/wordpress/aztec/ListTest.kt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,4 +788,85 @@ class ListTest(listTextFormat: ITextFormat, listHtmlTag: String) {
788788
editText.append("unordered")
789789
Assert.assertEquals("<ul><li>unordered</li></ul>", editText.toHtml())
790790
}
791+
792+
@Test
793+
@Throws(Exception::class)
794+
fun styleFirstEmptyLineAboveEmptyLine() {
795+
TestUtils.safeAppend(editText, "\n")
796+
editText.setSelection(0)
797+
editText.toggleFormatting(listType)
798+
799+
Assert.assertEquals("<$listTag><li></li></$listTag>", editText.toHtml())
800+
}
801+
802+
@Test
803+
@Throws(Exception::class)
804+
fun styleEmptyLineInTheMiddleOfEmptyLines() {
805+
TestUtils.safeAppend(editText, "\n")
806+
TestUtils.safeAppend(editText, "\n")
807+
TestUtils.safeAppend(editText, "\n")
808+
TestUtils.safeAppend(editText, "\n")
809+
TestUtils.safeAppend(editText, "\n")
810+
TestUtils.safeAppend(editText, "\n")
811+
editText.setSelection(3)
812+
editText.toggleFormatting(listType)
813+
814+
Assert.assertEquals("<br><br><br><$listTag><li></li></$listTag><br><br>", editText.toHtml())
815+
}
816+
817+
@Test
818+
@Throws(Exception::class)
819+
fun styleMultilineSelectionWithLeadingEmptyLIne() {
820+
TestUtils.safeAppend(editText, "\n")
821+
TestUtils.safeAppend(editText, "\n")
822+
TestUtils.safeAppend(editText, "\n")
823+
editText.setSelection(0,TestUtils.safeLength(editText))
824+
editText.toggleFormatting(listType)
825+
826+
Assert.assertEquals("<$listTag><li></li><li></li><li></li><li></li></$listTag>", editText.toHtml())
827+
828+
editText.toggleFormatting(listType)
829+
830+
Assert.assertEquals("<br><br><br>", editText.toHtml())
831+
}
832+
833+
@Test
834+
@Throws(Exception::class)
835+
fun styleEmptyLineSurroundedByText() {
836+
TestUtils.safeAppend(editText, "1\n")
837+
TestUtils.safeAppend(editText, "2\n")
838+
TestUtils.safeAppend(editText, "\n")
839+
TestUtils.safeAppend(editText, "3\n")
840+
TestUtils.safeAppend(editText, "4")
841+
editText.setSelection(4)
842+
editText.toggleFormatting(listType)
843+
844+
Assert.assertEquals("1<br>2<$listTag><li></li></$listTag>3<br>4", editText.toHtml())
845+
}
846+
847+
@Test
848+
@Throws(Exception::class)
849+
fun styleMultipleLinesWitEmptyLines() {
850+
TestUtils.safeAppend(editText, "1\n")
851+
TestUtils.safeAppend(editText, "2\n")
852+
TestUtils.safeAppend(editText, "\n")
853+
TestUtils.safeAppend(editText, "3\n")
854+
TestUtils.safeAppend(editText, "4\n")
855+
TestUtils.safeAppend(editText, "\n")
856+
editText.setSelection(0,TestUtils.safeLength(editText)-1)
857+
editText.toggleFormatting(listType)
858+
859+
Assert.assertEquals("<$listTag><li>1</li><li>2</li><li></li><li>3</li><li>4</li><li></li></$listTag>", editText.toHtml())
860+
}
861+
862+
@Test
863+
@Throws(Exception::class)
864+
fun styleEmptyLineAtTheEnd() {
865+
TestUtils.safeAppend(editText, "1")
866+
TestUtils.safeAppend(editText, "\n")
867+
editText.setSelection(TestUtils.safeLength(editText))
868+
editText.toggleFormatting(listType)
869+
870+
Assert.assertEquals("1<$listTag><li></li></$listTag>", editText.toHtml())
871+
}
791872
}

aztec/src/test/kotlin/org/wordpress/aztec/QuoteTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,4 +501,20 @@ class QuoteTest {
501501

502502
Assert.assertEquals("<$quoteTag><br><br>third item</$quoteTag>", editText.toHtml())
503503
}
504+
505+
@Test
506+
@Throws(Exception::class)
507+
fun styleMultilineSelectionWithLeadingEmptyLIne() {
508+
TestUtils.safeAppend(editText, "\n")
509+
TestUtils.safeAppend(editText, "\n")
510+
TestUtils.safeAppend(editText, "\n")
511+
editText.setSelection(0,TestUtils.safeLength(editText))
512+
editText.toggleFormatting(formattingType)
513+
514+
Assert.assertEquals("<$quoteTag></$quoteTag>", editText.toHtml())
515+
516+
editText.toggleFormatting(formattingType)
517+
518+
Assert.assertEquals("<br><br><br>", editText.toHtml())
519+
}
504520
}

0 commit comments

Comments
 (0)