Skip to content

Commit

Permalink
[23] Code completion for module imports
Browse files Browse the repository at this point in the history
  • Loading branch information
stephan-herrmann committed Aug 15, 2024
1 parent 3747271 commit 77d287e
Show file tree
Hide file tree
Showing 9 changed files with 394 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3612,7 +3612,7 @@ protected void setUpProjectCompliance(IJavaProject javaProject, String complianc
new Path(newJclSrcString),
entry.getSourceAttachmentRootPath(),
entry.getAccessRules(),
new IClasspathAttribute[0],
entry.getExtraAttributes(),
entry.isExported());
jclPathEntrySet = true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
/*******************************************************************************
* Copyright (c) 2024 GK Software SE and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* This is an implementation of an early-draft specification developed under the Java
* Community Process (JCP) and is made available for testing and evaluation purposes
* only. The code is not compatible with any specification of the JCP.
*
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/

package org.eclipse.jdt.core.tests.model;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

import junit.framework.Test;

public class CompletionTests23 extends AbstractJavaModelCompletionTests {

static {
// TESTS_NAMES = new String[] {"test006"};
}

private static int DEFAULT_RELEVANCE = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED;

public CompletionTests23(String name) {
super(name);
}

@Override
public void setUpSuite() throws Exception {
if (COMPLETION_PROJECT == null) {
COMPLETION_PROJECT = setUpJavaProject("Completion23", "23", false);
} else {
setUpProjectCompliance(COMPLETION_PROJECT, "23", false);
}
COMPLETION_PROJECT.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
super.setUpSuite();
}
public static Test suite() {
return buildModelTestSuite(CompletionTests23.class);
}

public void testKeyword() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy(
"/Completion23/src/p/X.java",
"""
package p;
import m
public class X {}
""");

CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "import m";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
int relevanceWithoutCase = DEFAULT_RELEVANCE - R_CASE;
assertResults(
"Map[TYPE_REF]{java.util.Map;, java.util, Ljava.util.Map;, null, null, "+relevanceWithoutCase+"}\n" +
"MethodHandle[TYPE_REF]{java.lang.invoke.MethodHandle;, java.lang.invoke, Ljava.lang.invoke.MethodHandle;, null, null, "+relevanceWithoutCase+"}\n" +
"MethodHandles[TYPE_REF]{java.lang.invoke.MethodHandles;, java.lang.invoke, Ljava.lang.invoke.MethodHandles;, null, null, "+relevanceWithoutCase+"}\n" +
"MethodType[TYPE_REF]{java.lang.invoke.MethodType;, java.lang.invoke, Ljava.lang.invoke.MethodType;, null, null, "+relevanceWithoutCase+"}\n" +
"module[KEYWORD]{module, null, null, module, null, " + DEFAULT_RELEVANCE + "}",
requestor.getResults());
}
public void testKeyword_neg() throws JavaModelException {
// keyword not enabled, other proposals don't match
COMPLETION_PROJECT.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.DISABLED);
try {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Completion23/src/p/X.java", """
package p;
import mod
public class X {}
""");
CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "import mod";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"",
requestor.getResults());
} finally {
COMPLETION_PROJECT.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
}
}

public void test001() throws JavaModelException {
// prefixed
// only java.base available (from JCL_23_LIB)
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy(
"/Completion23/src/p/X.java",
"""
package p;
import module java.
public class X {}
""");

CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "java.";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"[MODULE_REF]{java.base, java.base, null, null, null, " + DEFAULT_RELEVANCE + "}",
requestor.getResults());
}

public void test001_neg() throws JavaModelException {
// prefixed
// only java.base available (from JCL_23_LIB)
// preview JEP 476 not enabled
// other proposals don't match prefix
COMPLETION_PROJECT.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.DISABLED);
try {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy(
"/Completion23/src/p/X.java",
"""
package p;
import module java.b
public class X {}
""");

CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "java.b";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"",
requestor.getResults());
} finally {
COMPLETION_PROJECT.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
}
}

public void test002() throws JavaModelException {
// no prefix
// only java.base available (from JCL_23_LIB)
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy(
"/Completion23/src/p/X.java",
"""
package p;
import module\s
public class X {}
""");

CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "module ";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"[MODULE_REF]{java.base, java.base, null, null, null, " + DEFAULT_RELEVANCE + "}",
requestor.getResults());
}

public void test003() throws JavaModelException {
// no prefix
// 2 modules available: mod.one & java.base
// unnamed module reads them all
IPath jarPath = COMPLETION_PROJECT.getPath().append("mod.one.jar");
try {
addClasspathEntry(COMPLETION_PROJECT, newModularLibraryEntry(jarPath, null, null));
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Completion23/src/p/X.java", """
package p;
import module\s
public class X {}
""");
CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "module ";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"[MODULE_REF]{java.base, java.base, null, null, null, " + DEFAULT_RELEVANCE + "}\n" +
"[MODULE_REF]{mod.one, mod.one, null, null, null, " + DEFAULT_RELEVANCE + "}",
requestor.getResults());
} finally {
removeClasspathEntry(COMPLETION_PROJECT, jarPath);
}
}

public void test004() throws JavaModelException {
// with prefix
// 3 modules on the module path: mod.two, mod.one & java.base
// prefix selects 2 out of 3
IPath jarOnePath = COMPLETION_PROJECT.getPath().append("mod.one.jar");
IPath jarTwoPath = COMPLETION_PROJECT.getPath().append("mod.two.jar");
try {
addClasspathEntry(COMPLETION_PROJECT, newModularLibraryEntry(jarOnePath, null, null));
addClasspathEntry(COMPLETION_PROJECT, newModularLibraryEntry(jarTwoPath, null, null));
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Completion23/src/p/X.java", """
package p;
import module mo
public class X {}
""");
CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "module mo";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"[MODULE_REF]{mod.one, mod.one, null, null, null, " + DEFAULT_RELEVANCE + "}\n" +
"[MODULE_REF]{mod.two, mod.two, null, null, null, " + DEFAULT_RELEVANCE + "}",
requestor.getResults());
} finally {
removeClasspathEntry(COMPLETION_PROJECT, jarOnePath);
removeClasspathEntry(COMPLETION_PROJECT, jarTwoPath);
}
}

public void test005() throws CoreException {
// with prefix
// 4 modules available: mod.test (self), mod.two (required), mod.one (transitively required) & java.base (from JCL_23_LIB)
// prefix selects 3 out of 4
IPath jarOnePath = COMPLETION_PROJECT.getPath().append("mod.one.jar");
IPath jarTwoPath = COMPLETION_PROJECT.getPath().append("mod.two.jar");
IFile moduleFile = null;
try {
addClasspathEntry(COMPLETION_PROJECT, newModularLibraryEntry(jarOnePath, null, null));
addClasspathEntry(COMPLETION_PROJECT, newModularLibraryEntry(jarTwoPath, null, null));
moduleFile = createFile("Completion23/src/module-info.java",
"""
module mod.test {
requires mod.two; // mod.two requires transitive mod.one
}
""");
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Completion23/src/p/X.java", """
package p;
import module mo
public class X {}
""");
CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "module mo";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"[MODULE_REF]{mod.one, mod.one, null, null, null, " + DEFAULT_RELEVANCE + "}\n" +
"[MODULE_REF]{mod.test, mod.test, null, null, null, " + DEFAULT_RELEVANCE + "}\n" +
"[MODULE_REF]{mod.two, mod.two, null, null, null, " + DEFAULT_RELEVANCE + "}",
requestor.getResults());
} finally {
removeClasspathEntry(COMPLETION_PROJECT, jarOnePath);
removeClasspathEntry(COMPLETION_PROJECT, jarTwoPath);
if (moduleFile != null)
deleteResource(moduleFile);
}
}
public void test006() throws CoreException {
// with prefix
// 4 modules present: mod.test(self), mod.one, mod.two & java.base available
// + prefix rules out java.base
// + mod.two is proposed with lower relevance, because it is not read by the current module
IPath jarOnePath = COMPLETION_PROJECT.getPath().append("mod.one.jar");
IPath jarTwoPath = COMPLETION_PROJECT.getPath().append("mod.two.jar");
try {
addClasspathEntry(COMPLETION_PROJECT, newModularLibraryEntry(jarOnePath, null, null));
addClasspathEntry(COMPLETION_PROJECT, newModularLibraryEntry(jarTwoPath, null, null)); // not read my the current module
createFile("Completion23/src/module-info.java",
"""
module mod.test {
requires mod.one;
}
""");
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Completion23/src/p/X.java", """
package p;
import module mo
public class X {}
""");
CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();
String str = this.workingCopies[0].getSource();
String completeBehind = "module mo";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
"[MODULE_REF]{mod.two, mod.two, null, null, null, " + (DEFAULT_RELEVANCE - R_NON_RESTRICTED) + "}\n" + // lower relevance, not read
"[MODULE_REF]{mod.one, mod.one, null, null, null, " + DEFAULT_RELEVANCE + "}\n" +
"[MODULE_REF]{mod.test, mod.test, null, null, null, " + DEFAULT_RELEVANCE + "}",
requestor.getResults());
} finally {
removeClasspathEntry(COMPLETION_PROJECT, jarOnePath);
removeClasspathEntry(COMPLETION_PROJECT, jarTwoPath);
}
}
}
13 changes: 13 additions & 0 deletions org.eclipse.jdt.core.tests.model/workspace/Completion23/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="var" path="JCL23_LIB" sourcepath="JCL23_SRC" rootpath="JCL_SRCROOT">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<!--
<classpathentry kind="lib" path="test285379.jar"/>
-->
<classpathentry kind="output" path="bin"/>
</classpath>
17 changes: 17 additions & 0 deletions org.eclipse.jdt.core.tests.model/workspace/Completion23/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Completion23</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 77d287e

Please sign in to comment.