Commit 60ebeea6 authored by Yaroslav Lepenkin's avatar Yaroslav Lepenkin
Browse files

range merge initial

parent 4744ce29
Showing with 119 additions and 4 deletions
+119 -4
...@@ -44,6 +44,10 @@ public class FormatTextRange { ...@@ -44,6 +44,10 @@ public class FormatTextRange {
public int getStartOffset() { public int getStartOffset() {
return formattingRange.getStartOffset(); return formattingRange.getStartOffset();
} }
public int getEndOffset() {
return formattingRange.getEndOffset();
}
public boolean isReadOnly(@NotNull TextRange range) { public boolean isReadOnly(@NotNull TextRange range) {
return range.getStartOffset() > formattingRange.getEndOffset() || range.getEndOffset() < formattingRange.getStartOffset(); return range.getStartOffset() > formattingRange.getEndOffset() || range.getEndOffset() < formattingRange.getStartOffset();
......
...@@ -19,14 +19,44 @@ import com.intellij.openapi.util.TextRange ...@@ -19,14 +19,44 @@ import com.intellij.openapi.util.TextRange
import java.util.* import java.util.*
internal class FormatRangesStorage { class FormatRangesStorage {
private val rangesByStartOffset = TreeMap<Int, FormatTextRange>() private val rangesByStartOffset = TreeMap<Int, FormatTextRange>()
fun add(range: TextRange, processHeadingWhitespace: Boolean) { fun add(range: TextRange, processHeadingWhitespace: Boolean) {
val formatRange = FormatTextRange(range, processHeadingWhitespace) if (range.isEmpty) return
rangesByStartOffset.put(formatRange.startOffset, formatRange) val newRange = FormatTextRange(range, processHeadingWhitespace)
val nearestBefore = getClosestOrSiblingRange(range)
if (nearestBefore != null && canBeMerged(nearestBefore, range)) {
val mergedRange = merge(newRange, nearestBefore)
rangesByStartOffset.remove(nearestBefore.startOffset)
rangesByStartOffset.put(mergedRange.startOffset, mergedRange)
return
}
assert(rangesByStartOffset[newRange.startOffset] == null)
rangesByStartOffset.put(newRange.startOffset, newRange)
} }
private fun canBeMerged(nearestBefore: FormatTextRange, newRange: TextRange): Boolean {
return newRange.endOffset < nearestBefore.endOffset || newRange.startOffset < nearestBefore.endOffset
}
private fun getClosestOrSiblingRange(range: TextRange): FormatTextRange? {
val closest = rangesByStartOffset.floorEntry(range.endOffset)?.value ?: return null
if (range.endOffset == closest.startOffset && !closest.isProcessHeadingWhitespace) {
return rangesByStartOffset.floorEntry(range.endOffset - 1)?.value
}
return closest
}
private fun merge(first: FormatTextRange, second: FormatTextRange): FormatTextRange {
val firstByStartOffset = listOf(first, second).sortedBy { it.startOffset }.first()
val endOffset = Math.max(first.endOffset, second.endOffset)
val range = TextRange(firstByStartOffset.startOffset, endOffset)
return FormatTextRange(range, firstByStartOffset.isProcessHeadingWhitespace)
}
fun isWhiteSpaceReadOnly(range: TextRange): Boolean { fun isWhiteSpaceReadOnly(range: TextRange): Boolean {
return rangesByStartOffset.values.find { !it.isWhitespaceReadOnly(range) } == null return rangesByStartOffset.values.find { !it.isWhitespaceReadOnly(range) } == null
} }
......
...@@ -28,4 +28,85 @@ class FormatTextRangesTest { ...@@ -28,4 +28,85 @@ class FormatTextRangesTest {
assertThat(ranges.isWhitespaceReadOnly(TextRange(25, 35))).isFalse() assertThat(ranges.isWhitespaceReadOnly(TextRange(25, 35))).isFalse()
} }
}
class FormatRangesStorageTest {
private fun FormatTextRange.assertRange(start: Int, end: Int, isProcessHeadingSpace: Boolean) {
assertThat(textRange).isEqualTo(TextRange(start, end))
assertThat(isProcessHeadingWhitespace).isEqualTo(isProcessHeadingSpace)
}
@Test
fun `check fully contained text range is not added`() {
val storage = FormatRangesStorage()
storage.add(TextRange(10, 20), false)
storage.add(TextRange(15, 18), false)
val ranges = storage.getRanges()
assertThat(ranges).hasSize(1)
ranges[0].assertRange(10, 20, false)
}
@Test
fun `check left boundary is not formatted`() {
val storage = FormatRangesStorage()
storage.add(TextRange(10, 20), false)
storage.add(TextRange(5, 10), false)
val ranges = storage.getRanges()
assertThat(ranges).hasSize(2)
}
@Test
fun `check left boundary is formatted`() {
val storage = FormatRangesStorage()
storage.add(TextRange(10, 20), true)
storage.add(TextRange(5, 10), false)
val ranges = storage.getRanges()
assertThat(ranges).hasSize(1)
ranges[0].assertRange(5, 20, false)
}
@Test
fun `check right boundary is not formatted`() {
val storage = FormatRangesStorage()
storage.add(TextRange(10, 20), false)
storage.add(TextRange(20, 30), false)
val ranges = storage.getRanges()
assertThat(ranges).hasSize(2)
}
@Test
fun `check right boundary is formatted`() {
val storage = FormatRangesStorage()
storage.add(TextRange(10, 20), false)
storage.add(TextRange(19, 30), false)
val ranges = storage.getRanges()
assertThat(ranges).hasSize(1)
ranges[0].assertRange(10, 30, false)
}
@Test
fun `ensure isProcessHeading space state merged`() {
val storage = FormatRangesStorage()
storage.add(TextRange(10, 20), false)
storage.add(TextRange(10, 20), true)
val ranges = storage.getRanges()
assertThat(ranges).hasSize(1)
ranges[0].assertRange(10, 20, true)
}
} }
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment