mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java, completion] refactoring: simplify code, add javadoc, add localization IJ-CR-127501
GitOrigin-RevId: e5adf605b24e8f74932f98c805d926ea4ef5d200
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0141f07298
commit
d9e11e9153
+6
-7
@@ -3,9 +3,10 @@ package com.intellij.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.codeInspection.jigsaw.JigsawApiConstants;
|
||||
import com.intellij.codeInspection.jigsaw.JigsawUtil;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiJavaModule;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class JigsawProviderLookupElement extends LookupElement {
|
||||
@@ -15,20 +16,18 @@ public class JigsawProviderLookupElement extends LookupElement {
|
||||
|
||||
@Override
|
||||
public @NotNull String getLookupString() {
|
||||
return JigsawApiConstants.PROVIDER;
|
||||
return PsiJavaModule.PROVIDER_METHOD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderElement(@NotNull LookupElementPresentation presentation) {
|
||||
super.renderElement(presentation);
|
||||
presentation.setTypeText("provider() method declaration");
|
||||
presentation.setTypeText(JavaBundle.message("completion.provider.method.declaration.type"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(@NotNull InsertionContext context) {
|
||||
int selectionLength = context.getSelectionEndOffset() - context.getStartOffset();
|
||||
JigsawUtil.addProviderMethod(myPsiClass, context.getEditor(), context.getStartOffset(),
|
||||
(offset, content) -> context.getDocument().replaceString(offset, offset + selectionLength,
|
||||
content));
|
||||
context.getDocument().replaceString(context.getStartOffset(), context.getSelectionEndOffset(), "");
|
||||
JigsawUtil.addProviderMethod(myPsiClass, context.getEditor(), context.getStartOffset());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -30,8 +30,7 @@ public class GenerateProviderMethodHandler implements CodeInsightActionHandler {
|
||||
if (!FileModificationService.getInstance().preparePsiElementsForWrite(targetClass)) return;
|
||||
|
||||
WriteCommandAction.writeCommandAction(project, file)
|
||||
.run(() -> JigsawUtil.addProviderMethod(targetClass, editor, getOffset(context, offset),
|
||||
(currentOffset, content) -> editor.getDocument().insertString(currentOffset, content)));
|
||||
.run(() -> JigsawUtil.addProviderMethod(targetClass, editor, getOffset(context, offset)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection.jigsaw;
|
||||
|
||||
public interface JigsawApiConstants {
|
||||
String PROVIDER = "provider";
|
||||
}
|
||||
@@ -12,30 +12,32 @@ import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public final class JigsawUtil {
|
||||
private JigsawUtil() { }
|
||||
|
||||
public static void addProviderMethod(@NotNull PsiClass targetClass,
|
||||
@NotNull Editor editor,
|
||||
int startOffset,
|
||||
@NotNull BiConsumer<Integer, String> setMethod) {
|
||||
/**
|
||||
* Adds a provider static method to a target class (java service) at the specified start offset in the editor.
|
||||
* The provider method returns the target class instance.
|
||||
*
|
||||
* @param targetClass The class to which the provider method will be added.
|
||||
* @param editor The editor in which the changes will be made.
|
||||
* @param startOffset The start offset in the editor where the provider method will be inserted.
|
||||
*/
|
||||
public static void addProviderMethod(@NotNull PsiClass targetClass, @NotNull Editor editor, int startOffset) {
|
||||
String className = targetClass.getName();
|
||||
if (className == null) return;
|
||||
|
||||
String methodStringBeforeCursor = "public static " + className + " " + JigsawApiConstants.PROVIDER + "() {" +
|
||||
String methodStringBeforeCursor = "public static " + className + " " + PsiJavaModule.PROVIDER_METHOD + "() {" +
|
||||
"return new " + className + "(";
|
||||
String methodStringAfterCursor = ");}";
|
||||
|
||||
Document document = editor.getDocument();
|
||||
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(targetClass.getProject());
|
||||
|
||||
setMethod.accept(startOffset, methodStringBeforeCursor + methodStringAfterCursor);
|
||||
editor.getDocument().insertString(startOffset, methodStringBeforeCursor + methodStringAfterCursor);
|
||||
|
||||
editor.getCaretModel().moveToOffset(startOffset + methodStringBeforeCursor.length());
|
||||
documentManager.commitDocument(document);
|
||||
documentManager.doPostponedOperationsAndUnblockDocument(document);
|
||||
|
||||
PsiFile psiFile = documentManager.getPsiFile(document);
|
||||
if (psiFile != null) {
|
||||
@@ -44,10 +46,19 @@ public final class JigsawUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the accessibility of the provider method for a specified class. The target class must meet the following criteria:
|
||||
* - It implements an interface that is listed in the module-info provider section.
|
||||
* - It is declared in the module-info provider section.
|
||||
* - It does not have a provider method.
|
||||
*
|
||||
* @param targetClass The class to be checked for provider method accessibility.
|
||||
* @return true if the provider method is accessible, false otherwise.
|
||||
*/
|
||||
@Contract("null -> false")
|
||||
public static boolean checkProviderMethodAccessible(@Nullable PsiClass targetClass) {
|
||||
if (targetClass == null || targetClass.getName() == null) return false;
|
||||
JvmMethod[] methods = targetClass.findMethodsByName(JigsawApiConstants.PROVIDER);
|
||||
JvmMethod[] methods = targetClass.findMethodsByName(PsiJavaModule.PROVIDER_METHOD);
|
||||
for (JvmMethod method : methods) {
|
||||
if (!method.hasParameters()) return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
@@ -16,9 +16,27 @@ public interface PsiJavaModule extends NavigatablePsiElement, PsiNameIdentifierO
|
||||
String MODULE_INFO_CLASS = "module-info";
|
||||
@NlsSafe String MODULE_INFO_FILE = MODULE_INFO_CLASS + ".java";
|
||||
String MODULE_INFO_CLS_FILE = MODULE_INFO_CLASS + ".class";
|
||||
|
||||
/**
|
||||
* "java.base" module is the the core module of the Java SE Platform.
|
||||
* As such, it does not require explicit declaration in the module's {@code requires} directive.
|
||||
*/
|
||||
String JAVA_BASE = "java.base";
|
||||
|
||||
/**
|
||||
* Represents the manifest attribute name used to specify an automatic module name.
|
||||
* When a JAR file does not contain a module-info.java file,
|
||||
* it can still be used as an automatic module by providing this attribute in its manifest.
|
||||
* The value of this attribute defines the module's name, allowing it to be required by other modules.
|
||||
*/
|
||||
String AUTO_MODULE_NAME = "Automatic-Module-Name";
|
||||
|
||||
/**
|
||||
* When a service provider specifies a "provider" method, the service loader uses this method to create a service provider instance.
|
||||
* This method must be public, static, have no parameters, and return a type compatible with the service's interface or class.
|
||||
*/
|
||||
String PROVIDER_METHOD = "provider";
|
||||
|
||||
/* See http://openjdk.org/jeps/261#Class-loaders, "Class loaders" */
|
||||
Set<String> UPGRADEABLE = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
|
||||
"java.activation", "java.compiler", "java.corba", "java.transaction", "java.xml.bind", "java.xml.ws", "java.xml.ws.annotation",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -39,10 +39,6 @@ public class GenerateProviderMethodTest extends LightFixtureCompletionTestCase {
|
||||
doTest("src/org/jetbrains/providers/MyProviderImpl.java");
|
||||
}
|
||||
|
||||
public void testWithRecord() {
|
||||
doTest("src/org/jetbrains/providers/MyRecord.java");
|
||||
}
|
||||
|
||||
public void testWithProvider() {
|
||||
doTest("src/org/jetbrains/providers/WithProvider.java");
|
||||
}
|
||||
|
||||
+30
-28
@@ -5,15 +5,18 @@ import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.completion.LightFixtureCompletionTestCase;
|
||||
import com.intellij.codeInsight.lookup.Lookup;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||
import com.intellij.java.testFramework.fixtures.MultiModuleProjectDescriptor;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.NeedsIndex;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@NeedsIndex.SmartMode(reason = "Provider method completion is not supported in the dumb mode")
|
||||
public class ProviderMethodCompletionTest extends LightFixtureCompletionTestCase {
|
||||
@@ -33,60 +36,59 @@ public class ProviderMethodCompletionTest extends LightFixtureCompletionTestCase
|
||||
}
|
||||
|
||||
public void testNonModuleClass() {
|
||||
doTest("src/org/jetbrains/providers/SimpleClass.java", "private",
|
||||
"protected");
|
||||
doTest("src/org/jetbrains/providers/SimpleClass.java", Set.of("private",
|
||||
"protected"));
|
||||
}
|
||||
|
||||
public void testSimpleProvider() {
|
||||
doTest("src/org/jetbrains/providers/MyProviderImpl.java", "provider",
|
||||
"private",
|
||||
"protected");
|
||||
}
|
||||
|
||||
public void testWithRecord() {
|
||||
doTest("src/org/jetbrains/providers/MyRecord.java", "provider",
|
||||
"private",
|
||||
"protected");
|
||||
doTest("src/org/jetbrains/providers/MyProviderImpl.java", Set.of("provider",
|
||||
"private",
|
||||
"protected"));
|
||||
}
|
||||
|
||||
public void testWithProvider() {
|
||||
doTest("src/org/jetbrains/providers/WithProvider.java", "private",
|
||||
"protected");
|
||||
doTest("src/org/jetbrains/providers/WithProvider.java", Set.of("private",
|
||||
"protected"));
|
||||
}
|
||||
|
||||
public void testSubClass() {
|
||||
doTest("src/org/jetbrains/providers/MySuperClass.java", "provider",
|
||||
"private",
|
||||
"protected");
|
||||
doTest("src/org/jetbrains/providers/MySuperClass.java", Set.of("provider",
|
||||
"private",
|
||||
"protected"));
|
||||
}
|
||||
|
||||
public void testWrongPlace() {
|
||||
doTest("src/org/jetbrains/providers/WrongPlace.java", "process", "program");
|
||||
doTest("src/org/jetbrains/providers/WrongPlace.java", Set.of("process", "program"));
|
||||
}
|
||||
|
||||
public void testWrongPlace2() {
|
||||
doTest("src/org/jetbrains/providers/WrongPlace2.java");
|
||||
doTest("src/org/jetbrains/providers/WrongPlace2.java", Set.of());
|
||||
}
|
||||
|
||||
private void doTest(String path, String... names) {
|
||||
private void doTest(String path, Set<String> names) {
|
||||
VirtualFile file = getModule().getModuleFile().getParent().findFileByRelativePath(path);
|
||||
myFixture.configureFromExistingVirtualFile(file);
|
||||
myFixture.completeBasic();
|
||||
List<LookupElement> items = Arrays.asList(myFixture.completeBasic());
|
||||
|
||||
if (names.length != 0) {
|
||||
assertStringItems(names);
|
||||
if (!names.isEmpty()) {
|
||||
// contains
|
||||
List<String> currentNames = ContainerUtil.map(items, LookupElement::getLookupString);
|
||||
assertTrue(currentNames + " should contains all " + names, currentNames.containsAll(names));
|
||||
// not contains
|
||||
if (!names.contains("provider")) {
|
||||
assertTrue("lookup items shouldn't contain a 'provider' element",
|
||||
items.stream().map(LookupElement::getLookupString).noneMatch(name -> "provider".equals(name)));
|
||||
}
|
||||
|
||||
Optional<LookupElement> item = getLookup().getItems().stream().filter(le -> "provider".equals(le.getLookupString())).findFirst();
|
||||
Optional<LookupElement> item = items.stream().filter(le -> "provider".equals(le.getLookupString())).findFirst();
|
||||
if (item.isPresent()) {
|
||||
myFixture.getLookup().setCurrentItem(item.get());
|
||||
myFixture.finishLookup(Lookup.NORMAL_SELECT_CHAR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LookupImpl lookup = getLookup();
|
||||
if (lookup != null) {
|
||||
assertStringItems(names);
|
||||
}
|
||||
assertTrue("lookup items shouldn't contain a 'provider' element",
|
||||
items.stream().map(LookupElement::getLookupString).noneMatch(name -> "provider".equals(name)));
|
||||
}
|
||||
checkResultByFile("after/" + path);
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ completion.smart.hint=Press {0} to show only variants that are suitable by type
|
||||
completion.smart.toar.hint=Press {0} once more to find collections of {1}
|
||||
completion.smart.type.generate.anonymous.body=generate anonymous body
|
||||
completion.unknown.type=Unknown type {0}
|
||||
completion.provider.method.declaration.type=provider() method declaration
|
||||
configurable.EqualsHashCodeTemplatesPanel.display.name=Templates
|
||||
configurable.GenerateToStringConfigurable.display.name=Settings
|
||||
configurable.TemplatesPanel.display.name=Templates
|
||||
|
||||
Reference in New Issue
Block a user