From 86e9a10687133806c4e5dbd57ec512d37680c653 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 10 Sep 2015 16:24:12 +0300 Subject: [PATCH] First formatting block found on new line may not be starting it, there could be part of multiline leaf block before, so we should skip it when detecting indent (IDEA-144945) --- .../FirstBlockOnNewLine_NotStartsIt.xml | 7 ++++ .../AbstractNewLineBlocksIteratorTest.java | 17 +++++----- .../autodetect/NewLineBlocksIteratorTest.java | 30 ++++++++++++++--- .../common/NewLineBlocksIterator.java | 33 +++++++++++++++++-- .../formatting/FormattingModelXmlReader.java | 5 +++ 5 files changed, 77 insertions(+), 15 deletions(-) create mode 100644 java/java-tests/testData/psi/autodetect/FirstBlockOnNewLine_NotStartsIt.xml diff --git a/java/java-tests/testData/psi/autodetect/FirstBlockOnNewLine_NotStartsIt.xml b/java/java-tests/testData/psi/autodetect/FirstBlockOnNewLine_NotStartsIt.xml new file mode 100644 index 000000000000..0beebd746e23 --- /dev/null +++ b/java/java-tests/testData/psi/autodetect/FirstBlockOnNewLine_NotStartsIt.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/AbstractNewLineBlocksIteratorTest.java b/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/AbstractNewLineBlocksIteratorTest.java index 7e7e7a35ef2c..71fa8c72a04d 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/AbstractNewLineBlocksIteratorTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/AbstractNewLineBlocksIteratorTest.java @@ -15,9 +15,7 @@ */ package com.intellij.psi.codeStyle.autodetect; -import com.intellij.formatting.Block; -import com.intellij.formatting.FormattingModel; -import com.intellij.formatting.FormattingModelBuilder; +import com.intellij.formatting.*; import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.editor.Document; import com.intellij.psi.PsiDocumentManager; @@ -37,21 +35,22 @@ public abstract class AbstractNewLineBlocksIteratorTest extends LightPlatformCod return getTestName(true); } - protected void checkNewLineBlocksStartOffsets(int[] newLineStartOffsets) { - Iterator iterator = createNewLineBlocksIterator(); - + protected void checkStartOffsets(int[] newLineStartOffset) { + checkStartOffsets(newLineStartOffset, newLineBlockIterator()); + } + + protected void checkStartOffsets(int[] newLineStartOffsets, Iterator iterator) { int i = 0; while (iterator.hasNext()) { - Assert.assertTrue("Detected unspecified new line block start offset ", i < newLineStartOffsets.length); Block next = iterator.next(); + Assert.assertTrue("Extra new line block found: " + next.getTextRange(), i < newLineStartOffsets.length); Assert.assertEquals("Block start offset do not match ", newLineStartOffsets[i++], next.getTextRange().getStartOffset()); } - Assert.assertEquals("Not detected new line block start offset ", i, newLineStartOffsets.length); } @NotNull - protected static Iterator createNewLineBlocksIterator() { + protected static Iterator newLineBlockIterator() { FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(myFile); Assert.assertNotNull(builder); diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/NewLineBlocksIteratorTest.java b/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/NewLineBlocksIteratorTest.java index 38225ac553f5..8448949f8828 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/NewLineBlocksIteratorTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/autodetect/NewLineBlocksIteratorTest.java @@ -17,8 +17,15 @@ package com.intellij.psi.codeStyle.autodetect; import com.intellij.JavaTestUtil; import com.intellij.formatting.Block; +import com.intellij.formatting.FormattingModelXmlReader; +import com.intellij.formatting.TestBlock; +import com.intellij.formatting.TestFormattingModel; +import com.intellij.openapi.editor.Document; +import com.intellij.psi.formatter.common.NewLineBlocksIterator; +import org.jdom.JDOMException; import org.jetbrains.annotations.NotNull; +import java.io.IOException; import java.util.Iterator; public class NewLineBlocksIteratorTest extends AbstractNewLineBlocksIteratorTest { @@ -37,7 +44,7 @@ public class NewLineBlocksIteratorTest extends AbstractNewLineBlocksIteratorTest 0, 3, 30, 60, 82, 107, 136, 154, 184, 220, 226, 232, 237 }; - checkNewLineBlocksStartOffsets(newLineBlocksStartOffsets); + checkStartOffsets(newLineBlocksStartOffsets); } public void testDoNotReverseBlocks() { @@ -47,15 +54,30 @@ public class NewLineBlocksIteratorTest extends AbstractNewLineBlocksIteratorTest 0, 39, 48, 118, 177, 235, 243 }; - checkNewLineBlocksStartOffsets(newLineBlocksStartOffsets); + checkStartOffsets(newLineBlocksStartOffsets); } + public void testFirstBlockOnNewLine_NotStartsIt() throws IOException, JDOMException { + String text = "var x = r'''\n" + + "''';"; + + Iterator it = newIteratorFromTestFormattingModel(text); + checkStartOffsets(new int[] {0}, it); + } + + public void testBigFileWithOnlyErrorElements_DoNotProduceSOE() { configureByFile(getFileName() + ".java"); - Iterator iterator = createNewLineBlocksIterator(); + Iterator iterator = newLineBlockIterator(); while (iterator.hasNext()) { iterator.next(); } } - + + protected Iterator newIteratorFromTestFormattingModel(String text) throws IOException, JDOMException { + TestFormattingModel model = new TestFormattingModel(text); + Document document = model.getDocument(); + TestBlock block = new FormattingModelXmlReader(model).readTestBlock(getTestDataPath(), getFileName() + ".xml"); + return new NewLineBlocksIterator(block, document); + } } diff --git a/platform/lang-impl/src/com/intellij/psi/formatter/common/NewLineBlocksIterator.java b/platform/lang-impl/src/com/intellij/psi/formatter/common/NewLineBlocksIterator.java index 25e8669c9241..c03b079e3568 100644 --- a/platform/lang-impl/src/com/intellij/psi/formatter/common/NewLineBlocksIterator.java +++ b/platform/lang-impl/src/com/intellij/psi/formatter/common/NewLineBlocksIterator.java @@ -18,6 +18,7 @@ package com.intellij.psi.formatter.common; import com.intellij.formatting.Block; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.TextRange; +import com.intellij.util.text.CharArrayUtil; import java.util.Iterator; import java.util.List; @@ -44,15 +45,32 @@ public class NewLineBlocksIterator implements Iterator { @Override public boolean hasNext() { if (myCurrentDocumentLine < myTotalLines) { - popUntilTopBlockStartOffsetGreaterOrEqual(myCurrentLineStartOffset); + popUntilTopBlockStartsNewLine(); return !myStack.isEmpty(); } return false; } + private void popUntilTopBlockStartsNewLine() { + popUntilTopBlockStartOffsetGreaterOrEqual(myCurrentLineStartOffset); + if (myStack.isEmpty()) return; + + Block block = myStack.peek(); + while (block != null && !isStartingNewLine(block)) { + myCurrentDocumentLine++; + if (myCurrentDocumentLine >= myTotalLines) { + myStack.clear(); + break; + } + myCurrentLineStartOffset = myDocument.getLineStartOffset(myCurrentDocumentLine); + popUntilTopBlockStartOffsetGreaterOrEqual(myCurrentLineStartOffset); + block = myStack.isEmpty() ? null : myStack.peek(); + } + } + @Override public Block next() { - popUntilTopBlockStartOffsetGreaterOrEqual(myCurrentLineStartOffset); + popUntilTopBlockStartsNewLine(); Block current = myStack.peek(); TextRange currentBlockRange = current.getTextRange(); @@ -101,6 +119,17 @@ public class NewLineBlocksIterator implements Iterator { } } + private boolean isStartingNewLine(Block block) { + TextRange range = block.getTextRange(); + int blockStart = range.getStartOffset(); + + int lineNumber = myDocument.getLineNumber(blockStart); + int lineStartOffset = myDocument.getLineStartOffset(lineNumber); + + CharSequence text = myDocument.getCharsSequence(); + return CharArrayUtil.isEmptyOrSpaces(text, lineStartOffset, blockStart); + } + @Override public void remove() { throw new UnsupportedOperationException(); diff --git a/platform/platform-tests/testSrc/com/intellij/formatting/FormattingModelXmlReader.java b/platform/platform-tests/testSrc/com/intellij/formatting/FormattingModelXmlReader.java index 505e2dfcc0da..c6854c19c00e 100644 --- a/platform/platform-tests/testSrc/com/intellij/formatting/FormattingModelXmlReader.java +++ b/platform/platform-tests/testSrc/com/intellij/formatting/FormattingModelXmlReader.java @@ -28,6 +28,11 @@ import java.util.Map; "/platform/platform-tests/testData/newGeneralFormatter", dataName + ".xml"); return readBlock(JDOMUtil.load(dataFile)); } + + public TestBlock readTestBlock(String path, String file) throws IOException, JDOMException { + final File dataFile = new File(path, file); + return readBlock(JDOMUtil.load(dataFile)); + } private TestBlock readBlock(final Element rootElement) { final int startOffset = Integer.parseInt(rootElement.getAttributeValue("start"));