summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Fraser <george@fivetran.com>2018-12-31 16:05:09 -0800
committerGeorge Fraser <george@fivetran.com>2018-12-31 16:05:09 -0800
commit25c67bb8f300f8af13b7312933dec157e67e678c (patch)
tree4eb2076b4da8571fe59338c7185b6aa63963e467
parent8f63f14f980f44af2c5747747320703819ebc402 (diff)
downloadjava-language-server-25c67bb8f300f8af13b7312933dec157e67e678c.zip
Add @Override
-rw-r--r--TODOS.md5
-rw-r--r--src/main/java/org/javacs/CompileFile.java56
-rw-r--r--src/main/java/org/javacs/JavaCompilerService.java3
-rw-r--r--src/main/java/org/javacs/JavaLanguageServer.java28
-rw-r--r--src/test/java/org/javacs/FormattingTest.java28
-rw-r--r--src/test/test-project/workspace/src/org/javacs/example/AddOverride.java7
6 files changed, 123 insertions, 4 deletions
diff --git a/TODOS.md b/TODOS.md
index 9a620b3..f66ed95 100644
--- a/TODOS.md
+++ b/TODOS.md
@@ -29,7 +29,4 @@
# Coloring
- new Foo< shouldn't make everything green
- void f() shouldn't mess up next line as you type it
-- { on next line breaks coloring
-
-# Formatter
-- Automatically add @Override annotations \ No newline at end of file
+- { on next line breaks coloring \ No newline at end of file
diff --git a/src/main/java/org/javacs/CompileFile.java b/src/main/java/org/javacs/CompileFile.java
index c26337d..9ed833c 100644
--- a/src/main/java/org/javacs/CompileFile.java
+++ b/src/main/java/org/javacs/CompileFile.java
@@ -122,6 +122,62 @@ public class CompileFile {
return ParseFile.range(task, contents, path);
}
+ private List<Element> overrides(ExecutableElement method) {
+ var elements = task.getElements();
+ var types = task.getTypes();
+ var results = new ArrayList<Element>();
+ var enclosingClass = (TypeElement) method.getEnclosingElement();
+ var enclosingType = enclosingClass.asType();
+ for (var superClass : types.directSupertypes(enclosingType)) {
+ var e = (TypeElement) types.asElement(superClass);
+ for (var other : e.getEnclosedElements()) {
+ if (!(other instanceof ExecutableElement)) continue;
+ if (elements.overrides(method, (ExecutableElement) other, enclosingClass)) {
+ results.add(other);
+ }
+ }
+ }
+ return results;
+ }
+
+ private boolean hasOverrideAnnotation(ExecutableElement method) {
+ for (var ann : method.getAnnotationMirrors()) {
+ var type = ann.getAnnotationType();
+ var el = type.asElement();
+ var name = el.toString();
+ if (name.equals("java.lang.Override")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Find methods that override a method from a superclass but don't have an @Override annotation. */
+ public List<TreePath> needsOverrideAnnotation() {
+ LOG.info(String.format("Looking for methods that need an @Override annotation in %s ...", file.getPath()));
+
+ var results = new ArrayList<TreePath>();
+ class FindMissingOverride extends TreePathScanner<Void, Void> {
+ @Override
+ public Void visitMethod(MethodTree t, Void __) {
+ var method = (ExecutableElement) trees.getElement(getCurrentPath());
+ var supers = overrides(method);
+ if (!supers.isEmpty() && !hasOverrideAnnotation(method)) {
+ var overridesMethod = supers.get(0);
+ var overridesClass = overridesMethod.getEnclosingElement();
+ LOG.info(
+ String.format(
+ "...`%s` has no @Override annotation but overrides `%s.%s`",
+ method, overridesClass, overridesMethod));
+ results.add(getCurrentPath());
+ }
+ return super.visitMethod(t, null);
+ }
+ }
+ new FindMissingOverride().scan(root, null);
+ return results;
+ }
+
/**
* Figure out what imports this file should have. Star-imports like `import java.util.*` are converted to individual
* class imports. Missing imports are inferred by looking at imports in other source files.
diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java
index e771538..2c6aa83 100644
--- a/src/main/java/org/javacs/JavaCompilerService.java
+++ b/src/main/java/org/javacs/JavaCompilerService.java
@@ -367,6 +367,9 @@ public class JavaCompilerService {
index.putAll(counts);
}
+ // TODO when computing the index, store the signature of the target.
+ // If the target has changed, reindex that file.
+ // This can take advantage of the fact that code lenses are resolved one-at-a-time!
public Map<Ptr, Integer> countReferences(URI file, String contents, ReportProgress progress) {
var root = Parser.parse(new StringFileObject(contents, file));
// List all files that import file
diff --git a/src/main/java/org/javacs/JavaLanguageServer.java b/src/main/java/org/javacs/JavaLanguageServer.java
index 30b869a..7bfa3a5 100644
--- a/src/main/java/org/javacs/JavaLanguageServer.java
+++ b/src/main/java/org/javacs/JavaLanguageServer.java
@@ -1016,6 +1016,14 @@ class JavaLanguageServer extends LanguageServer {
@Override
public List<TextEdit> formatting(DocumentFormattingParams params) {
updateHoverCache(params.textDocument.uri, contents(params.textDocument.uri).content);
+
+ var edits = new ArrayList<TextEdit>();
+ edits.addAll(fixImports());
+ edits.addAll(addOverrides());
+ return edits;
+ }
+
+ private List<TextEdit> fixImports() {
// TODO if imports already match fixed-imports, return empty list
// TODO preserve comments and other details of existing imports
var imports = hoverCache.fixImports();
@@ -1063,6 +1071,26 @@ class JavaLanguageServer extends LanguageServer {
return edits;
}
+ private List<TextEdit> addOverrides() {
+ var edits = new ArrayList<TextEdit>();
+ var methods = hoverCache.needsOverrideAnnotation();
+ var pos = hoverCache.sourcePositions();
+ var lines = hoverCache.root.getLineMap();
+ for (var t : methods) {
+ var methodStart = pos.getStartPosition(t.getCompilationUnit(), t.getLeaf());
+ var insertLine = lines.getLineNumber(methodStart);
+ var indent = methodStart - lines.getPosition(insertLine, 0);
+ var insertText = new StringBuilder();
+ for (var i = 0; i < indent; i++) insertText.append(' ');
+ insertText.append("@Override");
+ insertText.append('\n');
+ var insertPosition = new Position((int) insertLine, 0);
+ var insert = new TextEdit(new Range(insertPosition, insertPosition), insertText.toString());
+ edits.add(insert);
+ }
+ return edits;
+ }
+
@Override
public List<FoldingRange> foldingRange(FoldingRangeParams params) {
updateCachedParse(params.textDocument.uri);
diff --git a/src/test/java/org/javacs/FormattingTest.java b/src/test/java/org/javacs/FormattingTest.java
new file mode 100644
index 0000000..97974a4
--- /dev/null
+++ b/src/test/java/org/javacs/FormattingTest.java
@@ -0,0 +1,28 @@
+package org.javacs;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.util.List;
+import org.javacs.lsp.DocumentFormattingParams;
+import org.javacs.lsp.TextDocumentIdentifier;
+import org.javacs.lsp.TextEdit;
+import org.junit.Test;
+
+public class FormattingTest {
+
+ private static final JavaLanguageServer server = LanguageServerFixture.getJavaLanguageServer();
+
+ private List<TextEdit> formatting(String file) {
+ var uri = FindResource.uri(file);
+ var params = new DocumentFormattingParams();
+ params.textDocument = new TextDocumentIdentifier(uri);
+ return server.formatting(params);
+ }
+
+ @Test
+ public void addOverride() {
+ var edits = formatting("/org/javacs/example/AddOverride.java");
+ assertThat(edits, not(empty()));
+ }
+}
diff --git a/src/test/test-project/workspace/src/org/javacs/example/AddOverride.java b/src/test/test-project/workspace/src/org/javacs/example/AddOverride.java
new file mode 100644
index 0000000..d2fc1d2
--- /dev/null
+++ b/src/test/test-project/workspace/src/org/javacs/example/AddOverride.java
@@ -0,0 +1,7 @@
+package org.javacs.example;
+
+class AddOverride implements Runnable {
+ public void run() {
+ // Nothing to do
+ }
+} \ No newline at end of file