diff options
author | George Fraser <george@fivetran.com> | 2017-05-06 10:31:20 -0400 |
---|---|---|
committer | George Fraser <george@fivetran.com> | 2017-05-06 10:31:20 -0400 |
commit | 630df6ba27dcb88e2997c0897766ea1b6c7913ed (patch) | |
tree | bd0b05d24f244dd876384f1c9904b8915dd462ee /src | |
parent | 748fe64c7f4fd0bb7308a481699cb318d8de2b6c (diff) | |
download | java-language-server-630df6ba27dcb88e2997c0897766ea1b6c7913ed.zip |
Figure out sourcePath automatically
Diffstat (limited to 'src')
81 files changed, 388 insertions, 767 deletions
diff --git a/src/main/java/org/javacs/Completions.java b/src/main/java/org/javacs/Completions.java index c8de371..8c68f9e 100644 --- a/src/main/java/org/javacs/Completions.java +++ b/src/main/java/org/javacs/Completions.java @@ -1,7 +1,6 @@ package org.javacs; import com.google.common.reflect.ClassPath; -import com.google.common.reflect.ClassPath.ClassInfo; import com.sun.source.tree.*; import com.sun.source.util.JavacTask; import com.sun.source.util.TreePath; @@ -25,22 +24,23 @@ import java.util.stream.StreamSupport; class Completions { - static Stream<CompletionItem> at(FocusedResult compiled, SymbolIndex index) { + static Stream<CompletionItem> at(FocusedResult compiled, SymbolIndex index, Javadocs docs) { return compiled.cursor - .map(path -> new Completions(compiled.task, compiled.classPath, index, path).get()) + .map(path -> new Completions(compiled.task, compiled.classPath, index, docs, path).get()) .orElseGet(Stream::empty); } private final JavacTask task; private final ClassPathIndex classPath; private final SymbolIndex sourcePath; + private final Javadocs docs; private final Trees trees; private final Elements elements; private final Name thisName, superName; private final CompilationUnitTree compilationUnit; private final TreePath path; - private Completions(JavacTask task, ClassPathIndex classPath, SymbolIndex sourcePath, TreePath path) { + private Completions(JavacTask task, ClassPathIndex classPath, SymbolIndex sourcePath, Javadocs docs, TreePath path) { this.task = task; this.trees = Trees.instance(task); this.elements = task.getElements(); @@ -48,6 +48,7 @@ class Completions { this.superName = task.getElements().getName("super"); this.classPath = classPath; this.sourcePath = sourcePath; + this.docs = docs; this.compilationUnit = path.getCompilationUnit(); this.path = path; } @@ -637,7 +638,7 @@ class Completions { item.setSortText(name); item.setFilterText(name); item.setSortText("0/" + name); - item.setData(Javadocs.global().methodKey(method)); + item.setData(docs.methodKey(method)); return Stream.of(item); } @@ -661,7 +662,7 @@ class Completions { item.setFilterText(name); item.setAdditionalTextEdits(addImport(enclosingClass.getQualifiedName().toString())); item.setSortText(order + "/" + name); - item.setData(Javadocs.global().methodKey(method)); + item.setData(docs.methodKey(method)); return Stream.of(item); } diff --git a/src/main/java/org/javacs/FindConfig.java b/src/main/java/org/javacs/FindConfig.java deleted file mode 100644 index 7c854eb..0000000 --- a/src/main/java/org/javacs/FindConfig.java +++ /dev/null @@ -1,350 +0,0 @@ -package org.javacs; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import com.sun.source.util.TreePath; -import com.sun.source.util.Trees; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; -import org.eclipse.lsp4j.services.TextDocumentService; -import org.eclipse.lsp4j.services.WorkspaceService; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import javax.lang.model.element.Element; -import javax.tools.JavaFileObject; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; -import java.io.*; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Duration; -import java.time.Instant; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.javacs.Main.JSON; - -class FindConfig { - private final Path workspaceRoot; - - /** - * Instead of looking for javaconfig.json, just use this one. - * For testing. - */ - private final Optional<JavacConfig> testConfig; - - // TODO invalidate cache when VSCode notifies us config file has changed - private final Map<Path, List<JavacConfig>> configBySource = new HashMap<>(), configByDir = new HashMap<>(); - - FindConfig(Path workspaceRoot, Optional<JavacConfig> testConfig) { - this.workspaceRoot = workspaceRoot; - this.testConfig = testConfig; - } - - Optional<JavacConfig> forFile(Path file) { - if (!file.toFile().isDirectory()) - file = file.getParent(); - - if (file == null) - return Optional.empty(); - - List<JavacConfig> found = configBySource.computeIfAbsent(file, this::doFindConfig); - - return chooseConfig(found, file); - } - - private List<JavacConfig> doFindConfig(final Path sourceDir) { - if (testConfig.isPresent()) - return ImmutableList.of(testConfig.get()); - - Path dir = sourceDir; - - while (true) { - List<JavacConfig> found = readIfConfig(dir); - - if (!found.isEmpty()) { - LOG.info("Found " + dir + "/javaconfig.json for " + sourceDir); - - return found; - } - else if (workspaceRoot.startsWith(dir)) - return Collections.emptyList(); - else - dir = dir.getParent(); - } - } - - private Optional<JavacConfig> chooseConfig(List<JavacConfig> found, Path dir) { - return found.stream() - .filter(config -> matchesDir(config, dir)) - .findFirst(); - } - - private boolean matchesDir(JavacConfig config, Path sourceDir) { - for (Path each : config.sourcePath) { - if (sourceDir.startsWith(each)) - return true; - } - - return false; - } - - /** - * If directory contains a config file, for example javaconfig.json or an eclipse project file, read it. - */ - private List<JavacConfig> readIfConfig(Path dir) { - return configByDir.computeIfAbsent(dir, this::doReadIfConfig); - } - - private List<JavacConfig> doReadIfConfig(Path dir) { - Function<JavaConfigJson, JavacConfig> parseJavaConfigJson = json -> { - Set<Path> classPath = readClassPath(dir, json.classPath, json.classPathFile), - docPath = readClassPath(dir, json.docPath, json.docPathFile); - Set<Path> sourcePath = json.sourcePath.stream().map(dir::resolve).collect(Collectors.toSet()); - Path outputDirectory = dir.resolve(json.outputDirectory); - - return new JavacConfig(sourcePath, classPath, outputDirectory, CompletableFuture.completedFuture(docPath)); - }; - if (Files.exists(dir.resolve("javaconfig.json"))) { - return readJavaConfigJson(dir.resolve("javaconfig.json")).stream() - .map(parseJavaConfigJson) - .collect(Collectors.toList()); - } - else { - return Collections.emptyList(); - } - } - - private Set<Path> readClassPath(Path dir, Set<Path> jsonClassPath, Optional<Path> jsonClassPathFile) { - Set<Path> classPath = new HashSet<>(); - - jsonClassPathFile.ifPresent(classPathFile -> { - Path classPathFilePath = dir.resolve(classPathFile); - Set<Path> paths = readClassPathFile(classPathFilePath); - - classPath.addAll(paths); - }); - - jsonClassPath.forEach(entry -> classPath.add(dir.resolve(entry))); - - return classPath; - } - - private JavacConfig readPomXml(Path dir, boolean testScope) { - Path originalPom = dir.resolve("pom.xml"); - Path effectivePom = generateEffectivePom(originalPom); - - // Invoke maven to get classpath - Set<Path> classPath = buildClassPath(originalPom, testScope, false); - - // Get source directory from pom.xml - Set<Path> sourcePath = sourceDirectories(effectivePom, testScope); - - // Use maven output directory so incremental compilation uses maven-generated .class files - Path outputDirectory = testScope ? - Paths.get("target/test-classes").toAbsolutePath() : - Paths.get("target/classes").toAbsolutePath(); - - JavacConfig config = new JavacConfig( - sourcePath, - classPath, - outputDirectory, - CompletableFuture.supplyAsync(() -> buildClassPath(originalPom, testScope, true)) - ); - - LOG.info("Inferred from " + originalPom + ":"); - LOG.info("\tsourcePath: " + Joiner.on(" ").join(sourcePath)); - LOG.info("\tclassPath: " + Joiner.on(" ").join(classPath)); - LOG.info("\tdocPath: (pending)"); - LOG.info("\toutputDirectory: " + outputDirectory); - - return config; - } - - private static Path generateEffectivePom(Path pomXml) { - try { - Objects.requireNonNull(pomXml, "pom.xml path is null"); - - Path effectivePom = Files.createTempFile("effective-pom", ".xml"); - - LOG.info(String.format("Emit effective pom for %s to %s", pomXml, effectivePom)); - - String cmd = String.format( - "%s help:effective-pom -Doutput=%s", - getMvnCommand(), - effectivePom - ); - File workingDirectory = pomXml.toAbsolutePath().getParent().toFile(); - int result = Runtime.getRuntime().exec(cmd, null, workingDirectory).waitFor(); - - if (result != 0) - throw new RuntimeException("`" + cmd + "` returned " + result); - - return effectivePom; - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - } - - // TODO For sourceJars = true: - // use mvn dependency:list to list the dependencies quickly, - // then fetch each one individually using mvn dependency:build-classpath -DincludeGroupIds=? -DincludeArtifactIds=? ... - private static Set<Path> buildClassPath(Path pomXml, boolean testScope, boolean sourceJars) { - try { - Objects.requireNonNull(pomXml, "pom.xml path is null"); - - // Tell maven to output classpath to a temporary file - // TODO if pom.xml already specifies outputFile, use that location - Path classPathTxt = Files.createTempFile(sourceJars ? "sourcepath" : "classpath", ".txt"); - - LOG.info(String.format( - "Emit %s to %s", - sourceJars ? "docPath" : "classpath", - classPathTxt - )); - - String cmd = String.format( - "%s dependency:build-classpath -DincludeScope=%s -Dmdep.outputFile=%s %s", - getMvnCommand(), - testScope ? "test" : "compile", - classPathTxt, - sourceJars ? "-Dclassifier=sources" : "" - ); - File workingDirectory = pomXml.toAbsolutePath().getParent().toFile(); - int result = Runtime.getRuntime().exec(cmd, null, workingDirectory).waitFor(); - - if (result != 0) - throw new RuntimeException("`" + cmd + "` returned " + result); - - Set<Path> found = readClassPathFile(classPathTxt); - - LOG.info("Read " + Joiner.on(" ").join(found) + " from " + classPathTxt); - - return found; - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - } - - private static String getMvnCommand() { - String mvnCommand = "mvn"; - if (File.separatorChar == '\\') { - mvnCommand = findExecutableOnPath("mvn.cmd"); - if (mvnCommand == null) { - mvnCommand = findExecutableOnPath("mvn.bat"); - } - } - return mvnCommand; - } - - private static String findExecutableOnPath(String name) { - for (String dirname : System.getenv("PATH").split(File.pathSeparator)) { - File file = new File(dirname, name); - if (file.isFile() && file.canExecute()) { - return file.getAbsolutePath(); - } - } - return null; - } - - private static Set<Path> sourceDirectories(Path pomXml, boolean testScope) { - return testScope ? - ImmutableSet.of(onlySourceDirectories(pomXml, true), onlySourceDirectories(pomXml, false)) : - ImmutableSet.of(onlySourceDirectories(pomXml, false)); - } - - private static Path onlySourceDirectories(Path pomXml, boolean testScope) { - String defaultSourceDir = testScope ? "src/test/java" : "src/main/java"; - String xPath = testScope ? "/project/build/testSourceDirectory" : "/project/build/sourceDirectory"; - Document doc = parsePomXml(pomXml); - - try { - String sourceDir = XPathFactory.newInstance().newXPath().compile(xPath).evaluate(doc); - - if (sourceDir == null || sourceDir.isEmpty()) { - LOG.info("Use default source directory " + defaultSourceDir); - - sourceDir = defaultSourceDir; - } - else LOG.info("Use source directory from pom.xml " + sourceDir); - - return pomXml.resolveSibling(sourceDir).toAbsolutePath(); - } catch (XPathExpressionException e) { - throw new RuntimeException(e); - } - } - - private static Document parsePomXml(Path pomXml) { - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - - return builder.parse(pomXml.toFile()); - } catch (IOException | ParserConfigurationException | SAXException e) { - throw new RuntimeException(e); - } - } - - private List<JavaConfigJson> readJavaConfigJson(Path configFile) { - try { - JsonNode json = JSON.readValue(configFile.toFile(), JsonNode.class); - - if (json.isArray()) - return JSON.convertValue(json, new TypeReference<List<JavaConfigJson>>() { }); - else { - JavaConfigJson one = JSON.convertValue(json, JavaConfigJson.class); - - return ImmutableList.of(one); - } - } catch (IOException e) { - MessageParams message = new MessageParams(); - - message.setMessage("Error reading " + configFile); - message.setType(MessageType.Error); - - throw new ShowMessageException(message, e); - } - } - - private static Set<Path> readClassPathFile(Path classPathFilePath) { - try { - InputStream in = Files.newInputStream(classPathFilePath); - String text = new BufferedReader(new InputStreamReader(in)) - .lines() - .collect(Collectors.joining()); - Path dir = classPathFilePath.getParent(); - - return Arrays.stream(text.split(File.pathSeparator)) - .map(dir::resolve) - .collect(Collectors.toSet()); - } catch (IOException e) { - MessageParams message = new MessageParams(); - - message.setMessage("Error reading " + classPathFilePath); - message.setType(MessageType.Error); - - throw new ShowMessageException(message, e); - } - } - - private static final Logger LOG = Logger.getLogger("main"); -}
\ No newline at end of file diff --git a/src/main/java/org/javacs/Hovers.java b/src/main/java/org/javacs/Hovers.java index f7f489c..d12004b 100644 --- a/src/main/java/org/javacs/Hovers.java +++ b/src/main/java/org/javacs/Hovers.java @@ -1,36 +1,23 @@ package org.javacs; import com.sun.javadoc.MethodDoc; -import com.sun.javadoc.Parameter; import com.sun.javadoc.ProgramElementDoc; -import com.sun.source.util.JavacTask; -import com.sun.source.util.TreePath; -import com.sun.source.util.Trees; -import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; -import java.util.Arrays; -import java.util.stream.Collectors; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.MarkedString; import org.eclipse.lsp4j.jsonrpc.messages.Either; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; +import javax.lang.model.element.*; import javax.lang.model.type.TypeMirror; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.StringJoiner; -import java.util.function.Function; +import java.util.stream.Collectors; public class Hovers { - public static Hover hoverText(Element el) { - Optional<String> doc = Javadocs.global().doc(el) + public static Hover hoverText(Element el, Javadocs docs) { + Optional<String> doc = docs.doc(el) .map(Hovers::commentText) .map(Javadocs::htmlToMarkdown); String sig = signature(el); diff --git a/src/main/java/org/javacs/InferConfig.java b/src/main/java/org/javacs/InferConfig.java index b3f3031..02f7816 100644 --- a/src/main/java/org/javacs/InferConfig.java +++ b/src/main/java/org/javacs/InferConfig.java @@ -1,19 +1,24 @@ package org.javacs; import com.google.common.base.Joiner; -import com.google.common.collect.Sets; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ExpressionTree; +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.api.JavacTool; +import com.sun.tools.javac.file.JavacFileManager; + +import javax.tools.JavaFileObject; import java.io.File; import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.nio.file.Paths; -import java.util.*; -import java.util.concurrent.CompletableFuture; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.file.*; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.function.Function; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -54,11 +59,10 @@ class InferConfig { /** * Infer source directories by searching for .java files and examining their package declaration. */ - Set<Path> workspaceSourcePath() { - JavacHolder parser = JavacHolder.create(Collections.emptySet(), Collections.emptySet(), tempOutputDirectory("parser-out")); + static Set<Path> workspaceSourcePath(Path workspaceRoot) { PathMatcher match = FileSystems.getDefault().getPathMatcher("glob:*.java"); Function<Path, Optional<Path>> root = java -> { - CompilationUnitTree tree = parser.parse(java.toUri(), Optional.empty(), __ -> {}).tree; + CompilationUnitTree tree = parse(java.toUri(), Optional.empty()); // TODO get from JavaLanguageServer ExpressionTree packageTree = tree.getPackageName(); Path dir = java.getParent(); @@ -74,10 +78,12 @@ class InferConfig { } else { int up = Paths.get(packagePath).getNameCount(); - int truncate = dir.getNameCount() - up; - Path src = dir.subpath(0, truncate); + Path truncate = dir; + + for (int i = 0; i < up; i++) + truncate = truncate.getParent(); - return Optional.of(src); + return Optional.of(truncate); } } }; @@ -94,9 +100,33 @@ class InferConfig { } } - private Path tempOutputDirectory(String name) { + private static final JavacTool parser = JavacTool.create(); + + static CompilationUnitTree parse(URI uri, Optional<String> content) { + JavacFileManager fileManager = parser.getStandardFileManager(__ -> { }, null, Charset.defaultCharset()); + JavaFileObject file = content + .map(text -> (JavaFileObject) new StringFileObject(text, uri)) + .orElseGet(() -> fileManager.getRegularFile(new File(uri))); + List<String> options = ImmutableList.of("-d", tempOutputDirectory("parser-out").toAbsolutePath().toString()); + JavacTask task = parser.getTask(null, fileManager, __ -> {}, options, null, ImmutableList.of(file)); + + try { + List<CompilationUnitTree> trees = Lists.newArrayList(task.parse()); + + if (trees.isEmpty()) + throw new RuntimeException("Parsing " + file + " produced 0 results"); + else if (trees.size() == 1) + return trees.get(0); + else + throw new RuntimeException("Parsing " + file + " produced " + trees.size() + " results"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static Path tempOutputDirectory(String name) { try { - return Files.createTempDirectory("parser-out"); + return Files.createTempDirectory(name); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/org/javacs/JavaLanguageServer.java b/src/main/java/org/javacs/JavaLanguageServer.java index 9484642..459a85c 100644 --- a/src/main/java/org/javacs/JavaLanguageServer.java +++ b/src/main/java/org/javacs/JavaLanguageServer.java @@ -4,15 +4,8 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.util.concurrent.RateLimiter; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.ConstructorDoc; -import com.sun.javadoc.MethodDoc; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; -import java.nio.file.Files; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.CompletableFutures; import org.eclipse.lsp4j.jsonrpc.messages.Either; @@ -25,6 +18,7 @@ import javax.lang.model.element.Element; import javax.tools.JavaFileObject; import java.io.*; import java.net.URI; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; @@ -37,45 +31,24 @@ import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.stream.Collectors; -import java.util.stream.Stream; class JavaLanguageServer implements LanguageServer { private static final Logger LOG = Logger.getLogger("main"); int maxItems = 50; private Map<URI, VersionedContent> activeDocuments = new HashMap<>(); private CompletableFuture<LanguageClient> client = new CompletableFuture<>(); - - // TODO move this to FindConfig - private final Map<JavacConfig, JavacHolder> compilerCache = new HashMap<>(); - - /** - * Instead of looking for javaconfig.json and creating a JavacHolder, just use this. - * For testing. - */ - private final Optional<JavacHolder> testJavac; - - private Path workspaceRoot; - - private FindConfig findConfig; - + private Path workspaceRoot = Paths.get("."); private JavaSettings settings = new JavaSettings(); - - private final SymbolIndex index = new SymbolIndex(this); + private SymbolIndex index; JavaLanguageServer() { - this.testJavac = Optional.empty(); - } - - JavaLanguageServer(JavacHolder testJavac) { - this.testJavac = Optional.of(testJavac); - - index.addConfig(testJavac.config()).join(); } @Override public CompletableFuture<InitializeResult> initialize(InitializeParams params) { workspaceRoot = Paths.get(params.getRootPath()).toAbsolutePath().normalize(); - findConfig = new FindConfig(workspaceRoot, testJavac.map(JavacHolder::config)); + + createCompiler(); InitializeResult result = new InitializeResult(); @@ -119,9 +92,9 @@ class JavaLanguageServer implements LanguageServer { LOG.info(String.format("completion at %s %d:%d", uri, line, character)); - JavacHolder compiler = findCompiler(uri); + JavacHolder compiler = compiler(); FocusedResult result = compiler.compileFocused(uri, content, line, character, true); - List<CompletionItem> items = Completions.at(result, index) + List<CompletionItem> items = Completions.at(result, index, docs()) .limit(maxItems) .collect(Collectors.toList()); CompletionList list = new CompletionList(items.size() == maxItems, items); @@ -138,7 +111,7 @@ class JavaLanguageServer implements LanguageServer { @Override public CompletableFuture<CompletionItem> resolveCompletionItem(CompletionItem unresolved) { return CompletableFutures.computeAsync(cancel -> { - Javadocs.global().resolveCompletionItem(unresolved); + docs().resolveCompletionItem(unresolved); return unresolved; }); @@ -153,10 +126,10 @@ class JavaLanguageServer implements LanguageServer { LOG.info(String.format("hover at %s %d:%d", uri, line, character)); - JavacHolder compiler = findCompiler(uri); + JavacHolder compiler = compiler(); FocusedResult result = compiler.compileFocused(uri, content, line, character, false); Hover hover = elementAtCursor(result) - .map(Hovers::hoverText) + .map(this::hoverText) .orElseGet(this::emptyHover); return CompletableFuture.completedFuture(hover); @@ -170,6 +143,10 @@ class JavaLanguageServer implements LanguageServer { }); } + private Hover hoverText(Element el) { + return Hovers.hoverText(el, docs()); + } + private Hover emptyHover() { return new Hover(Collections.emptyList(), null); } @@ -183,9 +160,9 @@ class JavaLanguageServer implements LanguageServer { LOG.info(String.format("signatureHelp at %s %d:%d", uri, line, character)); - JavacHolder compiler = findCompiler(uri); + JavacHolder compiler = compiler(); FocusedResult result = compiler.compileFocused(uri, content, line, character, true); - SignatureHelp help = Signatures.help(result, line, character).orElseGet(SignatureHelp::new); + SignatureHelp help = Signatures.help(result, line, character, docs()).orElseGet(SignatureHelp::new); return CompletableFuture.completedFuture(help); } @@ -199,7 +176,7 @@ class JavaLanguageServer implements LanguageServer { LOG.info(String.format("definition at %s %d:%d", uri, line, character)); - JavacHolder compiler = findCompiler(uri); + JavacHolder compiler = compiler(); FocusedResult result = compiler.compileFocused(uri, content, line, character, false); List<Location> locations = References.gotoDefinition(result, index) .map(Collections::singletonList) @@ -216,7 +193,7 @@ class JavaLanguageServer implements LanguageServer { LOG.info(String.format("references at %s %d:%d", uri, line, character)); - JavacHolder compiler = findCompiler(uri); + JavacHolder compiler = compiler(); FocusedResult result = compiler.compileFocused(uri, content, line, character, false); List<Location> locations = References.findReferences(result, index) .collect(Collectors.toList()); @@ -251,7 +228,7 @@ class JavaLanguageServer implements LanguageServer { LOG.info(String.format("codeAction at %s %d:%d", uri, line, character)); - JavacHolder compiler = findCompiler(uri); + JavacHolder compiler = compiler(); List<Command> commands = new CodeActions(compiler, uri, activeContent(uri), line, character, index).find(params); return CompletableFuture.completedFuture(commands); @@ -384,23 +361,19 @@ class JavaLanguageServer implements LanguageServer { private void doLint(Collection<URI> paths) { LOG.info("Lint " + Joiner.on(", ").join(paths)); - Map<JavacHolder, Set<URI>> files = paths.stream() - .collect(Collectors.groupingBy(this::findCompiler, Collectors.toSet())); + JavacHolder compiler = compiler(); List<javax.tools.Diagnostic<? extends JavaFileObject>> errors = new ArrayList<>(); + Map<URI, Optional<String>> content = paths.stream() + .collect(Collectors.toMap(f -> f, this::activeContent)); + BatchResult compile = compiler.compileBatch(content); - files.forEach((compiler, someFiles) -> { - Map<URI, Optional<String>> content = someFiles.stream() - .collect(Collectors.toMap(f -> f, this::activeContent)); - BatchResult compile = compiler.compileBatch(content); - - errors.addAll(compile.errors.getDiagnostics()); - }); + errors.addAll(compile.errors.getDiagnostics()); publishDiagnostics(paths, errors); } private void doJavadoc(JavaFileObject source) { - Javadocs.global().update(source); + docs().update(source); } /** @@ -430,7 +403,7 @@ class JavaLanguageServer implements LanguageServer { URI fileUri = URI.create(fileString); String packageName = (String) params.getArguments().get(1); String className = (String) params.getArguments().get(2); - JavacHolder compiler = findCompiler(fileUri); + JavacHolder compiler = compiler(); FocusedResult compiled = compiler.compileFocused(fileUri, activeContent(fileUri), 1, 1, false); if (compiled.compilationUnit.getSourceFile().toUri().equals(fileUri)) { @@ -453,10 +426,6 @@ class JavaLanguageServer implements LanguageServer { @Override public CompletableFuture<List<? extends SymbolInformation>> symbol(WorkspaceSymbolParams params) { - // TODO shouldn't this be findCompilerForConfig? - Collection<JavacHolder> compilers = testJavac - .map(javac -> (Collection<JavacHolder>) Collections.singleton(javac)) - .orElseGet(compilerCache::values); List<SymbolInformation> infos = index.search(params.getQuery()).collect(Collectors.toList()); return CompletableFuture.completedFuture(infos); @@ -465,6 +434,8 @@ class JavaLanguageServer implements LanguageServer { @Override public void didChangeConfiguration(DidChangeConfigurationParams change) { settings = Main.JSON.convertValue(change.getSettings(), JavaSettings.class); + + createCompiler(); } @Override @@ -476,7 +447,7 @@ class JavaLanguageServer implements LanguageServer { activeDocuments.remove(uri); - JavacHolder compiler = findCompiler(uri); + JavacHolder compiler = compiler(); BatchResult result = compiler.delete(uri); publishDiagnostics(Collections.singleton(uri), result.errors.getDiagnostics()); @@ -514,57 +485,44 @@ class JavaLanguageServer implements LanguageServer { return p; } + private JavacHolder compiler = null; + private Javadocs docs = null; + /** - * Look for a configuration in a parent directory of uri + * Default compiler generated using InferConfig */ - JavacHolder findCompiler(URI uri) { - if (testJavac.isPresent()) - return testJavac.get(); - else - return dir(uri) - .flatMap(findConfig::forFile) - .map(this::findCompilerForConfig) - .orElseGet(this::defaultCompiler); + JavacHolder compiler() { + return compiler; } - private JavacHolder cacheDefaultCompiler = null; - private JavaSettings cacheDefaultCompilerSettings = null; - /** - * Default compiler generated using InferConfig + * Default javadocs generated using InferConfig */ - private JavacHolder defaultCompiler() { - if (cacheDefaultCompilerSettings != settings) { - Path userHome = Paths.get(System.getProperty("user.home")); - Path mavenHome = userHome.resolve(".m2"); - Path gradleHome = userHome.resolve(".gradle"); - Path outputDirectory = defaultOutputDirectory(); - List<Artifact> externalDependencies = Lists.transform(settings.java.externalDependencies, Artifact::parse); - InferConfig infer = new InferConfig(workspaceRoot, externalDependencies, mavenHome, gradleHome, outputDirectory); - Set<Path> classPath = infer.buildClassPath(), - docPath = infer.buildDocPath(); - Set<Path> sourcePath = settings.java.sourceDirectories.stream() - .map(workspaceRoot::resolve) - .collect(Collectors.toSet()); - - if (sourcePath.isEmpty()) - sourcePath = infer.workspaceSourcePath(); - - LOG.info("Inferred configuration: "); - LOG.info("\tsourcePath:" + Joiner.on(' ').join(sourcePath)); - LOG.info("\tclassPath:" + Joiner.on(' ').join(classPath)); - LOG.info("\tdocPath:" + Joiner.on(' ').join(docPath)); - LOG.info("\toutputDirectory:" + outputDirectory); - - // Index source dirs for javadocs - Javadocs.addSourcePath(sourcePath); - Javadocs.addSourcePath(docPath); - - cacheDefaultCompiler = JavacHolder.create(classPath, sourcePath, outputDirectory); - cacheDefaultCompilerSettings = settings; - } - - return cacheDefaultCompiler; + Javadocs docs() { + return docs; + } + + // TODO this function needs to be invoked whenever the user creates a new .java file outside the existing source root + private void createCompiler() { + Set<Path> sourcePath = InferConfig.workspaceSourcePath(workspaceRoot); + Path userHome = Paths.get(System.getProperty("user.home")); + Path mavenHome = userHome.resolve(".m2"); + Path gradleHome = userHome.resolve(".gradle"); + Path outputDirectory = defaultOutputDirectory(); + List<Artifact> externalDependencies = Lists.transform(settings.java.externalDependencies, Artifact::parse); + InferConfig infer = new InferConfig(workspaceRoot, externalDependencies, mavenHome, gradleHome, outputDirectory); + Set<Path> classPath = infer.buildClassPath(), + docPath = infer.buildDocPath(); + + LOG.info("Inferred configuration: "); + LOG.info("\tsourcePath:" + Joiner.on(' ').join(sourcePath)); + LOG.info("\tclassPath:" + Joiner.on(' ').join(classPath)); + LOG.info("\tdocPath:" + Joiner.on(' ').join(docPath)); + LOG.info("\toutputDirectory:" + outputDirectory); + + this.docs = new Javadocs(sourcePath, docPath, this::activeContent); + this.compiler = JavacHolder.create(sourcePath, classPath, outputDirectory); + this.index = new SymbolIndex(this); } private Path defaultOutputDirectory() { @@ -575,43 +533,9 @@ class JavaLanguageServer implements LanguageServer { } } - private static Optional<Path> dir(URI uri) { - return file(uri).map(path -> path.getParent()); - } - - private static Optional<Path> file(URI uri) { - if (!uri.getScheme().equals("file")) - return Optional.empty(); - else - return Optional.of(Paths.get(uri)); - } - - private JavacHolder findCompilerForConfig(JavacConfig config) { - if (testJavac.isPresent()) - return testJavac.get(); - else - return compilerCache.computeIfAbsent(config, this::newJavac); - } - - private JavacHolder newJavac(JavacConfig c) { - Javadocs.addSourcePath(c.sourcePath); - - // Add this project directory to SymbolIndex - index.addConfig(c); - - // When docPath resolves, add it to Javadocs - c.docPath.thenAccept(Javadocs::addSourcePath); - - return JavacHolder.create( - c.classPath, - c.sourcePath, - c.outputDirectory - ); - } - public Optional<Element> findSymbol(URI file, int line, int character) { Optional<String> content = activeContent(file); - JavacHolder compiler = findCompiler(file); + JavacHolder compiler = compiler(); FocusedResult result = compiler.compileFocused(file, content, line, character, false); Trees trees = Trees.instance(result.task); Function<TreePath, Optional<Element>> findSymbol = cursor -> Optional.ofNullable(trees.getElement(cursor)); @@ -662,4 +586,10 @@ class JavaLanguageServer implements LanguageServer { } }); } + + Path workspaceRoot() { + Objects.requireNonNull(workspaceRoot, "Language server has not been initialized"); + + return workspaceRoot; + } } diff --git a/src/main/java/org/javacs/JavaSettings.java b/src/main/java/org/javacs/JavaSettings.java index 6742633..a611d15 100644 --- a/src/main/java/org/javacs/JavaSettings.java +++ b/src/main/java/org/javacs/JavaSettings.java @@ -1,18 +1,14 @@ package org.javacs; -import java.nio.file.Path; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; public class JavaSettings { public Java java = new Java(); public static class Java { public List<String> externalDependencies = new ArrayList<>(); - public List<Path> sourceDirectories = new ArrayList<>(); public Optional<String> javaHome = Optional.empty(); } }
\ No newline at end of file diff --git a/src/main/java/org/javacs/JavacConfig.java b/src/main/java/org/javacs/JavacConfig.java index e8f4488..c10b897 100644 --- a/src/main/java/org/javacs/JavacConfig.java +++ b/src/main/java/org/javacs/JavacConfig.java @@ -3,15 +3,13 @@ package org.javacs; import java.nio.file.Path; import java.util.Objects; import java.util.Set; -import java.util.concurrent.CompletableFuture; public class JavacConfig { - public final Set<Path> sourcePath, classPath; + public final Set<Path> classPath; public final Path outputDirectory; - public final CompletableFuture<Set<Path>> docPath; + public final Set<Path> docPath; - public JavacConfig(Set<Path> sourcePath, Set<Path> classPath, Path outputDirectory, CompletableFuture<Set<Path>> docPath) { - this.sourcePath = sourcePath; + public JavacConfig(Set<Path> classPath, Path outputDirectory, Set<Path> docPath) { this.classPath = classPath; this.outputDirectory = outputDirectory; this.docPath = docPath; @@ -22,13 +20,13 @@ public class JavacConfig { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; JavacConfig that = (JavacConfig) o; - return Objects.equals(sourcePath, that.sourcePath) && - Objects.equals(classPath, that.classPath) && - Objects.equals(outputDirectory, that.outputDirectory); + return Objects.equals(classPath, that.classPath) && + Objects.equals(outputDirectory, that.outputDirectory) && + Objects.equals(docPath, that.docPath); } @Override public int hashCode() { - return Objects.hash(sourcePath, classPath, outputDirectory); + return Objects.hash(classPath, outputDirectory, docPath); } } diff --git a/src/main/java/org/javacs/JavacHolder.java b/src/main/java/org/javacs/JavacHolder.java index 64ffd09..f32d8fb 100644 --- a/src/main/java/org/javacs/JavacHolder.java +++ b/src/main/java/org/javacs/JavacHolder.java @@ -14,15 +14,11 @@ import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.util.Options; -import java.util.function.BiConsumer; -import java.util.function.Function; -import org.eclipse.lsp4j.SymbolInformation; import javax.lang.model.element.Element; import javax.tools.*; import java.io.File; import java.io.IOException; -import java.io.UncheckedIOException; import java.net.URI; import java.nio.charset.Charset; import java.nio.file.Files; @@ -31,11 +27,10 @@ import java.nio.file.Paths; import java.time.Duration; import java.time.Instant; import java.util.*; -import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Maintains a reference to a Java compiler, @@ -45,8 +40,8 @@ import java.util.stream.Stream; */ public class JavacHolder { - public static JavacHolder create(Set<Path> classPath, Set<Path> sourcePath, Path outputDirectory) { - return new JavacHolder(classPath, sourcePath, outputDirectory); + public static JavacHolder create(Set<Path> sourcePath, Set<Path> classPath, Path outputDirectory) { + return new JavacHolder(sourcePath, classPath, outputDirectory); } /** @@ -188,9 +183,9 @@ public class JavacHolder { public final ClassPathIndex classPathIndex; - private JavacHolder(Set<Path> classPath, Set<Path> sourcePath, Path outputDirectory) { + private JavacHolder(Set<Path> sourcePath, Set<Path> classPath, Path outputDirectory) { + this.sourcePath = sourcePath; this.classPath = Collections.unmodifiableSet(classPath); - this.sourcePath = Collections.unmodifiableSet(sourcePath); this.outputDirectory = outputDirectory; this.classPathIndex = new ClassPathIndex(classPath); @@ -260,10 +255,6 @@ public class JavacHolder { throw ShowMessageException.error("Output directory " + dir + " is not a directory", null); } - private static <T> Stream<T> stream(Optional<T> option) { - return option.map(Stream::of).orElseGet(Stream::empty); - } - /** * File has been deleted */ @@ -379,13 +370,4 @@ public class JavacHolder { // TODO use index to find dependencies return Collections.emptyList(); } - - JavacConfig config() { - return new JavacConfig( - this.sourcePath, - this.classPath, - this.outputDirectory, - CompletableFuture.completedFuture(Collections.emptySet()) - ); - } }
\ No newline at end of file diff --git a/src/main/java/org/javacs/Javadocs.java b/src/main/java/org/javacs/Javadocs.java index d326f98..54255e5 100644 --- a/src/main/java/org/javacs/Javadocs.java +++ b/src/main/java/org/javacs/Javadocs.java @@ -1,6 +1,5 @@ package org.javacs; -import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.overzealous.remark.Options; @@ -10,11 +9,8 @@ import com.sun.source.util.JavacTask; import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javadoc.api.JavadocTool; +import org.eclipse.lsp4j.CompletionItem; -import java.text.BreakIterator; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ForkJoinPool; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; @@ -23,46 +19,24 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.DocumentationTool; -import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.File; import java.io.IOException; +import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; +import java.text.BreakIterator; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import org.eclipse.lsp4j.CompletionItem; public class Javadocs { /** - * Stores known javadocs across all source paths, including dependencies - */ - private static Javadocs global = new Javadocs(Collections.emptySet()); - - /** - * Add another source path to global() - */ - public static void addSourcePath(Set<Path> additionalSourcePath) { - global = global.withSourcePath(additionalSourcePath); - } - - /** - * A single global instance of Javadocs that incorporates all source paths - */ - public static Javadocs global() { - return global; - } - - /** - * The indexed source path, not including src.zip - */ - private final Set<Path> userSourcePath; - - /** * Cache for performance reasons */ private final JavacFileManager actualFileManager; @@ -81,9 +55,8 @@ public class Javadocs { private final Elements elements; - Javadocs(Set<Path> sourcePath) { - this.userSourcePath = sourcePath; - this.actualFileManager = createFileManager(allSourcePaths(sourcePath)); + Javadocs(Set<Path> sourcePath, Set<Path> docPath, Function<URI, Optional<String>> activeContent) { + actualFileManager = createFileManager(allSourcePaths(sourcePath, docPath)); JavacTask task = JavacTool.create().getTask( null, @@ -98,12 +71,14 @@ public class Javadocs { elements = task.getElements(); } - private static Set<File> allSourcePaths(Set<Path> userSourcePath) { + private static Set<File> allSourcePaths(Set<Path>... userSourcePath) { Set<File> allSourcePaths = new HashSet<>(); // Add userSourcePath - for (Path each : userSourcePath) - allSourcePaths.add(each.toFile()); + for (Set<Path> eachPath : userSourcePath) { + for (Path each : eachPath) + allSourcePaths.add(each.toFile()); + } // Add src.zip from JDK findSrcZip().ifPresent(allSourcePaths::add); @@ -123,15 +98,6 @@ public class Javadocs { return actualFileManager; } - private Javadocs withSourcePath(Set<Path> additionalSourcePath) { - Set<Path> all = new HashSet<>(); - - all.addAll(userSourcePath); - all.addAll(additionalSourcePath); - - return new Javadocs(all); - } - /** * Convert Javadoc HTML to Markdown */ diff --git a/src/main/java/org/javacs/Signatures.java b/src/main/java/org/javacs/Signatures.java index 083627f..ba331a1 100644 --- a/src/main/java/org/javacs/Signatures.java +++ b/src/main/java/org/javacs/Signatures.java @@ -9,34 +9,34 @@ import com.sun.source.tree.Tree; import com.sun.source.util.JavacTask; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - import com.sun.source.util.Trees; import org.eclipse.lsp4j.ParameterInformation; import org.eclipse.lsp4j.SignatureHelp; import org.eclipse.lsp4j.SignatureInformation; import javax.lang.model.element.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; class Signatures { - static Optional<SignatureHelp> help(FocusedResult compiled, int line, int column) { + static Optional<SignatureHelp> help(FocusedResult compiled, int line, int column, Javadocs docs) { long offset = compiled.compilationUnit.getLineMap().getPosition(line, column); - return compiled.cursor.flatMap(c -> new Signatures(c, offset, compiled.task).get()); + return compiled.cursor.flatMap(c -> new Signatures(c, offset, compiled.task, docs).get()); } private final TreePath cursor; private final long cursorOffset; private final JavacTask task; + private final Javadocs docs; - private Signatures(TreePath cursor, long cursorOffset, JavacTask task) { + private Signatures(TreePath cursor, long cursorOffset, JavacTask task, Javadocs docs) { this.cursor = cursor; this.cursorOffset = cursorOffset; this.task = task; + this.docs = docs; } @@ -95,7 +95,6 @@ class Signatures { } private SignatureInformation constructorInfo(ExecutableElement method) { - Javadocs docs = Javadocs.global(); Optional<ConstructorDoc> doc = docs.constructorDoc(docs.methodKey(method)); Optional<String> docText = doc.flatMap(constructor -> Optional.ofNullable(constructor.commentText())) .map(Javadocs::htmlToMarkdown) @@ -109,7 +108,6 @@ class Signatures { } private SignatureInformation methodInfo(ExecutableElement method) { - Javadocs docs = Javadocs.global(); Optional<MethodDoc> doc = docs.methodDoc(docs.methodKey(method)); Optional<String> docText = doc.flatMap(Javadocs::commentText) .map(Javadocs::htmlToMarkdown) diff --git a/src/main/java/org/javacs/SymbolIndex.java b/src/main/java/org/javacs/SymbolIndex.java index 174053a..b95c791 100644 --- a/src/main/java/org/javacs/SymbolIndex.java +++ b/src/main/java/org/javacs/SymbolIndex.java @@ -1,18 +1,8 @@ package org.javacs; -import com.google.common.base.Joiner; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.sun.source.tree.*; import com.sun.source.util.*; -import com.sun.source.util.TaskEvent.Kind; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; import org.eclipse.lsp4j.*; import javax.lang.model.element.Element; @@ -22,8 +12,14 @@ import javax.lang.model.element.PackageElement; import javax.tools.Diagnostic; import java.io.IOException; import java.net.URI; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -32,9 +28,6 @@ import java.util.stream.Stream; */ public class SymbolIndex { - /** - * Parent language server - */ private final JavaLanguageServer languageServer; /** @@ -42,16 +35,23 @@ public class SymbolIndex { */ private final Map<URI, SourceFileIndex> sourcePathFiles = new HashMap<>(); - SymbolIndex(JavaLanguageServer languageServer) { - this.languageServer = languageServer; + SymbolIndex(JavaLanguageServer parent) { + this.languageServer = parent; + + index(javaSources(parent.workspaceRoot())); } - CompletableFuture<?> addConfig(JavacConfig config) { - Set<URI> sources = config.sourcePath.stream() - .flatMap(this::findAllSources) - .collect(Collectors.toSet()); + private static Set<URI> javaSources(Path workspaceRoot) { + PathMatcher match = FileSystems.getDefault().getPathMatcher("glob:*.java"); - return index(sources, config); + try { + return Files.walk(workspaceRoot) + .filter(java -> match.matches(java.getFileName())) + .map(Path::toUri) + .collect(Collectors.toSet()); + } catch (IOException e) { + throw new RuntimeException(e); + } } private CompletableFuture<?> indexQueue = CompletableFuture.completedFuture(null); @@ -60,14 +60,14 @@ public class SymbolIndex { * Index exported declarations and references for all files on the source path * This may take a while, so we'll do it on an extra thread */ - private CompletableFuture<?> index(Set<URI> sources, JavacConfig config) { - LOG.info("Index " + sources.size() + " sources in " + Joiner.on(", ").join(config.sourcePath)); + private CompletableFuture<?> index(Set<URI> sources) { + LOG.info("Index " + sources.size() + " sources in " + languageServer.workspaceRoot()); Map<URI, Optional<String>> active = sources.stream() .collect(Collectors.toMap(key -> key, languageServer::activeContent)); indexQueue = indexQueue.thenRun(() -> { - BatchResult compiled = JavacHolder.create(config.classPath, config.sourcePath, config.outputDirectory) + BatchResult compiled = languageServer.compiler() .compileBatch(active, this::update); languageServer.publishDiagnostics(sources, compiled.errors.getDiagnostics()); @@ -77,51 +77,16 @@ public class SymbolIndex { } private void updateOpenFiles() { - Map<JavacHolder, Map<URI, String>> open = new HashMap<>(); - - languageServer.activeDocuments().forEach((uri, content) -> { - JavacHolder compiler = languageServer.findCompiler(uri); + Map<URI, Optional<String>> open = Maps.transformValues(languageServer.activeDocuments(), Optional::of); - open.computeIfAbsent(compiler, __ -> new HashMap<>()) - .put(uri, content); - }); - - open.forEach((compiler, files) -> { - LOG.info("Update index for " + files.size() + " source files"); - - compiler.compileBatch(Maps.transformValues(files, Optional::of), this::update); - }); + languageServer.compiler().compileBatch(open, this::update); } private void updateOpenFile(URI file) { - JavacHolder compiler = languageServer.findCompiler(file); - LOG.info("Update index for " + file); - BatchResult compiled = compiler.compileBatch(Collections.singletonMap(file, languageServer.activeContent(file)), this::update); - } - - /** - * Look for .java files - */ - private Stream<URI> findAllSources(Path dir) { - // TODO make this lazy - Set<URI> result = new HashSet<>(); - - doFindAllSources(dir, result); - - return result.stream(); - } - - private void doFindAllSources(Path path, Set<URI> uris) { - if (Files.isDirectory(path)) try { - Files.list(path).forEach(p -> doFindAllSources(p, uris)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - else if (path.getFileName().toString().endsWith(".java")) { - uris.add(path.toUri()); - } + Map<URI, Optional<String>> todo = Collections.singletonMap(file, languageServer.activeContent(file)); + BatchResult compiled = languageServer.compiler().compileBatch(todo, this::update); } /** diff --git a/src/main/java/org/javacs/WorkspaceFileManager.java b/src/main/java/org/javacs/WorkspaceFileManager.java new file mode 100644 index 0000000..bbdfeed --- /dev/null +++ b/src/main/java/org/javacs/WorkspaceFileManager.java @@ -0,0 +1,130 @@ +package org.javacs; + +import com.google.common.collect.Iterables; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.tools.javac.file.JavacFileManager; + +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * JavaFileManager that searches all .java files in workspace. + * + * Unlike the standard JavaFileManager, WorkspaceFileManager does not assume that a java class com.foo.package.MyClass will be located in a directory com/foo/package, + * and it will find files in the workspace that are not on the source path. + * + * It *does* assume that it will be located in a file named MyClass.java. + */ +class WorkspaceFileManager extends ForwardingJavaFileManager<JavaFileManager> { + private final JavacFileManager delegate; + + /** + * Root of the workspace that is currently open in VSCode + */ + private final Path workspaceRoot; + + private final Function<URI, Optional<String>> activeContent; + + WorkspaceFileManager(JavacFileManager delegate, Path workspaceRoot, Function<URI, Optional<String>> activeContent) { + super(delegate); + + this.delegate = delegate; + this.workspaceRoot = workspaceRoot; + this.activeContent = activeContent; + } + + @Override + public Iterable<JavaFileObject> list(Location location, + String packageName, + Set<JavaFileObject.Kind> kinds, + boolean recurse) throws IOException { + Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse); + + if (location == StandardLocation.SOURCE_PATH) { + List<JavaFileObject> workspaceFiles = findPackageClasses(packageName); + + files = Iterables.concat(files, workspaceFiles); + } + + return files; + } + + private List<JavaFileObject> findPackageClasses(String packageName) throws IOException { + PathMatcher match = FileSystems.getDefault().getPathMatcher("glob:*.java"); + + return Files.walk(workspaceRoot) + .filter(java -> match.matches(java.getFileName())) + .filter(java -> parsePackage(java).equals(packageName)) + .map(java -> delegate.getRegularFile(java.toFile())) + .collect(Collectors.toList()); + } + + // TODO cache this based on file modification-time/active-set + private String parsePackage(Path java) { + URI uri = java.toUri(); + Optional<String> content = activeContent.apply(uri); + // TODO recognize a file beginning with 'package ...' as a special case and skip parsing + CompilationUnitTree tree = InferConfig.parse(uri, content); + ExpressionTree packageTree = tree.getPackageName(); + + if (packageTree == null) + return ""; + else + return packageTree.toString(); + } + + @Override + public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException { + JavaFileObject result = super.getJavaFileForInput(location, className, kind); + + if (result != null) + return result; + + if (location == StandardLocation.SOURCE_PATH) { + Optional<JavaFileObject> scan = findClass(className); + + return scan.orElse(null); + } + + return null; + } + + private Optional<JavaFileObject> findClass(String className) throws IOException { + int split = className.lastIndexOf('.'); + String packageName = split == -1 ? "" : className.substring(0, split); + String simpleClassName = className.substring(split + 1); + + return Files.walk(workspaceRoot) + .filter(java -> java.getFileName().toString().equals(simpleClassName + ".java")) + .filter(java -> parsePackage(java).equals(packageName)) + .map(java -> delegate.getRegularFile(java.toFile())) + .findFirst(); + } + + private static Path tempOutputDirectory(String name) { + try { + return Files.createTempDirectory("parser-out"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public JavaFileObject getRegularFile(File file) { + return delegate.getRegularFile(file); + } +}
\ No newline at end of file diff --git a/src/test/java/org/javacs/CodeActionsTest.java b/src/test/java/org/javacs/CodeActionsTest.java index 36924c8..71bf488 100644 --- a/src/test/java/org/javacs/CodeActionsTest.java +++ b/src/test/java/org/javacs/CodeActionsTest.java @@ -5,9 +5,9 @@ import org.eclipse.lsp4j.services.LanguageClient; import org.junit.Before; import org.junit.Test; -import java.io.BufferedReader; -import java.io.InputStreamReader; +import java.io.*; import java.net.URI; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -80,21 +80,27 @@ public class CodeActionsTest { private List<? extends Command> commands(String file, int row, int column) { URI uri = FindResource.uri(file); TextDocumentIdentifier document = new TextDocumentIdentifier(uri.toString()); - String content = new BufferedReader(new InputStreamReader(FindResource.class.getResourceAsStream(file))).lines() - .collect(Collectors.joining("\n")); - TextDocumentItem open = new TextDocumentItem(); - open.setText(content); - open.setUri(uri.toString()); - open.setLanguageId("java"); - - server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(open, content)); - server.getTextDocumentService().didSave(new DidSaveTextDocumentParams(document, content)); - - return diagnostics.stream() - .filter(diagnostic -> includes(diagnostic.getRange(), row - 1, column - 1)) - .flatMap(diagnostic -> codeActionsAt(document, diagnostic)) - .collect(Collectors.toList()); + try { + InputStream in = Files.newInputStream(new File(uri).toPath()); + String content = new BufferedReader(new InputStreamReader(in)).lines() + .collect(Collectors.joining("\n")); + TextDocumentItem open = new TextDocumentItem(); + + open.setText(content); + open.setUri(uri.toString()); + open.setLanguageId("java"); + + server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(open, content)); + server.getTextDocumentService().didSave(new DidSaveTextDocumentParams(document, content)); + + return diagnostics.stream() + .filter(diagnostic -> includes(diagnostic.getRange(), row - 1, column - 1)) + .flatMap(diagnostic -> codeActionsAt(document, diagnostic)) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new RuntimeException(e); + } } private boolean includes(Range range, int line, int character) { diff --git a/src/test/java/org/javacs/CompilerProfiling.java b/src/test/java/org/javacs/CompilerProfiling.java index 1114b3e..020bbf7 100644 --- a/src/test/java/org/javacs/CompilerProfiling.java +++ b/src/test/java/org/javacs/CompilerProfiling.java @@ -29,7 +29,11 @@ public class CompilerProfiling { private Duration compileLargeFile(URI file) { long start = System.nanoTime(); - JavacHolder compiler = JavacHolder.create(Collections.emptySet(), Collections.emptySet(), Paths.get("target/test-output")); + JavacHolder compiler = JavacHolder.create( + Collections.singleton(Paths.get("src/test/test-project/workspace/src")), + Collections.emptySet(), + Paths.get("target/test-output") + ); BatchResult result = compiler.compileBatch(Collections.singletonMap(file, Optional.empty())); long finish = System.nanoTime(); diff --git a/src/test/java/org/javacs/FindResource.java b/src/test/java/org/javacs/FindResource.java index 355223a..0f26115 100644 --- a/src/test/java/org/javacs/FindResource.java +++ b/src/test/java/org/javacs/FindResource.java @@ -6,14 +6,14 @@ import java.nio.file.Path; import java.nio.file.Paths; /** - * Represents a java source on the system resource path. + * Find java sources in test-project/workspace/src */ public class FindResource { public static URI uri(String resourcePath) { if (resourcePath.startsWith("/")) resourcePath = resourcePath.substring(1); - Path path = Paths.get("./src/test/resources").resolve(resourcePath).normalize(); + Path path = Paths.get("./src/test/test-project/workspace/src").resolve(resourcePath).normalize(); File file = path.toFile(); if (!file.exists()) diff --git a/src/test/java/org/javacs/InferConfigTest.java b/src/test/java/org/javacs/InferConfigTest.java index 46be918..0c42e2d 100644 --- a/src/test/java/org/javacs/InferConfigTest.java +++ b/src/test/java/org/javacs/InferConfigTest.java @@ -1,28 +1,27 @@ package org.javacs; import com.google.common.collect.ImmutableList; +import org.junit.Test; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import org.junit.Test; -import static org.junit.Assert.*; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.assertThat; public class InferConfigTest { - Path workspaceRoot = Paths.get("src/test/test-project/workspace"); - Path mavenHome = Paths.get("src/test/test-project/home/.m2"); - Path gradleHome = Paths.get("src/test/test-project/home/.gradle"); - Path outputDirectory = createOutputDir(); - List<Artifact> externalDependencies = ImmutableList.of(new Artifact("com.external", "external-library", "1.2")); - InferConfig both = new InferConfig(workspaceRoot, externalDependencies, mavenHome, gradleHome, outputDirectory), + private Path workspaceRoot = Paths.get("src/test/test-project/workspace"); + private Path mavenHome = Paths.get("src/test/test-project/home/.m2"); + private Path gradleHome = Paths.get("src/test/test-project/home/.gradle"); + private Path outputDirectory = createOutputDir(); + private List<Artifact> externalDependencies = ImmutableList.of(new Artifact("com.external", "external-library", "1.2")); + private InferConfig both = new InferConfig(workspaceRoot, externalDependencies, mavenHome, gradleHome, outputDirectory), gradle = new InferConfig(workspaceRoot, externalDependencies, Paths.get("nowhere"), gradleHome, outputDirectory); - Path createOutputDir() { + private Path createOutputDir() { try { return Files.createTempDirectory("output").toAbsolutePath(); } catch (IOException e) { @@ -33,7 +32,7 @@ public class InferConfigTest { @Test public void workspaceSourcePath() { assertThat( - both.workspaceSourcePath(), + InferConfig.workspaceSourcePath(workspaceRoot), contains(workspaceRoot.resolve("src")) ); } diff --git a/src/test/java/org/javacs/JavaCompilerTest.java b/src/test/java/org/javacs/JavaCompilerTest.java index 657b125..3575afc 100644 --- a/src/test/java/org/javacs/JavaCompilerTest.java +++ b/src/test/java/org/javacs/JavaCompilerTest.java @@ -3,15 +3,10 @@ package org.javacs; import com.google.common.collect.ImmutableList; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.util.JavacTask; -import com.sun.source.util.TaskEvent; -import com.sun.source.util.TaskEvent.Kind; -import com.sun.source.util.TaskListener; import com.sun.tools.javac.api.JavacTool; -import java.util.EnumMap; import org.junit.Test; import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; @@ -25,9 +20,6 @@ import java.util.List; import java.util.Optional; import java.util.logging.Logger; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; - public class JavaCompilerTest { private static final Logger LOG = Logger.getLogger("main"); @@ -49,8 +41,12 @@ public class JavaCompilerTest { @Test public void javacHolder() { - JavacHolder javac = JavacHolder.create(Collections.emptySet(), Collections.singleton(Paths.get("src/test/resources")), Paths.get("target/test-output")); - File file = Paths.get("src/test/resources/org/javacs/example/Bad.java").toFile(); + JavacHolder javac = JavacHolder.create( + Collections.singleton(Paths.get("src/test/test-project/workspace/src")), + Collections.emptySet(), + Paths.get("target/test-output") + ); + File file = Paths.get("src/test/test-project/workspace/src/org/javacs/example/Bad.java").toFile(); BatchResult compile = javac.compileBatch(Collections.singletonMap(file.toURI(), Optional.empty())); } diff --git a/src/test/java/org/javacs/JavadocsTest.java b/src/test/java/org/javacs/JavadocsTest.java index ae20f42..ce248f7 100644 --- a/src/test/java/org/javacs/JavadocsTest.java +++ b/src/test/java/org/javacs/JavadocsTest.java @@ -2,31 +2,25 @@ package org.javacs; import com.sun.javadoc.MethodDoc; import com.sun.javadoc.RootDoc; -import com.sun.source.util.Trees; import org.junit.Ignore; import org.junit.Test; -import javax.lang.model.element.ExecutableElement; import java.io.IOException; import java.nio.file.Paths; import java.util.Collections; import java.util.Optional; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public class JavadocsTest { - private final Javadocs docs = new Javadocs(Collections.singleton(Paths.get("src/test/resources"))); - private final JavacHolder compiler = newCompiler(); - - private static JavacHolder newCompiler() { - return JavacHolder.create( - Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), - Paths.get("target/test-output") - ); - } + private final Javadocs docs = new Javadocs( + Collections.singleton(Paths.get("src/test/test-project/workspace/src")), + Collections.emptySet(), + __ -> Optional.empty() + ); @Test public void findSystemDoc() throws IOException { @@ -45,7 +39,8 @@ public class JavadocsTest { assertTrue("Found method", docs.methodDoc("org.javacs.docs.TrickyDocstring#parameterized(java.lang.Object)").isPresent()); } - @Test + @Test + @Ignore // Blocked by emptyFileManager public void findInheritedDoc() { Optional<MethodDoc> found = docs.methodDoc("org.javacs.docs.SubDoc#method()"); diff --git a/src/test/java/org/javacs/LanguageServerFixture.java b/src/test/java/org/javacs/LanguageServerFixture.java index e803bdb..656ed59 100644 --- a/src/test/java/org/javacs/LanguageServerFixture.java +++ b/src/test/java/org/javacs/LanguageServerFixture.java @@ -5,8 +5,6 @@ import org.eclipse.lsp4j.services.LanguageClient; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collections; -import java.util.Set; import java.util.concurrent.CompletableFuture; class LanguageServerFixture { @@ -45,16 +43,12 @@ class LanguageServerFixture { } static JavaLanguageServer getJavaLanguageServer(LanguageClient client) { - Set<Path> classPath = Collections.emptySet(); - Set<Path> sourcePath = Collections.singleton(Paths.get("src/test/resources").toAbsolutePath()); - Path outputDirectory = Paths.get("target/test-output").toAbsolutePath(); - JavacHolder javac = JavacHolder.create(classPath, sourcePath, outputDirectory); - JavaLanguageServer server = new JavaLanguageServer(javac); + Path workspaceRoot = Paths.get("src/test/test-project/workspace").toAbsolutePath(); + JavaLanguageServer server = new JavaLanguageServer(); InitializeParams init = new InitializeParams(); - String workspaceRoot = Paths.get(".").toAbsolutePath().toString(); - init.setRootPath(workspaceRoot); + init.setRootPath(workspaceRoot.toString()); server.initialize(init); server.installClient(client); diff --git a/src/test/java/org/javacs/LinterTest.java b/src/test/java/org/javacs/LinterTest.java index ddad1a8..76d37e1 100644 --- a/src/test/java/org/javacs/LinterTest.java +++ b/src/test/java/org/javacs/LinterTest.java @@ -19,8 +19,8 @@ public class LinterTest { private static final Logger LOG = Logger.getLogger("main"); private static final JavacHolder compiler = JavacHolder.create( + Collections.singleton(Paths.get("src/test/test-project/workspace/src")), Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), Paths.get("target/test-output") ); diff --git a/src/test/java/org/javacs/RecompileTest.java b/src/test/java/org/javacs/RecompileTest.java index 5c02b3c..05b3aa2 100644 --- a/src/test/java/org/javacs/RecompileTest.java +++ b/src/test/java/org/javacs/RecompileTest.java @@ -63,8 +63,8 @@ public class RecompileTest { private static JavacHolder newCompiler() { return JavacHolder.create( + Collections.singleton(Paths.get("src/test/test-project/workspace/src")), Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), Paths.get("target/test-output") ); } diff --git a/src/test/java/org/javacs/RefactorFileTest.java b/src/test/java/org/javacs/RefactorFileTest.java index 258a5f6..8d8bc62 100644 --- a/src/test/java/org/javacs/RefactorFileTest.java +++ b/src/test/java/org/javacs/RefactorFileTest.java @@ -200,7 +200,11 @@ public class RefactorFileTest { } private ParseResult file(String content) { - JavacHolder compiler = JavacHolder.create(Collections.emptySet(), Collections.emptySet(), Paths.get("test-output")); + JavacHolder compiler = JavacHolder.create( + Collections.singleton(Paths.get("src/test/test-project/workspace/src")), + Collections.emptySet(), + Paths.get("target/test-output") + ); return compiler.parse(FAKE_FILE, Optional.of(content), error -> LOG.warning(error.toString())); } diff --git a/src/test/java/org/javacs/SearchTest.java b/src/test/java/org/javacs/SearchTest.java index 75fe289..7e2b4cd 100644 --- a/src/test/java/org/javacs/SearchTest.java +++ b/src/test/java/org/javacs/SearchTest.java @@ -25,7 +25,7 @@ public class SearchTest { @BeforeClass public static void openSource() throws URISyntaxException, IOException { - URI uri = SearchTest.class.getResource("/org/javacs/example/AutocompleteBetweenLines.java").toURI(); + URI uri = FindResource.uri("/org/javacs/example/AutocompleteBetweenLines.java"); String textContent = Joiner.on("\n").join(Files.readAllLines(Paths.get(uri))); TextDocumentItem document = new TextDocumentItem(); diff --git a/src/test/java/org/javacs/SymbolUnderCursorTest.java b/src/test/java/org/javacs/SymbolUnderCursorTest.java index fa68618..5608fbf 100644 --- a/src/test/java/org/javacs/SymbolUnderCursorTest.java +++ b/src/test/java/org/javacs/SymbolUnderCursorTest.java @@ -4,8 +4,6 @@ import org.junit.Ignore; import org.junit.Test; import javax.lang.model.element.Element; -import java.nio.file.Paths; -import java.util.Collections; import java.util.Optional; import static org.junit.Assert.assertEquals; @@ -78,19 +76,11 @@ public class SymbolUnderCursorTest { assertEquals("localVariable", symbolAt("/org/javacs/example/SymbolUnderCursor.java", 10, 16)); } + private final JavaLanguageServer server = LanguageServerFixture.getJavaLanguageServer(); + private String symbolAt(String file, int line, int character) { - Optional<Element> symbol = new JavaLanguageServer(compiler).findSymbol(FindResource.uri(file), line, character); + Optional<Element> symbol = server.findSymbol(FindResource.uri(file), line, character); return symbol.map(s -> s.getSimpleName().toString()).orElse(null); } - - private static JavacHolder compiler = newCompiler(); - - private static JavacHolder newCompiler() { - return JavacHolder.create( - Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), - Paths.get("target/test-output") - ); - } } diff --git a/src/test/resources/org/javacs/docs/InterfaceDoc.java b/src/test/test-project/workspace/src/org/javacs/docs/InterfaceDoc.java index 26e4aaa..26e4aaa 100644 --- a/src/test/resources/org/javacs/docs/InterfaceDoc.java +++ b/src/test/test-project/workspace/src/org/javacs/docs/InterfaceDoc.java diff --git a/src/test/resources/org/javacs/docs/SubDoc.java b/src/test/test-project/workspace/src/org/javacs/docs/SubDoc.java index bb687f6..bb687f6 100644 --- a/src/test/resources/org/javacs/docs/SubDoc.java +++ b/src/test/test-project/workspace/src/org/javacs/docs/SubDoc.java diff --git a/src/test/resources/org/javacs/docs/SuperDoc.java b/src/test/test-project/workspace/src/org/javacs/docs/SuperDoc.java index b116eda..b116eda 100644 --- a/src/test/resources/org/javacs/docs/SuperDoc.java +++ b/src/test/test-project/workspace/src/org/javacs/docs/SuperDoc.java diff --git a/src/test/resources/org/javacs/docs/TrickyDocstring.java b/src/test/test-project/workspace/src/org/javacs/docs/TrickyDocstring.java index 445e4ee..445e4ee 100644 --- a/src/test/resources/org/javacs/docs/TrickyDocstring.java +++ b/src/test/test-project/workspace/src/org/javacs/docs/TrickyDocstring.java diff --git a/src/test/resources/org/javacs/example/AutocompleteBetweenLines.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteBetweenLines.java index 49d26bd..49d26bd 100644 --- a/src/test/resources/org/javacs/example/AutocompleteBetweenLines.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteBetweenLines.java diff --git a/src/test/resources/org/javacs/example/AutocompleteClasses.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteClasses.java index a92b404..a92b404 100644 --- a/src/test/resources/org/javacs/example/AutocompleteClasses.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteClasses.java diff --git a/src/test/resources/org/javacs/example/AutocompleteConstructor.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteConstructor.java index 5b21493..5b21493 100644 --- a/src/test/resources/org/javacs/example/AutocompleteConstructor.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteConstructor.java diff --git a/src/test/resources/org/javacs/example/AutocompleteContext.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteContext.java index 970da5a..970da5a 100644 --- a/src/test/resources/org/javacs/example/AutocompleteContext.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteContext.java diff --git a/src/test/resources/org/javacs/example/AutocompleteDocstring.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteDocstring.java index 96ab552..96ab552 100644 --- a/src/test/resources/org/javacs/example/AutocompleteDocstring.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteDocstring.java diff --git a/src/test/resources/org/javacs/example/AutocompleteEditMethodName.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteEditMethodName.java index c8f619b..c8f619b 100644 --- a/src/test/resources/org/javacs/example/AutocompleteEditMethodName.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteEditMethodName.java diff --git a/src/test/resources/org/javacs/example/AutocompleteFromClasspath.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteFromClasspath.java index a74d775..a74d775 100644 --- a/src/test/resources/org/javacs/example/AutocompleteFromClasspath.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteFromClasspath.java diff --git a/src/test/resources/org/javacs/example/AutocompleteInners.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteInners.java index 608e1f2..608e1f2 100644 --- a/src/test/resources/org/javacs/example/AutocompleteInners.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteInners.java diff --git a/src/test/resources/org/javacs/example/AutocompleteMember.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteMember.java index 3c555e2..3c555e2 100644 --- a/src/test/resources/org/javacs/example/AutocompleteMember.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteMember.java diff --git a/src/test/resources/org/javacs/example/AutocompleteMemberFixed.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteMemberFixed.java index 0c2e575..0c2e575 100644 --- a/src/test/resources/org/javacs/example/AutocompleteMemberFixed.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteMemberFixed.java diff --git a/src/test/resources/org/javacs/example/AutocompleteMembers.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteMembers.java index c36449a..c36449a 100644 --- a/src/test/resources/org/javacs/example/AutocompleteMembers.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteMembers.java diff --git a/src/test/resources/org/javacs/example/AutocompleteOther.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteOther.java index 8491f72..8491f72 100644 --- a/src/test/resources/org/javacs/example/AutocompleteOther.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteOther.java diff --git a/src/test/resources/org/javacs/example/AutocompleteOuter.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteOuter.java index c7a4c0d..c7a4c0d 100644 --- a/src/test/resources/org/javacs/example/AutocompleteOuter.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteOuter.java diff --git a/src/test/resources/org/javacs/example/AutocompletePackage.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompletePackage.java index e9b11f7..e9b11f7 100644 --- a/src/test/resources/org/javacs/example/AutocompletePackage.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompletePackage.java diff --git a/src/test/resources/org/javacs/example/AutocompleteReference.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteReference.java index 2ba5ca4..2ba5ca4 100644 --- a/src/test/resources/org/javacs/example/AutocompleteReference.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteReference.java diff --git a/src/test/resources/org/javacs/example/AutocompleteRest.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteRest.java index ac0dc7a..ac0dc7a 100644 --- a/src/test/resources/org/javacs/example/AutocompleteRest.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteRest.java diff --git a/src/test/resources/org/javacs/example/AutocompleteScopes.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteScopes.java index 2a2fa58..2a2fa58 100644 --- a/src/test/resources/org/javacs/example/AutocompleteScopes.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteScopes.java diff --git a/src/test/resources/org/javacs/example/AutocompleteStaticImport.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteStaticImport.java index 4f382e4..4f382e4 100644 --- a/src/test/resources/org/javacs/example/AutocompleteStaticImport.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteStaticImport.java diff --git a/src/test/resources/org/javacs/example/AutocompleteStaticMember.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteStaticMember.java index 93191c1..93191c1 100644 --- a/src/test/resources/org/javacs/example/AutocompleteStaticMember.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteStaticMember.java diff --git a/src/test/resources/org/javacs/example/AutocompleteStaticReference.java b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteStaticReference.java index 75a5d8f..75a5d8f 100644 --- a/src/test/resources/org/javacs/example/AutocompleteStaticReference.java +++ b/src/test/test-project/workspace/src/org/javacs/example/AutocompleteStaticReference.java diff --git a/src/test/resources/org/javacs/example/Bad.java b/src/test/test-project/workspace/src/org/javacs/example/Bad.java index f21f237..f21f237 100644 --- a/src/test/resources/org/javacs/example/Bad.java +++ b/src/test/test-project/workspace/src/org/javacs/example/Bad.java diff --git a/src/test/resources/org/javacs/example/CompileTwice.java b/src/test/test-project/workspace/src/org/javacs/example/CompileTwice.java index e905afc..e905afc 100644 --- a/src/test/resources/org/javacs/example/CompileTwice.java +++ b/src/test/test-project/workspace/src/org/javacs/example/CompileTwice.java diff --git a/src/test/resources/org/javacs/example/DependsOnTarget.java b/src/test/test-project/workspace/src/org/javacs/example/DependsOnTarget.java index 60cd332..60cd332 100644 --- a/src/test/resources/org/javacs/example/DependsOnTarget.java +++ b/src/test/test-project/workspace/src/org/javacs/example/DependsOnTarget.java diff --git a/src/test/resources/org/javacs/example/DeprecatedClass.java b/src/test/test-project/workspace/src/org/javacs/example/DeprecatedClass.java index 221051f..221051f 100644 --- a/src/test/resources/org/javacs/example/DeprecatedClass.java +++ b/src/test/test-project/workspace/src/org/javacs/example/DeprecatedClass.java diff --git a/src/test/resources/org/javacs/example/DeprecationWarning.java b/src/test/test-project/workspace/src/org/javacs/example/DeprecationWarning.java index 558ba6a..558ba6a 100644 --- a/src/test/resources/org/javacs/example/DeprecationWarning.java +++ b/src/test/test-project/workspace/src/org/javacs/example/DeprecationWarning.java diff --git a/src/test/resources/org/javacs/example/ErrorInDependency.java b/src/test/test-project/workspace/src/org/javacs/example/ErrorInDependency.java index f9ca67c..f9ca67c 100644 --- a/src/test/resources/org/javacs/example/ErrorInDependency.java +++ b/src/test/test-project/workspace/src/org/javacs/example/ErrorInDependency.java diff --git a/src/test/resources/org/javacs/example/ExposesStaticMembers.java b/src/test/test-project/workspace/src/org/javacs/example/ExposesStaticMembers.java index f164f15..f164f15 100644 --- a/src/test/resources/org/javacs/example/ExposesStaticMembers.java +++ b/src/test/test-project/workspace/src/org/javacs/example/ExposesStaticMembers.java diff --git a/src/test/resources/org/javacs/example/FixParseErrorAfter.java b/src/test/test-project/workspace/src/org/javacs/example/FixParseErrorAfter.java index 00ba976..00ba976 100644 --- a/src/test/resources/org/javacs/example/FixParseErrorAfter.java +++ b/src/test/test-project/workspace/src/org/javacs/example/FixParseErrorAfter.java diff --git a/src/test/resources/org/javacs/example/FixParseErrorBefore.java b/src/test/test-project/workspace/src/org/javacs/example/FixParseErrorBefore.java index 624136e..624136e 100644 --- a/src/test/resources/org/javacs/example/FixParseErrorBefore.java +++ b/src/test/test-project/workspace/src/org/javacs/example/FixParseErrorBefore.java diff --git a/src/test/resources/org/javacs/example/FixTypeErrorAfter.java b/src/test/test-project/workspace/src/org/javacs/example/FixTypeErrorAfter.java index 130aebb..130aebb 100644 --- a/src/test/resources/org/javacs/example/FixTypeErrorAfter.java +++ b/src/test/test-project/workspace/src/org/javacs/example/FixTypeErrorAfter.java diff --git a/src/test/resources/org/javacs/example/FixTypeErrorBefore.java b/src/test/test-project/workspace/src/org/javacs/example/FixTypeErrorBefore.java index 42107f9..42107f9 100644 --- a/src/test/resources/org/javacs/example/FixTypeErrorBefore.java +++ b/src/test/test-project/workspace/src/org/javacs/example/FixTypeErrorBefore.java diff --git a/src/test/resources/org/javacs/example/FooString.java b/src/test/test-project/workspace/src/org/javacs/example/FooString.java index 2be005a..2be005a 100644 --- a/src/test/resources/org/javacs/example/FooString.java +++ b/src/test/test-project/workspace/src/org/javacs/example/FooString.java diff --git a/src/test/resources/org/javacs/example/Goto.java b/src/test/test-project/workspace/src/org/javacs/example/Goto.java index 2dd774f..2dd774f 100644 --- a/src/test/resources/org/javacs/example/Goto.java +++ b/src/test/test-project/workspace/src/org/javacs/example/Goto.java diff --git a/src/test/resources/org/javacs/example/GotoDefaultConstructor.java b/src/test/test-project/workspace/src/org/javacs/example/GotoDefaultConstructor.java index 52a1f65..52a1f65 100644 --- a/src/test/resources/org/javacs/example/GotoDefaultConstructor.java +++ b/src/test/test-project/workspace/src/org/javacs/example/GotoDefaultConstructor.java diff --git a/src/test/resources/org/javacs/example/GotoEnum.java b/src/test/test-project/workspace/src/org/javacs/example/GotoEnum.java index 12344cf..12344cf 100644 --- a/src/test/resources/org/javacs/example/GotoEnum.java +++ b/src/test/test-project/workspace/src/org/javacs/example/GotoEnum.java diff --git a/src/test/resources/org/javacs/example/GotoOther.java b/src/test/test-project/workspace/src/org/javacs/example/GotoOther.java index 3afaf4b..3afaf4b 100644 --- a/src/test/resources/org/javacs/example/GotoOther.java +++ b/src/test/test-project/workspace/src/org/javacs/example/GotoOther.java diff --git a/src/test/resources/org/javacs/example/HelloWorld.java b/src/test/test-project/workspace/src/org/javacs/example/HelloWorld.java index 425854f..425854f 100644 --- a/src/test/resources/org/javacs/example/HelloWorld.java +++ b/src/test/test-project/workspace/src/org/javacs/example/HelloWorld.java diff --git a/src/test/resources/org/javacs/example/IncompleteAssignment.java b/src/test/test-project/workspace/src/org/javacs/example/IncompleteAssignment.java index d8d8b76..d8d8b76 100644 --- a/src/test/resources/org/javacs/example/IncompleteAssignment.java +++ b/src/test/test-project/workspace/src/org/javacs/example/IncompleteAssignment.java diff --git a/src/test/resources/org/javacs/example/LargeFile.java b/src/test/test-project/workspace/src/org/javacs/example/LargeFile.java index b41cdc0..b41cdc0 100644 --- a/src/test/resources/org/javacs/example/LargeFile.java +++ b/src/test/test-project/workspace/src/org/javacs/example/LargeFile.java diff --git a/src/test/resources/org/javacs/example/MissingImport.java b/src/test/test-project/workspace/src/org/javacs/example/MissingImport.java index a697cdc..84233be 100644 --- a/src/test/resources/org/javacs/example/MissingImport.java +++ b/src/test/test-project/workspace/src/org/javacs/example/MissingImport.java @@ -1,4 +1,4 @@ -package org.javacs; +package org.javacs.example; class MissingImport { void test() { diff --git a/src/test/resources/org/javacs/example/MissingMethodBody.java b/src/test/test-project/workspace/src/org/javacs/example/MissingMethodBody.java index 02f395e..02f395e 100644 --- a/src/test/resources/org/javacs/example/MissingMethodBody.java +++ b/src/test/test-project/workspace/src/org/javacs/example/MissingMethodBody.java diff --git a/src/test/resources/org/javacs/example/MissingSemicolon.java b/src/test/test-project/workspace/src/org/javacs/example/MissingSemicolon.java index aa32602..aa32602 100644 --- a/src/test/resources/org/javacs/example/MissingSemicolon.java +++ b/src/test/test-project/workspace/src/org/javacs/example/MissingSemicolon.java diff --git a/src/test/resources/org/javacs/example/NotJava.java.txt b/src/test/test-project/workspace/src/org/javacs/example/NotJava.java.txt index bde7ea4..bde7ea4 100644 --- a/src/test/resources/org/javacs/example/NotJava.java.txt +++ b/src/test/test-project/workspace/src/org/javacs/example/NotJava.java.txt diff --git a/src/test/resources/org/javacs/example/ReferenceConstructor.java b/src/test/test-project/workspace/src/org/javacs/example/ReferenceConstructor.java index 1076674..1076674 100644 --- a/src/test/resources/org/javacs/example/ReferenceConstructor.java +++ b/src/test/test-project/workspace/src/org/javacs/example/ReferenceConstructor.java diff --git a/src/test/resources/org/javacs/example/ReferenceFrom.java b/src/test/test-project/workspace/src/org/javacs/example/ReferenceFrom.java index e437318..e437318 100644 --- a/src/test/resources/org/javacs/example/ReferenceFrom.java +++ b/src/test/test-project/workspace/src/org/javacs/example/ReferenceFrom.java diff --git a/src/test/resources/org/javacs/example/ReferenceTo.java b/src/test/test-project/workspace/src/org/javacs/example/ReferenceTo.java index bf23e95..bf23e95 100644 --- a/src/test/resources/org/javacs/example/ReferenceTo.java +++ b/src/test/test-project/workspace/src/org/javacs/example/ReferenceTo.java diff --git a/src/test/resources/org/javacs/example/SignatureHelp.java b/src/test/test-project/workspace/src/org/javacs/example/SignatureHelp.java index 3e1aa38..6f61525 100644 --- a/src/test/resources/org/javacs/example/SignatureHelp.java +++ b/src/test/test-project/workspace/src/org/javacs/example/SignatureHelp.java @@ -1,4 +1,4 @@ -package org.javacs; +package org.javacs.example; import java.util.concurrent.CompletableFuture; import java.util.ArrayList; diff --git a/src/test/resources/org/javacs/example/SingleLineUndefinedSymbol.java b/src/test/test-project/workspace/src/org/javacs/example/SingleLineUndefinedSymbol.java index 0f99554..0f99554 100644 --- a/src/test/resources/org/javacs/example/SingleLineUndefinedSymbol.java +++ b/src/test/test-project/workspace/src/org/javacs/example/SingleLineUndefinedSymbol.java diff --git a/src/test/resources/org/javacs/example/SymbolUnderCursor.java b/src/test/test-project/workspace/src/org/javacs/example/SymbolUnderCursor.java index 8ec75ec..8ec75ec 100644 --- a/src/test/resources/org/javacs/example/SymbolUnderCursor.java +++ b/src/test/test-project/workspace/src/org/javacs/example/SymbolUnderCursor.java diff --git a/src/test/resources/org/javacs/example/Target.java b/src/test/test-project/workspace/src/org/javacs/example/Target.java index 3aa1dbf..3aa1dbf 100644 --- a/src/test/resources/org/javacs/example/Target.java +++ b/src/test/test-project/workspace/src/org/javacs/example/Target.java diff --git a/src/test/resources/org/javacs/example/UndefinedSymbol.java b/src/test/test-project/workspace/src/org/javacs/example/UndefinedSymbol.java index f1e18f2..f1e18f2 100644 --- a/src/test/resources/org/javacs/example/UndefinedSymbol.java +++ b/src/test/test-project/workspace/src/org/javacs/example/UndefinedSymbol.java diff --git a/src/test/resources/org/javacs/other/OtherPackagePrivate.java b/src/test/test-project/workspace/src/org/javacs/other/OtherPackagePrivate.java index 12fda5c..12fda5c 100644 --- a/src/test/resources/org/javacs/other/OtherPackagePrivate.java +++ b/src/test/test-project/workspace/src/org/javacs/other/OtherPackagePrivate.java diff --git a/src/test/resources/org/javacs/other/OtherPackagePublic.java b/src/test/test-project/workspace/src/org/javacs/other/OtherPackagePublic.java index c159ced..c159ced 100644 --- a/src/test/resources/org/javacs/other/OtherPackagePublic.java +++ b/src/test/test-project/workspace/src/org/javacs/other/OtherPackagePublic.java |