1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-264 Editor | Enhancement | Enable search-on-page for simple table block - highlight and select target (#2669)

* DROID-264 stubs of the search field, the table block and the pattern

* DROID-264 search highlighting and targeting for table cellls

* DROID-264 tests

Co-authored-by: konstantiniiv <ki@anytype.io>
This commit is contained in:
Konstantin Ivanov 2022-10-20 13:25:53 +02:00 committed by GitHub
parent 8fb9e4c967
commit 7babee5052
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 823 additions and 81 deletions

View file

@ -1456,4 +1456,441 @@ class BlockViewSearchTextTest {
assertEquals(expected = expected, actual = actual)
}
@Test
fun `when no highlighted text is targeted in simple table expect to target row2 column1 cell`() {
//SETUP
val pattern = StubPattern(query = "bc")
val simpleTableBlock = StubTwoRowsThreeColumnsSimpleTable(
textR1C1 = "ab1",
textR1C2 = "ab2",
textR1C3 = "ac3",
textR2C1 = "bc1",
textR2C2 = "bb2",
textR2C3 = "bc3"
)
val cellR1C1 = simpleTableBlock.cells[0] as BlockView.Table.Cell.Text
val cellR1C2 = simpleTableBlock.cells[1] as BlockView.Table.Cell.Text
val cellR1C3 = simpleTableBlock.cells[2] as BlockView.Table.Cell.Text
val cellR2C1 = simpleTableBlock.cells[3] as BlockView.Table.Cell.Text
val cellR2C2 = simpleTableBlock.cells[4] as BlockView.Table.Cell.Text
val cellR2C3 = simpleTableBlock.cells[5] as BlockView.Table.Cell.Text
val blocks = listOf<BlockView>(simpleTableBlock)
//TESTING
val actualHighlighted = blocks.highlight { pairs ->
pairs.map { (key, txt) ->
BlockView.Searchable.Field(
key = key,
highlights = txt.search(pattern)
)
}
}
val actualFirstHighlightedIsTargeted = actualHighlighted.nextSearchTarget()
//EXPECTING
val expectedTargetedCell = cellR2C1.copy(
block = cellR2C1.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2)),
target = IntRange(0, 2)
)
)
)
)
val expectedHighlightedCell = cellR2C3.copy(
block = cellR2C3.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(
IntRange(0, 2)
)
)
)
)
)
val expectedCells = listOf(
cellR1C1.copy(
block = cellR1C1.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C2.copy(
block = cellR1C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C3.copy(
block = cellR1C3.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
expectedTargetedCell,
cellR2C2.copy(
block = cellR2C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
expectedHighlightedCell
)
val expectedFirstHighlightedIsTargeted = listOf(
simpleTableBlock.copy(cells = expectedCells)
)
//ASSERT
assertEquals(
expected = expectedFirstHighlightedIsTargeted,
actual = actualFirstHighlightedIsTargeted
)
}
@Test
fun `when first highlighted text is targeted in simple table expect to target next one`() {
//SETUP
val pattern = StubPattern(query = "bc")
val simpleTableBlock = StubTwoRowsThreeColumnsSimpleTable(
textR1C1 = "ab1",
textR1C2 = "ab2",
textR1C3 = "ac3",
textR2C1 = "bc1",
textR2C2 = "bb2",
textR2C3 = "bc3"
)
val cellR1C1 = simpleTableBlock.cells[0] as BlockView.Table.Cell.Text
val cellR1C2 = simpleTableBlock.cells[1] as BlockView.Table.Cell.Text
val cellR1C3 = simpleTableBlock.cells[2] as BlockView.Table.Cell.Text
val cellR2C1 = simpleTableBlock.cells[3] as BlockView.Table.Cell.Text
val cellR2C2 = simpleTableBlock.cells[4] as BlockView.Table.Cell.Text
val cellR2C3 = simpleTableBlock.cells[5] as BlockView.Table.Cell.Text
val blocks = listOf<BlockView>(simpleTableBlock)
//TESTING
val actualHighlighted = blocks.highlight { pairs ->
pairs.map { (key, txt) ->
BlockView.Searchable.Field(
key = key,
highlights = txt.search(pattern)
)
}
}
val actualFirstHighlightedIsTargeted = actualHighlighted.nextSearchTarget()
val actualSecondHighlightedIsTargeted = actualFirstHighlightedIsTargeted.nextSearchTarget()
//EXPECTING
val expectedCells = listOf(
cellR1C1.copy(
block = cellR1C1.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C2.copy(
block = cellR1C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C3.copy(
block = cellR1C3.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C1.copy(
block = cellR2C1.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2))
)
)
)
),
cellR2C2.copy(
block = cellR2C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C3.copy(
block = cellR2C3.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2)),
target = IntRange(0, 2)
)
)
)
)
)
val expectedSecondHighlightedIsTargeted = listOf(
simpleTableBlock.copy(
cells = expectedCells
)
)
//ASSERT
assertEquals(
expected = expectedSecondHighlightedIsTargeted,
actual = actualSecondHighlightedIsTargeted
)
}
@Test
fun `when second highlighted text is targeted in simple table expect to target second one`() {
//SETUP
val pattern = StubPattern(query = "bc")
val simpleTableBlock = StubTwoRowsThreeColumnsSimpleTable(
textR1C1 = "ab1",
textR1C2 = "ab2",
textR1C3 = "ac3",
textR2C1 = "bc1",
textR2C2 = "bb2",
textR2C3 = "bc3"
)
val cellR1C1 = simpleTableBlock.cells[0] as BlockView.Table.Cell.Text
val cellR1C2 = simpleTableBlock.cells[1] as BlockView.Table.Cell.Text
val cellR1C3 = simpleTableBlock.cells[2] as BlockView.Table.Cell.Text
val cellR2C1 = simpleTableBlock.cells[3] as BlockView.Table.Cell.Text
val cellR2C2 = simpleTableBlock.cells[4] as BlockView.Table.Cell.Text
val cellR2C3 = simpleTableBlock.cells[5] as BlockView.Table.Cell.Text
val blocks = listOf<BlockView>(simpleTableBlock)
//TESTING
val actualHighlighted = blocks.highlight { pairs ->
pairs.map { (key, txt) ->
BlockView.Searchable.Field(
key = key,
highlights = txt.search(pattern)
)
}
}
val actualFirstHighlightedIsTargeted = actualHighlighted.nextSearchTarget()
val actualSecondHighlightedIsTargeted = actualFirstHighlightedIsTargeted.nextSearchTarget()
val actualSecondHighlightedIsTargeted2 =
actualSecondHighlightedIsTargeted.nextSearchTarget()
//EXPECTING
val expectedCells = listOf(
cellR1C1.copy(
block = cellR1C1.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C2.copy(
block = cellR1C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C3.copy(
block = cellR1C3.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C1.copy(
block = cellR2C1.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2))
)
)
)
),
cellR2C2.copy(
block = cellR2C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C3.copy(
block = cellR2C3.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2)),
target = IntRange(0, 2)
)
)
)
)
)
val expectedSecondHighlightedIsTargeted = listOf(
simpleTableBlock.copy(
cells = expectedCells
)
)
//ASSERT
assertEquals(
expected = expectedSecondHighlightedIsTargeted,
actual = actualSecondHighlightedIsTargeted2
)
}
@Test
fun `when second highlighted text is targeted in simple table expect to target first one`() {
//SETUP
val pattern = StubPattern(query = "bc")
val simpleTableBlock = StubTwoRowsThreeColumnsSimpleTable(
textR1C1 = "ab1",
textR1C2 = "ab2",
textR1C3 = "ac3",
textR2C1 = "bc1",
textR2C2 = "bb2",
textR2C3 = "bc3"
)
val cellR1C1 = simpleTableBlock.cells[0] as BlockView.Table.Cell.Text
val cellR1C2 = simpleTableBlock.cells[1] as BlockView.Table.Cell.Text
val cellR1C3 = simpleTableBlock.cells[2] as BlockView.Table.Cell.Text
val cellR2C1 = simpleTableBlock.cells[3] as BlockView.Table.Cell.Text
val cellR2C2 = simpleTableBlock.cells[4] as BlockView.Table.Cell.Text
val cellR2C3 = simpleTableBlock.cells[5] as BlockView.Table.Cell.Text
val blocks = listOf<BlockView>(simpleTableBlock)
//TESTING
val actualHighlighted = blocks.highlight { pairs ->
pairs.map { (key, txt) ->
BlockView.Searchable.Field(
key = key,
highlights = txt.search(pattern)
)
}
}
val actualFirstHighlightedIsTargeted = actualHighlighted.nextSearchTarget()
val actualSecondHighlightedIsTargeted = actualFirstHighlightedIsTargeted.nextSearchTarget()
val actualFirstHighlightedIsTargeted2 =
actualSecondHighlightedIsTargeted.previousSearchTarget()
//EXPECTING
val expectedCells = listOf(
cellR1C1.copy(
block = cellR1C1.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C2.copy(
block = cellR1C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C3.copy(
block = cellR1C3.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C1.copy(
block = cellR2C1.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2)),
target = IntRange(0, 2)
)
)
)
),
cellR2C2.copy(
block = cellR2C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C3.copy(
block = cellR2C3.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2))
)
)
)
)
)
val expectedSecondHighlightedIsTargeted = listOf(
simpleTableBlock.copy(
cells = expectedCells
)
)
//ASSERT
assertEquals(
expected = expectedSecondHighlightedIsTargeted,
actual = actualFirstHighlightedIsTargeted2
)
}
@Test
fun `when first highlighted text is targeted in simple table expect to still target first one`() {
//SETUP
val pattern = StubPattern(query = "bc")
val simpleTableBlock = StubTwoRowsThreeColumnsSimpleTable(
textR1C1 = "ab1",
textR1C2 = "ab2",
textR1C3 = "ac3",
textR2C1 = "bc1",
textR2C2 = "bb2",
textR2C3 = "bc3"
)
val cellR1C1 = simpleTableBlock.cells[0] as BlockView.Table.Cell.Text
val cellR1C2 = simpleTableBlock.cells[1] as BlockView.Table.Cell.Text
val cellR1C3 = simpleTableBlock.cells[2] as BlockView.Table.Cell.Text
val cellR2C1 = simpleTableBlock.cells[3] as BlockView.Table.Cell.Text
val cellR2C2 = simpleTableBlock.cells[4] as BlockView.Table.Cell.Text
val cellR2C3 = simpleTableBlock.cells[5] as BlockView.Table.Cell.Text
val blocks = listOf<BlockView>(simpleTableBlock)
//TESTING
val actualHighlighted = blocks.highlight { pairs ->
pairs.map { (key, txt) ->
BlockView.Searchable.Field(
key = key,
highlights = txt.search(pattern)
)
}
}
val actualFirstHighlightedIsTargeted = actualHighlighted.nextSearchTarget()
val actualSecondHighlightedIsTargeted = actualFirstHighlightedIsTargeted.nextSearchTarget()
val actualFirstHighlightedIsTargeted2 =
actualSecondHighlightedIsTargeted.previousSearchTarget()
val actualFirstHighlightedIsTargeted3 =
actualFirstHighlightedIsTargeted2.previousSearchTarget()
//EXPECTING
val expectedCells = listOf(
cellR1C1.copy(
block = cellR1C1.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C2.copy(
block = cellR1C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR1C3.copy(
block = cellR1C3.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C1.copy(
block = cellR2C1.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2)),
target = IntRange(0, 2)
)
)
)
),
cellR2C2.copy(
block = cellR2C2.block.copy(searchFields = listOf(StubBlockViewSearchFiled()))
),
cellR2C3.copy(
block = cellR2C3.block.copy(
searchFields = listOf(
StubBlockViewSearchFiled(
highlights = listOf(IntRange(0, 2))
)
)
)
)
)
val expectedSecondHighlightedIsTargeted = listOf(
simpleTableBlock.copy(
cells = expectedCells
)
)
//ASSERT
assertEquals(
expected = expectedSecondHighlightedIsTargeted,
actual = actualFirstHighlightedIsTargeted3
)
}
}

