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