From 6afae67d2f11b59bf5504169c34351611209648f Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 6 Sep 2011 15:35:36 +0200 Subject: [PATCH] groovy live templates granularity --- plugins/groovy/src/META-INF/plugin.xml | 6 +- .../lang/completion/GroovyCompletionData.java | 2 +- .../template/GroovyTemplateContextType.java | 122 +++++++++++++++++- .../groovy/lang/GroovyLiveTemplatesTest.java | 40 ++++++ resources/src/liveTemplates/iterations.xml | 32 ++--- resources/src/liveTemplates/other.xml | 56 ++++---- resources/src/liveTemplates/output.xml | 18 +-- resources/src/liveTemplates/plain.xml | 32 ++--- resources/src/liveTemplates/surround.xml | 6 +- 9 files changed, 238 insertions(+), 76 deletions(-) diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 580f953c9b08..18082ea1fa19 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -191,7 +191,11 @@ - + + + + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionData.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionData.java index 5dd733d78783..ad843193936a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionData.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionData.java @@ -250,7 +250,7 @@ public class GroovyCompletionData { return context.getTextRange().getStartOffset() == 0 && !(context instanceof OuterLanguageElement); } - private static boolean suggestClassInterfaceEnum(PsiElement context) { + public static boolean suggestClassInterfaceEnum(PsiElement context) { if (context.getParent() != null && (context.getParent() instanceof GrReferenceExpression) && context.getParent().getParent() instanceof GroovyFile) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/template/GroovyTemplateContextType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/template/GroovyTemplateContextType.java index 865e5b405319..6255b8d895f3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/template/GroovyTemplateContextType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/template/GroovyTemplateContextType.java @@ -15,16 +15,130 @@ */ package org.jetbrains.plugins.groovy.template; -import com.intellij.codeInsight.template.FileTypeBasedContextType; +import com.intellij.codeInsight.template.EverywhereContextType; +import com.intellij.codeInsight.template.TemplateContextType; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtilBase; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionData; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import static com.intellij.patterns.PsiJavaPatterns.psiElement; /** * @author peter */ -public class GroovyTemplateContextType extends FileTypeBasedContextType{ +public abstract class GroovyTemplateContextType extends TemplateContextType { - protected GroovyTemplateContextType() { - super("GROOVY", "Groovy", GroovyFileType.GROOVY_FILE_TYPE); + protected GroovyTemplateContextType(@NotNull @NonNls String id, + @NotNull String presentableName, + @Nullable Class baseContextType) { + super(id, presentableName, baseContextType); } + public boolean isInContext(@NotNull final PsiFile file, final int offset) { + if (PsiUtilBase.getLanguageAtOffset(file, offset).isKindOf(GroovyFileType.GROOVY_LANGUAGE)) { + PsiElement element = file.findElementAt(offset); + if (element instanceof PsiWhiteSpace) { + return false; + } + return element != null && isInContext(element); + } + + return false; + } + + protected abstract boolean isInContext(@NotNull PsiElement element); + + @Override + public boolean isInContext(@NotNull final FileType fileType) { + return false; + } + + public static class Generic extends GroovyTemplateContextType { + public Generic() { + super("GROOVY", "Groovy", EverywhereContextType.class); + } + + @Override + protected boolean isInContext(@NotNull PsiElement element) { + return true; + } + } + + public static class Statement extends GroovyTemplateContextType { + public Statement() { + super("GROOVY_STATEMENT", "Statement", Generic.class); + } + + @Override + protected boolean isInContext(@NotNull PsiElement element) { + return isStatementContext(element); + } + + private static boolean isStatementContext(PsiElement element) { + return Expression.isExpressionContext(element) && PsiUtil.isExpressionStatement(element.getParent()); + } + } + public static class Expression extends GroovyTemplateContextType { + public Expression() { + super("GROOVY_EXPRESSION", "Expression", Generic.class); + } + + @Override + protected boolean isInContext(@NotNull PsiElement element) { + return isExpressionContext(element); + } + + private static boolean isExpressionContext(PsiElement element) { + final PsiElement parent = element.getParent(); + if (!(parent instanceof GrReferenceExpression)) { + return false; + } + if (((GrReferenceExpression)parent).isQualified()) { + return false; + } + if (parent.getParent() instanceof GrCall) { + return false; + } + if ( + psiElement().afterLeaf( + psiElement().inside( + psiElement(GrExpression.class).afterLeaf( + psiElement().withText("(").withParent(GrIfStatement.class)))) + .accepts(element)) { + return false; + } + return true; + } + } + public static class Declaration extends GroovyTemplateContextType { + public Declaration() { + super("GROOVY_DECLARATION", "Declaration", Generic.class); + } + + @Override + protected boolean isInContext(@NotNull PsiElement element) { + if (PsiTreeUtil.getParentOfType(element, GrCodeBlock.class, false, GrTypeDefinition.class) != null) { + return false; + } + + return GroovyCompletionData.suggestClassInterfaceEnum(element) || GroovyCompletionData.suggestFinalDef(element); + } + } + + } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyLiveTemplatesTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyLiveTemplatesTest.java index 85d78b5cc9b8..5987779ff7ab 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyLiveTemplatesTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyLiveTemplatesTest.java @@ -7,6 +7,9 @@ package org.jetbrains.plugins.groovy.lang; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.lookup.impl.LookupImpl; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.codeInsight.template.impl.TemplateManagerImpl; +import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.codeInsight.template.impl.actions.ListTemplatesAction; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; @@ -14,6 +17,8 @@ import com.intellij.openapi.editor.Editor; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import org.jetbrains.plugins.groovy.util.TestUtils; +import java.io.IOException; + /** * @author peter */ @@ -39,4 +44,39 @@ public class GroovyLiveTemplatesTest extends LightCodeInsightFixtureTestCase{ }.execute(); } + public void testGroovyStatementContext() throws Exception { + final TemplateImpl template = TemplateSettings.getInstance().getTemplate("inst", "other"); + assertFalse(isApplicable("class Foo {{ if (a inst) }}", template)); + assertTrue(isApplicable("class Foo {{ inst }}", template)); + assertTrue(isApplicable("inst", template)); + assertFalse(isApplicable("class Foo {{ return (inst) }}", template)); + } + + public void testGroovyExpressionContext() throws Exception { + final TemplateImpl template = TemplateSettings.getInstance().getTemplate("lst", "other"); + assertFalse(isApplicable("class Foo {{ if (a toar) }}", template)); + assertTrue(isApplicable("class Foo {{ toar }}", template)); + assertTrue(isApplicable("xxx", template)); + assertTrue(isApplicable("xxx", template)); + assertTrue(isApplicable("class Foo {{ return (toar) }}", template)); + } + + public void testGroovyDeclarationContext() throws Exception { + final TemplateImpl template = TemplateSettings.getInstance().getTemplate("psvm", "other"); + assertFalse(isApplicable("class Foo {{ xxx }}", template)); + assertFalse(isApplicable("class Foo {{ toar }}", template)); + assertFalse(isApplicable("class Foo {{ if (a toar) }}", template)); + assertFalse(isApplicable("class Foo {{ return (toar) }}", template)); + assertTrue(isApplicable("class Foo { xxx }", template)); + assertTrue(isApplicable("class Foo { xxx }", template)); + assertFalse(isApplicable("class Foo { int xxx }", template)); + assertTrue(isApplicable("class Foo {}\nxxx", template)); + assertTrue(isApplicable("xxx", template)); + } + + private boolean isApplicable(String text, TemplateImpl inst) throws IOException { + myFixture.configureByText("a.groovy", text); + return TemplateManagerImpl.isApplicable(myFixture.getFile(), myFixture.getEditor().getCaretModel().getOffset(), inst); + } + } diff --git a/resources/src/liveTemplates/iterations.xml b/resources/src/liveTemplates/iterations.xml index 0c224edb9dba..b5098ceca9bc 100644 --- a/resources/src/liveTemplates/iterations.xml +++ b/resources/src/liveTemplates/iterations.xml @@ -9,8 +9,8 @@ @@ -26,7 +27,8 @@