mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Completion in commit window improved (IDEA-CR-50723)
1. Return lookup elements in pure function way 2. TopLevelCompletionContributor renamed to SymbolNameCompletionContributor 3. Added support for Groovy GitOrigin-RevId: 9428d52da9e22aaeb507afa1d86b6704116311c0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7701ba273f
commit
2876989890
@@ -1221,7 +1221,7 @@
|
||||
<completion.confidence language="JAVA" implementationClass="com.intellij.codeInsight.completion.JavadocCompletionConfidence" id="javadoc" order="before javaComments"/>
|
||||
<completion.confidence language="JAVA" implementationClass="com.intellij.codeInsight.completion.SkipAutopopupInComments" id="javaComments"/>
|
||||
<completion.confidence language="JAVA" implementationClass="com.intellij.codeInsight.completion.SkipAutopopupInStrings" id="javaSkipAutopopupInStrings"/>
|
||||
<completion.toplevel language="JAVA" implementationClass="com.intellij.codeInsight.completion.JavaTopLevelCompletionContributor"/>
|
||||
<completion.toplevel language="JAVA" implementationClass="com.intellij.codeInsight.completion.JavaSymbolNameCompletionContributor"/>
|
||||
<lookup.actionProvider implementation="com.intellij.codeInsight.completion.ExcludeFromCompletionLookupActionProvider" id="excludeFromCompletion" order="last"/>
|
||||
<lookup.actionProvider implementation="com.intellij.codeInsight.completion.ImportStaticLookupActionProvider" id="importStatic"/>
|
||||
<fileTypeFactory implementation="com.intellij.openapi.fileTypes.impl.JavaFileTypeFactory"/>
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class JavaSymbolNameCompletionContributor implements SymbolNameCompletionContributor {
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<LookupElement> getLookupElements(@NotNull PsiFile file, int invocationCount, @NotNull String prefix) {
|
||||
PsiClassOwner javaFile = ObjectUtils.tryCast(file, PsiClassOwner.class);
|
||||
if (javaFile == null) return Collections.emptyList();
|
||||
List<LookupElement> result = new ArrayList<>();
|
||||
for (PsiClass aClass : javaFile.getClasses()) {
|
||||
String name = aClass.getName();
|
||||
if (name != null) {
|
||||
result.add(LookupElementBuilder.create(name).withIcon(aClass.getIcon(0)));
|
||||
String infix = getInfix(prefix, name);
|
||||
String memberPrefix = null;
|
||||
if (infix != null) {
|
||||
memberPrefix = prefix.substring(0, name.length() + infix.length());
|
||||
}
|
||||
else if (invocationCount <= 0) continue;
|
||||
processClassBody(invocationCount, result, aClass, infix, memberPrefix);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void processClassBody(int invocationCount, List<LookupElement> result, PsiElement aClass, String infix, String memberPrefix) {
|
||||
for (PsiElement child = aClass.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
if (child instanceof PsiField || child instanceof PsiMethod || child instanceof PsiClass) {
|
||||
String memberName = ((PsiMember)child).getName();
|
||||
Icon icon = child.getIcon(0);
|
||||
if (memberName != null) {
|
||||
LookupElementBuilder element = LookupElementBuilder.create(memberName).withIcon(icon);
|
||||
if (invocationCount > 0) {
|
||||
result.add(element);
|
||||
}
|
||||
if (memberPrefix != null) {
|
||||
if (child instanceof PsiMethod || child instanceof PsiField && !infix.equals("::") || infix.equals(".")) {
|
||||
result.add(LookupElementBuilder.create(memberPrefix + memberName).withIcon(icon));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getInfix(String currentPrefix, String className) {
|
||||
if (!currentPrefix.startsWith(className)) return null;
|
||||
for (String infix : new String[]{".", "#", "::"}) {
|
||||
if (currentPrefix.startsWith(infix, className.length())) {
|
||||
return infix;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class JavaTopLevelCompletionContributor implements TopLevelCompletionContributor {
|
||||
@Override
|
||||
public void addLookupElements(@NotNull PsiFile file, int invocationCount, @NotNull CompletionResultSet result) {
|
||||
PsiJavaFile javaFile = ObjectUtils.tryCast(file, PsiJavaFile.class);
|
||||
if (javaFile == null) return;
|
||||
PrefixMatcher currentMatcher = result.getPrefixMatcher();
|
||||
for (PsiClass aClass : javaFile.getClasses()) {
|
||||
String name = aClass.getName();
|
||||
if (name != null) {
|
||||
result.addElement(LookupElementBuilder.create(name).withIcon(aClass.getIcon(0)));
|
||||
String infix = getInfix(currentMatcher.getPrefix(), name);
|
||||
CompletionResultSet prefixed = null;
|
||||
if (infix == null) {
|
||||
if (invocationCount <= 0) continue;
|
||||
}
|
||||
else {
|
||||
String memberPrefix = currentMatcher.getPrefix().substring(name.length() + infix.length());
|
||||
prefixed = result.withPrefixMatcher(currentMatcher.cloneWithPrefix(memberPrefix));
|
||||
}
|
||||
for (PsiElement child = aClass.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
if (child instanceof PsiField || child instanceof PsiMethod || child instanceof PsiClass) {
|
||||
String memberName = ((PsiMember)child).getName();
|
||||
Icon icon = child.getIcon(0);
|
||||
if (memberName != null) {
|
||||
LookupElementBuilder element = LookupElementBuilder.create(memberName).withIcon(icon);
|
||||
if (invocationCount > 0) {
|
||||
result.addElement(element);
|
||||
}
|
||||
if (prefixed != null) {
|
||||
if (child instanceof PsiMethod || child instanceof PsiField && !infix.equals("::") || infix.equals(".")) {
|
||||
prefixed.addElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getInfix(String currentPrefix, String className) {
|
||||
if (!currentPrefix.startsWith(className)) return null;
|
||||
for (String infix : new String[]{".", "#", "::"}) {
|
||||
if (currentPrefix.startsWith(infix, className.length())) {
|
||||
return infix;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+16
-17
@@ -1,19 +1,20 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet;
|
||||
import com.intellij.codeInsight.completion.TopLevelCompletionContributor;
|
||||
import com.intellij.codeInsight.completion.TopLevelCompletionContributorEP;
|
||||
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl;
|
||||
import com.intellij.codeInsight.completion.PlainPrefixMatcher;
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher;
|
||||
import com.intellij.codeInsight.completion.SymbolNameCompletionContributor;
|
||||
import com.intellij.codeInsight.completion.SymbolNameCompletionContributorEP;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import one.util.streamex.StreamEx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public class JavaTopLevelCompletionContributorTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
public class JavaSymbolNameCompletionContributorTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
public void testBasics() {
|
||||
PsiFile file = myFixture.configureByText("Test.java", "class Foo {\n" +
|
||||
" void fooMethod() {}\n" +
|
||||
@@ -25,20 +26,18 @@ public class JavaTopLevelCompletionContributorTest extends LightJavaCodeInsightF
|
||||
"}");
|
||||
checkCompletion(file, "", 0, "Foo", "Bar");
|
||||
checkCompletion(file, "F", 1, "Foo", "fooMethod", "fooField", "FooClass");
|
||||
checkCompletion(file, "Foo::", 0, "fooMethod");
|
||||
checkCompletion(file, "Foo#", 0, "fooMethod", "fooField");
|
||||
checkCompletion(file, "Foo.", 0, "fooMethod", "fooField", "FooClass");
|
||||
checkCompletion(file, "Foo::", 0, "Foo::fooMethod");
|
||||
checkCompletion(file, "Foo#", 0, "Foo#fooMethod", "Foo#fooField");
|
||||
checkCompletion(file, "Foo.", 0, "Foo.fooMethod", "Foo.fooField", "Foo.FooClass");
|
||||
checkCompletion(file, "B", 1, "Bar", "Baz");
|
||||
}
|
||||
|
||||
private static void checkCompletion(PsiFile file, String prefix, int invocationCount, String... expected) {
|
||||
TopLevelCompletionContributor contributor = TopLevelCompletionContributorEP.forLanguage(JavaLanguage.INSTANCE);
|
||||
SymbolNameCompletionContributor contributor = SymbolNameCompletionContributorEP.forLanguage(JavaLanguage.INSTANCE);
|
||||
assertNotNull(contributor);
|
||||
List<String> options = new ArrayList<>();
|
||||
CompletionResultSet resultSet = CompletionServiceImpl.createResultSetForTest(
|
||||
option -> options.add(option.getLookupElement().getLookupString()));
|
||||
resultSet = resultSet.withPrefixMatcher(prefix);
|
||||
contributor.addLookupElements(file, invocationCount, resultSet);
|
||||
assertEquals(Arrays.asList(expected), options);
|
||||
Collection<LookupElement> options = contributor.getLookupElements(file, invocationCount, prefix);
|
||||
PrefixMatcher matcher = new PlainPrefixMatcher(prefix);
|
||||
assertEquals(Arrays.asList(expected),
|
||||
StreamEx.of(options).filter(matcher::prefixMatches).map(LookupElement::getLookupString).toList());
|
||||
}
|
||||
}
|
||||
+10
-4
@@ -1,23 +1,29 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A language-specific completion contributor which provides reasonable amount of symbols declared in given file
|
||||
* (likely only top-level declarations). Such contributor could be used in plain text editors like VCS commit message field
|
||||
* to help users referring to code symbols from the text.
|
||||
*
|
||||
* @see TopLevelCompletionContributorEP
|
||||
* @see SymbolNameCompletionContributorEP
|
||||
*/
|
||||
public interface TopLevelCompletionContributor {
|
||||
public interface SymbolNameCompletionContributor {
|
||||
/**
|
||||
* Adds lookup elements from given file.
|
||||
*
|
||||
* @param file file to add elements from.
|
||||
* @param invocationCount number of times the completion was invoked (see {@link CompletionParameters#getInvocationCount()}).
|
||||
* @param result a result object to add elements to (could be already prefixed with current context).
|
||||
* @param prefix a prefix string. It's not required to return only matches starting with prefix, but this may be used to improve
|
||||
* performance.
|
||||
* @return a collection of {@link LookupElement}'s to suggest.
|
||||
*/
|
||||
void addLookupElements(@NotNull PsiFile file, int invocationCount, @NotNull CompletionResultSet result);
|
||||
@NotNull
|
||||
Collection<LookupElement> getLookupElements(@NotNull PsiFile file, int invocationCount, @NotNull String prefix);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.lang.LanguageExtensionPoint;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SymbolNameCompletionContributorEP extends LanguageExtensionPoint<SymbolNameCompletionContributor> {
|
||||
private static final ExtensionPointName<SymbolNameCompletionContributorEP> EP = new ExtensionPointName<>("com.intellij.completion.toplevel");
|
||||
private static final LanguageExtension<SymbolNameCompletionContributor> INSTANCE = new CompletionExtension<>(EP.getName());
|
||||
|
||||
public static SymbolNameCompletionContributor forLanguage(@NotNull Language language) {
|
||||
return INSTANCE.forLanguage(language);
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.lang.LanguageExtensionPoint;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class TopLevelCompletionContributorEP extends LanguageExtensionPoint<TopLevelCompletionContributor> {
|
||||
private static final ExtensionPointName<TopLevelCompletionContributorEP> EP = new ExtensionPointName<>("com.intellij.completion.toplevel");
|
||||
private static final LanguageExtension<TopLevelCompletionContributor> INSTANCE = new CompletionExtension<>(EP.getName());
|
||||
|
||||
public static TopLevelCompletionContributor forLanguage(@NotNull Language language) {
|
||||
return INSTANCE.forLanguage(language);
|
||||
}
|
||||
}
|
||||
-6
@@ -20,7 +20,6 @@ import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ExceptionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -101,11 +100,6 @@ public final class CompletionServiceImpl extends CompletionService {
|
||||
return new CompletionResultSetImpl(consumer, matcher, contributor, parameters, defaultSorter(parameters, matcher), null);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public static CompletionResultSet createResultSetForTest(Consumer<? super CompletionResult> consumer) {
|
||||
return new CompletionResultSetImpl(consumer, PrefixMatcher.ALWAYS_TRUE, null, null, new CompletionSorterImpl(new ArrayList<>()), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionProcess getCurrentCompletion() {
|
||||
CompletionProgressIndicator indicator = getCurrentCompletionProgressIndicator();
|
||||
|
||||
@@ -545,8 +545,8 @@
|
||||
<with attribute="implementationClass" implements="com.intellij.codeInsight.completion.CompletionConfidence"/>
|
||||
</extensionPoint>
|
||||
|
||||
<extensionPoint name="completion.toplevel" beanClass="com.intellij.codeInsight.completion.TopLevelCompletionContributorEP">
|
||||
<with attribute="implementationClass" implements="com.intellij.codeInsight.completion.TopLevelCompletionContributor"/>
|
||||
<extensionPoint name="completion.toplevel" beanClass="com.intellij.codeInsight.completion.SymbolNameCompletionContributorEP">
|
||||
<with attribute="implementationClass" implements="com.intellij.codeInsight.completion.SymbolNameCompletionContributor"/>
|
||||
</extensionPoint>
|
||||
|
||||
<extensionPoint name="completion.preselectionBehaviourProvider"
|
||||
|
||||
@@ -80,8 +80,8 @@ public class CommitCompletionContributor extends CompletionContributor {
|
||||
if (vFile == null) return;
|
||||
PsiFile psiFile = PsiManagerEx.getInstanceEx(project).findFile(vFile);
|
||||
if (psiFile == null) return;
|
||||
TopLevelCompletionContributor contributor = TopLevelCompletionContributorEP.forLanguage(psiFile.getLanguage());
|
||||
SymbolNameCompletionContributor contributor = SymbolNameCompletionContributorEP.forLanguage(psiFile.getLanguage());
|
||||
if (contributor == null) return;
|
||||
contributor.addLookupElements(psiFile, count, prefixed);
|
||||
prefixed.addAllElements(contributor.getLookupElements(psiFile, count, prefixed.getPrefixMatcher().getPrefix()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,6 +514,7 @@
|
||||
<codeInsight.gotoSuper language="Groovy"
|
||||
implementationClass="org.jetbrains.plugins.groovy.codeInsight.navigation.actions.GroovyGotoSuperHandler"/>
|
||||
<lookup.charFilter implementation="org.jetbrains.plugins.groovy.lang.completion.GroovyReferenceCharFilter"/>
|
||||
<completion.toplevel language="Groovy" implementationClass="org.jetbrains.plugins.groovy.lang.completion.GroovySymbolNameCompletionContributor"/>
|
||||
<completion.contributor language="Groovy" implementationClass="org.jetbrains.plugins.groovy.lang.completion.GroovyNoVariantsDelegator"
|
||||
id="groovyBasic2ClassName" order="first, after liveTemplates"/>
|
||||
<completion.contributor language="Groovy" implementationClass="org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionContributor"
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.lang.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.JavaSymbolNameCompletionContributor;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class GroovySymbolNameCompletionContributor extends JavaSymbolNameCompletionContributor {
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<LookupElement> getLookupElements(@NotNull PsiFile file, int invocationCount, @NotNull String prefix) {
|
||||
Collection<LookupElement> elements = super.getLookupElements(file, invocationCount, prefix);
|
||||
if (file instanceof GroovyFile) {
|
||||
GrMethod[] methods = ((GroovyFile)file).getMethods();
|
||||
for (GrMethod method : methods) {
|
||||
elements.add(LookupElementBuilder.create(method.getName())
|
||||
.withTailText(" in "+file.getName(), true).withIcon(method.getIcon(0)));
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processClassBody(int invocationCount, List<LookupElement> result, PsiElement aClass, String infix, String memberPrefix) {
|
||||
if (aClass instanceof GrTypeDefinition) {
|
||||
GrTypeDefinitionBody body = ((GrTypeDefinition)aClass).getBody();
|
||||
if (body != null) {
|
||||
super.processClassBody(invocationCount, result, body, infix, memberPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user