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 6f14d3f68c53..9a685d49ba99 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy @@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.impl.DocumentImpl +import com.intellij.openapi.util.JDOMUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiDocumentManager import com.intellij.psi.codeStyle.CodeStyleSettingsManager @@ -36,10 +37,10 @@ import com.intellij.testFramework.fixtures.CodeInsightTestUtil import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import com.intellij.util.containers.ContainerUtil import com.intellij.util.ui.UIUtil +import org.jdom.Element import org.jetbrains.annotations.NotNull import static com.intellij.codeInsight.template.Template.Property.USE_STATIC_IMPORT_IF_POSSIBLE - /** * @author spleaner */ @@ -415,7 +416,7 @@ class Outer { } private TemplateState getState() { - TemplateManagerImpl.getTemplateState(getEditor()) + editor?.with { TemplateManagerImpl.getTemplateState(it) } } public void testIter1() throws Throwable { @@ -537,6 +538,60 @@ class Outer { EverywhereContextType.class); } + public void testJavaOtherContext() throws IOException { + def manager = (TemplateManagerImpl)TemplateManager.getInstance(project) + def stmtContext = TemplateContextType.EP_NAME.findExtension(JavaCodeContextType.Statement) + + configureFromFileText("a.java", "class Foo {{ iter }}"); + + TemplateImpl template = TemplateSettings.instance.getTemplate("iter", "iterations") + assert (template in manager.findMatchingTemplates(myFixture.file, editor, Lookup.REPLACE_SELECT_CHAR, TemplateSettings.instance)?.keySet()) + + assert template.templateContext.getOwnValue(stmtContext) + assert !template.templateContext.getOwnValue(stmtContext.baseContextType) + template.templateContext.putValue(stmtContext, false) + template.templateContext.putValue(stmtContext.baseContextType, true) + try { + assert !(template in manager.findMatchingTemplates(myFixture.file, editor, Lookup.REPLACE_SELECT_CHAR, TemplateSettings.instance)?.keySet()) + } finally { + template.templateContext.putValue(stmtContext, true) + template.templateContext.putValue(stmtContext.baseContextType, false) + } + } + + public void testDontSaveDefaultContexts() { + def defElement = JDOMUtil.loadDocument('''\ + + ''').rootElement + def defContext = new TemplateContext() + defContext.readTemplateContext(defElement) + + assert !defContext.isEnabled(TemplateContextType.EP_NAME.findExtension(JavaCodeContextType.Statement)) + assert defContext.isEnabled(TemplateContextType.EP_NAME.findExtension(JavaCodeContextType.Declaration)) + assert defContext.isEnabled(TemplateContextType.EP_NAME.findExtension(JavaCodeContextType.Generic)) + + def copy = defContext.createCopy() + + def write = new Element("context") + copy.writeTemplateContext(write, defContext) + assert write.children.empty + + copy.putValue(TemplateContextType.EP_NAME.findExtension(JavaCommentContextType), false) + + write = new Element("context") + copy.writeTemplateContext(write, defContext) + assert JDOMUtil.writeElement(write) == '''\ + + ''' + + write = new Element("context") + copy.writeTemplateContext(write, null) + assert write.children.size() == 3 : JDOMUtil.writeElement(write) + } + private boolean isApplicable(String text, TemplateImpl inst) throws IOException { configureFromFileText("a.java", text); return TemplateManagerImpl.isApplicable(myFixture.getFile(), getEditor().getCaretModel().getOffset(), inst); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java index 98c20d4b69df..200c6d9c49d0 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateContext.java @@ -17,10 +17,12 @@ package com.intellij.codeInsight.template.impl; +import com.google.common.annotations.VisibleForTesting; import com.intellij.codeInsight.template.EverywhereContextType; import com.intellij.codeInsight.template.TemplateContextType; import com.intellij.openapi.util.WriteExternalException; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.JBIterable; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,10 +46,8 @@ public class TemplateContext { synchronized (defaultContext == null ? myContextStates : defaultContext.myContextStates) { for (TemplateContextType contextType : TemplateManagerImpl.getAllContextTypes()) { Boolean ownValue = getOwnValue(contextType); - if (ownValue != null) { - if (defaultContext == null || isEnabled(contextType) != defaultContext.isEnabled(contextType)) { - result.put(contextType, ownValue); - } + if (ownValue != null && shouldSaveContextValue(defaultContext, contextType, ownValue)) { + result.put(contextType, ownValue); } } } @@ -55,7 +55,15 @@ public class TemplateContext { return result; } - public boolean isEnabled(TemplateContextType contextType) { + private boolean shouldSaveContextValue(@Nullable TemplateContext defaultContext, @NotNull TemplateContextType contextType, boolean enabled) { + if (defaultContext == null) { + TemplateContextType base = contextType.getBaseContextType(); + return base == null ? enabled : enabled != isEnabled(base); + } + return enabled != defaultContext.isEnabled(contextType); + } + + public boolean isEnabled(@NotNull TemplateContextType contextType) { synchronized (myContextStates) { Boolean storedValue = getOwnValue(contextType); if (storedValue == null) { @@ -101,7 +109,8 @@ public class TemplateContext { } // used during initialization => no sync - void readTemplateContext(Element element) { + @VisibleForTesting + public void readTemplateContext(Element element) { for (Element option : element.getChildren("option")) { String name = option.getAttributeValue("name"); String value = option.getAttributeValue("value"); @@ -109,9 +118,19 @@ public class TemplateContext { myContextStates.put(name, Boolean.parseBoolean(value)); } } + + Map explicitStates = ContainerUtil.newHashMap(); + for (TemplateContextType type : TemplateManagerImpl.getAllContextTypes()) { + if (getOwnValue(type) == null) { + Iterable bases = JBIterable.generate(type, TemplateContextType::getBaseContextType); + explicitStates.put(type.getContextId(), ContainerUtil.getFirstItem(ContainerUtil.mapNotNull(bases, this::getOwnValue), false)); + } + } + myContextStates.putAll(explicitStates); } - void writeTemplateContext(Element element, @Nullable TemplateContext defaultContext) throws WriteExternalException { + @VisibleForTesting + public void writeTemplateContext(Element element, @Nullable TemplateContext defaultContext) throws WriteExternalException { Map diff = getDifference(defaultContext); for (TemplateContextType type : diff.keySet()) { Element optionElement = new Element("option");