View file

@ -1,6 +1,8 @@
package com.anytypeio.anytype.core_ui
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.StubParagraph
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
@ -8,6 +10,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.model.Indent
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.test_utils.MockDataFactory
import java.util.regex.Pattern
fun StubParagraphView(
id: Id = MockDataFactory.randomString(),
@ -301,4 +304,111 @@ fun StubFilePlaceholderView(
isPreviousBlockMedia = isPreviousBlockMedia,
background = background,
decorations = decorations
)
)
fun StubBlockViewSearchFiled(
key: Key = BlockView.Searchable.Field.DEFAULT_SEARCH_FIELD_KEY,
highlights: List<IntRange> = emptyList(),
target: IntRange = IntRange.EMPTY
): BlockView.Searchable.Field =
BlockView.Searchable.Field(
key = key,
highlights = highlights,
target = target
)
fun StubTwoRowsThreeColumnsSimpleTable(
tableId: String = MockDataFactory.randomUuid(),
rowId1: String = "rowId1",
rowId2: String = "rowId2",
columnId1: String = "columnId1",
columnId2: String = "columnId2",
columnId3: String = "columnId3",
textR1C1: String = MockDataFactory.randomString(),
textR1C2: String = MockDataFactory.randomString(),
textR1C3: String = MockDataFactory.randomString(),
textR2C1: String = MockDataFactory.randomString(),
textR2C2: String = MockDataFactory.randomString(),
textR2C3: String = MockDataFactory.randomString(),
): BlockView.Table {
val row1Block1 = StubParagraph(id = "$rowId1-$columnId1", text = textR1C1)
val row1Block2 = StubParagraph(id = "$rowId1-$columnId2", text = textR1C2)
val row1Block3 = StubParagraph(id = "$rowId1-$columnId3", text = textR1C3)
val row2Block1 = StubParagraph(id = "$rowId2-$columnId1", text = textR2C1)
val row2Block2 = StubParagraph(id = "$rowId2-$columnId2", text = textR2C2)
val row2Block3 = StubParagraph(id = "$rowId2-$columnId3", text = textR2C3)
val cells = listOf(
BlockView.Table.Cell.Text(
block = BlockView.Text.Paragraph(
id = row1Block1.id,
text = row1Block1.content.asText().text
),
rowId = rowId1,
columnId = columnId1
),
BlockView.Table.Cell.Text(
block = BlockView.Text.Paragraph(
id = row1Block2.id,
text = row1Block2.content.asText().text
),
rowId = rowId1,
columnId = columnId2
),
BlockView.Table.Cell.Text(
block = BlockView.Text.Paragraph(
id = row1Block3.id,
text = row1Block3.content.asText().text
),
rowId = rowId1,
columnId = columnId3
),
BlockView.Table.Cell.Text(
block = BlockView.Text.Paragraph(
id = row2Block1.id,
text = row2Block1.content.asText().text
),
rowId = rowId2,
columnId = columnId1
),
BlockView.Table.Cell.Text(
block = BlockView.Text.Paragraph(
id = row2Block2.id,
text = row2Block2.content.asText().text
),
rowId = rowId2,
columnId = columnId2
),
BlockView.Table.Cell.Text(
block = BlockView.Text.Paragraph(
id = row2Block3.id,
text = row2Block3.content.asText().text
),
rowId = rowId2,
columnId = columnId3
)
)
val columns = listOf(
BlockView.Table.Column(id = columnId1, background = ThemeColor.DEFAULT),
BlockView.Table.Column(id = columnId2, background = ThemeColor.DEFAULT),
BlockView.Table.Column(id = columnId3, background = ThemeColor.DEFAULT)
)
return BlockView.Table(
id = tableId,
cells = cells,
columns = columns,
rowCount = 2,
isSelected = false
)
}
fun StubPattern(
query: String
): Pattern {
val flags = Pattern.MULTILINE or Pattern.CASE_INSENSITIVE
val escaped = Pattern.quote(query)
return Pattern.compile(escaped, flags)
}

