summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Fraser <george@fivetran.com>2018-12-24 11:13:50 -0800
committerGeorge Fraser <george@fivetran.com>2018-12-24 11:13:50 -0800
commitffff502f7b01c84fdddb9c706962c622f9d57b5f (patch)
treeb4421f6f01c2a0e956701541d197f84bf80dab2e
parent5f7975f84ab86350f9e1c0e4cf297a50a2ae0261 (diff)
downloadjava-language-server-ffff502f7b01c84fdddb9c706962c622f9d57b5f.zip
WIP
-rw-r--r--TODOS.md2
-rw-r--r--src/main/java/org/javacs/Index.java4
-rw-r--r--src/main/java/org/javacs/JavaCompilerService.java182
-rw-r--r--src/main/java/org/javacs/JavaTextDocumentService.java16
-rw-r--r--src/main/java/org/javacs/Ptr.java99
-rw-r--r--src/main/java/org/javacs/Ref.java19
-rw-r--r--src/main/java/org/javacs/SourceRange.java27
7 files changed, 163 insertions, 186 deletions
diff --git a/TODOS.md b/TODOS.md
index d5ec7e9..9788a72 100644
--- a/TODOS.md
+++ b/TODOS.md
@@ -8,6 +8,8 @@
- Files created in session don't autocomplete
- EnumMap default methods don't autocomplete
- Crashes when you create the first file in a new maven project
+- Deleted files remain in compiler, even when you restart (via classpath?)
+- Always shows last javadoc
## Autocomplete
- Annotation fields
diff --git a/src/main/java/org/javacs/Index.java b/src/main/java/org/javacs/Index.java
index d3a8861..2e7aaab 100644
--- a/src/main/java/org/javacs/Index.java
+++ b/src/main/java/org/javacs/Index.java
@@ -6,11 +6,11 @@ import java.util.List;
public class Index {
public static final Index EMPTY = new Index(List.of(), Instant.EPOCH);
- public final List<Ref> refs;
+ public final List<Ptr> refs;
// TODO modified time can rewind when you switch branches, need to track modified and look for exact match
public final Instant created;
- public Index(List<Ref> refs, Instant created) {
+ public Index(List<Ptr> refs, Instant created) {
this.refs = refs;
this.created = created;
}
diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java
index f499a98..0bc4d36 100644
--- a/src/main/java/org/javacs/JavaCompilerService.java
+++ b/src/main/java/org/javacs/JavaCompilerService.java
@@ -22,6 +22,8 @@ import java.util.stream.Stream;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.tools.*;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
// TODO eliminate uses of URI in favor of Path
public class JavaCompilerService {
@@ -1113,7 +1115,6 @@ public class JavaCompilerService {
return Optional.ofNullable(result);
}
- /** */
private boolean containsTopLevelDeclaration(Path file, String simpleClassName) {
var find = Pattern.compile("\\b(class|interface|enum) +" + simpleClassName + "\\b");
try (var lines = Files.newBufferedReader(file)) {
@@ -1160,38 +1161,8 @@ public class JavaCompilerService {
return Optional.empty();
}
- private static Optional<Ref> ref(TreePath from, JavacTask task) {
- var trees = Trees.instance(task);
- var pos = trees.getSourcePositions();
- var root = from.getCompilationUnit();
- var lines = root.getLineMap();
- var to = trees.getElement(from);
- // Skip elements we can't find
- if (to == null) {
- LOG.warning(String.format("No element for %s", from.getLeaf()));
- return Optional.empty();
- }
- // Skip non-methods
- if (!(to instanceof ExecutableElement || to instanceof TypeElement || to instanceof VariableElement)) {
- return Optional.empty();
- }
- // TODO skip anything not on source path
- var toEl = idEl(to);
- long start = pos.getStartPosition(root, from.getLeaf()), end = pos.getEndPosition(root, from.getLeaf());
- if (start == -1) {
- LOG.warning(String.format("No position for %s", from.getLeaf()));
- return Optional.empty();
- }
- if (end == -1) end = start + from.getLeaf().toString().length();
- int startLine = (int) lines.getLineNumber(start), startCol = (int) lines.getColumnNumber(start);
- int endLine = (int) lines.getLineNumber(end), endCol = (int) lines.getColumnNumber(end);
- var fromFile = root.getSourceFile().toUri();
- var ref = new Ref(fromFile, startLine, startCol, endLine, endCol, toEl);
- return Optional.of(ref);
- }
-
/** Compile `file` and locate `e` in it */
- private Optional<Ref> findIn(Element e, Path file, String contents) {
+ private Optional<TreePath> findIn(Element e, Path file, String contents) {
var task = singleFileTask(file.toUri(), contents);
CompilationUnitTree tree;
try {
@@ -1202,7 +1173,7 @@ public class JavaCompilerService {
}
var trees = Trees.instance(task);
class Find extends TreePathScanner<Void, Void> {
- Optional<Ref> found = Optional.empty();
+ TreePath found;
boolean toStringEquals(Object left, Object right) {
return Objects.equals(Objects.toString(left, ""), Objects.toString(right, ""));
@@ -1218,7 +1189,7 @@ public class JavaCompilerService {
void check() {
if (sameSymbol()) {
- found = ref(getCurrentPath(), cache.task);
+ found = getCurrentPath();
}
}
@@ -1239,16 +1210,13 @@ public class JavaCompilerService {
check();
return super.visitVariable(node, aVoid);
}
-
- Optional<Ref> run() {
- scan(tree, null);
- return found;
- }
}
- return new Find().run();
+ var finder = new Find();
+ finder.scan(tree, null);
+ return Optional.ofNullable(finder.found);
}
- public Optional<Ref> definition(URI file, int line, int character, Function<URI, String> contents) {
+ public Optional<TreePath> definition(URI file, int line, int character, Function<URI, String> contents) {
recompile(file, contents.apply(file), line, character);
var trees = Trees.instance(cache.task);
@@ -1397,63 +1365,6 @@ public class JavaCompilerService {
return result;
}
- private static String reverseAndJoin(List<CharSequence> parts, String sep) {
- var join = new StringJoiner(sep);
- for (var i = parts.size() - 1; i >= 0; i--) {
- join.add(parts.get(i));
- }
- return join.toString();
- }
-
- private static String idPath(TreePath path) {
- var packageName = path.getCompilationUnit().getPackageName();
- var rev = new ArrayList<CharSequence>();
- while (path != null) {
- var part = path.getLeaf();
- if (part instanceof ClassTree) {
- var cls = (ClassTree) part;
- rev.add(cls.getSimpleName());
- } else if (part instanceof MethodTree) {
- var method = (MethodTree) part;
- // TODO overloads
- rev.add(method.getName());
- } else if (part instanceof VariableTree) {
- var variable = (VariableTree) part;
- rev.add(variable.getName());
- }
- path = path.getParentPath();
- }
- if (packageName != null) rev.add(packageName.toString());
- var name = reverseAndJoin(rev, ".");
- if (!name.matches("(\\w+\\.)*(\\w+|<init>)")) LOG.warning(String.format("`%s` doesn't look like a name", name));
- return name;
- }
-
- private static String idEl(Element e) {
- var rev = new ArrayList<CharSequence>();
- while (e != null) {
- if (e instanceof PackageElement) {
- var pkg = (PackageElement) e;
- if (!pkg.isUnnamed())
- rev.add(pkg.getQualifiedName());
- } else if (e instanceof TypeElement) {
- var type = (TypeElement) e;
- rev.add(type.getSimpleName());
- } else if (e instanceof ExecutableElement) {
- var method = (ExecutableElement) e;
- // TODO overloads
- rev.add(method.getSimpleName());
- } else if (e instanceof VariableElement) {
- var field = (VariableElement) e;
- rev.add(field.getSimpleName());
- }
- e = e.getEnclosingElement();
- }
- var name = reverseAndJoin(rev, ".");
- if (!name.matches("(\\w+\\.)*(\\w+|<init>)")) LOG.warning(String.format("`%s` doesn't look like a name", name));
- return name;
- }
-
/**
* Represents a batch compilation of many files. The batch context is different that the incremental context, so
* methods in this class should not access `cache`.
@@ -1478,14 +1389,38 @@ public class JavaCompilerService {
&& toStringEquals(to, from);
}
- List<Ref> referencesToElement(CompilationUnitTree root, Element to) {
+ Optional<TreePath> ref(TreePath from) {
var trees = Trees.instance(task);
- var results = new ArrayList<Ref>();
+ var pos = trees.getSourcePositions();
+ var root = from.getCompilationUnit();
+ var lines = root.getLineMap();
+ var to = trees.getElement(from);
+ // Skip elements we can't find
+ if (to == null) {
+ LOG.warning(String.format("No element for `%s`", from.getLeaf()));
+ return Optional.empty();
+ }
+ // Skip non-methods
+ if (!(to instanceof ExecutableElement || to instanceof TypeElement || to instanceof VariableElement)) {
+ return Optional.empty();
+ }
+ // TODO skip anything not on source path
+ var result = trees.getPath(to);
+ if (result == null) {
+ LOG.warning(String.format("Element `%s` has no TreePath", to));
+ return Optional.empty();
+ }
+ return Optional.of(result);
+ }
+
+ List<TreePath> referencesToElement(CompilationUnitTree root, Element to) {
+ var trees = Trees.instance(task);
+ var results = new ArrayList<TreePath>();
class FindReferencesElement extends TreePathScanner<Void, Void> {
void check(TreePath from) {
var found = trees.getElement(from);
if (sameSymbol(found, to)) {
- ref(from, task).ifPresent(results::add);
+ ref(from).ifPresent(results::add);
}
}
@@ -1511,11 +1446,11 @@ public class JavaCompilerService {
return results;
}
- List<Ref> index(CompilationUnitTree root) {
- var refs = new ArrayList<Ref>();
+ List<Ptr> index(CompilationUnitTree root) {
+ var refs = new ArrayList<Ptr>();
class IndexFile extends TreePathScanner<Void, Void> {
void check(TreePath from) {
- ref(from, task).ifPresent(refs::add);
+ ref(from).map(Ptr::new).ifPresent(refs::add);
}
@Override
@@ -1585,7 +1520,7 @@ public class JavaCompilerService {
return result;
}
- public List<Ref> references(URI file, String contents, int line, int character, ReportReferencesProgress progress) {
+ public List<TreePath> references(URI file, String contents, int line, int character, ReportReferencesProgress progress) {
recompile(file, contents, -1, -1);
var trees = Trees.instance(cache.task);
@@ -1604,7 +1539,7 @@ public class JavaCompilerService {
progress.checkPotentialReferences(0, possible.size());
// TODO optimize by pruning method bodies that don't contain potential references
var batch = compileBatch(possible);
- var result = new ArrayList<Ref>();
+ var result = new ArrayList<TreePath>();
var nChecked = 0;
for (var f : batch.roots) {
result.addAll(batch.referencesToElement(f, toEl));
@@ -1616,12 +1551,12 @@ public class JavaCompilerService {
private Map<Path, Index> index = new HashMap<>();
- private boolean referencesAnyClass(Ref r, String packageName, List<String> classNames) {
- if (!r.toEl.startsWith(packageName))
+ private boolean referencesAnyClass(Ptr p, String packageName, List<String> classNames) {
+ if (!p.inPackage(packageName))
return false;
for (var c : classNames) {
var qualifiedName = packageName.isEmpty() ? c : packageName + "." + c;
- if (r.toEl.startsWith(qualifiedName))
+ if (p.inClass(qualifiedName))
return true;
}
return false;
@@ -1664,7 +1599,7 @@ public class JavaCompilerService {
}
}
- public List<Ref> referencesFile(URI file, String contents, ReportReferencesProgress progress) {
+ public Map<Ptr, Integer> countReferences(URI file, String contents, ReportReferencesProgress progress) {
LOG.info(String.format("Finding all references to %s", Paths.get(file).getFileName()));
recompile(file, contents, -1, -1);
@@ -1674,17 +1609,17 @@ public class JavaCompilerService {
var possible = potentialReferencesToClasses(toPackage, toClasses, progress);
if (possible.isEmpty()) {
LOG.info("No potential references to " + file);
- return List.of();
+ return Map.of();
}
// Reindex only files that are out-of-date
updateIndex(possible, progress);
// Assemble results
- var result = new ArrayList<Ref>();
+ var result = new HashMap<Ptr, Integer>();
for (var p : possible) {
var i = index.get(p);
for (var r : i.refs) {
- if (referencesAnyClass(r, toPackage, toClasses))
- result.add(r);
+ var count = result.getOrDefault(r, 0);
+ result.put(r, count + 1);
}
}
return result;
@@ -1779,11 +1714,11 @@ public class JavaCompilerService {
return new FixImports(tree, trees.getSourcePositions(), qualifiedNames);
}
- public Set<String> testMethods(URI file, String contents) {
+ public List<Ptr> testMethods(URI file, String contents) {
LOG.info(String.format("Finding test methods in %s", Paths.get(file).getFileName()));
var root = Parser.parse(new StringFileObject(contents, file));
- var found = new LinkedHashSet<String>();
+ var found = new ArrayList<Ptr>();
class FindTestMethods extends TreePathScanner<Void, Void> {
boolean isTestMethod(MethodTree node) {
for (var ann : node.getModifiers().getAnnotations()) {
@@ -1811,13 +1746,13 @@ public class JavaCompilerService {
@Override
public Void visitClass​(ClassTree node, Void __) {
- if (isTestClass(node)) found.add(idPath(getCurrentPath()));
+ if (isTestClass(node)) found.add(new Ptr(getCurrentPath()));
return super.visitClass(node, null);
}
@Override
public Void visitMethod(MethodTree node, Void __) {
- if (isTestMethod(node)) found.add(idPath(getCurrentPath()));
+ if (isTestMethod(node)) found.add(new Ptr(getCurrentPath()));
return null;
}
}
@@ -1826,7 +1761,8 @@ public class JavaCompilerService {
return found;
}
- public Optional<SourceRange> position(URI file, String contents, String id) {
+ // TODO this doesn't belong here
+ public Optional<Range> position(URI file, String contents, Ptr id) {
var task = Parser.parseTask(new StringFileObject(contents, file));
CompilationUnitTree root;
try {
@@ -1861,7 +1797,7 @@ public class JavaCompilerService {
public Void visitClass(ClassTree c, Void nothing) {
// Check if id references this class
var path = getCurrentPath();
- if (idPath(path).equals(id)) {
+ if (new Ptr(path).equals(id)) {
found = path;
className = c.getSimpleName().toString();
memberName = null;
@@ -1869,7 +1805,7 @@ public class JavaCompilerService {
// Check if id references each method of this class
for (var m : c.getMembers()) {
var child = new TreePath(path, m);
- if (idPath(child).equals(id)) {
+ if (new Ptr(child).equals(id)) {
found = child;
className = c.getSimpleName().toString();
memberName = memberName(m);
@@ -1896,9 +1832,7 @@ public class JavaCompilerService {
var startCol = (int) lines.getColumnNumber(start);
var endLine = (int) lines.getLineNumber(end);
var endCol = (int) lines.getColumnNumber(end);
- var c = Optional.of(finder.className);
- var m = Optional.ofNullable(finder.memberName);
- var range = new SourceRange(file, startLine, startCol, endLine, endCol, c, m);
+ var range = new Range(new Position(startLine, startCol), new Position(endLine, endCol));
return Optional.of(range);
}
start++;
diff --git a/src/main/java/org/javacs/JavaTextDocumentService.java b/src/main/java/org/javacs/JavaTextDocumentService.java
index 88325d1..839d638 100644
--- a/src/main/java/org/javacs/JavaTextDocumentService.java
+++ b/src/main/java/org/javacs/JavaTextDocumentService.java
@@ -5,6 +5,7 @@ import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.ParamTree;
import com.sun.source.tree.MethodTree;
+import com.sun.source.util.TreePath;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
@@ -441,19 +442,6 @@ class JavaTextDocumentService implements TextDocumentService {
return null;
}
- private Range asRange(SourceRange pos) {
- var start = new Position(pos.startLine - 1, pos.startCol - 1);
- var end = new Position(pos.endLine - 1, pos.endCol - 1);
- return new Range(start, end);
- }
-
- private Location asLocation(Ref ref) {
- var start = new Position(ref.startLine - 1, ref.startCol - 1);
- var end = new Position(ref.endLine - 1, ref.endCol - 1);
- var range = new Range(start, end);
- return new Location(ref.fromFile.toString(), range);
- }
-
private List<CodeLens> testMethods(URI uri) {
var content = contents(uri).content;
var tests = server.compiler.testMethods(uri, content);
@@ -489,7 +477,7 @@ class JavaTextDocumentService implements TextDocumentService {
try (var progress = new ReportProgress(startMessage, scanMessage, checkMessage)) {
// Organize by method
var refs = server.compiler.referencesFile(uri, content, progress);
- var byId = new HashMap<String, List<Ref>>();
+ var byId = new HashMap<String, List<TreePath>>();
for (var r : refs) {
byId.computeIfAbsent(r.toEl, __ -> new ArrayList<>()).add(r);
}
diff --git a/src/main/java/org/javacs/Ptr.java b/src/main/java/org/javacs/Ptr.java
new file mode 100644
index 0000000..71520a3
--- /dev/null
+++ b/src/main/java/org/javacs/Ptr.java
@@ -0,0 +1,99 @@
+package org.javacs;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringJoiner;
+import java.util.logging.Logger;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/** Ptr is a serialized TreePath, suitable for storing in Index and remember across compiler invocations */
+public class Ptr {
+ private final String path;
+
+ public Ptr(TreePath path) {
+ var packageName = path.getCompilationUnit().getPackageName();
+ var rev = new ArrayList<CharSequence>();
+ while (path != null) {
+ var part = path.getLeaf();
+ if (part instanceof ClassTree) {
+ var cls = (ClassTree) part;
+ rev.add(cls.getSimpleName());
+ } else if (part instanceof MethodTree) {
+ var method = (MethodTree) part;
+ // TODO overloads
+ rev.add(method.getName());
+ } else if (part instanceof VariableTree) {
+ var variable = (VariableTree) part;
+ rev.add(variable.getName());
+ }
+ path = path.getParentPath();
+ }
+ if (packageName != null) rev.add(packageName.toString());
+ var name = reverseAndJoin(rev, ".");
+ if (!name.matches("(\\w+\\.)*(\\w+|<init>)")) LOG.warning(String.format("`%s` doesn't look like a name", name));
+ this.path = name;
+ }
+
+ public Ptr(Element e) {
+ var rev = new ArrayList<CharSequence>();
+ while (e != null) {
+ if (e instanceof PackageElement) {
+ var pkg = (PackageElement) e;
+ if (!pkg.isUnnamed()) rev.add(pkg.getQualifiedName());
+ } else if (e instanceof TypeElement) {
+ var type = (TypeElement) e;
+ rev.add(type.getSimpleName());
+ } else if (e instanceof ExecutableElement) {
+ var method = (ExecutableElement) e;
+ // TODO overloads
+ rev.add(method.getSimpleName());
+ } else if (e instanceof VariableElement) {
+ var field = (VariableElement) e;
+ rev.add(field.getSimpleName());
+ }
+ e = e.getEnclosingElement();
+ }
+ var name = reverseAndJoin(rev, ".");
+ if (!name.matches("(\\w+\\.)*(\\w+|<init>)")) LOG.warning(String.format("`%s` doesn't look like a name", name));
+ this.path = name;
+ }
+
+ private static String reverseAndJoin(List<CharSequence> parts, String sep) {
+ var join = new StringJoiner(sep);
+ for (var i = parts.size() - 1; i >= 0; i--) {
+ join.add(parts.get(i));
+ }
+ return join.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Ptr)) return false;
+ var that = (Ptr) other;
+ return this.path.equals(that.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(path);
+ }
+
+ public boolean inPackage(String packageName) {
+ return path.startsWith(packageName);
+ }
+
+ public boolean inClass(String qualifiedName) {
+ return path.startsWith(qualifiedName);
+ }
+
+ private static final Logger LOG = Logger.getLogger("main");
+}
diff --git a/src/main/java/org/javacs/Ref.java b/src/main/java/org/javacs/Ref.java
deleted file mode 100644
index a33a8ec..0000000
--- a/src/main/java/org/javacs/Ref.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.javacs;
-
-import java.net.URI;
-
-/** Reference from file:line(start,end) to a class or method */
-public class Ref {
- public final URI fromFile;
- public final int startLine, startCol, endLine, endCol;
- public final String toEl;
-
- public Ref(URI fromFile, int startLine, int startCol, int endLine, int endCol, String toEl) {
- this.fromFile = fromFile;
- this.startLine = startLine;
- this.startCol = startCol;
- this.endLine = endLine;
- this.endCol = endCol;
- this.toEl = toEl;
- }
-}
diff --git a/src/main/java/org/javacs/SourceRange.java b/src/main/java/org/javacs/SourceRange.java
deleted file mode 100644
index 65c1aee..0000000
--- a/src/main/java/org/javacs/SourceRange.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.javacs;
-
-import java.net.URI;
-import java.util.Optional;
-
-public class SourceRange {
- public final URI file;
- public final int startLine, startCol, endLine, endCol;
- public final Optional<String> className, memberName;
-
- public SourceRange(
- URI file,
- int startLine,
- int startCol,
- int endLine,
- int endCol,
- Optional<String> className,
- Optional<String> memberName) {
- this.file = file;
- this.startLine = startLine;
- this.startCol = startCol;
- this.endLine = endLine;
- this.endCol = endCol;
- this.className = className;
- this.memberName = memberName;
- }
-}