IDEA-87425 New category classes in Groovy 2.0

This commit is contained in:
Maxim.Medvedev
2012-06-15 12:58:36 +04:00
parent 800e69f616
commit 45565ae31d
13 changed files with 523 additions and 3 deletions
@@ -17,6 +17,9 @@
package standardDsls
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.plugins.groovy.dgm.GroovyExtensionProvider
/**
* @author Maxim.Medvedev
*/
@@ -35,4 +38,12 @@ contributor([:]) {
category "org.codehaus.groovy.runtime.SwingGroovyMethods"
category "org.codehaus.groovy.runtime.XmlGroovyMethods"
def pair = GroovyExtensionProvider.getInstance(project).collectExtensions(GlobalSearchScope.allScope(project))
for (def inst : pair.first) {
category inst, false
}
for (def stat : pair.second) {
category stat, true
}
}
+12
View File
@@ -139,6 +139,10 @@
<groovyShellRunner implementation="org.jetbrains.plugins.groovy.console.DefaultGroovyShellRunner" order="last"/>
</extensions>
<extensions defaultExtensionNs="com.intellij.properties">
<implicitPropertyUsageProvider implementation="org.jetbrains.plugins.groovy.dgm.DGMImplicitPropertyUsageProvider"/>
</extensions>
<extensions defaultExtensionNs="com.intellij">
<testFramework implementation="org.jetbrains.plugins.groovy.testIntegration.GroovyTestFramework"/>
<testCreator language="Groovy" implementationClass="com.intellij.testIntegration.JavaTestCreator"/>
@@ -163,6 +167,7 @@
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<fileTypeFactory implementation="org.jetbrains.plugins.groovy.GroovyFileTypeLoader"/>
<fileTypeFactory implementation="org.jetbrains.plugins.groovy.dgm.DGMFileTypeFactory"/>
<projectConfigurable instance="org.jetbrains.plugins.groovy.gant.GantConfigurable"/>
@@ -290,6 +295,8 @@
<completion.contributor language="Groovy" implementationClass="org.jetbrains.plugins.groovy.lang.completion.GrMethodMergingContributor"
id="grMethodMerger" order="before methodMerger"/>
<completion.contributor language="Properties" implementationClass="org.jetbrains.plugins.groovy.dgm.DGMCompletionContributor"/>
<completion.confidence language="Groovy" implementationClass="com.intellij.codeInsight.completion.UnfocusedNameIdentifier"
id="groovyNameIdentifier"/>
<completion.confidence language="Groovy" implementationClass="org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionConfidence"
@@ -299,6 +306,8 @@
<completion.confidence language="Groovy" implementationClass="com.intellij.codeInsight.completion.AlwaysFocusLookup" id="groovyTrue"
order="last"/>
<psi.referenceContributor language="Properties" implementation="org.jetbrains.plugins.groovy.dgm.DGMReferenceContributor"/>
<weigher key="completion" implementationClass="org.jetbrains.plugins.groovy.lang.completion.weighers.GrWithWeigher"
id="groovyWithWeigher" order="before stats"/>
<weigher key="completion" implementationClass="org.jetbrains.plugins.groovy.lang.completion.weighers.GrKindWeigher"
@@ -413,6 +422,9 @@
<projectService serviceInterface="org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager"
serviceImplementation="org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager"/>
<projectService serviceInterface="org.jetbrains.plugins.groovy.dgm.GroovyExtensionProvider"
serviceImplementation="org.jetbrains.plugins.groovy.dgm.GroovyExtensionProvider"/>
<problemFileHighlightFilter implementation="org.jetbrains.plugins.groovy.GroovyProblemFileHighlightFilter"/>
<renameInputValidator implementation="org.jetbrains.plugins.groovy.GroovyRenameInputValidator"/>
@@ -0,0 +1,95 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.dgm;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
/**
* @author Max Medvedev
*/
public class DGMClassReference implements PsiReference {
private final PsiElement myElement;
private TextRange myRange;
public DGMClassReference(PsiElement element, int start, int end) {
myElement = element;
myRange = new TextRange(start, end);
}
@Override
public PsiElement getElement() {
return myElement;
}
@Override
public TextRange getRangeInElement() {
return myRange;
}
@Override
public PsiElement resolve() {
Project project = myElement.getProject();
return JavaPsiFacade.getInstance(project).findClass(myRange.substring(myElement.getText()), myElement.getResolveScope());
}
@NotNull
@Override
public String getCanonicalText() {
return myRange.substring(myElement.getText());
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
if (element instanceof PsiClass) {
String qname = ((PsiClass)element).getQualifiedName();
if (qname == null) return myElement;
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myElement.getProject());
Document document = documentManager.getDocument(myElement.getContainingFile());
TextRange range = myRange.shiftRight(myElement.getTextRange().getStartOffset());
document.replaceString(range.getStartOffset(), range.getEndOffset(), qname);
documentManager.commitDocument(document);
}
return myElement;
}
@Override
public boolean isReferenceTo(PsiElement element) {
return myElement.getManager().areElementsEquivalent(element, resolve());
}
@NotNull
@Override
public Object[] getVariants() {
return EMPTY_ARRAY;
}
@Override
public boolean isSoft() {
return true;
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.dgm;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.lang.properties.parsing.PropertiesTokenTypes;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionUtil;
import java.util.Map;
/**
* @author Max Medvedev
*/
public class DGMCompletionContributor extends CompletionContributor {
public DGMCompletionContributor() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(PropertiesTokenTypes.KEY_CHARACTERS),
new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
PsiElement position = parameters.getPosition();
if (!DGMUtil.isInDGMFile(position)) return;
Map<String, String> map = ((PropertiesFile)position.getContainingFile()).getNamesMap();
for (String key : DGMUtil.KEYS) {
if (!map.containsKey(key)) {
result.addElement(LookupElementBuilder.create(key));
}
}
}
});
extend(CompletionType.BASIC, PlatformPatterns.psiElement(PropertiesTokenTypes.VALUE_CHARACTERS),
new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull final CompletionResultSet result) {
PsiElement position = parameters.getPosition();
if (!DGMUtil.isInDGMFile(position)) return;
AllClassesGetter.processJavaClasses(parameters, result.getPrefixMatcher(), true, new Consumer<PsiClass>() {
@Override
public void consume(PsiClass aClass) {
result.addElement(GroovyCompletionUtil.createClassLookupItem(aClass));
}
});
}
});
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.dgm;
import com.intellij.lang.properties.PropertiesFileType;
import com.intellij.openapi.fileTypes.ExactFileNameMatcher;
import com.intellij.openapi.fileTypes.FileTypeConsumer;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
/**
* @author Max Medvedev
*/
public class DGMFileTypeFactory extends FileTypeFactory {
@Override
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
ExactFileNameMatcher matcher = new ExactFileNameMatcher(GroovyExtensionProvider.ORG_CODEHAUS_GROOVY_RUNTIME_EXTENSION_MODULE,
!SystemInfo.isFileSystemCaseSensitive);
consumer.consume(PropertiesFileType.INSTANCE, matcher);
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.dgm;
import com.intellij.codeInspection.unused.ImplicitPropertyUsageProvider;
import com.intellij.lang.properties.psi.Property;
import com.intellij.util.ArrayUtil;
/**
* @author Max Medvedev
*/
public class DGMImplicitPropertyUsageProvider extends ImplicitPropertyUsageProvider {
@Override
protected boolean isUsed(Property property) {
if (DGMUtil.isInDGMFile(property)) {
String name = property.getName();
return ArrayUtil.find(DGMUtil.KEYS, name) >= 0;
}
return false;
}
}
@@ -0,0 +1,80 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.dgm;
import com.intellij.lang.properties.IProperty;
import com.intellij.lang.properties.parsing.PropertiesTokenTypes;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
/**
* @author Max Medvedev
*/
public class DGMReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(PropertiesTokenTypes.VALUE_CHARACTERS), new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
if (!DGMUtil.isInDGMFile(element)) return PsiReference.EMPTY_ARRAY;
IProperty parent = (IProperty)element.getParent();
if (!"extensionClasses".equals(parent.getName())) {
return PsiReference.EMPTY_ARRAY;
}
ArrayList<PsiReference> result = new ArrayList<PsiReference>();
String text = element.getText();
int i = 0;
while ((i = skipWhiteSpace(i, text)) < text.length()) {
int end = findWhiteSpaceOrComma(i, text);
if (end <= text.length()) {
result.add(new DGMClassReference(element, i, end));
}
i = end;
i = skipWhiteSpace(i, text);
if (i == text.length()) break;
if (text.charAt(i) == ',') i++;
i = skipWhiteSpace(i, text);
}
return result.toArray(new PsiReference[result.size()]);
}
});
}
private static int skipWhiteSpace(int i, String text) {
while (i < text.length() && Character.isWhitespace(text.charAt(i))) {
i++;
}
return i;
}
private static int findWhiteSpaceOrComma(int i, String text) {
while (i < text.length() && !Character.isWhitespace(text.charAt(i)) && text.charAt(i) != ',') {
i++;
}
return i;
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.dgm;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
/**
* @author Max Medvedev
*/
public class DGMUtil {
public static final String[] KEYS = new String[]{"moduleName", "moduleVersion", "extensionClasses", "staticExtensionClasses",};
public static boolean isInDGMFile(PsiElement e) {
PsiFile file = e.getContainingFile();
return file instanceof PropertiesFile &&
Comparing.equal(file.getName(), GroovyExtensionProvider.ORG_CODEHAUS_GROOVY_RUNTIME_EXTENSION_MODULE,
SystemInfo.isFileSystemCaseSensitive);
}
}
@@ -0,0 +1,80 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.dgm;
import com.intellij.lang.properties.IProperty;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Max Medvedev
*/
public class GroovyExtensionProvider {
@NonNls public static final String ORG_CODEHAUS_GROOVY_RUNTIME_EXTENSION_MODULE = "org.codehaus.groovy.runtime.ExtensionModule";
private final Project myProject;
public GroovyExtensionProvider(Project project) {
myProject = project;
}
public static GroovyExtensionProvider getInstance(Project project) {
return ServiceManager.getService(project, GroovyExtensionProvider.class);
}
public Pair<List<String>, List<String>> collectExtensions(GlobalSearchScope resolveScope) {
PsiPackage aPackage = JavaPsiFacade.getInstance(myProject).findPackage("META-INF.services");
if (aPackage == null) {
return new Pair<List<String>, List<String>>(Collections.<String>emptyList(), Collections.<String>emptyList());
}
List<String> instanceClasses = new ArrayList<String>();
List<String> staticClasses = new ArrayList<String>();
for (PsiDirectory directory : aPackage.getDirectories(resolveScope)) {
PsiFile file = directory.findFile("org.codehaus.groovy.runtime.ExtensionModule");
if (file instanceof PropertiesFile) {
IProperty inst = ((PropertiesFile)file).findPropertyByKey("extensionClasses");
IProperty stat = ((PropertiesFile)file).findPropertyByKey("staticExtensionClasses");
if (inst != null) collectClasses(inst, instanceClasses);
if (stat != null) collectClasses(stat, staticClasses);
}
}
return new Pair<List<String>, List<String>>(instanceClasses, staticClasses);
}
private static void collectClasses(IProperty pr, List<String> classes) {
String value = pr.getValue();
if (value == null) return;
value = value.trim();
String[] qnames = value.split("\\s*,\\s*");
ContainerUtil.addAll(classes, qnames);
}
}
@@ -1053,4 +1053,21 @@ class Category2 {
assertNotNull(ref.resolve())
}
void testGroovyExtensions() {
def ref = configureByText('pack._a.groovy', '''\
package pack
class StringExt {
static sub(String s) {}
}
"".su<caret>b()''')
myFixture.addFileToProject("META-INF/services/org.codehaus.groovy.runtime.ExtensionModule", """\
extensionClasses=pack.StringExt
""")
assertNotNull(ref.resolve())
}
}
+6 -1
View File
@@ -6,6 +6,11 @@
This plugin enables smart editing of properties files.
</description>
<vendor logo="/general/ijLogo.png">JetBrains</vendor>
<extensionPoints>
<extensionPoint name="implicitPropertyUsageProvider" interface="com.intellij.codeInspection.unused.ImplicitPropertyUsageProvider"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<applicationService serviceInterface="com.intellij.lang.properties.LastSelectedPropertiesFileStore"
@@ -51,7 +56,7 @@
<localInspection language="Properties" shortName="UnusedProperty" bundle="messages.PropertiesBundle" key="unused.property.inspection.display.name"
groupKey="properties.files.inspection.group.display.name" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.lang.properties.UnusedPropertyInspection"/>
implementationClass="com.intellij.codeInspection.unused.UnusedPropertyInspection"/>
<globalInspection shortName="DuplicatePropertyInspection" bundle="messages.InspectionsBundle" key="duplicate.property.display.name"
groupKey="group.names.properties.files" enabledByDefault="false" level="WARNING"
implementationClass="com.intellij.codeInspection.duplicatePropertyInspection.DuplicatePropertyInspection"/>
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.unused;
import com.intellij.lang.properties.psi.Property;
import com.intellij.openapi.extensions.ExtensionPointName;
/**
* @author Max Medvedev
*/
public abstract class ImplicitPropertyUsageProvider {
private static final ExtensionPointName<ImplicitPropertyUsageProvider> EP_NAME =
ExtensionPointName.create("com.intellij.properties.implicitPropertyUsageProvider");
public static boolean isImplicitlyUsed(Property property) {
for (ImplicitPropertyUsageProvider provider : EP_NAME.getExtensions()) {
if (provider.isUsed(property)) return true;
}
return false;
}
protected abstract boolean isUsed(Property property);
}
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lang.properties;
package com.intellij.codeInspection.unused;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.lang.ASTNode;
import com.intellij.lang.properties.PropertiesBundle;
import com.intellij.lang.properties.PropertySuppressableInspectionBase;
import com.intellij.lang.properties.RemovePropertyLocalFix;
import com.intellij.lang.properties.findUsages.PropertySearcher;
import com.intellij.lang.properties.psi.Property;
import com.intellij.openapi.extensions.Extensions;
@@ -75,6 +78,8 @@ public class UnusedPropertyInspection extends PropertySuppressableInspectionBase
original.setText(PropertiesBundle.message("searching.for.property.key.progress.text", property.getUnescapedKey()));
}
if (ImplicitPropertyUsageProvider.isImplicitlyUsed(property)) return;
String name = property.getName();
if (name == null) return;
if (searcher != null) {
@@ -97,7 +102,7 @@ public class UnusedPropertyInspection extends PropertySuppressableInspectionBase
PsiElement key = nodes.length == 0 ? property : nodes[0].getPsi();
String description = PropertiesBundle.message("unused.property.problem.descriptor.name");
holder.registerProblem(key, description, ProblemHighlightType.LIKE_UNUSED_SYMBOL,RemovePropertyLocalFix.INSTANCE);
holder.registerProblem(key, description, ProblemHighlightType.LIKE_UNUSED_SYMBOL, RemovePropertyLocalFix.INSTANCE);
}
};
}