View file

@ -731,13 +731,9 @@ fun BlockView.setGhostEditorSelection(
}
fun List<BlockView>.nextSearchTarget(): List<BlockView> {
val currentTargetView = find { view ->
view is BlockView.Searchable && view.searchFields.any { it.isTargeted }
}
val currentTargetView: BlockView? = findHighlightedTarget()
if (currentTargetView == null) {
val nextCandidate = find { view ->
view is BlockView.Searchable && view.searchFields.any { it.highlights.isNotEmpty() }
}
val nextCandidate: BlockView? = findFirstHighlighted()
if (nextCandidate == null) {
return this
} else {
@ -752,13 +748,10 @@ fun List<BlockView>.nextSearchTarget(): List<BlockView> {
field
}
}
return map { view ->
if (view.id == nextCandidate.id) {
view.setHighlight(highlights)
} else {
view
}
}
return highlightBlockById(
id = nextCandidate.id,
highlights = highlights
)
}
} else {
check(currentTargetView is BlockView.Searchable)
@ -772,13 +765,10 @@ fun List<BlockView>.nextSearchTarget(): List<BlockView> {
field
}
}
return map { view ->
if (view.id == currentTargetView.id) {
view.setHighlight(highlights)
} else {
view
}
}
return highlightBlockById(
id = currentTargetView.id,
highlights = highlights
)
} else {
val currentTargetFieldIndex = currentTargetView.searchFields.indexOf(currentField)
val nextFields = currentTargetView.searchFields.subList(
@ -796,18 +786,13 @@ fun List<BlockView>.nextSearchTarget(): List<BlockView> {
else -> field
}
}
return map { view ->
if (view.id == currentTargetView.id) {
view.setHighlight(highlights)
} else {
view
}
}
return highlightBlockById(
id = currentTargetView.id,
highlights = highlights
)
} else {
val nextViews = subList(indexOf(currentTargetView).inc(), size)
val nextCandidate = nextViews.find { view ->
view is BlockView.Searchable && view.searchFields.any { it.highlights.isNotEmpty() }
}
val nextViews = subListFromIdToEnd(id = currentTargetView.id)
val nextCandidate = nextViews.findFirstHighlighted()
if (nextCandidate == null) {
return this
} else {
@ -816,22 +801,54 @@ fun List<BlockView>.nextSearchTarget(): List<BlockView> {
field.highlights.isNotEmpty()
}
return map { view ->
when (view.id) {
nextCandidate.id -> view.setHighlight(
nextCandidate.searchFields.mapIndexed { index, field ->
if (index == nextFieldCandidateIndex) {
field.copy(target = field.highlights.first())
} else {
field
if (view is BlockView.Table) {
val cells = view.cells
val updatedCells = cells.map { cell ->
when (cell) {
is BlockView.Table.Cell.Empty -> cell
BlockView.Table.Cell.Space -> cell
is BlockView.Table.Cell.Text -> {
val block = cell.block
val updatedBlock = when (block.id) {
nextCandidate.id -> block.copy(
searchFields = nextCandidate.searchFields.mapIndexed { index, field ->
if (index == nextFieldCandidateIndex) {
field.copy(target = field.highlights.first())
} else {
field
}
}
)
currentTargetView.id -> block.copy(
searchFields = currentTargetView.searchFields.map { field ->
field.copy(target = IntRange.EMPTY)
}
)
else -> block
}
cell.copy(block = updatedBlock)
}
}
)
currentTargetView.id -> view.setHighlight(
currentTargetView.searchFields.map { field ->
field.copy(target = IntRange.EMPTY)
}
)
else -> view
}
view.copy(cells = updatedCells)
} else {
when (view.id) {
nextCandidate.id -> view.setHighlight(
nextCandidate.searchFields.mapIndexed { index, field ->
if (index == nextFieldCandidateIndex) {
field.copy(target = field.highlights.first())
} else {
field
}
}
)
currentTargetView.id -> view.setHighlight(
currentTargetView.searchFields.map { field ->
field.copy(target = IntRange.EMPTY)
}
)
else -> view
}
}
}
}
@ -841,9 +858,7 @@ fun List<BlockView>.nextSearchTarget(): List<BlockView> {
}
fun List<BlockView>.previousSearchTarget(): List<BlockView> {
val currentTargetView = find { view ->
view is BlockView.Searchable && view.searchFields.any { it.isTargeted }
}
val currentTargetView = findHighlightedTarget()
if (currentTargetView == null) {
return this
} else {
@ -858,13 +873,10 @@ fun List<BlockView>.previousSearchTarget(): List<BlockView> {
field
}
}
return map { view ->
if (view.id == currentTargetView.id) {
view.setHighlight(highlights)
} else {
view
}
}
return highlightBlockById(
id = currentTargetView.id,
highlights = highlights
)
} else {
val currentTargetFieldIndex = currentTargetView.searchFields.indexOf(currentField)
val previousFields = currentTargetView.searchFields.subList(
@ -883,39 +895,67 @@ fun List<BlockView>.previousSearchTarget(): List<BlockView> {
else -> field
}
}
return map { view ->
if (view.id == currentTargetView.id) {
view.setHighlight(highlights)
} else {
view
}
}
return highlightBlockById(
id = currentTargetView.id,
highlights = highlights
)
} else {
val previousViews = subList(0, indexOf(currentTargetView))
val previousCandidate = previousViews.findLast { view ->
view is BlockView.Searchable && view.searchFields.any { it.highlights.isNotEmpty() }
}
val previousViews = subListFrom0ToId(id = currentTargetView.id)
val previousCandidate = previousViews.findLastHighlighted()
if (previousCandidate == null) {
return this
} else {
check(previousCandidate is BlockView.Searchable)
return map { view ->
when (view.id) {
previousCandidate.id -> view.setHighlight(
previousCandidate.searchFields.mapIndexed { index, field ->
if (index == previousCandidate.searchFields.size.dec()) {
field.copy(target = field.highlights.last())
} else {
field
if (view is BlockView.Table) {
val cells = view.cells
val updatedCells = cells.map { cell ->
when (cell) {
is BlockView.Table.Cell.Empty -> cell
BlockView.Table.Cell.Space -> cell
is BlockView.Table.Cell.Text -> {
val block = cell.block
val updatedBlock = when (block.id) {
previousCandidate.id -> block.copy(
searchFields = previousCandidate.searchFields.mapIndexed { index, field ->
if (index == previousCandidate.searchFields.size.dec()) {
field.copy(target = field.highlights.last())
} else {
field
}
}
)
currentTargetView.id -> block.copy(
searchFields = currentTargetView.searchFields.map { field ->
field.copy(target = IntRange.EMPTY)
}
)
else -> block
}
cell.copy(block = updatedBlock)
}
}
)
currentTargetView.id -> view.setHighlight(
currentTargetView.searchFields.map { field ->
field.copy(target = IntRange.EMPTY)
}
)
else -> view
}
view.copy(cells = updatedCells)
} else {
when (view.id) {
previousCandidate.id -> view.setHighlight(
previousCandidate.searchFields.mapIndexed { index, field ->
if (index == previousCandidate.searchFields.size.dec()) {
field.copy(target = field.highlights.last())
} else {
field
}
}
)
currentTargetView.id -> view.setHighlight(
currentTargetView.searchFields.map { field ->
field.copy(target = IntRange.EMPTY)
}
)
else -> view
}
}
}
}
@ -1199,4 +1239,159 @@ fun List<BlockView>.findTableCellView(id: Id): BlockView.Table.Cell? {
}
}
return null
}
fun List<BlockView>.findHighlightedTarget(): BlockView? {
forEach { block ->
if (block is BlockView.Table) {
val target =
block.getTextCells().find { cell -> cell.searchFields.any { it.isTargeted } }
if (target != null) return target
} else {
val isTargeted =
block is BlockView.Searchable && block.searchFields.any { it.isTargeted }
if (isTargeted) return block
}
}
return null
}
fun List<BlockView>.findFirstHighlighted(): BlockView? {
forEach { block ->
if (block is BlockView.Table) {
val target =
block.getTextCells()
.find { cell -> cell.searchFields.any { it.highlights.isNotEmpty() } }
if (target != null) return target
} else {
val isHighlighted =
block is BlockView.Searchable && block.searchFields.any { it.highlights.isNotEmpty() }
if (isHighlighted) return block
}
}
return null
}
fun List<BlockView>.findLastHighlighted(): BlockView? {
var lastHighlighted: BlockView? = null
forEach { block ->
if (block is BlockView.Table) {
val target =
block.getTextCells()
.findLast { cell -> cell.searchFields.any { it.highlights.isNotEmpty() } }
if (target != null) lastHighlighted = target
} else {
val isHighlighted =
block is BlockView.Searchable && block.searchFields.any { it.highlights.isNotEmpty() }
if (isHighlighted) lastHighlighted = block
}
}
return lastHighlighted
}
fun BlockView.Table.getTextCells(): List<BlockView.Text.Paragraph> {
return cells.mapNotNull { cell ->
when (cell) {
is BlockView.Table.Cell.Empty -> null
BlockView.Table.Cell.Space -> null
is BlockView.Table.Cell.Text -> cell.block
}
}
}
fun List<BlockView>.highlightBlockById(
id: Id,
highlights: List<BlockView.Searchable.Field>
): List<BlockView> = map { view ->
if (view is BlockView.Table) {
val updatedCells = view.cells.map { cell ->
when (cell) {
BlockView.Table.Cell.Space -> cell
is BlockView.Table.Cell.Empty -> cell
is BlockView.Table.Cell.Text -> {
if (cell.getId() == id) {
val block = cell.block
cell.copy(
block = block.copy(searchFields = highlights)
)
} else {
cell
}
}
}
}
view.copy(cells = updatedCells)
} else {
if (view.id == id) {
view.setHighlight(highlights)
} else {
view
}
}
}
fun List<BlockView>.subListFromIdToEnd(id: Id): List<BlockView> {
val indexOfFirst = indexOfFirst { it.id == id }
if (indexOfFirst != -1) {
return subList(indexOfFirst.inc(), this.size)
} else {
val blockTable = findBlockTableByCellId(cellId = id)
return if (blockTable != null) {
val list = mutableListOf<BlockView>()
val cells = blockTable.cells
val indexOfTarget =
cells.indexOfFirst { cell -> cell is BlockView.Table.Cell.Text && cell.block.id == id }
val subListOfCells = if (indexOfTarget != -1 && indexOfTarget.inc() <= cells.size) {
cells.subList(indexOfTarget.inc(), cells.size)
} else {
cells
}
list.add(blockTable.copy(cells = subListOfCells))
val tableIndex = this.indexOfFirst { it.id == blockTable.id }
list.addAll(subList(tableIndex.inc(), size))
list
} else {
this
}
}
}
fun List<BlockView>.subListFrom0ToId(id: Id): List<BlockView> {
val indexOfFirst = indexOfFirst { it.id == id }
if (indexOfFirst != -1) {
return subList(0, indexOfFirst)
} else {
val blockTable = findBlockTableByCellId(cellId = id)
return if (blockTable != null) {
val list = mutableListOf<BlockView>()
val cells = blockTable.cells
val indexOfTarget =
cells.indexOfFirst { cell -> cell is BlockView.Table.Cell.Text && cell.block.id == id }
val subListOfCells = if (indexOfTarget != -1 && indexOfTarget <= cells.size) {
cells.subList(0, indexOfTarget)
} else {
cells
}
val tableIndex = this.indexOfFirst { it.id == blockTable.id }
list.addAll(subList(0, tableIndex))
list.add(blockTable.copy(cells = subListOfCells))
list
} else {
this
}
}
}
fun List<BlockView>.findBlockTableByCellId(cellId: Id): BlockView.Table? {
this.forEach { blockView ->
if (blockView is BlockView.Table) {
val cells = blockView.cells
val index =
cells.indexOfFirst { it is BlockView.Table.Cell.Text && it.block.id == cellId }
if (index != -1) {
return blockView
}
}
}
return null
}