diff options
-rwxr-xr-x | scripts/test.sh | 12 | ||||
-rw-r--r-- | src/main/java/org/javacs/JavaCompilerService.java | 64 | ||||
-rw-r--r-- | src/main/java/org/javacs/JavaTextDocumentService.java | 2 | ||||
-rw-r--r-- | src/main/java/org/javacs/README.md | 1 | ||||
-rw-r--r-- | src/test/java/org/javacs/JavaCompilerServiceTest.java | 73 |
5 files changed, 70 insertions, 82 deletions
diff --git a/scripts/test.sh b/scripts/test.sh deleted file mode 100755 index c80ccdb..0000000 --- a/scripts/test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -e - -# Set java version 11 -JAVA_HOME=$(/usr/libexec/java_home -v 11) - -# Figure out test classpath -mvn dependency:build-classpath -DincludeScope=test -Dmdep.outputFile=cp.txt - -# Run all tests directly -./.circleci/test.sh diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java index 8855c5c..6b31e3b 100644 --- a/src/main/java/org/javacs/JavaCompilerService.java +++ b/src/main/java/org/javacs/JavaCompilerService.java @@ -25,6 +25,7 @@ import javax.tools.*; // TODO eliminate uses of URI in favor of Path public class JavaCompilerService { + public static final int MAX_COMPLETION_ITEMS = 50; private static final Logger LOG = Logger.getLogger("main"); // Not modifiable! If you want to edit these, you need to create a new instance private final Set<Path> sourcePath, classPath, docPath; @@ -362,8 +363,18 @@ public class JavaCompilerService { return result; } + static boolean matchesPartialName(CharSequence candidate, CharSequence partialName) { + if (candidate.length() < partialName.length()) + return false; + for (int i = 0; i < partialName.length(); i++) { + if (candidate.charAt(i) != partialName.charAt(i)) + return false; + } + return true; + } + /** Find all identifiers in scope at line:character */ - public List<Element> scopeMembers(URI file, String contents, int line, int character) { + public List<Element> scopeMembers(URI file, String contents, int line, int character, String partialName) { recompile(file, contents, line, character); var trees = Trees.instance(cache.task); @@ -385,8 +396,8 @@ public class JavaCompilerService { return e.getModifiers().contains(Modifier.STATIC); } - boolean isThisOrSuper(VariableElement ve) { - var name = ve.getSimpleName(); + boolean isThisOrSuper(Element e) { + var name = e.getSimpleName(); return name.contentEquals("this") || name.contentEquals("super"); } @@ -403,6 +414,7 @@ public class JavaCompilerService { for (var thisMember : thisElement.getEnclosedElements()) { if (isStatic(start) && !isStatic(thisMember)) continue; if (thisMember.getSimpleName().contentEquals("<init>")) continue; + if (!matchesPartialName(thisMember.getSimpleName(), partialName)) continue; // Check if member is accessible from original scope if (trees.isAccessible(start, thisMember, thisDeclaredType)) { @@ -415,20 +427,20 @@ public class JavaCompilerService { void walkLocals(Scope s) { try { for (var e : s.getLocalElements()) { - if (e instanceof TypeElement) { - var te = (TypeElement) e; - if (trees.isAccessible(start, te)) result.add(te); - } else if (e instanceof VariableElement) { - var ve = (VariableElement) e; - if (isThisOrSuper(ve)) { - unwrapThisSuper(ve); - if (!isStatic(s)) result.add(ve); + if (matchesPartialName(e.getSimpleName(), partialName)) { + if (e instanceof TypeElement) { + var te = (TypeElement) e; + if (trees.isAccessible(start, te)) result.add(te); + } else if (isThisOrSuper(e)) { + if (!isStatic(s)) result.add(e); } else { - result.add(ve); + result.add(e); } - } else { - result.add(e); } + if (isThisOrSuper(e)) { + unwrapThisSuper((VariableElement) e); + } + if (result.size() >= MAX_COMPLETION_ITEMS) return; } } catch (Exception e) { LOG.log(Level.WARNING, "error walking locals in scope", e); @@ -437,8 +449,10 @@ public class JavaCompilerService { // Walk each enclosing scope, placing its members into `results` List<Element> walkScopes() { + // TODO consider limiting how many ancestors we go through, and rely on sourcepath/classpath completions for (var s = start; s != null; s = s.getEnclosingScope()) { walkLocals(s); + if (result.size() >= MAX_COMPLETION_ITEMS) return result; } return result; @@ -692,7 +706,7 @@ public class JavaCompilerService { * Complete members or identifiers at the cursor. Delegates to `members` or `scopeMembers`, depending on whether the * expression before the cursor looks like `foo.bar` or `foo` */ - public CompletionResult completions(URI file, String contents, int line, int character, int limitHint) { + public CompletionResult completions(URI file, String contents, int line, int character) { var started = Instant.now(); LOG.info(String.format("Completing at %s[%d,%d]...", file.getPath(), line, character)); @@ -861,7 +875,7 @@ public class JavaCompilerService { private void addKeywords(String[] keywords, String partialName) { for (var k : keywords) { - if (k.startsWith(partialName)) { + if (matchesPartialName(k, partialName)) { result.add(Completion.ofKeyword(k)); } } @@ -871,21 +885,19 @@ public class JavaCompilerService { var startsWithUpperCase = partialName.length() > 0 && Character.isUpperCase(partialName.charAt(0)); var alreadyImported = new HashSet<String>(); // Add names that have already been imported - for (var m : scopeMembers(file, contents, line, character)) { - if (m.getSimpleName().toString().startsWith(partialName)) { - result.add(Completion.ofElement(m)); + for (var m : scopeMembers(file, contents, line, character, partialName)) { + result.add(Completion.ofElement(m)); - if (m instanceof TypeElement && startsWithUpperCase) { - var t = (TypeElement) m; - alreadyImported.add(t.getQualifiedName().toString()); - } + if (m instanceof TypeElement && startsWithUpperCase) { + var t = (TypeElement) m; + alreadyImported.add(t.getQualifiedName().toString()); } } // Add names of classes that haven't been imported if (startsWithUpperCase) { var packageName = Objects.toString(parse.getPackageName(), ""); BooleanSupplier full = () -> { - if (result.size() >= limitHint) { + if (result.size() >= MAX_COMPLETION_ITEMS) { isIncomplete = true; return true; } @@ -894,7 +906,7 @@ public class JavaCompilerService { Predicate<String> matchesPartialName = qualifiedName -> { var className = Parser.lastName(qualifiedName); - return className.startsWith(partialName); + return matchesPartialName(className, partialName); }; Predicate<String> notAlreadyImported = className -> !alreadyImported.contains(className); for (var c : jdkClasses.classes()) { @@ -910,7 +922,7 @@ public class JavaCompilerService { } } Predicate<Path> matchesFileName = - file -> file.getFileName().toString().startsWith(partialName); + file -> matchesPartialName(file.getFileName().toString(), partialName); Predicate<Path> isPublic = file -> { var fileName = file.getFileName().toString(); diff --git a/src/main/java/org/javacs/JavaTextDocumentService.java b/src/main/java/org/javacs/JavaTextDocumentService.java index e051aea..d24d959 100644 --- a/src/main/java/org/javacs/JavaTextDocumentService.java +++ b/src/main/java/org/javacs/JavaTextDocumentService.java @@ -81,7 +81,7 @@ class JavaTextDocumentService implements TextDocumentService { var column = position.getPosition().getCharacter() + 1; var result = new ArrayList<CompletionItem>(); lastCompletions.clear(); - var completions = server.compiler.completions(uri, content, line, column, 50); + var completions = server.compiler.completions(uri, content, line, column); for (var c : completions.items) { var i = new CompletionItem(); var id = UUID.randomUUID().toString(); diff --git a/src/main/java/org/javacs/README.md b/src/main/java/org/javacs/README.md new file mode 100644 index 0000000..0453716 --- /dev/null +++ b/src/main/java/org/javacs/README.md @@ -0,0 +1 @@ +All classes in this directory need to be added to the list in .circleci/test.sh
\ No newline at end of file diff --git a/src/test/java/org/javacs/JavaCompilerServiceTest.java b/src/test/java/org/javacs/JavaCompilerServiceTest.java index 43547d5..d5cf119 100644 --- a/src/test/java/org/javacs/JavaCompilerServiceTest.java +++ b/src/test/java/org/javacs/JavaCompilerServiceTest.java @@ -85,7 +85,11 @@ public class JavaCompilerServiceTest { public void identifiers() { var found = compiler.scopeMembers( - URI.create("/CompleteIdentifiers.java"), contents("/CompleteIdentifiers.java"), 13, 21); + URI.create("/CompleteIdentifiers.java"), + contents("/CompleteIdentifiers.java"), + 13, + 21, + "complete"); var names = elementNames(found); assertThat(names, hasItem("completeLocal")); assertThat(names, hasItem("completeParam")); @@ -95,13 +99,14 @@ public class JavaCompilerServiceTest { assertThat(names, hasItem("completeInnerField")); assertThat(names, hasItem("completeOuterField")); assertThat(names, hasItem("completeOuterStatic")); - assertThat(names, hasItem("CompleteIdentifiers")); + // assertThat(names, hasItem("CompleteIdentifiers")); } @Test public void identifiersInMiddle() { var found = - compiler.scopeMembers(URI.create("/CompleteInMiddle.java"), contents("/CompleteInMiddle.java"), 13, 21); + compiler.scopeMembers( + URI.create("/CompleteInMiddle.java"), contents("/CompleteInMiddle.java"), 13, 21, "complete"); var names = elementNames(found); assertThat(names, hasItem("completeLocal")); assertThat(names, hasItem("completeParam")); @@ -111,18 +116,14 @@ public class JavaCompilerServiceTest { assertThat(names, hasItem("completeInnerField")); assertThat(names, hasItem("completeOuterField")); assertThat(names, hasItem("completeOuterStatic")); - assertThat(names, hasItem("CompleteInMiddle")); + // assertThat(names, hasItem("CompleteInMiddle")); } @Test public void completeIdentifiers() { var found = compiler.completions( - URI.create("/CompleteIdentifiers.java"), - contents("/CompleteIdentifiers.java"), - 13, - 21, - Integer.MAX_VALUE) + URI.create("/CompleteIdentifiers.java"), contents("/CompleteIdentifiers.java"), 13, 21) .items; var names = completionNames(found); assertThat(names, hasItem("completeLocal")); @@ -149,12 +150,7 @@ public class JavaCompilerServiceTest { @Test public void completeMembers() { var found = - compiler.completions( - URI.create("/CompleteMembers.java"), - contents("/CompleteMembers.java"), - 3, - 15, - Integer.MAX_VALUE) + compiler.completions(URI.create("/CompleteMembers.java"), contents("/CompleteMembers.java"), 3, 15) .items; var names = completionNames(found); assertThat(names, hasItem("subMethod")); @@ -166,11 +162,7 @@ public class JavaCompilerServiceTest { public void completeExpression() { var found = compiler.completions( - URI.create("/CompleteExpression.java"), - contents("/CompleteExpression.java"), - 3, - 37, - Integer.MAX_VALUE) + URI.create("/CompleteExpression.java"), contents("/CompleteExpression.java"), 3, 37) .items; var names = completionNames(found); assertThat(names, hasItem("instanceMethod")); @@ -181,13 +173,7 @@ public class JavaCompilerServiceTest { @Test public void completeClass() { var found = - compiler.completions( - URI.create("/CompleteClass.java"), - contents("/CompleteClass.java"), - 3, - 23, - Integer.MAX_VALUE) - .items; + compiler.completions(URI.create("/CompleteClass.java"), contents("/CompleteClass.java"), 3, 23).items; var names = completionNames(found); assertThat(names, hasItems("staticMethod", "staticField")); assertThat(names, hasItems("class")); @@ -198,12 +184,7 @@ public class JavaCompilerServiceTest { @Test public void completeImports() { var found = - compiler.completions( - URI.create("/CompleteImports.java"), - contents("/CompleteImports.java"), - 1, - 18, - Integer.MAX_VALUE) + compiler.completions(URI.create("/CompleteImports.java"), contents("/CompleteImports.java"), 1, 18) .items; var names = completionNames(found); assertThat(names, hasItem("List")); @@ -232,16 +213,16 @@ public class JavaCompilerServiceTest { @Test public void references() { - ReportReferencesProgress rrp = new ReportReferencesProgress() { - @Override - public void scanForPotentialReferences(int nScanned, int nFiles) {} - - @Override - public void checkPotentialReferences(int nCompiled, int nPotential) {} - }; - var refs = - compiler.references( - URI.create("/GotoDefinition.java"), contents("/GotoDefinition.java"), 6, 13, rrp); + ReportReferencesProgress rrp = + new ReportReferencesProgress() { + @Override + public void scanForPotentialReferences(int nScanned, int nFiles) {} + + @Override + public void checkPotentialReferences(int nCompiled, int nPotential) {} + }; + var refs = + compiler.references(URI.create("/GotoDefinition.java"), contents("/GotoDefinition.java"), 6, 13, rrp); boolean found = false; for (var t : refs) { var unit = t.getCompilationUnit(); @@ -291,4 +272,10 @@ public class JavaCompilerServiceTest { compiler.fixImports(resourceUri("/MissingImport.java"), contents("/MissingImport.java")).fixedImports; assertThat(qualifiedNames, hasItem("java.util.List")); } + + @Test + public void matchesPartialName() { + assertTrue(JavaCompilerService.matchesPartialName("foobar", "foo")); + assertFalse(JavaCompilerService.matchesPartialName("foo", "foobar")); + } } |