From 8b69428bf5e092c22c87f08c852f10c20035af5b Mon Sep 17 00:00:00 2001 From: Robert Brown Date: Thu, 19 Jan 2023 21:25:41 -0500 Subject: [PATCH] Allow setting JDK path via a java.home JSON setting. Update the documentation. --- README.md | 42 ++++++++++++++----- src/main/java/org/javacs/Docs.java | 22 ++++++---- .../java/org/javacs/JavaCompilerService.java | 4 +- .../java/org/javacs/JavaLanguageServer.java | 30 ++++++++----- src/test/java/org/javacs/BenchmarkPruner.java | 8 ++-- src/test/java/org/javacs/FindSrcZipTest.java | 12 +++--- .../org/javacs/JavaCompilerServiceTest.java | 9 +++- 7 files changed, 87 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index c19b97417..7847cf1f2 100644 --- a/README.md +++ b/README.md @@ -128,26 +128,37 @@ Note: This tool is not compatible with [vim-lsp](https://github.com/prabirshrest ## Usage -The language server will provide autocomplete and other features using: -* .java files anywhere in your workspace +The language server provides autocomplete and other features using: +* Java source files anywhere in your workspace * Java platform classes -* External dependencies specified using `pom.xml`, Bazel, or [settings](#Settings) +* External dependencies specified using `pom.xml`, Bazel, or via explicit [settings](#Settings) ## Settings -If the language server doesn't detect your external dependencies automatically, you can specify them using [.vscode/settings.json](https://code.visualstudio.com/docs/getstarted/settings) +Generally, the language server infers the location of the JDK and external +dependency jar files. If this process does not work correctly, you can specify +them explicitly +using [.vscode/settings.json](https://code.visualstudio.com/docs/getstarted/settings). + +### Location of the JDK + +The location of the JDK is determined by reading the JAVA_HOME environment +variable. If JAVA_HOME is unset, then the language server searches operating +system specific locations for a JDK. The JDK location can be explicitly set +with: ```json { - "java.externalDependencies": [ - "junit:junit:jar:4.12:test", // Maven format - "junit:junit:4.12" // Gradle-style format is also allowed - ] + "java.home": "/file/path/of/the/jdk" } ``` -If all else fails, you can specify the Java class path and the locations of -source jars manually: +### External dependency jar files + +By default the language server infers the Java classpath and finds source code +jars for external dependencies by running Maven or Bazel. The inference +process can be diabled by explicitly specifying classpath. Both paths may be +set explicitly with: ```json { @@ -160,6 +171,17 @@ source jars manually: } ``` +External dependencies can also be specified in Maven or Gradle format with: + +```json +{ + "java.externalDependencies": [ + "junit:junit:jar:4.12:test", // Maven format + "junit:junit:4.12" // Gradle-style format is also allowed + ] +} +``` + You can generate a list of external dependencies using your build tool: * Maven: `mvn dependency:list` * Gradle: `gradle dependencies` diff --git a/src/main/java/org/javacs/Docs.java b/src/main/java/org/javacs/Docs.java index a2c21cfba..33be1cd43 100644 --- a/src/main/java/org/javacs/Docs.java +++ b/src/main/java/org/javacs/Docs.java @@ -14,8 +14,8 @@ public class Docs { /** File manager with source-path + platform sources, which we will use to look up individual source files */ final SourceFileManager fileManager = new SourceFileManager(); - Docs(Set docPath) { - var srcZipPath = srcZip(); + Docs(Set docPath, Path javaHome) { + var srcZipPath = srcZip(javaHome); // Path to source .jars + src.zip var sourcePath = new ArrayList(docPath); if (srcZipPath != NOT_FOUND) { @@ -34,9 +34,9 @@ public class Docs { static final Path NOT_FOUND = Paths.get(""); private static Path cacheSrcZip; - private static Path srcZip() { + private static Path srcZip(Path javaHome) { if (cacheSrcZip == null) { - cacheSrcZip = findSrcZip(); + cacheSrcZip = findSrcZip(javaHome); } if (cacheSrcZip == NOT_FOUND) { return NOT_FOUND; @@ -49,15 +49,23 @@ private static Path srcZip() { } } - static Path findSrcZip() { - var javaHome = JavaHomeHelper.javaHome(); + static Path findSrcZip(Path javaHome) { + if (javaHome == NOT_FOUND) { + javaHome = JavaHomeHelper.javaHome(); + } + + if (javaHome == NOT_FOUND) { + LOG.warning("Couldn't find Java home."); + return NOT_FOUND; + } + String[] locations = { "lib/src.zip", "src.zip", "libexec/openjdk.jdk/Contents/Home/lib/src.zip" }; for (var rel : locations) { var abs = javaHome.resolve(rel); if (Files.exists(abs)) { - LOG.info("Found " + abs); + LOG.info("Found src.zip " + abs); return abs; } } diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java index 48564f97e..a6eecef01 100644 --- a/src/main/java/org/javacs/JavaCompilerService.java +++ b/src/main/java/org/javacs/JavaCompilerService.java @@ -21,7 +21,7 @@ class JavaCompilerService implements CompilerProvider { // TODO intercept files that aren't in the batch and erase method bodies so compilation is faster final SourceFileManager fileManager; - JavaCompilerService(Set classPath, Set docPath, Set addExports) { + JavaCompilerService(Set classPath, Set docPath, Set addExports, Path javaHome) { System.err.println("Class path:"); for (var p : classPath) { System.err.println(" " + p); @@ -34,7 +34,7 @@ class JavaCompilerService implements CompilerProvider { this.classPath = Collections.unmodifiableSet(classPath); this.docPath = Collections.unmodifiableSet(docPath); this.addExports = Collections.unmodifiableSet(addExports); - this.docs = new Docs(docPath); + this.docs = new Docs(docPath, javaHome); this.classPathClasses = ScanClassPath.classPathTopLevelClasses(classPath); this.fileManager = new SourceFileManager(); } diff --git a/src/main/java/org/javacs/JavaLanguageServer.java b/src/main/java/org/javacs/JavaLanguageServer.java index a27bac478..ec85e520c 100644 --- a/src/main/java/org/javacs/JavaLanguageServer.java +++ b/src/main/java/org/javacs/JavaLanguageServer.java @@ -93,24 +93,25 @@ private JavaCompilerService createCompiler() { var externalDependencies = externalDependencies(); var classPath = classPath(); var addExports = addExports(); + var javaHome = javaHome(); + // If classpath is specified by the user, don't infer anything if (!classPath.isEmpty()) { javaEndProgress(); - return new JavaCompilerService(classPath, docPath(), addExports); + return new JavaCompilerService(classPath, docPath(), addExports, javaHome); } - // Otherwise, combine inference with user-specified external dependencies - else { - var infer = new InferConfig(workspaceRoot, externalDependencies); - javaReportProgress(new JavaReportProgressParams("Inferring class path")); - classPath = infer.classPath(); + // Otherwise, combine inference with user-specified external dependencies. + var infer = new InferConfig(workspaceRoot, externalDependencies); - javaReportProgress(new JavaReportProgressParams("Inferring doc path")); - var docPath = infer.buildDocPath(); + javaReportProgress(new JavaReportProgressParams("Inferring class path")); + classPath = infer.classPath(); - javaEndProgress(); - return new JavaCompilerService(classPath, docPath, addExports); - } + javaReportProgress(new JavaReportProgressParams("Inferring doc path")); + var docPath = infer.buildDocPath(); + + javaEndProgress(); + return new JavaCompilerService(classPath, docPath, addExports, javaHome); } private Set externalDependencies() { @@ -153,6 +154,13 @@ private Set addExports() { return strings; } + private Path javaHome() { + if (settings.has("home")) { + return Paths.get(settings.get("home").getAsString()); + } + return Docs.NOT_FOUND; + } + @Override public InitializeResult initialize(InitializeParams params) { this.workspaceRoot = Paths.get(params.rootUri); diff --git a/src/test/java/org/javacs/BenchmarkPruner.java b/src/test/java/org/javacs/BenchmarkPruner.java index 0cf0015a2..f903f93a6 100644 --- a/src/test/java/org/javacs/BenchmarkPruner.java +++ b/src/test/java/org/javacs/BenchmarkPruner.java @@ -1,5 +1,8 @@ package org.javacs; +import org.javacs.completion.PruneMethodBodies; +import org.openjdk.jmh.annotations.*; + import java.nio.file.Paths; import java.time.Instant; import java.util.Collections; @@ -7,8 +10,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; -import org.javacs.completion.PruneMethodBodies; -import org.openjdk.jmh.annotations.*; @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @@ -38,7 +39,8 @@ private static JavaCompilerService createCompiler() { var workspaceRoot = Paths.get(".").normalize().toAbsolutePath(); FileStore.setWorkspaceRoots(Set.of(workspaceRoot)); var classPath = new InferConfig(workspaceRoot).classPath(); - return new JavaCompilerService(classPath, Collections.emptySet(), Collections.emptySet()); + return new JavaCompilerService( + classPath, Collections.emptySet(), Collections.emptySet(), Docs.NOT_FOUND); } } diff --git a/src/test/java/org/javacs/FindSrcZipTest.java b/src/test/java/org/javacs/FindSrcZipTest.java index 1b434abb7..c5c5af03d 100644 --- a/src/test/java/org/javacs/FindSrcZipTest.java +++ b/src/test/java/org/javacs/FindSrcZipTest.java @@ -1,19 +1,21 @@ package org.javacs; -import static org.hamcrest.Matchers.*; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertTrue; +import org.junit.Test; + import java.nio.file.Files; import java.nio.file.Path; -import org.junit.Test; public class FindSrcZipTest { @Test public void testFindSrcZip() { - // This test is not run in the CI pipeline, but it can be run locally to check if the src.zip file is found correctly. - Path srcZip = Docs.findSrcZip(); + // This test is not run in the CI pipeline, but it can be run locally to check if the + // src.zip file is found correctly. + Path srcZip = Docs.findSrcZip(Docs.NOT_FOUND); assertThat(srcZip, not(equalTo(Docs.NOT_FOUND))); assertTrue(Files.exists(srcZip)); } -} \ No newline at end of file +} diff --git a/src/test/java/org/javacs/JavaCompilerServiceTest.java b/src/test/java/org/javacs/JavaCompilerServiceTest.java index 8b6cfbd98..846081aa0 100644 --- a/src/test/java/org/javacs/JavaCompilerServiceTest.java +++ b/src/test/java/org/javacs/JavaCompilerServiceTest.java @@ -3,9 +3,10 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import org.junit.*; + import java.nio.file.*; import java.util.*; -import org.junit.*; public class JavaCompilerServiceTest { static { @@ -13,7 +14,11 @@ public class JavaCompilerServiceTest { } private JavaCompilerService compiler = - new JavaCompilerService(Collections.emptySet(), Collections.emptySet(), Collections.emptySet()); + new JavaCompilerService( + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet(), + Docs.NOT_FOUND); static Path simpleProjectSrc() { return Paths.get("src/test/examples/simple-project").normalize();