From 5c45aa7782b344aa0f6e5af291b4d167c82236a8 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sat, 14 Sep 2024 16:26:43 +0400 Subject: [PATCH 01/11] #2894 Added test illustrating incorrect behavior Editor is not closed but updated text is wrong --- .../java/intellilang/PerlQuickEditTest.kt | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 plugin/src/test/java/intellilang/PerlQuickEditTest.kt diff --git a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt new file mode 100644 index 0000000000..0b62c8bec1 --- /dev/null +++ b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2015-2024 Alexandr Evstigneev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package intellilang + +import base.PerlLightTestCase +import com.intellij.codeInsight.intention.impl.QuickEditAction +import com.intellij.testFramework.fixtures.InjectionTestFixture +import org.jetbrains.annotations.NonNls +import org.junit.Test + +class PerlQuickEditTest : PerlLightTestCase() { + override fun getBaseDataPath(): @NonNls String? = "intellilang/perl/quickedit" + private val injectionTestFixture: InjectionTestFixture get() = InjectionTestFixture(myFixture) + + @Test + fun testEditHeredoc() { + initWithTextSmart( + """ + use v5.36; + + sub foo{ + say <<~HTML; +
+ + +
+ HTML + }""".trimIndent() + ) + val originalEditor = injectionTestFixture.topLevelEditor + val quickEditHandler = QuickEditAction().invokeImpl(project, injectionTestFixture.topLevelEditor, injectionTestFixture.topLevelFile) + val fragmentFile = quickEditHandler.newFile + assertEquals( + """ +
+ + +
+ """.trimIndent(), fragmentFile.text.trim() + ) + + myFixture.openFileInEditor(fragmentFile.virtualFile) + + myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexAfter("")) + myFixture.type("\nhello there") + assertFalse(myFixture.editor.isDisposed) + assertEquals( + """ +
+ + hello there + +
""".trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") + ) + + assertEquals( + """ + use v5.36; + + sub foo{ + say <<~HTML; +
+ + hello there + +
+ HTML + }""".trimIndent(), originalEditor.document.text + ) + } + + private fun String.indexAfter(string: String): Int { + val r = indexOf(string) + return if (r == -1) -1 else r + string.length + } + +} \ No newline at end of file From eab1cd3f9c84593a6bf07c07aa4622056e6b91aa Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 10:05:35 +0400 Subject: [PATCH 02/11] #2894 Do not update content if it didn't change --- ...PerlInjectedFileChangesHandlerFactory.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java b/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java index 0b4cb105dd..43b6451f2f 100644 --- a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java +++ b/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 Alexandr Evstigneev + * Copyright 2015-2024 Alexandr Evstigneev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.util.ProperTextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.ElementManipulators; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -83,15 +84,16 @@ public void commitToOriginal(@NotNull DocumentEvent e) { var localInsideHost = new ProperTextRange(hostMarker.getStartOffset() - hostOffset, hostMarker.getEndOffset() - hostOffset); var localInsideFile = new ProperTextRange(fragmentMarker.getStartOffset(), fragmentMarker.getEndOffset()); - // fixme we could optimize here and check if host text has been changed and update only really changed fragments, not all of them - if (currentHost != null && localInsideFile.getEndOffset() <= text.length() && !localInsideFile.isEmpty()) { + if (localInsideFile.getEndOffset() <= text.length() && !localInsideFile.isEmpty()) { var decodedText = localInsideFile.substring(text); - currentHost = ElementManipulators.handleContentChange(currentHost, localInsideHost, decodedText); - if (currentHost == null) { - failAndReport("Updating host returned null. Original host" + host + - "; original text: " + originalText + - "; updated range in host: " + localInsideHost + - "; decoded text to replace: " + decodedText, e, null); + if (!StringUtil.equals(decodedText, localInsideHost.subSequence(currentHost.getText()))) { + currentHost = ElementManipulators.handleContentChange(currentHost, localInsideHost, decodedText); + if (currentHost == null) { + failAndReport("Updating host returned null. Original host" + host + + "; original text: " + originalText + + "; updated range in host: " + localInsideHost + + "; decoded text to replace: " + decodedText, e, null); + } } } } From e9e52637694dc8e9d60d20e392332b798ba97b8a Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 10:06:37 +0400 Subject: [PATCH 03/11] #2894 Improved heredoc content update process - No fancy indentation adjustments. - One line updates will work by themselves, because injection is already indented - One line to multiline updates are now properly indented - Added tests --- .../PerlHeredocElementManipulator.java | 61 +++------- .../java/intellilang/PerlQuickEditTest.kt | 108 +++++++++++++++--- 2 files changed, 109 insertions(+), 60 deletions(-) diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java index 3e9080697e..a2dd58d0e7 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 Alexandr Evstigneev + * Copyright 2015-2024 Alexandr Evstigneev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,10 @@ package com.perl5.lang.perl.idea.manipulators; -import com.intellij.application.options.CodeStyle; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.AbstractElementManipulator; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.util.IncorrectOperationException; -import com.perl5.lang.perl.PerlLanguage; import com.perl5.lang.perl.psi.impl.PerlHeredocElementImpl; import com.perl5.lang.perl.psi.utils.PerlElementFactory; import org.jetbrains.annotations.NotNull; @@ -34,58 +30,29 @@ public class PerlHeredocElementManipulator extends AbstractElementManipulator 0) { - sb.insert(0, getIndenter(project, range.getStartOffset())); - range = TextRange.from(0, range.getEndOffset()); - } + var lineStart = StringUtil.skipWhitespaceBackward(elementText, range.getStartOffset() - 1); + if (lineStart < range.getStartOffset()) { + indent = elementText.substring(lineStart, range.getStartOffset()); + range = TextRange.create(lineStart, range.getEndOffset()); - normalizeIndentation(project, sb, element.getRealIndentSize()); + newContent = prependLines(newContent, indent); + } + } - String newElementText = range.replace(element.getText(), sb.toString()); + String newElementText = range.replace(elementText, newContent); PerlHeredocElementImpl replacement = PerlElementFactory.createHeredocBodyReplacement(element, newElementText); return (PerlHeredocElementImpl)element.replace(replacement); } - private static @NotNull String getIndenter(@NotNull Project project, int indentSize) { - CommonCodeStyleSettings.IndentOptions indentOptions = - CodeStyle.getSettings(project).getCommonSettings(PerlLanguage.INSTANCE).getIndentOptions(); - - return StringUtil.repeat(indentOptions != null && indentOptions.USE_TAB_CHARACTER ? "\t" : " ", indentSize); - } - - private static void normalizeIndentation(@NotNull Project project, @NotNull StringBuilder sb, int indentSize) { - int offset = 0; - int currentLineStart = 0; - int currentLineIndentSize = 0; - boolean hasNonSpaces = false; - - while (offset < sb.length()) { - char currentChar = sb.charAt(offset++); - if (currentChar == '\n') { - if (hasNonSpaces && currentLineIndentSize < indentSize) { - int indentAdjustment = indentSize - currentLineIndentSize; - sb.insert(currentLineStart, getIndenter(project, indentAdjustment)); - offset += indentAdjustment; - } - currentLineStart = offset; - hasNonSpaces = false; - currentLineIndentSize = 0; - } - else if (!hasNonSpaces) { - if (Character.isWhitespace(currentChar)) { - currentLineIndentSize++; - } - else { - hasNonSpaces = true; - } - } - } + private static @NotNull String prependLines(@NotNull String newContent, @NotNull String prefix) { + return prefix + String.join(prefix, StringUtil.split(newContent, "\n", false, true)); } } diff --git a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt index 0b62c8bec1..db1157bbf2 100644 --- a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt +++ b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt @@ -18,6 +18,8 @@ package intellilang import base.PerlLightTestCase import com.intellij.codeInsight.intention.impl.QuickEditAction +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiFile import com.intellij.testFramework.fixtures.InjectionTestFixture import org.jetbrains.annotations.NonNls import org.junit.Test @@ -27,42 +29,88 @@ class PerlQuickEditTest : PerlLightTestCase() { private val injectionTestFixture: InjectionTestFixture get() = InjectionTestFixture(myFixture) @Test - fun testEditHeredoc() { - initWithTextSmart( + fun testEditHeredocMiddle() { + val (originalEditor, fragmentFile) = initFileWithTestSample() + + myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexAfter("")) + myFixture.type("\nhello there") + assertFalse(myFixture.editor.isDisposed) + assertEquals( + """ +
+ + hello there + +
""".trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") + ) + + assertEquals( """ use v5.36; sub foo{ say <<~HTML;
- + + hello there
HTML - }""".trimIndent() + }""".trimIndent(), originalEditor.document.text ) - val originalEditor = injectionTestFixture.topLevelEditor - val quickEditHandler = QuickEditAction().invokeImpl(project, injectionTestFixture.topLevelEditor, injectionTestFixture.topLevelFile) - val fragmentFile = quickEditHandler.newFile + } + + @Test + fun testEditHeredocEnd() { + val (originalEditor, fragmentFile) = initFileWithTestSample() + + myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexAfter("")) + myFixture.type("\n\n\nhello there\n") + assertFalse(myFixture.editor.isDisposed) assertEquals( """
- """.trimIndent(), fragmentFile.text.trim() + + + hello there + """.trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") ) - myFixture.openFileInEditor(fragmentFile.virtualFile) + assertEquals( + """ + use v5.36; + + sub foo{ + say <<~HTML; +
+ + +
+ + + hello there + + HTML + }""".trimIndent(), originalEditor.document.text + ) + } - myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexAfter("")) - myFixture.type("\nhello there") + @Test + fun testEditHeredocStart() { + val (originalEditor, fragmentFile) = initFileWithTestSample() + + myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexOf("
")) + myFixture.type("\n\n\nhello there\n\n") assertFalse(myFixture.editor.isDisposed) assertEquals( """ + hello there +
- hello there
""".trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") ) @@ -73,9 +121,13 @@ class PerlQuickEditTest : PerlLightTestCase() { sub foo{ say <<~HTML; + + + + hello there +
- hello there
HTML @@ -83,6 +135,36 @@ class PerlQuickEditTest : PerlLightTestCase() { ) } + private fun initFileWithTestSample(): Pair { + initWithTextSmart( + """ + use v5.36; + + sub foo{ + say <<~HTML; +
+ + +
+ HTML + }""".trimIndent() + ) + val originalEditor = injectionTestFixture.topLevelEditor + val quickEditHandler = QuickEditAction().invokeImpl(project, injectionTestFixture.topLevelEditor, injectionTestFixture.topLevelFile) + val fragmentFile = quickEditHandler.newFile + assertEquals( + """ +
+ + +
+ """.trimIndent(), fragmentFile.text.trim() + ) + + myFixture.openFileInEditor(fragmentFile.virtualFile) + return Pair(originalEditor, fragmentFile) + } + private fun String.indexAfter(string: String): Int { val r = indexOf(string) return if (r == -1) -1 else r + string.length From adbfd88d0cf4d69425681136cc9920797231b723 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 10:57:53 +0400 Subject: [PATCH 04/11] #2894 moved to the platform changes handler --- .../PerlHeredocElementManipulator.java | 11 +- ...PerlInjectedFileChangesHandlerFactory.java | 103 ------------------ .../resources/perl5.plugin.intelliLang.xml | 2 - .../java/intellilang/PerlQuickEditTest.kt | 34 ++++++ 4 files changed, 40 insertions(+), 110 deletions(-) delete mode 100644 plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java index a2dd58d0e7..419d14bfce 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java @@ -30,19 +30,20 @@ public class PerlHeredocElementManipulator extends AbstractElementManipulator 0) { var lineStart = StringUtil.skipWhitespaceBackward(elementText, range.getStartOffset() - 1); if (lineStart < range.getStartOffset()) { - indent = elementText.substring(lineStart, range.getStartOffset()); + if (!contentRemoval) { + var indent = elementText.substring(lineStart, range.getStartOffset()); + newContent = prependLines(newContent, indent); + } range = TextRange.create(lineStart, range.getEndOffset()); - - newContent = prependLines(newContent, indent); } } diff --git a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java b/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java deleted file mode 100644 index 43b6451f2f..0000000000 --- a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlInjectedFileChangesHandlerFactory.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2015-2024 Alexandr Evstigneev - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.perl5.lang.perl.intellilang; - -import com.intellij.injected.editor.InjectedFileChangesHandler; -import com.intellij.injected.editor.InjectedFileChangesHandlerProvider; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.util.ProperTextRange; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.ElementManipulators; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiLanguageInjectionHost; -import com.intellij.psi.impl.source.tree.injected.changesHandler.CommonInjectedFileChangesHandler; -import com.intellij.psi.impl.source.tree.injected.changesHandler.MarkersMapping; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -@ApiStatus.ScheduledForRemoval(inVersion = "2020.3") -@Deprecated -public class PerlInjectedFileChangesHandlerFactory implements InjectedFileChangesHandlerProvider { - @Override - public InjectedFileChangesHandler createFileChangesHandler(List shreds, - Editor hostEditor, - Document newDocument, - PsiFile injectedFile) { - return new PerlInjectedChangesHandler(shreds, hostEditor, newDocument, injectedFile); - } - - private static class PerlInjectedChangesHandler extends CommonInjectedFileChangesHandler { - public PerlInjectedChangesHandler(@NotNull List shreds, - @NotNull Editor hostEditor, - @NotNull Document fragmentDocument, - @NotNull PsiFile injectedFile) { - super(shreds, hostEditor, fragmentDocument, injectedFile); - } - - @Override - public void commitToOriginal(@NotNull DocumentEvent e) { - var text = myFragmentDocument.getText(); - var map = ContainerUtil.groupBy(getMarkers(), MarkersMapping::getHost); - - var documentManager = PsiDocumentManager.getInstance(myProject); - documentManager.commitDocument(myHostDocument); - - for (PsiLanguageInjectionHost host : map.keySet()) { - if (host == null) { - continue; - } - var hostRange = host.getTextRange(); - var hostOffset = hostRange.getStartOffset(); - var originalText = host.getText(); - var currentHost = host; - var mappings = new ArrayList<>(map.get(host)); - if (mappings.isEmpty()) { - continue; - } - Collections.reverse(mappings); - - for (var mapping : mappings) { - var hostMarker = mapping.getHostMarker(); - var fragmentMarker = mapping.getFragmentMarker(); - var localInsideHost = new ProperTextRange(hostMarker.getStartOffset() - hostOffset, hostMarker.getEndOffset() - hostOffset); - var localInsideFile = new ProperTextRange(fragmentMarker.getStartOffset(), fragmentMarker.getEndOffset()); - - if (localInsideFile.getEndOffset() <= text.length() && !localInsideFile.isEmpty()) { - var decodedText = localInsideFile.substring(text); - if (!StringUtil.equals(decodedText, localInsideHost.subSequence(currentHost.getText()))) { - currentHost = ElementManipulators.handleContentChange(currentHost, localInsideHost, decodedText); - if (currentHost == null) { - failAndReport("Updating host returned null. Original host" + host + - "; original text: " + originalText + - "; updated range in host: " + localInsideHost + - "; decoded text to replace: " + decodedText, e, null); - } - } - } - } - } - } - } -} diff --git a/plugin/intelliLang/src/main/resources/perl5.plugin.intelliLang.xml b/plugin/intelliLang/src/main/resources/perl5.plugin.intelliLang.xml index ceae50433e..e4cf40144f 100644 --- a/plugin/intelliLang/src/main/resources/perl5.plugin.intelliLang.xml +++ b/plugin/intelliLang/src/main/resources/perl5.plugin.intelliLang.xml @@ -28,8 +28,6 @@ - + + +
+ HTML + }""".trimIndent(), originalEditor.document.text + ) + } + + private fun initFileWithTestSample(): Pair { initWithTextSmart( """ From 29f0e1f7a6f052af867d6542a89487cd9cbacd81 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 11:56:06 +0400 Subject: [PATCH 05/11] #2894 handled empty heredoc injection (need to adjust indentation for the first element) --- .../intellilang/PerlHeredocLiteralEscaper.java | 15 ++++++++++++--- .../test/java/intellilang/PerlQuickEditTest.kt | 10 +--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java index cae76494b6..f8bb678724 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 Alexandr Evstigneev + * Copyright 2015-2024 Alexandr Evstigneev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,8 +35,17 @@ public boolean isOneLine() { @Override public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { - outChars.append(rangeInsideHost.subSequence(myHost.getNode().getChars())); - return true; + var hostTextLength = myHost.getTextLength(); + var effectiveRange = hostTextLength < rangeInsideHost.getEndOffset() ? + rangeInsideHost : + TextRange.create(rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset() - 1); + if (!effectiveRange.isEmpty()) { + outChars.append(rangeInsideHost.subSequence(myHost.getNode().getChars())); + return true; + } + else { + return false; + } } @Override diff --git a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt index d50c159208..54404389d1 100644 --- a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt +++ b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt @@ -154,15 +154,7 @@ class PerlQuickEditTest : PerlLightTestCase() { sub foo{ say <<~HTML; - - - - hello there - -
- - -
+ hello there HTML }""".trimIndent(), originalEditor.document.text ) From c9257b6f4751b4a3a08fa76c7031c45f6604e373 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 12:31:03 +0400 Subject: [PATCH 06/11] #2894 final fix --- .../idea/intellilang/PerlHeredocLiteralEscaper.java | 2 +- .../testFixtures/java/base/PerlLightTestCaseBase.java | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java index f8bb678724..2da7b01b04 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java @@ -36,7 +36,7 @@ public boolean isOneLine() { @Override public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { var hostTextLength = myHost.getTextLength(); - var effectiveRange = hostTextLength < rangeInsideHost.getEndOffset() ? + var effectiveRange = rangeInsideHost.getEndOffset() < hostTextLength ? rangeInsideHost : TextRange.create(rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset() - 1); if (!effectiveRange.isEmpty()) { diff --git a/plugin/src/testFixtures/java/base/PerlLightTestCaseBase.java b/plugin/src/testFixtures/java/base/PerlLightTestCaseBase.java index 26cf511f85..76c8953373 100644 --- a/plugin/src/testFixtures/java/base/PerlLightTestCaseBase.java +++ b/plugin/src/testFixtures/java/base/PerlLightTestCaseBase.java @@ -1377,8 +1377,14 @@ protected T getElementAtCaretWithoutInjection(@NotNull Cl } protected @NotNull PsiElement getElementAtCaretWithoutInjection() { - PsiElement result = getFile().getViewProvider().findElementAt(getEditor().getCaretModel().getOffset()); - assertNotNull(result); + PsiElement result; + if (getFile().getText().isEmpty()) { + result = getFile(); + } + else { + result = getFile().getViewProvider().findElementAt(getEditor().getCaretModel().getOffset()); + assertNotNull(result); + } PsiFile leafFile = result.getContainingFile(); if (InjectedLanguageManager.getInstance(getProject()).isInjectedFragment(leafFile)) { result = leafFile.getContext(); From 8720773a4bfc813ce2f68a28f5d00a14d6fd50c9 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 13:01:41 +0400 Subject: [PATCH 07/11] #2894 better test samples, better empty handling --- .../PerlHeredocElementManipulator.java | 14 +++++ .../java/intellilang/PerlQuickEditTest.kt | 54 +++++++++++-------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java index 419d14bfce..250a4c6ed6 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java @@ -16,10 +16,14 @@ package com.perl5.lang.perl.idea.manipulators; +import com.intellij.application.options.CodeStyle; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.AbstractElementManipulator; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.util.IncorrectOperationException; +import com.perl5.lang.perl.PerlLanguage; import com.perl5.lang.perl.psi.impl.PerlHeredocElementImpl; import com.perl5.lang.perl.psi.utils.PerlElementFactory; import org.jetbrains.annotations.NotNull; @@ -46,6 +50,9 @@ public PerlHeredocElementImpl handleContentChange(@NotNull PerlHeredocElementImp range = TextRange.create(lineStart, range.getEndOffset()); } } + else if (range.getStartOffset() == 0) { + newContent = prependLines(newContent, getIndenter(element.getProject(), element.getRealIndentSize())); + } String newElementText = range.replace(elementText, newContent); PerlHeredocElementImpl replacement = PerlElementFactory.createHeredocBodyReplacement(element, newElementText); @@ -53,6 +60,13 @@ public PerlHeredocElementImpl handleContentChange(@NotNull PerlHeredocElementImp return (PerlHeredocElementImpl)element.replace(replacement); } + private static @NotNull String getIndenter(@NotNull Project project, int indentSize) { + CommonCodeStyleSettings.IndentOptions indentOptions = + CodeStyle.getSettings(project).getCommonSettings(PerlLanguage.INSTANCE).getIndentOptions(); + + return StringUtil.repeat(indentOptions != null && indentOptions.USE_TAB_CHARACTER ? "\t" : " ", indentSize); + } + private static @NotNull String prependLines(@NotNull String newContent, @NotNull String prefix) { return prefix + String.join(prefix, StringUtil.split(newContent, "\n", false, true)); } diff --git a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt index 54404389d1..d563850fb2 100644 --- a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt +++ b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt @@ -33,13 +33,14 @@ class PerlQuickEditTest : PerlLightTestCase() { val (originalEditor, fragmentFile) = initFileWithTestSample() myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexAfter("")) - myFixture.type("\nhello there") + myFixture.type("\nhello\n there") assertFalse(myFixture.editor.isDisposed) assertEquals( """
- hello there + hello + there
""".trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") ) @@ -52,7 +53,8 @@ class PerlQuickEditTest : PerlLightTestCase() { say <<~HTML;
- hello there + hello + there
HTML @@ -65,7 +67,7 @@ class PerlQuickEditTest : PerlLightTestCase() { val (originalEditor, fragmentFile) = initFileWithTestSample() myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexAfter("")) - myFixture.type("\n\n\nhello there\n") + myFixture.type("\n\n\nhello\n there\n") assertFalse(myFixture.editor.isDisposed) assertEquals( """ @@ -75,7 +77,8 @@ class PerlQuickEditTest : PerlLightTestCase() { - hello there + hello + there """.trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") ) @@ -91,7 +94,8 @@ class PerlQuickEditTest : PerlLightTestCase() { - hello there + hello + there HTML }""".trimIndent(), originalEditor.document.text @@ -103,11 +107,12 @@ class PerlQuickEditTest : PerlLightTestCase() { val (originalEditor, fragmentFile) = initFileWithTestSample() myFixture.editor.caretModel.moveToOffset(fragmentFile.text.indexOf("
")) - myFixture.type("\n\n\nhello there\n\n") + myFixture.type("\n\n\nhello\n there\n\n") assertFalse(myFixture.editor.isDisposed) assertEquals( """ - hello there + hello + there
@@ -124,7 +129,8 @@ class PerlQuickEditTest : PerlLightTestCase() { - hello there + hello + there
@@ -140,11 +146,12 @@ class PerlQuickEditTest : PerlLightTestCase() { val (originalEditor, fragmentFile) = initFileWithTestSample() myFixture.editor.selectionModel.setSelection(0, myFixture.editor.document.text.length) - myFixture.type("hello there") + myFixture.type("hello\n there") assertFalse(myFixture.editor.isDisposed) assertEquals( """ - hello there + hello + there """.trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") ) @@ -154,15 +161,16 @@ class PerlQuickEditTest : PerlLightTestCase() { sub foo{ say <<~HTML; - hello there + hello + there HTML }""".trimIndent(), originalEditor.document.text ) } - private fun initFileWithTestSample(): Pair { - initWithTextSmart( + private fun initFileWithTestSample( + testSample: String = """ use v5.36; @@ -173,19 +181,19 @@ class PerlQuickEditTest : PerlLightTestCase() {
HTML - }""".trimIndent() - ) - val originalEditor = injectionTestFixture.topLevelEditor - val quickEditHandler = QuickEditAction().invokeImpl(project, injectionTestFixture.topLevelEditor, injectionTestFixture.topLevelFile) - val fragmentFile = quickEditHandler.newFile - assertEquals( - """ + }""".trimIndent(), + injectedSample: String = """
- """.trimIndent(), fragmentFile.text.trim() - ) + """.trimIndent() + ): Pair { + initWithTextSmart(testSample) + val originalEditor = injectionTestFixture.topLevelEditor + val quickEditHandler = QuickEditAction().invokeImpl(project, injectionTestFixture.topLevelEditor, injectionTestFixture.topLevelFile) + val fragmentFile = quickEditHandler.newFile + assertEquals(injectedSample, fragmentFile.text.trim()) myFixture.openFileInEditor(fragmentFile.virtualFile) return Pair(originalEditor, fragmentFile) From 4762ed9131aef55f56f26a7951d490e02891169c Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 13:13:59 +0400 Subject: [PATCH 08/11] #2894 two tests still failing --- .../java/intellilang/PerlQuickEditTest.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt index d563850fb2..cba7d27335 100644 --- a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt +++ b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt @@ -141,6 +141,46 @@ class PerlQuickEditTest : PerlLightTestCase() { ) } + @Test + fun testEmptyHeredoc() { + val (originalEditor, fragmentFile) = initFileWithTestSample( + """ + use v5.36; + + sub foo{ + say <<~HTML; + + HTML + }""".trimIndent(), + "" + ) + + myFixture.type("\n hello\n there\n") + assertFalse(myFixture.editor.isDisposed) + assertEquals( + """ + + hello + there + + """.trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") + ) + + assertEquals( + """ + use v5.36; + + sub foo{ + say <<~HTML; + + hello + there + + HTML + }""".trimIndent(), originalEditor.document.text + ) + } + @Test fun testReplaceHeredoc() { val (originalEditor, fragmentFile) = initFileWithTestSample() From e67e3ad9a8f3adba3a6d1a48907b000c8366abb6 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sun, 15 Sep 2024 18:32:38 +0400 Subject: [PATCH 09/11] #2894 final enter not working because it changes outside the smart range --- .../PerlHeredocLiteralEscaper.java | 14 ++---- .../PerlHeredocElementManipulator.java | 48 ++++++++++++++----- .../PerlHeredocLanguageInjector.java | 9 ++-- .../java/intellilang/PerlQuickEditTest.kt | 24 +++++----- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java index 2da7b01b04..9033341de5 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java @@ -36,16 +36,12 @@ public boolean isOneLine() { @Override public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { var hostTextLength = myHost.getTextLength(); - var effectiveRange = rangeInsideHost.getEndOffset() < hostTextLength ? - rangeInsideHost : - TextRange.create(rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset() - 1); - if (!effectiveRange.isEmpty()) { - outChars.append(rangeInsideHost.subSequence(myHost.getNode().getChars())); - return true; - } - else { - return false; + var endOffset = rangeInsideHost.getEndOffset(); + if (endOffset == hostTextLength) { + endOffset--; } + outChars.append(myHost.getNode().getChars().subSequence(rangeInsideHost.getStartOffset(), endOffset)); + return true; } @Override diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java index 250a4c6ed6..f971fac862 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/manipulators/PerlHeredocElementManipulator.java @@ -32,34 +32,50 @@ public class PerlHeredocElementManipulator extends AbstractElementManipulator { @Override - public PerlHeredocElementImpl handleContentChange(@NotNull PerlHeredocElementImpl element, @NotNull TextRange range, String newContent) + public PerlHeredocElementImpl handleContentChange(final @NotNull PerlHeredocElementImpl element, + final @NotNull TextRange range, + final String newContent) throws IncorrectOperationException { var contentRemoval = newContent.isEmpty(); - if (element.getTextLength() == range.getEndOffset() && !contentRemoval && !StringUtil.endsWith(newContent, "\n")) { - newContent += "\n"; + var replacementContent = newContent; + var effectiveRange = range; + if (range.isEmpty()) { + // this handles empty heredoc update. We have a single empty shred pointing to the offset of closing \n + effectiveRange = TextRange.create(range.getStartOffset(), element.getTextLength() - 1); } var elementText = element.getText(); - if (range.getStartOffset() > 0) { - var lineStart = StringUtil.skipWhitespaceBackward(elementText, range.getStartOffset() - 1); - if (lineStart < range.getStartOffset()) { + if (effectiveRange.getStartOffset() > 0) { + var lineStart = getLineStartOffset(elementText, effectiveRange); + if (lineStart < effectiveRange.getStartOffset()) { if (!contentRemoval) { - var indent = elementText.substring(lineStart, range.getStartOffset()); - newContent = prependLines(newContent, indent); + var indent = elementText.substring(lineStart, effectiveRange.getStartOffset()); + replacementContent = prependLines(replacementContent, indent); } - range = TextRange.create(lineStart, range.getEndOffset()); + effectiveRange = TextRange.create(lineStart, effectiveRange.getEndOffset()); } } - else if (range.getStartOffset() == 0) { - newContent = prependLines(newContent, getIndenter(element.getProject(), element.getRealIndentSize())); + else if (effectiveRange.getStartOffset() == 0) { + replacementContent = prependLines(newContent, getIndenter(element.getProject(), element.getRealIndentSize())); } - String newElementText = range.replace(elementText, newContent); + String newElementText = effectiveRange.replace(elementText, replacementContent); PerlHeredocElementImpl replacement = PerlElementFactory.createHeredocBodyReplacement(element, newElementText); return (PerlHeredocElementImpl)element.replace(replacement); } + private static int getLineStartOffset(@NotNull String elementText, @NotNull TextRange effectiveRange) { + var startOffset = effectiveRange.getStartOffset(); + if (startOffset == 0) { + return startOffset; + } + if (elementText.charAt(startOffset - 1) == '\n') { + return startOffset; + } + return StringUtil.skipWhitespaceBackward(elementText, startOffset - 1); + } + private static @NotNull String getIndenter(@NotNull Project project, int indentSize) { CommonCodeStyleSettings.IndentOptions indentOptions = CodeStyle.getSettings(project).getCommonSettings(PerlLanguage.INSTANCE).getIndentOptions(); @@ -68,6 +84,12 @@ else if (range.getStartOffset() == 0) { } private static @NotNull String prependLines(@NotNull String newContent, @NotNull String prefix) { - return prefix + String.join(prefix, StringUtil.split(newContent, "\n", false, true)); + var result = new StringBuilder(); + StringUtil.split(newContent, "\n", false, true) + .forEach(line -> { + result.append(prefix); + result.append(line); + }); + return result.toString(); } } diff --git a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java b/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java index 9785aecbd9..9a34eb7239 100644 --- a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java +++ b/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 Alexandr Evstigneev + * Copyright 2015-2024 Alexandr Evstigneev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -107,7 +107,8 @@ private void addPlace(@NotNull PerlHeredocElementImpl heredocElement, @NotNull M while (sourceOffset < sourceLength) { char currentChar = sourceText.charAt(sourceOffset); if (currentChar == '\n') { - registrar.addPlace(null, null, heredocElement, TextRange.from(sourceOffset, 1)); + var suffix = sourceOffset + 1 < sourceLength ? null : "\n"; + registrar.addPlace(null, suffix, heredocElement, TextRange.from(sourceOffset, 1)); currentLineIndent = 0; } else if (Character.isWhitespace(currentChar) && currentLineIndent < indentSize) { @@ -124,7 +125,9 @@ else if (Character.isWhitespace(currentChar) && currentLineIndent < indentSize) } } - registrar.addPlace(null, null, heredocElement, TextRange.create(sourceOffset, sourceEnd)); + var suffix = sourceEnd < sourceLength ? null : "\n"; + + registrar.addPlace(null, suffix, heredocElement, TextRange.create(sourceOffset, sourceEnd)); sourceOffset = sourceEnd; currentLineIndent = 0; continue; diff --git a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt index cba7d27335..7eac004af2 100644 --- a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt +++ b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt @@ -42,7 +42,7 @@ class PerlQuickEditTest : PerlLightTestCase() { hello there -
""".trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") +
""".trimIndent(), myFixture.editor.document.text.trim().trimLines() ) assertEquals( @@ -79,7 +79,7 @@ class PerlQuickEditTest : PerlLightTestCase() { hello there - """.trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") + """.trimIndent(), myFixture.editor.document.text.trim().trimLines() ) assertEquals( @@ -117,7 +117,7 @@ class PerlQuickEditTest : PerlLightTestCase() {
-
""".trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") + """.trimIndent(), myFixture.editor.document.text.trim().trimLines() ) assertEquals( @@ -155,15 +155,13 @@ class PerlQuickEditTest : PerlLightTestCase() { "" ) - myFixture.type("\n hello\n there\n") + myFixture.type("\nhello\n there\n") assertFalse(myFixture.editor.isDisposed) assertEquals( """ - - hello + hello there - - """.trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") + """.trimIndent(), myFixture.editor.document.text.trim().trimLines() ) assertEquals( @@ -173,11 +171,11 @@ class PerlQuickEditTest : PerlLightTestCase() { sub foo{ say <<~HTML; - hello + hello there HTML - }""".trimIndent(), originalEditor.document.text + }""".trimIndent().trimLines(), originalEditor.document.text.trimLines() ) } @@ -192,7 +190,7 @@ class PerlQuickEditTest : PerlLightTestCase() { """ hello there - """.trimIndent(), myFixture.editor.document.text.trim().replace(Regex("[ \t]+\n"), "\n") + """.trimIndent(), myFixture.editor.document.text.trim().trimLines() ) assertEquals( @@ -244,4 +242,8 @@ class PerlQuickEditTest : PerlLightTestCase() { return if (r == -1) -1 else r + string.length } + private fun String.trimLines(): String { + return replace(Regex("[ \t]+\n"), "\n") + } + } \ No newline at end of file From e8211ce00cf59e5e54ab03dc7154535ef7222d04 Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sat, 21 Sep 2024 18:19:02 +0400 Subject: [PATCH 10/11] #2894 do not inject into last newline --- .../intellilang/PerlHeredocLiteralEscaper.java | 7 +------ .../PerlHeredocLanguageInjector.java | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java index 9033341de5..46e5b310b5 100644 --- a/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java +++ b/plugin/core/src/main/java/com/perl5/lang/perl/idea/intellilang/PerlHeredocLiteralEscaper.java @@ -35,12 +35,7 @@ public boolean isOneLine() { @Override public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { - var hostTextLength = myHost.getTextLength(); - var endOffset = rangeInsideHost.getEndOffset(); - if (endOffset == hostTextLength) { - endOffset--; - } - outChars.append(myHost.getNode().getChars().subSequence(rangeInsideHost.getStartOffset(), endOffset)); + outChars.append(rangeInsideHost.subSequence(myHost.getNode().getChars())); return true; } diff --git a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java b/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java index 9a34eb7239..6587a5d9d7 100644 --- a/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java +++ b/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlHeredocLanguageInjector.java @@ -94,7 +94,8 @@ public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull private void addPlace(@NotNull PerlHeredocElementImpl heredocElement, @NotNull MultiHostRegistrar registrar) { int indentSize = heredocElement.getRealIndentSize(); if (indentSize == 0) { - registrar.addPlace(null, null, heredocElement, ElementManipulators.getValueTextRange(heredocElement)); + var elementRange = ElementManipulators.getValueTextRange(heredocElement); + registrar.addPlace(null, null, heredocElement, TextRange.create(elementRange.getStartOffset(), elementRange.getEndOffset() - 1)); return; } @@ -107,8 +108,13 @@ private void addPlace(@NotNull PerlHeredocElementImpl heredocElement, @NotNull M while (sourceOffset < sourceLength) { char currentChar = sourceText.charAt(sourceOffset); if (currentChar == '\n') { - var suffix = sourceOffset + 1 < sourceLength ? null : "\n"; - registrar.addPlace(null, suffix, heredocElement, TextRange.from(sourceOffset, 1)); + String suffix = null; + int endOffset = sourceOffset + 1; + if (endOffset == sourceLength) { + suffix = "\n"; + endOffset--; + } + registrar.addPlace(null, suffix, heredocElement, TextRange.create(sourceOffset, endOffset)); currentLineIndent = 0; } else if (Character.isWhitespace(currentChar) && currentLineIndent < indentSize) { @@ -125,7 +131,11 @@ else if (Character.isWhitespace(currentChar) && currentLineIndent < indentSize) } } - var suffix = sourceEnd < sourceLength ? null : "\n"; + String suffix = null; + if (sourceEnd == sourceLength) { + suffix = "\n"; + sourceEnd--; + } registrar.addPlace(null, suffix, heredocElement, TextRange.create(sourceOffset, sourceEnd)); sourceOffset = sourceEnd; From 77f09366550218f86b12065f8ecc52943b73dc3b Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Mon, 23 Sep 2024 09:47:49 +0400 Subject: [PATCH 11/11] wip --- plugin/src/test/java/intellilang/PerlQuickEditTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt index 7eac004af2..81cf839365 100644 --- a/plugin/src/test/java/intellilang/PerlQuickEditTest.kt +++ b/plugin/src/test/java/intellilang/PerlQuickEditTest.kt @@ -96,7 +96,8 @@ class PerlQuickEditTest : PerlLightTestCase() { hello there - + + HTML }""".trimIndent(), originalEditor.document.text )