diff --git a/src/main/java/org/javacs/CompilerProvider.java b/src/main/java/org/javacs/CompilerProvider.java index 9ca1fd363..66e8c0e01 100644 --- a/src/main/java/org/javacs/CompilerProvider.java +++ b/src/main/java/org/javacs/CompilerProvider.java @@ -32,6 +32,8 @@ public interface CompilerProvider { CompileTask compile(Path... files); CompileTask compile(Collection sources); - + + boolean containsImport(Path file, String className); + Path NOT_FOUND = Paths.get(""); } diff --git a/src/main/java/org/javacs/FileStore.java b/src/main/java/org/javacs/FileStore.java index 2b9a35a82..6673632ec 100644 --- a/src/main/java/org/javacs/FileStore.java +++ b/src/main/java/org/javacs/FileStore.java @@ -274,9 +274,8 @@ static InputStream inputStream(Path file) { } static BufferedReader bufferedReader(Path file) { - var uri = file.toUri(); - if (activeDocuments.containsKey(uri)) { - var string = activeDocuments.get(uri).content; + if (activeDocuments.containsKey(file)) { + var string = activeDocuments.get(file).content; return new BufferedReader(new StringReader(string)); } try { diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java index cfe727830..7ad0f79c9 100644 --- a/src/main/java/org/javacs/JavaCompilerService.java +++ b/src/main/java/org/javacs/JavaCompilerService.java @@ -205,7 +205,7 @@ public List packagePrivateTopLevelTypes(String packageName) { return List.of("TODO"); } - private boolean containsImport(Path file, String className) { + public boolean containsImport(Path file, String className) { var packageName = packageName(className); if (FileStore.packageName(file).equals(packageName)) return true; var star = packageName + ".*"; diff --git a/src/main/java/org/javacs/completion/CompletionProvider.java b/src/main/java/org/javacs/completion/CompletionProvider.java index 709cec91e..68e2c98c1 100644 --- a/src/main/java/org/javacs/completion/CompletionProvider.java +++ b/src/main/java/org/javacs/completion/CompletionProvider.java @@ -15,6 +15,7 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -45,6 +46,8 @@ import org.javacs.lsp.CompletionItemKind; import org.javacs.lsp.CompletionList; import org.javacs.lsp.InsertTextFormat; +import org.javacs.lsp.TextEdit; +import org.javacs.rewrite.AddImport; public class CompletionProvider { private final CompilerProvider compiler; @@ -153,7 +156,7 @@ private CompletionList compileAndComplete(Path file, String contents, long curso var path = new FindCompletionsAt(task.task).scan(task.root(), cursor); switch (path.getLeaf().getKind()) { case IDENTIFIER: - return completeIdentifier(task, path, partial, endsWithParen); + return completeIdentifier(task, path, partial, endsWithParen, file); case MEMBER_SELECT: return completeMemberSelect(task, path, partial, endsWithParen); case MEMBER_REFERENCE: @@ -161,7 +164,7 @@ private CompletionList compileAndComplete(Path file, String contents, long curso case SWITCH: return completeSwitchConstant(task, path, partial); case IMPORT: - return completeImport(qualifiedPartialIdentifier(contents, (int) cursor)); + return completeImport(qualifiedPartialIdentifier(contents, (int) cursor), file); default: var list = new CompletionList(); addKeywords(path, partial, list); @@ -229,13 +232,13 @@ private boolean isQualifiedIdentifierChar(char c) { return c == '.' || Character.isJavaIdentifierPart(c); } - private CompletionList completeIdentifier(CompileTask task, TreePath path, String partial, boolean endsWithParen) { + private CompletionList completeIdentifier(CompileTask task, TreePath path, String partial, boolean endsWithParen, Path file) { LOG.info("...complete identifiers"); var list = new CompletionList(); list.items = completeUsingScope(task, path, partial, endsWithParen); addStaticImports(task, path.getCompilationUnit(), partial, endsWithParen, list); if (!list.isIncomplete && partial.length() > 0 && Character.isUpperCase(partial.charAt(0))) { - addClassNames(path.getCompilationUnit(), partial, list); + addClassNames(path.getCompilationUnit(), partial, list, file); } addKeywords(path, partial, list); return list; @@ -332,13 +335,13 @@ private boolean memberMatchesImport(Name staticImport, Element member) { return staticImport.contentEquals("*") || staticImport.contentEquals(member.getSimpleName()); } - private void addClassNames(CompilationUnitTree root, String partial, CompletionList list) { + private void addClassNames(CompilationUnitTree root, String partial, CompletionList list, Path file) { var packageName = Objects.toString(root.getPackageName(), ""); var uniques = new HashSet(); var previousSize = list.items.size(); for (var className : compiler.packagePrivateTopLevelTypes(packageName)) { if (!StringSearch.matchesPartialName(className, partial)) continue; - list.items.add(classItem(className)); + list.items.add(classItem(className, file)); uniques.add(className); } for (var className : compiler.publicTopLevelTypes()) { @@ -348,7 +351,7 @@ private void addClassNames(CompilationUnitTree root, String partial, CompletionL list.isIncomplete = true; break; } - list.items.add(classItem(className)); + list.items.add(classItem(className, file)); uniques.add(className); } LOG.info("...found " + (list.items.size() - previousSize) + " class names"); @@ -545,7 +548,7 @@ private CompletionList completeSwitchConstant(CompileTask task, TreePath path, S return new CompletionList(false, list); } - private CompletionList completeImport(String path) { + private CompletionList completeImport(String path, Path file) { LOG.info("...complete import"); var names = new HashSet(); var list = new CompletionList(); @@ -559,7 +562,7 @@ private CompletionList completeImport(String path) { names.add(segment); var isClass = end == path.length(); if (isClass) { - list.items.add(classItem(className)); + list.items.add(classItem(className, file)); } else { list.items.add(packageItem(segment)); } @@ -579,8 +582,19 @@ private CompletionItem packageItem(String name) { return i; } - private CompletionItem classItem(String className) { + private CompletionItem classItem(String className, Path file) { var i = new CompletionItem(); + + i.additionalTextEdits = new ArrayList<>(); + + if (!compiler.containsImport(file, className)) { + var add = new AddImport(file, className); + TextEdit[] edits = add.rewrite(compiler).get(file); + if (edits != null) { + Collections.addAll(i.additionalTextEdits, edits); + } + } + i.label = simpleName(className).toString(); i.kind = CompletionItemKind.Class; i.detail = className;