From c738a20ef90d5af117dc4d2c06dbae91bb0f2f36 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Thu, 6 Mar 2014 21:05:34 +0400 Subject: [PATCH] IDEA-89020 Disappears value of the variables standing next in Live templates IDEA-121634 Live Template: template is broken with 3+ variables and leading space --- .../template/LiveTemplateTest.groovy | 53 ++++++++++++++++--- .../template/impl/TemplateSegments.java | 13 +++-- .../template/impl/TemplateState.java | 27 ++++++---- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy index 48b8456d527a..cef42e9ea06d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.impl.DocumentImpl import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiDocumentManager import com.intellij.psi.codeStyle.CodeStyleSettingsManager import com.intellij.psi.codeStyle.CommonCodeStyleSettings @@ -74,15 +75,50 @@ public class LiveTemplateTest extends LightCodeInsightFixtureTestCase { String group = "user"; final Template template = manager.createTemplate(templateName, group, templateText); template.addVariable("ARG", "", "", false); - final TemplateContextType contextType = - ContainerUtil.findInstance(TemplateContextType.EP_NAME.getExtensions(), JavaCodeContextType.class); + TemplateContextType contextType = contextType(JavaCodeContextType.class); ((TemplateImpl)template).getTemplateContext().setEnabled(contextType, true); addTemplate(template, testRootDisposable) - final Editor editor = getEditor(); manager.startTemplate(editor, (char)'\t'); checkResultByText(expected); } + + public void testTemplateWithSegmentsAtTheSamePosition_1() { + doTestTemplateWithThreeVariables("", "", "", "class A { void test() { for(TestValue1TestValue2TestValue3) {} } }") + } + + public void testTemplateWithSegmentsAtTheSamePosition_2() { + doTestTemplateWithThreeVariables("Def1", "Def2", "DefaultValue", "class A { void test() { for(Def1Def2DefaultValue) {} } }") + } + + public void testTemplateWithSegmentsAtTheSamePosition_3() { + doTestTemplateWithThreeVariables("", "DefaultValue", "", "class A { void test() { for(TestValue1DefaultValueTestValue3) {} } }") + } + + private void doTestTemplateWithThreeVariables(String firstDefaultValue, String secondDefaultValue, String thirdDefaultValue, + String expectedText) { + configureFromFileText("dummy.java", "class A { void test() { } }") + + TemplateManager manager = TemplateManager.getInstance(getProject()) + def templateName = "tst_template" + def templateGroup = "user" + final Template template = manager.createTemplate(templateName, templateGroup, 'for($TEST1$$TEST2$$TEST3$) {}') + template.addVariable("TEST1", "", StringUtil.wrapWithDoubleQuote(firstDefaultValue), true) + template.addVariable("TEST2", "", StringUtil.wrapWithDoubleQuote(secondDefaultValue), true) + template.addVariable("TEST3", "", StringUtil.wrapWithDoubleQuote(thirdDefaultValue), true) + ((TemplateImpl)template).templateContext.setEnabled(contextType(JavaCodeContextType.class), true) + addTemplate(template, testRootDisposable) + + startTemplate(templateName, templateGroup) + if (firstDefaultValue.empty) myFixture.type("TestValue1") + myFixture.type("\t") + if (secondDefaultValue.empty) myFixture.type("TestValue2") + myFixture.type("\t") + if (thirdDefaultValue.empty) myFixture.type("TestValue3") + myFixture.type("\t") + assert state == null + checkResultByText(expectedText); + } public void testTemplateWithArg1() throws IOException { doTestTemplateWithArg("tst", 'wrap($ARG$)', "tst arg", "wrap(arg)"); @@ -245,6 +281,10 @@ class Foo { TemplateManager.getInstance(getProject()).startTemplate(getEditor(), TemplateSettings.getInstance().getTemplate(name, group)); } + private static T contextType(Class clazz) { + ContainerUtil.findInstance(TemplateContextType.EP_NAME.getExtensions(), clazz) + } + private void configure() { myFixture.configureByFile(getTestName(false) + ".java"); } @@ -549,8 +589,7 @@ class Foo { public void testTemplateExpandingWithSelection() { final TemplateManager manager = TemplateManager.getInstance(getProject()); final Template template = manager.createTemplate("tpl", "user", 'expanded'); - final JavaStringContextType contextType = - ContainerUtil.findInstance(TemplateContextType.EP_NAME.getExtensions(), JavaStringContextType.class); + final JavaStringContextType contextType = contextType(JavaStringContextType.class); ((TemplateImpl)template).getTemplateContext().setEnabled(contextType, true); myFixture.configureByText("a.java", "class A { void f() { String s = \"tpl\"; } }") @@ -750,4 +789,4 @@ class Foo { } """ } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java index 031476f39e9a..183ca778ced7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSegments.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,12 +73,19 @@ public class TemplateSegments { } public void replaceSegmentAt(int index, int start, int end) { + replaceSegmentAt(index, start, end, false); + } + + public void replaceSegmentAt(int index, int start, int end, boolean preserveGreediness) { RangeMarker rangeMarker = mySegments.get(index); + boolean greedyToLeft = rangeMarker.isGreedyToLeft(); + boolean greedyToRight = rangeMarker.isGreedyToRight(); rangeMarker.dispose(); + Document doc = myEditor.getDocument(); rangeMarker = doc.createRangeMarker(start, end); - rangeMarker.setGreedyToLeft(true); - rangeMarker.setGreedyToRight(true); + rangeMarker.setGreedyToLeft(greedyToLeft || !preserveGreediness); + rangeMarker.setGreedyToRight(greedyToRight || !preserveGreediness); mySegments.set(index, rangeMarker); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index d3f658cdcf24..47120b38ff79 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -631,6 +631,8 @@ public class TemplateState implements Disposable { } } + fixOverlappedSegments(myCurrentSegmentNumber); + ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { @@ -682,6 +684,19 @@ public class TemplateState implements Disposable { }); } + private void fixOverlappedSegments(int currentSegment) { + if (currentSegment >= 0) { + int currentSegmentStart = mySegments.getSegmentStart(currentSegment); + int currentSegmentEnd = mySegments.getSegmentEnd(currentSegment); + for (int i = currentSegment + 1; i < mySegments.getSegmentsCount(); i++) { + final int startOffset = mySegments.getSegmentStart(i); + if (currentSegmentStart <= startOffset && startOffset < currentSegmentEnd) { + mySegments.replaceSegmentAt(i, currentSegmentEnd, Math.max(mySegments.getSegmentEnd(i), currentSegmentEnd), true); + } + } + } + } + @NotNull private String getVariableValueText(String variableName) { TextResult value = getVariableValue(variableName); @@ -726,18 +741,13 @@ public class TemplateState implements Disposable { String oldText = myDocument.getCharsSequence().subSequence(start, end).toString(); if (!oldText.equals(newValue)) { - int segmentNumberWithTheSameStart = mySegments.getSegmentWithTheSameStart(segmentNumber, start); mySegments.setNeighboursGreedy(segmentNumber, false); myDocument.replaceString(start, end, newValue); int newEnd = start + newValue.length(); mySegments.replaceSegmentAt(segmentNumber, start, newEnd); mySegments.setNeighboursGreedy(segmentNumber, true); - if (segmentNumberWithTheSameStart != -1) { - mySegments.replaceSegmentAt(segmentNumberWithTheSameStart, newEnd, - newEnd + mySegments.getSegmentEnd(segmentNumberWithTheSameStart) - - mySegments.getSegmentStart(segmentNumberWithTheSameStart)); - } + fixOverlappedSegments(segmentNumber); } } @@ -998,10 +1008,7 @@ public class TemplateState implements Disposable { if (e instanceof MacroCallNode) { marker = ((MacroCallNode)e).getMacro().getDefaultValue(); } - int start = mySegments.getSegmentStart(i); - int end = start + marker.length(); - myDocument.insertString(start, marker); - mySegments.replaceSegmentAt(i, start, end); + replaceString(marker, mySegments.getSegmentStart(i), mySegments.getSegmentEnd(i), i); indices.add(i); break; }