diff options
author | George Fraser <george@fivetran.com> | 2018-12-28 17:24:27 -0800 |
---|---|---|
committer | George Fraser <george@fivetran.com> | 2018-12-28 17:24:27 -0800 |
commit | c8cca030cd5313618e6d8b3071482419a92452bd (patch) | |
tree | 0a87e1e02bfafcebe9b9ba6fdf643dcadf0d381f /src | |
parent | c527250e77f7242156a965e4286325dc1938ca88 (diff) | |
download | java-language-server-c8cca030cd5313618e6d8b3071482419a92452bd.zip |
Fork ClassPath from guava
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/org/javacs/Classes.java | 2 | ||||
-rw-r--r-- | src/main/java/org/javacs/CompileBatch.java | 15 | ||||
-rw-r--r-- | src/main/java/org/javacs/InferSourcePath.java | 3 | ||||
-rw-r--r-- | src/main/java/org/javacs/JavaCompilerService.java | 15 | ||||
-rw-r--r-- | src/main/java/org/javacs/guava/ClassPath.java | 570 | ||||
-rw-r--r-- | src/main/java/org/javacs/guava/README.md | 1 | ||||
-rw-r--r-- | src/test/java/org/javacs/ClassesTest.java | 2 | ||||
-rw-r--r-- | src/test/java/org/javacs/CodeLensTest.java | 3 |
8 files changed, 592 insertions, 19 deletions
diff --git a/src/main/java/org/javacs/Classes.java b/src/main/java/org/javacs/Classes.java index a8a85c1..6249070 100644 --- a/src/main/java/org/javacs/Classes.java +++ b/src/main/java/org/javacs/Classes.java @@ -1,6 +1,5 @@ package org.javacs; -import com.google.common.reflect.ClassPath; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; @@ -18,6 +17,7 @@ import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.javacs.guava.ClassPath; class Classes { diff --git a/src/main/java/org/javacs/CompileBatch.java b/src/main/java/org/javacs/CompileBatch.java index 9acd669..5a6fa95 100644 --- a/src/main/java/org/javacs/CompileBatch.java +++ b/src/main/java/org/javacs/CompileBatch.java @@ -191,7 +191,11 @@ public class CompileBatch { var refs = new ArrayList<Ptr>(); class IndexFile extends TreePathScanner<Void, Void> { void check(TreePath from) { - ref(from).map(Ptr::new).ifPresent(refs::add); + var r = ref(from); + if (r.isPresent()) { + var ptr = new Ptr(r.get()); + refs.add(ptr); + } } @Override @@ -219,10 +223,11 @@ public class CompileBatch { } } new IndexFile().scan(root, null); - if (refs.size() > 0) - LOG.info( - String.format( - "Found %d refs in %s", refs.size(), Paths.get(root.getSourceFile().toUri()).getFileName())); + if (refs.size() > 0) { + var n = refs.size(); + var file = Parser.fileName(root.getSourceFile().toUri()); + LOG.info(String.format("Found %d refs in %s", n, file)); + } return refs; } diff --git a/src/main/java/org/javacs/InferSourcePath.java b/src/main/java/org/javacs/InferSourcePath.java index b6f30f2..f1fd1d2 100644 --- a/src/main/java/org/javacs/InferSourcePath.java +++ b/src/main/java/org/javacs/InferSourcePath.java @@ -46,7 +46,8 @@ class InferSourcePath { var packagePath = packageName.replace('.', File.separatorChar); var dir = java.getParent(); if (packagePath.isEmpty()) { - return Optional.of(dir); + LOG.warning("Ignoring file with missing package declaration " + java); + return Optional.empty(); } else if (!dir.endsWith(packagePath)) { LOG.warning("Java source file " + java + " is not in " + packagePath); return Optional.empty(); diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java index cc73cb2..f59957d 100644 --- a/src/main/java/org/javacs/JavaCompilerService.java +++ b/src/main/java/org/javacs/JavaCompilerService.java @@ -265,16 +265,15 @@ public class JavaCompilerService { return Optional.empty(); } - private List<URI> potentialReferencesToClasses(String toPackage, List<String> toClasses) { - // Enumerate all files on source path - var allFiles = allJavaSources(); + // TODO should probably cache this + private Collection<URI> potentialReferencesToClasses(String toPackage, List<String> toClasses) { // Filter for files that import toPackage.toClass - // TODO should probably cache this - var result = new ArrayList<URI>(); - int nScanned = 0; + var result = new LinkedHashSet<URI>(); for (var dir : sourcePath) { for (var file : javaSourcesInDir(dir)) { - if (importsAnyClass(toPackage, toClasses, file)) result.add(file.toUri()); + if (importsAnyClass(toPackage, toClasses, file)) { + result.add(file.toUri()); + } } } return result; @@ -296,7 +295,7 @@ public class JavaCompilerService { private Map<URI, Index> index = new HashMap<>(); - private void updateIndex(List<URI> possible) { + private void updateIndex(Collection<URI> possible) { LOG.info(String.format("Check %d files for modifications compared to index...", possible.size())); var outOfDate = new ArrayList<URI>(); for (var p : possible) { diff --git a/src/main/java/org/javacs/guava/ClassPath.java b/src/main/java/org/javacs/guava/ClassPath.java new file mode 100644 index 0000000..7a6ddac --- /dev/null +++ b/src/main/java/org/javacs/guava/ClassPath.java @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.javacs.guava; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; +import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; +import static java.util.logging.Level.WARNING; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.MultimapBuilder; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; +import com.google.common.io.ByteSource; +import com.google.common.io.CharSource; +import com.google.common.io.Resources; +import com.google.common.reflect.Reflection; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.Charset; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +/** + * Scans the source of a {@link ClassLoader} and finds all loadable classes and resources. + * + * <p><b>Warning:</b> Current limitations: + * + * <ul> + * <li>Looks only for files and JARs in URLs available from {@link URLClassLoader} instances or the {@linkplain + * ClassLoader#getSystemClassLoader() system class loader}. + * <li>Only understands {@code file:} URLs. + * </ul> + * + * <p>In the case of directory classloaders, symlinks are supported but cycles are not traversed. This guarantees + * discovery of each <em>unique</em> loadable resource. However, not all possible aliases for resources on cyclic paths + * will be listed. + * + * @author Ben Yu + * @since 14.0 + */ +public final class ClassPath { + private static final Logger logger = Logger.getLogger(ClassPath.class.getName()); + + private static final Predicate<ClassInfo> IS_TOP_LEVEL = + new Predicate<ClassInfo>() { + @Override + public boolean apply(ClassInfo info) { + return info.className.indexOf('$') == -1; + } + }; + + /** Separator for the Class-Path manifest attribute value in jar files. */ + private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR = Splitter.on(" ").omitEmptyStrings(); + + private static final String CLASS_FILE_NAME_EXTENSION = ".class"; + + private final ImmutableSet<ResourceInfo> resources; + + private ClassPath(ImmutableSet<ResourceInfo> resources) { + this.resources = resources; + } + + /** + * Returns a {@code ClassPath} representing all classes and resources loadable from {@code classloader} and its + * ancestor class loaders. + * + * <p><b>Warning:</b> {@code ClassPath} can find classes and resources only from: + * + * <ul> + * <li>{@link URLClassLoader} instances' {@code file:} URLs + * <li>the {@linkplain ClassLoader#getSystemClassLoader() system class loader}. To search the system class loader + * even when it is not a {@link URLClassLoader} (as in Java 9), {@code ClassPath} searches the files from the + * {@code java.class.path} system property. + * </ul> + * + * @throws IOException if the attempt to read class path resources (jar files or directories) failed. + */ + public static ClassPath from(ClassLoader classloader) throws IOException { + DefaultScanner scanner = new DefaultScanner(); + scanner.scan(classloader); + return new ClassPath(scanner.getResources()); + } + + /** + * Returns all resources loadable from the current class path, including the class files of all loadable classes but + * excluding the "META-INF/MANIFEST.MF" file. + */ + public ImmutableSet<ResourceInfo> getResources() { + return resources; + } + + /** + * Returns all classes loadable from the current class path. + * + * @since 16.0 + */ + public ImmutableSet<ClassInfo> getAllClasses() { + return FluentIterable.from(resources).filter(ClassInfo.class).toSet(); + } + + /** Returns all top level classes loadable from the current class path. */ + public ImmutableSet<ClassInfo> getTopLevelClasses() { + return FluentIterable.from(resources).filter(ClassInfo.class).filter(IS_TOP_LEVEL).toSet(); + } + + /** Returns all top level classes whose package name is {@code packageName}. */ + public ImmutableSet<ClassInfo> getTopLevelClasses(String packageName) { + checkNotNull(packageName); + ImmutableSet.Builder<ClassInfo> builder = ImmutableSet.builder(); + for (ClassInfo classInfo : getTopLevelClasses()) { + if (classInfo.getPackageName().equals(packageName)) { + builder.add(classInfo); + } + } + return builder.build(); + } + + /** + * Returns all top level classes whose package name is {@code packageName} or starts with {@code packageName} + * followed by a '.'. + */ + public ImmutableSet<ClassInfo> getTopLevelClassesRecursive(String packageName) { + checkNotNull(packageName); + String packagePrefix = packageName + '.'; + ImmutableSet.Builder<ClassInfo> builder = ImmutableSet.builder(); + for (ClassInfo classInfo : getTopLevelClasses()) { + if (classInfo.getName().startsWith(packagePrefix)) { + builder.add(classInfo); + } + } + return builder.build(); + } + + /** + * Represents a class path resource that can be either a class file or any other resource file loadable from the + * class path. + * + * @since 14.0 + */ + public static class ResourceInfo { + private final String resourceName; + + final ClassLoader loader; + + static ResourceInfo of(String resourceName, ClassLoader loader) { + if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION)) { + return new ClassInfo(resourceName, loader); + } else { + return new ResourceInfo(resourceName, loader); + } + } + + ResourceInfo(String resourceName, ClassLoader loader) { + this.resourceName = checkNotNull(resourceName); + this.loader = checkNotNull(loader); + } + + /** + * Returns the url identifying the resource. + * + * <p>See {@link ClassLoader#getResource} + * + * @throws NoSuchElementException if the resource cannot be loaded through the class loader, despite physically + * existing in the class path. + */ + public final URL url() { + URL url = loader.getResource(resourceName); + if (url == null) { + throw new NoSuchElementException(resourceName); + } + return url; + } + + /** + * Returns a {@link ByteSource} view of the resource from which its bytes can be read. + * + * @throws NoSuchElementException if the resource cannot be loaded through the class loader, despite physically + * existing in the class path. + * @since 20.0 + */ + public final ByteSource asByteSource() { + return Resources.asByteSource(url()); + } + + /** + * Returns a {@link CharSource} view of the resource from which its bytes can be read as characters decoded with + * the given {@code charset}. + * + * @throws NoSuchElementException if the resource cannot be loaded through the class loader, despite physically + * existing in the class path. + * @since 20.0 + */ + public final CharSource asCharSource(Charset charset) { + return Resources.asCharSource(url(), charset); + } + + /** Returns the fully qualified name of the resource. Such as "com/mycomp/foo/bar.txt". */ + public final String getResourceName() { + return resourceName; + } + + @Override + public int hashCode() { + return resourceName.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ResourceInfo) { + ResourceInfo that = (ResourceInfo) obj; + return resourceName.equals(that.resourceName) && loader == that.loader; + } + return false; + } + + // Do not change this arbitrarily. We rely on it for sorting ResourceInfo. + @Override + public String toString() { + return resourceName; + } + } + + /** + * Represents a class that can be loaded through {@link #load}. + * + * @since 14.0 + */ + public static final class ClassInfo extends ResourceInfo { + private final String className; + + ClassInfo(String resourceName, ClassLoader loader) { + super(resourceName, loader); + this.className = getClassName(resourceName); + } + + /** + * Returns the package name of the class, without attempting to load the class. + * + * <p>Behaves identically to {@link Package#getName()} but does not require the class (or package) to be loaded. + */ + public String getPackageName() { + return Reflection.getPackageName(className); + } + + /** + * Returns the simple name of the underlying class as given in the source code. + * + * <p>Behaves identically to {@link Class#getSimpleName()} but does not require the class to be loaded. + */ + public String getSimpleName() { + int lastDollarSign = className.lastIndexOf('$'); + if (lastDollarSign != -1) { + String innerClassName = className.substring(lastDollarSign + 1); + // local and anonymous classes are prefixed with number (1,2,3...), anonymous classes are + // entirely numeric whereas local classes have the user supplied name as a suffix + return CharMatcher.digit().trimLeadingFrom(innerClassName); + } + String packageName = getPackageName(); + if (packageName.isEmpty()) { + return className; + } + + // Since this is a top level class, its simple name is always the part after package name. + return className.substring(packageName.length() + 1); + } + + /** + * Returns the fully qualified name of the class. + * + * <p>Behaves identically to {@link Class#getName()} but does not require the class to be loaded. + */ + public String getName() { + return className; + } + + /** + * Loads (but doesn't link or initialize) the class. + * + * @throws LinkageError when there were errors in loading classes that this class depends on. For example, + * {@link NoClassDefFoundError}. + */ + public Class<?> load() { + try { + return loader.loadClass(className); + } catch (ClassNotFoundException e) { + // Shouldn't happen, since the class name is read from the class path. + throw new IllegalStateException(e); + } + } + + @Override + public String toString() { + return className; + } + } + + /** + * Abstract class that scans through the class path represented by a {@link ClassLoader} and calls {@link + * #scanDirectory} and {@link #scanJarFile} for directories and jar files on the class path respectively. + */ + abstract static class Scanner { + + // We only scan each file once independent of the classloader that resource might be associated + // with. + private final Set<File> scannedUris = Sets.newHashSet(); + + public final void scan(ClassLoader classloader) throws IOException { + for (Entry<File, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) { + scan(entry.getKey(), entry.getValue()); + } + } + + final void scan(File file, ClassLoader classloader) throws IOException { + if (scannedUris.add(file.getCanonicalFile())) { + scanFrom(file, classloader); + } + } + + /** Called when a directory is scanned for resource files. */ + protected abstract void scanDirectory(ClassLoader loader, File directory) throws IOException; + + /** Called when a jar file is scanned for resource entries. */ + protected abstract void scanJarFile(ClassLoader loader, JarFile file) throws IOException; + + private void scanFrom(File file, ClassLoader classloader) throws IOException { + try { + if (!file.exists()) { + return; + } + } catch (SecurityException e) { + logger.warning("Cannot access " + file + ": " + e); + // TODO(emcmanus): consider whether to log other failure cases too. + return; + } + if (file.isDirectory()) { + scanDirectory(classloader, file); + } else { + scanJar(file, classloader); + } + } + + private void scanJar(File file, ClassLoader classloader) throws IOException { + JarFile jarFile; + try { + jarFile = new JarFile(file); + } catch (IOException e) { + // Not a jar file + return; + } + try { + for (File path : getClassPathFromManifest(file, jarFile.getManifest())) { + scan(path, classloader); + } + scanJarFile(classloader, jarFile); + } finally { + try { + jarFile.close(); + } catch (IOException ignored) { + } + } + } + + /** + * Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according to <a + * href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR File + * Specification</a>. If {@code manifest} is null, it means the jar file has no manifest, and an empty set will + * be returned. + */ + static ImmutableSet<File> getClassPathFromManifest(File jarFile, Manifest manifest) { + if (manifest == null) { + return ImmutableSet.of(); + } + ImmutableSet.Builder<File> builder = ImmutableSet.builder(); + String classpathAttribute = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString()); + if (classpathAttribute != null) { + for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) { + URL url; + try { + url = getClassPathEntry(jarFile, path); + } catch (MalformedURLException e) { + // Ignore bad entry + logger.warning("Invalid Class-Path entry: " + path); + continue; + } + if (url.getProtocol().equals("file")) { + builder.add(toFile(url)); + } + } + } + return builder.build(); + } + + static ImmutableMap<File, ClassLoader> getClassPathEntries(ClassLoader classloader) { + LinkedHashMap<File, ClassLoader> entries = Maps.newLinkedHashMap(); + // Search parent first, since it's the order ClassLoader#loadClass() uses. + ClassLoader parent = classloader.getParent(); + if (parent != null) { + entries.putAll(getClassPathEntries(parent)); + } + for (URL url : getClassLoaderUrls(classloader)) { + if (url.getProtocol().equals("file")) { + File file = toFile(url); + if (!entries.containsKey(file)) { + entries.put(file, classloader); + } + } + } + return ImmutableMap.copyOf(entries); + } + + private static ImmutableList<URL> getClassLoaderUrls(ClassLoader classloader) { + if (classloader instanceof URLClassLoader) { + return ImmutableList.copyOf(((URLClassLoader) classloader).getURLs()); + } + if (classloader.equals(ClassLoader.getSystemClassLoader())) { + return parseJavaClassPath(); + } + return ImmutableList.of(); + } + + /** + * Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain System#getProperty + * system property}. + */ + // TODO(b/65488446): Make this a public API. + static ImmutableList<URL> parseJavaClassPath() { + ImmutableList.Builder<URL> urls = ImmutableList.builder(); + for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) { + try { + try { + urls.add(new File(entry).toURI().toURL()); + } catch (SecurityException e) { // File.toURI checks to see if the file is a directory + urls.add(new URL("file", null, new File(entry).getAbsolutePath())); + } + } catch (MalformedURLException e) { + logger.log(WARNING, "malformed classpath entry: " + entry, e); + } + } + return urls.build(); + } + + /** + * Returns the absolute uri of the Class-Path entry value as specified in <a + * href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">JAR File + * Specification</a>. Even though the specification only talks about relative urls, absolute urls are actually + * supported too (for example, in Maven surefire plugin). + */ + static URL getClassPathEntry(File jarFile, String path) throws MalformedURLException { + return new URL(jarFile.toURI().toURL(), path); + } + } + + static final class DefaultScanner extends Scanner { + private final SetMultimap<ClassLoader, String> resources = + MultimapBuilder.hashKeys().linkedHashSetValues().build(); + + ImmutableSet<ResourceInfo> getResources() { + ImmutableSet.Builder<ResourceInfo> builder = ImmutableSet.builder(); + for (Entry<ClassLoader, String> entry : resources.entries()) { + builder.add(ResourceInfo.of(entry.getValue(), entry.getKey())); + } + return builder.build(); + } + + @Override + protected void scanJarFile(ClassLoader classloader, JarFile file) { + Enumeration<JarEntry> entries = file.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) { + continue; + } + resources.get(classloader).add(entry.getName()); + } + } + + @Override + protected void scanDirectory(ClassLoader classloader, File directory) throws IOException { + Set<File> currentPath = new HashSet<>(); + currentPath.add(directory.getCanonicalFile()); + scanDirectory(directory, classloader, "", currentPath); + } + + /** + * Recursively scan the given directory, adding resources for each file encountered. Symlinks which have already + * been traversed in the current tree path will be skipped to eliminate cycles; otherwise symlinks are + * traversed. + * + * @param directory the root of the directory to scan + * @param classloader the classloader that includes resources found in {@code directory} + * @param packagePrefix resource path prefix inside {@code classloader} for any files found under {@code + * directory} + * @param currentPath canonical files already visited in the current directory tree path, for cycle elimination + */ + private void scanDirectory(File directory, ClassLoader classloader, String packagePrefix, Set<File> currentPath) + throws IOException { + File[] files = directory.listFiles(); + if (files == null) { + logger.warning("Cannot read directory " + directory); + // IO error, just skip the directory + return; + } + for (File f : files) { + String name = f.getName(); + if (f.isDirectory()) { + File deref = f.getCanonicalFile(); + if (currentPath.add(deref)) { + scanDirectory(deref, classloader, packagePrefix + name + "/", currentPath); + currentPath.remove(deref); + } + } else { + String resourceName = packagePrefix + name; + if (!resourceName.equals(JarFile.MANIFEST_NAME)) { + resources.get(classloader).add(resourceName); + } + } + } + } + } + + static String getClassName(String filename) { + int classNameEnd = filename.length() - CLASS_FILE_NAME_EXTENSION.length(); + return filename.substring(0, classNameEnd).replace('/', '.'); + } + + // TODO(benyu): Try java.nio.file.Paths#get() when Guava drops JDK 6 support. + + static File toFile(URL url) { + checkArgument(url.getProtocol().equals("file")); + try { + return new File(url.toURI()); // Accepts escaped characters like %20. + } catch (URISyntaxException e) { // URL.toURI() doesn't escape chars. + return new File(url.getPath()); // Accepts non-escaped chars like space. + } + } +} diff --git a/src/main/java/org/javacs/guava/README.md b/src/main/java/org/javacs/guava/README.md new file mode 100644 index 0000000..1145953 --- /dev/null +++ b/src/main/java/org/javacs/guava/README.md @@ -0,0 +1 @@ +Forked from Guava so we can eliminate the dependency and make jlink work.
\ No newline at end of file diff --git a/src/test/java/org/javacs/ClassesTest.java b/src/test/java/org/javacs/ClassesTest.java index 035c22a..95212fe 100644 --- a/src/test/java/org/javacs/ClassesTest.java +++ b/src/test/java/org/javacs/ClassesTest.java @@ -3,11 +3,11 @@ package org.javacs; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import com.google.common.reflect.ClassPath; import java.net.URI; import java.nio.file.FileSystems; import java.nio.file.Files; import java.util.Collections; +import org.javacs.guava.ClassPath; import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/org/javacs/CodeLensTest.java b/src/test/java/org/javacs/CodeLensTest.java index 29935c5..b77afc6 100644 --- a/src/test/java/org/javacs/CodeLensTest.java +++ b/src/test/java/org/javacs/CodeLensTest.java @@ -3,7 +3,6 @@ package org.javacs; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import com.google.gson.Gson; import java.util.ArrayList; import java.util.List; import org.javacs.lsp.*; @@ -20,8 +19,6 @@ public class CodeLensTest { var resolved = new ArrayList<CodeLens>(); for (var lens : lenses) { if (lens.command == null) { - var gson = new Gson(); - var data = lens.data; lens = server.resolveCodeLens(lens); } resolved.add(lens); |