From 15582d4a590fdd0b84c5098756aae63be21b7c4a Mon Sep 17 00:00:00 2001 From: Igor Slobodskov Date: Wed, 2 Aug 2023 22:17:34 +0000 Subject: [PATCH] [EA-263022] SOE fix corner case with greedy block * Tests testDocumentGuardedTextUtilDuplicateLine and testDocumentGuardedTextUtilDeleteLine are for valid cases. * Tests testDocumentGuardedTextUtilDuplicateLineWithGreedy and testDocumentGuardedTextUtilDeleteLineWithGreedy for corner cases with greedy range marker. Before fix this cases leaded to stack overflow exception inside DocumentGuardedTextUtil. Merge-request: IJ-MR-111809 Merged-by: Igor Slobodskov GitOrigin-RevId: b2e1bd6868af754bcd99e555c5a3b8272edbc3d3 --- .../actions/DocumentGuardedTextUtil.java | 4 +- .../openapi/editor/impl/GuardBlockTest.java | 62 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DocumentGuardedTextUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DocumentGuardedTextUtil.java index d5bd6310fcd3..d3eb04ed67fd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DocumentGuardedTextUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DocumentGuardedTextUtil.java @@ -28,7 +28,7 @@ public final class DocumentGuardedTextUtil { CharSequence textBefore = document.getImmutableCharSequence().subSequence(Math.max(0, startOffset - blockPrefix.length()), startOffset); - if (blockPrefix.toString().equals(textBefore.toString())) { + if (!blockPrefix.isEmpty() && blockPrefix.toString().equals(textBefore.toString())) { deleteString(document, startOffset - blockPrefix.length(), endOffset - blockPrefix.length()); } else { @@ -48,7 +48,7 @@ public final class DocumentGuardedTextUtil { } catch (ReadOnlyFragmentModificationException ex) { String blockPrefix = document.getImmutableCharSequence().subSequence(ex.getGuardedBlock().getStartOffset(), offset).toString(); - if (s.toString().endsWith(blockPrefix)) { + if (!blockPrefix.isEmpty() && s.toString().endsWith(blockPrefix)) { int newOffset = offset - blockPrefix.length(); String newString = blockPrefix + s.subSequence(0, s.length() - blockPrefix.length()); insertString(document, newOffset, newString); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/GuardBlockTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/GuardBlockTest.java index ba7a2016572a..471f0ada2a2d 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/GuardBlockTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/GuardBlockTest.java @@ -95,4 +95,66 @@ public class GuardBlockTest extends BasePlatformTestCase { myFixture.getEditor().getCaretModel().moveToOffset(offset - 1); assertNotNull(myFixture.completeBasic()); } + + public void testDocumentGuardedTextUtilDuplicateLine() { + String text = "a\n#%%\nc"; + // ^^^^^^^ <- guarded + // com.intellij.openapi.editor.actions.DocumentGuardedTextUtil.insertString handles this case and inserts "\na" after first "a" + myFixture.configureByText("x.txt", text); + RangeMarker guard = createGuard(1, 6); + + myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DUPLICATE_LINES); + myFixture.checkResult("a\na\n#%%\nc"); + assertTrue(guard.isValid()); + assertEquals(3, guard.getStartOffset()); + assertEquals(8, guard.getEndOffset()); + } + + public void testDocumentGuardedTextUtilDeleteLine() { + String text = "a\nb\n#%%\nc"; + // ^^^^^^^ <- guarded + // com.intellij.openapi.editor.actions.DocumentGuardedTextUtil.deleteString handles this case and removes \n before caret + myFixture.configureByText("x.txt", text); + RangeMarker guard = createGuard(3, 8); + + myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE_LINE); + myFixture.checkResult("a\n#%%\nc"); + assertTrue(guard.isValid()); + assertEquals(1, guard.getStartOffset()); + assertEquals(6, guard.getEndOffset()); + } + + public void testDocumentGuardedTextUtilDuplicateLineWithGreedy() { + String text = "a\n#%%\nc"; + // ^^^^^^^ <- guarded + myFixture.configureByText("x.txt", text); + createGuard(1, 6).setGreedyToLeft(true); + + try { + myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DUPLICATE_LINES); + } + catch (RuntimeException e) { + assertEquals("Unable to perform an action since it changes read-only fragments of the current document", e.getMessage()); + myFixture.checkResult(text); + return; + } + fail("must be read only at " + 1); + } + + public void testDocumentGuardedTextUtilDeleteLineWithGreedy() { + String text = "a\nb\n#%%\nc"; + // ^^^^^^^ <- guarded + myFixture.configureByText("x.txt", text); + createGuard(3, 8).setGreedyToLeft(true); + + try { + myFixture.performEditorAction(IdeActions.ACTION_EDITOR_DELETE_LINE); + } + catch (RuntimeException e) { + assertEquals("Unable to perform an action since it changes read-only fragments of the current document", e.getMessage()); + myFixture.checkResult(text); + return; + } + fail("must be read only at " + 3); + } }