From 60ebeea6c0a0015a03fde4e688a1ed870f460649 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin <yaroslav.lepenkin@jetbrains.com> Date: Thu, 4 Aug 2016 19:23:28 +0300 Subject: [PATCH] range merge initial --- .../intellij/formatting/FormatTextRange.java | 4 + .../intellij/formatting/FormatTextRanges.kt | 38 ++++++++- .../formatting/FormatTextRangesTest.kt | 81 +++++++++++++++++++ 3 files changed, 119 insertions(+), 4 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatTextRange.java b/platform/lang-impl/src/com/intellij/formatting/FormatTextRange.java index 4915c0a3350f..b240d2c557d3 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatTextRange.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatTextRange.java @@ -44,6 +44,10 @@ public class FormatTextRange { public int getStartOffset() { return formattingRange.getStartOffset(); } + + public int getEndOffset() { + return formattingRange.getEndOffset(); + } public boolean isReadOnly(@NotNull TextRange range) { return range.getStartOffset() > formattingRange.getEndOffset() || range.getEndOffset() < formattingRange.getStartOffset(); diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatTextRanges.kt b/platform/lang-impl/src/com/intellij/formatting/FormatTextRanges.kt index ca19337534bb..f022de23ad23 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatTextRanges.kt +++ b/platform/lang-impl/src/com/intellij/formatting/FormatTextRanges.kt @@ -19,14 +19,44 @@ import com.intellij.openapi.util.TextRange import java.util.* -internal class FormatRangesStorage { +class FormatRangesStorage { private val rangesByStartOffset = TreeMap<Int, FormatTextRange>() fun add(range: TextRange, processHeadingWhitespace: Boolean) { - val formatRange = FormatTextRange(range, processHeadingWhitespace) - rangesByStartOffset.put(formatRange.startOffset, formatRange) + if (range.isEmpty) return + 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 { return rangesByStartOffset.values.find { !it.isWhitespaceReadOnly(range) } == null } diff --git a/platform/platform-tests/testSrc/com/intellij/formatting/FormatTextRangesTest.kt b/platform/platform-tests/testSrc/com/intellij/formatting/FormatTextRangesTest.kt index 0e9e4c37347b..3f3d923e79c6 100644 --- a/platform/platform-tests/testSrc/com/intellij/formatting/FormatTextRangesTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/formatting/FormatTextRangesTest.kt @@ -28,4 +28,85 @@ class FormatTextRangesTest { 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 -- GitLab