summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGeorge Fraser <george@fivetran.com>2018-12-27 13:02:36 -0800
committerGeorge Fraser <george@fivetran.com>2018-12-27 13:02:36 -0800
commitcbcbbd22afdd867f4f43817dc53df3800814b0ba (patch)
treeedd829d47706136c40025afe222f9ef10094e7e5 /src
parent8321c9206bffe676c7794f2404aa69a451e3749b (diff)
downloadjava-language-server-cbcbbd22afdd867f4f43817dc53df3800814b0ba.zip
Fix completion when many static imports are present
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/javacs/CompileFocus.java51
-rw-r--r--src/main/java/org/javacs/JavaTextDocumentService.java7
-rw-r--r--src/main/java/org/javacs/ParseFile.java3
-rw-r--r--src/test/java/org/javacs/CompletionsTest.java5
-rw-r--r--src/test/java/org/javacs/JavaCompilerServiceTest.java10
-rw-r--r--src/test/test-project/workspace/src/org/javacs/example/AutocompleteClasses.java1
6 files changed, 59 insertions, 18 deletions
diff --git a/src/main/java/org/javacs/CompileFocus.java b/src/main/java/org/javacs/CompileFocus.java
index c90e99b..e8a9a27 100644
--- a/src/main/java/org/javacs/CompileFocus.java
+++ b/src/main/java/org/javacs/CompileFocus.java
@@ -132,7 +132,8 @@ public class CompileFocus {
}
public List<Completion> completeIdentifiers(boolean insideClass, boolean insideMethod, String partialName) {
- LOG.info("...completing identifiers");
+ LOG.info(String.format("Completing identifiers starting with `%s`...", partialName));
+
var result = new ArrayList<Completion>();
// Add snippets
@@ -581,7 +582,7 @@ public class CompileFocus {
if (isThisOrSuper(e)) {
unwrapThisSuper((VariableElement) e);
}
- if (result.size() >= MAX_COMPLETION_ITEMS) return;
+ if (tooManyItems(result.size())) return;
}
} catch (Exception e) {
LOG.log(Level.WARNING, "error walking locals in scope", e);
@@ -621,7 +622,7 @@ public class CompileFocus {
walkLocals(s);
profileEnd(s);
// Return early?
- if (result.size() >= MAX_COMPLETION_ITEMS) {
+ if (tooManyItems(result.size())) {
printProfile();
return result;
}
@@ -634,6 +635,12 @@ public class CompileFocus {
return new Walk().walkScopes();
}
+ private boolean tooManyItems(int count) {
+ var test = count >= MAX_COMPLETION_ITEMS;
+ if (test) LOG.warning(String.format("...# of items %d reached max %s", count, MAX_COMPLETION_ITEMS));
+ return test;
+ }
+
private Set<String> subPackages(String parentPackage) {
var result = new HashSet<String>();
Consumer<String> checkClassName =
@@ -661,16 +668,22 @@ public class CompileFocus {
}
private void completeScopeIdentifiers(String partialName, List<Completion> result) {
- var startsWithUpperCase = partialName.length() > 0 && Character.isUpperCase(partialName.charAt(0));
// Add locals
- for (var m : scopeMembers(partialName)) {
+ var locals = scopeMembers(partialName);
+ for (var m : locals) {
result.add(Completion.ofElement(m));
}
+ LOG.info(String.format("...found %d locals", locals.size()));
+
// Add static imports
- for (var m : staticImports(file, contents, partialName)) {
+ var staticImports = staticImports(file, contents, partialName);
+ for (var m : staticImports) {
result.add(Completion.ofElement(m));
}
+ LOG.info(String.format("...found %d static imports", staticImports.size()));
+
// Add classes
+ var startsWithUpperCase = partialName.length() > 0 && Character.isUpperCase(partialName.charAt(0));
if (startsWithUpperCase) {
var packageName = Objects.toString(root.getPackageName(), "");
Predicate<String> matchesPartialName =
@@ -678,18 +691,27 @@ public class CompileFocus {
var className = Parser.lastName(qualifiedName);
return matchesPartialName(className, partialName);
};
+
+ // Check JDK
+ LOG.info("...checking JDK");
for (var c : parent.jdkClasses.classes()) {
- if (result.size() >= MAX_COMPLETION_ITEMS) return;
+ if (tooManyItems(result.size())) return;
if (matchesPartialName.test(c) && parent.jdkClasses.isAccessibleFromPackage(c, packageName)) {
result.add(Completion.ofClassName(c, isImported(c)));
}
}
+
+ // Check classpath
+ LOG.info("...checking classpath");
for (var c : parent.classPathClasses.classes()) {
- if (result.size() >= MAX_COMPLETION_ITEMS) return;
+ if (tooManyItems(result.size())) return;
if (matchesPartialName.test(c) && parent.classPathClasses.isAccessibleFromPackage(c, packageName)) {
result.add(Completion.ofClassName(c, isImported(c)));
}
}
+
+ // Check sourcepath
+ LOG.info("...checking source path");
Predicate<Path> matchesFileName = file -> matchesPartialName(file.getFileName().toString(), partialName);
Predicate<Path> isPublic =
file -> {
@@ -712,7 +734,7 @@ public class CompileFocus {
return relative.substring(0, relative.length() - ".java".length());
};
for (var file : JavaCompilerService.javaSourcesInDir(dir)) {
- if (result.size() >= MAX_COMPLETION_ITEMS) return;
+ if (tooManyItems(result.size())) return;
// Fast check, file name only
if (matchesFileName.test(file)) {
var c = qualifiedName.apply(file);
@@ -735,12 +757,19 @@ public class CompileFocus {
var el = (TypeElement) trees.getElement(path);
if (id.getIdentifier().contentEquals("*")) {
for (var member : el.getEnclosedElements()) {
- if (member.getModifiers().contains(Modifier.STATIC)) result.add(member);
+ if (matchesPartialName(member.getSimpleName(), partialName)
+ && member.getModifiers().contains(Modifier.STATIC)) {
+ result.add(member);
+ if (tooManyItems(result.size())) return result;
+ }
}
} else {
for (var member : el.getEnclosedElements()) {
if (matchesPartialName(member.getSimpleName(), partialName)
- && member.getModifiers().contains(Modifier.STATIC)) result.add(member);
+ && member.getModifiers().contains(Modifier.STATIC)) {
+ result.add(member);
+ if (tooManyItems(result.size())) return result;
+ }
}
}
}
diff --git a/src/main/java/org/javacs/JavaTextDocumentService.java b/src/main/java/org/javacs/JavaTextDocumentService.java
index 672a288..c23244b 100644
--- a/src/main/java/org/javacs/JavaTextDocumentService.java
+++ b/src/main/java/org/javacs/JavaTextDocumentService.java
@@ -10,6 +10,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
+import java.lang.annotation.Annotation;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
@@ -80,7 +81,8 @@ class JavaTextDocumentService implements TextDocumentService {
var line = position.getPosition().getLine() + 1;
var column = position.getPosition().getCharacter() + 1;
lastCompletions.clear();
- var maybeCtx = server.compiler.parseFile(uri, content).completionPosition(line, column);
+ // Figure out what kind of completion we want to do
+ var maybeCtx = server.compiler.parseFile(uri, content).completionContext(line, column);
if (!maybeCtx.isPresent()) {
var items = new ArrayList<CompletionItem>();
for (var name : CompileFocus.TOP_LEVEL_KEYWORDS) {
@@ -93,8 +95,10 @@ class JavaTextDocumentService implements TextDocumentService {
var list = new CompletionList(true, items);
return CompletableFuture.completedFuture(Either.forRight(list));
}
+ // Compile again, focusing on a region that depends on what type of completion we want to do
var ctx = maybeCtx.get();
var focus = server.compiler.compileFocus(uri, content, ctx.line, ctx.character);
+ // Do a specific type of completion
List<Completion> cs;
boolean isIncomplete;
switch (ctx.kind) {
@@ -121,6 +125,7 @@ class JavaTextDocumentService implements TextDocumentService {
default:
throw new RuntimeException("Unexpected completion context " + ctx.kind);
}
+ // Convert to CompletionItem
var result = new ArrayList<CompletionItem>();
for (var c : cs) {
var i = new CompletionItem();
diff --git a/src/main/java/org/javacs/ParseFile.java b/src/main/java/org/javacs/ParseFile.java
index 7455f5e..55c7111 100644
--- a/src/main/java/org/javacs/ParseFile.java
+++ b/src/main/java/org/javacs/ParseFile.java
@@ -89,8 +89,9 @@ public class ParseFile {
}
// TODO maybe this should return TreePath?
- public Optional<CompletionContext> completionPosition(int line, int character) {
+ public Optional<CompletionContext> completionContext(int line, int character) {
LOG.info(String.format("Finding completion position near %s(%d,%d)...", file, line, character));
+
var pos = trees.getSourcePositions();
var lines = root.getLineMap();
var cursor = lines.getPosition(line, character);
diff --git a/src/test/java/org/javacs/CompletionsTest.java b/src/test/java/org/javacs/CompletionsTest.java
index 343a2b8..569a620 100644
--- a/src/test/java/org/javacs/CompletionsTest.java
+++ b/src/test/java/org/javacs/CompletionsTest.java
@@ -452,6 +452,11 @@ public class CompletionsTest extends CompletionsBase {
suggestions = insertText(file, 6, 13);
assertThat(suggestions, hasItems("SomeInnerClass"));
+
+ // List?
+ suggestions = insertText(file, 7, 12);
+
+ assertThat(suggestions, hasItems("List"));
}
@Test
diff --git a/src/test/java/org/javacs/JavaCompilerServiceTest.java b/src/test/java/org/javacs/JavaCompilerServiceTest.java
index a05cf84..ffcbc26 100644
--- a/src/test/java/org/javacs/JavaCompilerServiceTest.java
+++ b/src/test/java/org/javacs/JavaCompilerServiceTest.java
@@ -128,7 +128,7 @@ public class JavaCompilerServiceTest {
public void completeIdentifiers() {
var uri = resourceUri("/CompleteIdentifiers.java");
var contents = contents("/CompleteIdentifiers.java");
- var ctx = compiler.parseFile(uri, contents).completionPosition(13, 21).get();
+ var ctx = compiler.parseFile(uri, contents).completionContext(13, 21).get();
var focus = compiler.compileFocus(uri, contents, ctx.line, ctx.character);
var found = focus.completeIdentifiers(ctx.inClass, ctx.inMethod, ctx.partialName);
var names = completionNames(found);
@@ -159,7 +159,7 @@ public class JavaCompilerServiceTest {
public void completeMembers() {
var uri = resourceUri("/CompleteMembers.java");
var contents = contents("/CompleteMembers.java");
- var ctx = compiler.parseFile(uri, contents).completionPosition(3, 15).get();
+ var ctx = compiler.parseFile(uri, contents).completionContext(3, 15).get();
var focus = compiler.compileFocus(uri, contents, ctx.line, ctx.character);
var found = focus.completeMembers(false);
var names = completionNames(found);
@@ -172,7 +172,7 @@ public class JavaCompilerServiceTest {
public void completeExpression() {
var uri = resourceUri("/CompleteExpression.java");
var contents = contents("/CompleteExpression.java");
- var ctx = compiler.parseFile(uri, contents).completionPosition(3, 37).get();
+ var ctx = compiler.parseFile(uri, contents).completionContext(3, 37).get();
var focus = compiler.compileFocus(uri, contents, ctx.line, ctx.character);
var found = focus.completeMembers(false);
var names = completionNames(found);
@@ -185,7 +185,7 @@ public class JavaCompilerServiceTest {
public void completeClass() {
var uri = resourceUri("/CompleteClass.java");
var contents = contents("/CompleteClass.java");
- var ctx = compiler.parseFile(uri, contents).completionPosition(3, 23).get();
+ var ctx = compiler.parseFile(uri, contents).completionContext(3, 23).get();
var focus = compiler.compileFocus(uri, contents, ctx.line, ctx.character);
var found = focus.completeMembers(false);
var names = completionNames(found);
@@ -199,7 +199,7 @@ public class JavaCompilerServiceTest {
public void completeImports() {
var uri = resourceUri("/CompleteImports.java");
var contents = contents("/CompleteImports.java");
- var ctx = compiler.parseFile(uri, contents).completionPosition(1, 18).get();
+ var ctx = compiler.parseFile(uri, contents).completionContext(1, 18).get();
var focus = compiler.compileFocus(uri, contents, ctx.line, ctx.character);
var found = focus.completeMembers(false);
var names = completionNames(found);
diff --git a/src/test/test-project/workspace/src/org/javacs/example/AutocompleteClasses.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteClasses.java
index bddf9f4..beb8bfc 100644
--- a/src/test/test-project/workspace/src/org/javacs/example/AutocompleteClasses.java
+++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteClasses.java
@@ -4,6 +4,7 @@ public class AutocompleteClasses {
public static void test() {
Fix;
Some;
+ Lis;
}
public static class SomeInnerClass {