This is an automated email from the ASF dual-hosted git repository.
dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 0f2565c GR-33089: Enrich Java Outline view by more details. (#3263)
0f2565c is described below
commit 0f2565c436deec16d518df47d5161426bc86cfe9
Author: Dusan Balek <[email protected]>
AuthorDate: Mon Oct 25 14:58:59 2021 +0200
GR-33089: Enrich Java Outline view by more details. (#3263)
---
.../netbeans/modules/java/lsp/server/Utils.java | 135 +++++++++++++++++++--
.../lsp/server/protocol/CodeActionsProvider.java | 74 ++---------
.../server/protocol/TextDocumentServiceImpl.java | 109 ++++++++++++-----
.../java/lsp/server/protocol/ServerTest.java | 24 +---
4 files changed, 213 insertions(+), 129 deletions(-)
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java
index ec5af37..46ec105 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java
@@ -19,21 +19,28 @@
package org.netbeans.modules.java.lsp.server;
import com.google.gson.stream.JsonWriter;
+import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.LineMap;
+import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
+import com.sun.source.tree.VariableTree;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Properties;
+import java.util.Iterator;
+import java.util.List;
+import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
import javax.swing.text.StyledDocument;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
@@ -41,11 +48,10 @@ import org.eclipse.lsp4j.SymbolKind;
import org.netbeans.api.editor.document.LineDocument;
import org.netbeans.api.editor.document.LineDocumentUtils;
import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.modules.editor.java.Utilities;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
-import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
-import org.openide.modules.Places;
import org.openide.text.NbDocument;
import org.openide.util.Exceptions;
@@ -95,6 +101,97 @@ public class Utils {
}
}
+ public static String label(CompilationInfo info, Element e, boolean fqn) {
+ switch (e.getKind()) {
+ case PACKAGE:
+ PackageElement pe = (PackageElement) e;
+ return fqn ? pe.getQualifiedName().toString() :
pe.getSimpleName().toString();
+ case CLASS:
+ case INTERFACE:
+ case ENUM:
+ case ANNOTATION_TYPE:
+ TypeElement te = (TypeElement) e;
+ StringBuilder sb = new StringBuilder();
+ sb.append(fqn ? te.getQualifiedName() : te.getSimpleName());
+ List<? extends TypeParameterElement> typeParams =
te.getTypeParameters();
+ if (typeParams != null && !typeParams.isEmpty()) {
+ sb.append("<"); // NOI18N
+ for(Iterator<? extends TypeParameterElement> it =
typeParams.iterator(); it.hasNext();) {
+ TypeParameterElement tp = it.next();
+ sb.append(tp.getSimpleName());
+ List<? extends TypeMirror> bounds = tp.getBounds();
+ if (!bounds.isEmpty()) {
+ if (bounds.size() > 1 ||
!"java.lang.Object".equals(bounds.get(0).toString())) { // NOI18N
+ sb.append(" extends "); // NOI18N
+ for (Iterator<? extends TypeMirror> bIt =
bounds.iterator(); bIt.hasNext();) {
+ sb.append(Utilities.getTypeName(info,
bIt.next(), fqn));
+ if (bIt.hasNext()) {
+ sb.append(" & "); // NOI18N
+ }
+ }
+ }
+ }
+ if (it.hasNext()) {
+ sb.append(", "); // NOI18N
+ }
+ }
+ sb.append(">"); // NOI18N
+ }
+ return sb.toString();
+ case FIELD:
+ case ENUM_CONSTANT:
+ return e.getSimpleName().toString();
+ case CONSTRUCTOR:
+ case METHOD:
+ ExecutableElement ee = (ExecutableElement) e;
+ sb = new StringBuilder();
+ if (ee.getKind() == ElementKind.CONSTRUCTOR) {
+ sb.append(ee.getEnclosingElement().getSimpleName());
+ } else {
+ sb.append(ee.getSimpleName());
+ }
+ sb.append("("); // NOI18N
+ for (Iterator<? extends VariableElement> it =
ee.getParameters().iterator(); it.hasNext();) {
+ VariableElement param = it.next();
+ if (!it.hasNext() && ee.isVarArgs() &&
param.asType().getKind() == TypeKind.ARRAY) {
+ sb.append(Utilities.getTypeName(info, ((ArrayType)
param.asType()).getComponentType(), fqn));
+ sb.append("...");
+ } else {
+ sb.append(Utilities.getTypeName(info, param.asType(),
fqn));
+ }
+ sb.append(" "); // NOI18N
+ sb.append(param.getSimpleName());
+ if (it.hasNext()) {
+ sb.append(", "); // NOI18N
+ }
+ }
+ sb.append(")"); // NOI18N
+ return sb.toString();
+ }
+ return null;
+ }
+
+ public static String detail(CompilationInfo info, Element e, boolean fqn) {
+ switch (e.getKind()) {
+ case FIELD:
+ StringBuilder sb = new StringBuilder();
+ sb.append(": " );
+ sb.append(Utilities.getTypeName(info, e.asType(), fqn));
+ return sb.toString();
+ case METHOD:
+ sb = new StringBuilder();
+ TypeMirror rt = ((ExecutableElement) e).getReturnType();
+ if (rt.getKind() == TypeKind.VOID) {
+ sb.append(": void" );
+ } else {
+ sb.append(": ");
+ sb.append(Utilities.getTypeName(info, rt, fqn));
+ }
+ return sb.toString();
+ }
+ return null;
+ }
+
public static Range treeRange(CompilationInfo info, Tree tree) {
long start =
info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(),
tree);
long end =
info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(),
tree);
@@ -105,6 +202,26 @@ public class Utils {
createPosition(info.getCompilationUnit(), (int) end));
}
+ public static Range selectionRange(CompilationInfo info, Tree tree) {
+ int[] span = null;
+ switch (tree.getKind()) {
+ case CLASS:
+ span = info.getTreeUtilities().findNameSpan((ClassTree) tree);
+ break;
+ case METHOD:
+ span = info.getTreeUtilities().findNameSpan((MethodTree)tree);
+ break;
+ case VARIABLE:
+ span =
info.getTreeUtilities().findNameSpan((VariableTree)tree);
+ break;
+ }
+ if (span == null) {
+ return null;
+ }
+ return new Range(createPosition(info.getCompilationUnit(), (int)
span[0]),
+ createPosition(info.getCompilationUnit(), (int)
span[1]));
+ }
+
public static Position createPosition(CompilationUnitTree cut, int offset)
{
return createPosition(cut.getLineMap(), offset);
}
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java
index 50c8b76..db57677 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/CodeActionsProvider.java
@@ -20,7 +20,6 @@ package org.netbeans.modules.java.lsp.server.protocol;
import com.sun.source.tree.LineMap;
import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -29,11 +28,7 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
@@ -41,7 +36,7 @@ import org.eclipse.lsp4j.Position;
import org.eclipse.xtext.xbase.lib.Pure;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.ElementHandle;
-import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.lsp.server.Utils;
import org.netbeans.modules.java.source.ElementHandleAccessor;
import org.netbeans.modules.parsing.api.ResultIterator;
@@ -99,33 +94,7 @@ public abstract class CodeActionsProvider {
}
protected static String createLabel(CompilationInfo info, TypeElement e,
boolean fqn) {
- StringBuilder sb = new StringBuilder();
- sb.append(fqn ? e.getQualifiedName() : e.getSimpleName());
- List<? extends TypeParameterElement> typeParams =
e.getTypeParameters();
- if (typeParams != null && !typeParams.isEmpty()) {
- sb.append("<"); // NOI18N
- for(Iterator<? extends TypeParameterElement> it =
typeParams.iterator(); it.hasNext();) {
- TypeParameterElement tp = it.next();
- sb.append(tp.getSimpleName());
- List<? extends TypeMirror> bounds = tp.getBounds();
- if (!bounds.isEmpty()) {
- if (bounds.size() > 1 ||
!"java.lang.Object".equals(bounds.get(0).toString())) { // NOI18N
- sb.append(" extends "); // NOI18N
- for (Iterator<? extends TypeMirror> bIt =
bounds.iterator(); bIt.hasNext();) {
- sb.append(Utilities.getTypeName(info, bIt.next(),
fqn));
- if (bIt.hasNext()) {
- sb.append(" & "); // NOI18N
- }
- }
- }
- }
- if (it.hasNext()) {
- sb.append(", "); // NOI18N
- }
- }
- sb.append(">"); // NOI18N
- }
- return sb.toString();
+ return Utils.label(info, e, fqn);
}
protected static String createLabel(CompilationInfo info, VariableElement
e) {
@@ -134,11 +103,9 @@ public abstract class CodeActionsProvider {
protected static String createLabel(CompilationInfo info, VariableElement
e, boolean fqn) {
StringBuilder sb = new StringBuilder();
- sb.append(e.getSimpleName());
- if (e.getKind() != ElementKind.ENUM_CONSTANT) {
- sb.append(" : "); // NOI18N
- sb.append(Utilities.getTypeName(info, e.asType(), fqn));
- }
+ sb.append(Utils.label(info, e, fqn));
+ sb.append(" : "); // NOI18N
+ sb.append(Utils.detail(info, e, fqn));
return sb.toString();
}
@@ -148,34 +115,9 @@ public abstract class CodeActionsProvider {
protected static String createLabel(CompilationInfo info,
ExecutableElement e, boolean fqn) {
StringBuilder sb = new StringBuilder();
- if (e.getKind() == ElementKind.CONSTRUCTOR) {
- sb.append(e.getEnclosingElement().getSimpleName());
- } else {
- sb.append(e.getSimpleName());
- }
- sb.append("("); // NOI18N
- for (Iterator<? extends VariableElement> it =
e.getParameters().iterator(); it.hasNext();) {
- VariableElement param = it.next();
- if (!it.hasNext() && e.isVarArgs() && param.asType().getKind() ==
TypeKind.ARRAY) {
- sb.append(Utilities.getTypeName(info, ((ArrayType)
param.asType()).getComponentType(), fqn));
- sb.append("...");
- } else {
- sb.append(Utilities.getTypeName(info, param.asType(), fqn));
- }
- sb.append(" "); // NOI18N
- sb.append(param.getSimpleName());
- if (it.hasNext()) {
- sb.append(", "); // NOI18N
- }
- }
- sb.append(")"); // NOI18N
- if (e.getKind() != ElementKind.CONSTRUCTOR) {
- TypeMirror rt = e.getReturnType();
- if (rt.getKind() != TypeKind.VOID) {
- sb.append(" : "); // NOI18N
- sb.append(Utilities.getTypeName(info, e.getReturnType(), fqn));
- }
- }
+ sb.append(Utils.label(info, e, fqn));
+ sb.append(" : "); // NOI18N
+ sb.append(Utils.detail(info, e, fqn));
return sb.toString();
}
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
index af201b7..7b75f94 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
@@ -22,8 +22,10 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.LineMap;
import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
@@ -124,6 +126,7 @@ import org.eclipse.lsp4j.ResourceOperation;
import org.eclipse.lsp4j.SignatureHelp;
import org.eclipse.lsp4j.SignatureHelpParams;
import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextEdit;
@@ -745,39 +748,51 @@ public class TextDocumentServiceImpl implements
TextDocumentService, LanguageCli
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>>
documentSymbol(DocumentSymbolParams params) {
- // shortcut: if the projects are not yet initialized, return empty:
- if (server.openedProjects().getNow(null) == null) {
- return CompletableFuture.completedFuture(Collections.emptyList());
- }
- JavaSource js = getJavaSource(params.getTextDocument().getUri());
- if (js == null) {
- return CompletableFuture.completedFuture(Collections.emptyList());
- }
- List<Either<SymbolInformation, DocumentSymbol>> result = new
ArrayList<>();
- try {
- js.runUserActionTask(cc -> {
- cc.toPhase(JavaSource.Phase.RESOLVED);
- for (Element tel : cc.getTopLevelElements()) {
- DocumentSymbol ds = element2DocumentSymbol(cc, tel);
- if (ds != null)
- result.add(Either.forRight(ds));
- }
- }, true);
- } catch (IOException ex) {
- //TODO: include stack trace:
- client.logMessage(new MessageParams(MessageType.Error,
ex.getMessage()));
- }
-
- return CompletableFuture.completedFuture(result);
+ return server.openedProjects().thenCompose(projects -> {
+ JavaSource js = getJavaSource(params.getTextDocument().getUri());
+ if (js == null) {
+ return
CompletableFuture.completedFuture(Collections.emptyList());
+ }
+ List<Either<SymbolInformation, DocumentSymbol>> result = new
ArrayList<>();
+ try {
+ js.runUserActionTask(cc -> {
+ cc.toPhase(JavaSource.Phase.RESOLVED);
+ Trees trees = cc.getTrees();
+ CompilationUnitTree cu = cc.getCompilationUnit();
+ if (cu.getPackage() != null) {
+ TreePath tp = trees.getPath(cu, cu.getPackage());
+ Element el = trees.getElement(tp);
+ if (el != null && el.getKind() == ElementKind.PACKAGE)
{
+ String name = Utils.label(cc, el, true);
+ SymbolKind kind =
Utils.elementKind2SymbolKind(el.getKind());
+ Range range = Utils.treeRange(cc, tp.getLeaf());
+ result.add(Either.forRight(new
DocumentSymbol(name, kind, range, range)));
+ }
+ }
+ for (Element tel : cc.getTopLevelElements()) {
+ DocumentSymbol ds = element2DocumentSymbol(cc, tel);
+ if (ds != null)
+ result.add(Either.forRight(ds));
+ }
+ }, true);
+ } catch (IOException ex) {
+ client.logMessage(new MessageParams(MessageType.Error,
ex.getMessage()));
+ }
+ return CompletableFuture.completedFuture(result);
+ });
}
- private DocumentSymbol element2DocumentSymbol(CompilationInfo info,
Element el) throws BadLocationException {
+ private static DocumentSymbol element2DocumentSymbol(CompilationInfo info,
Element el) {
TreePath path = info.getTrees().getPath(el);
if (path == null)
return null;
+ TreeUtilities tu = info.getTreeUtilities();
+ if (tu.isSynthetic(path))
+ return null;
Range range = Utils.treeRange(info, path.getLeaf());
if (range == null)
return null;
+ Range selection = Utils.selectionRange(info, path.getLeaf());
List<DocumentSymbol> children = new ArrayList<>();
for (Element c : el.getEnclosedElements()) {
DocumentSymbol ds = element2DocumentSymbol(info, c);
@@ -785,16 +800,44 @@ public class TextDocumentServiceImpl implements
TextDocumentService, LanguageCli
children.add(ds);
}
}
-
- String simpleName;
-
- if (el.getKind() == ElementKind.CONSTRUCTOR) {
- simpleName = el.getEnclosingElement().getSimpleName().toString();
- } else {
- simpleName = el.getSimpleName().toString();
+ if (path.getLeaf().getKind() == Kind.METHOD ||
path.getLeaf().getKind() == Kind.VARIABLE) {
+ children.addAll(getAnonymousInnerClasses(info, path));
}
+ String name = Utils.label(info, el, false);
+ String detail = Utils.detail(info, el, false);
+ return new DocumentSymbol(name,
Utils.elementKind2SymbolKind(el.getKind()), range, selection != null ?
selection : range, detail, children);
+ }
- return new DocumentSymbol(simpleName,
Utils.elementKind2SymbolKind(el.getKind()), range, range, null, children);
+ private static List<DocumentSymbol>
getAnonymousInnerClasses(CompilationInfo info, TreePath path) {
+ List<DocumentSymbol> inner = new ArrayList<>();
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void visitNewClass(NewClassTree node, Void p) {
+ if (node.getClassBody() != null) {
+ Range range = Utils.treeRange(info, node);
+ if (range != null) {
+ Range selectionRange = Utils.treeRange(info,
node.getIdentifier());
+ Element e = info.getTrees().getElement(new
TreePath(getCurrentPath(), node.getClassBody()));
+ if (e != null) {
+ Element te = info.getTrees().getElement(new
TreePath(getCurrentPath(), node.getIdentifier()));
+ if (te != null) {
+ String name = "new " + te.getSimpleName() +
"() {...}";
+ List<DocumentSymbol> children = new
ArrayList<>();
+ for (Element c : e.getEnclosedElements()) {
+ DocumentSymbol ds =
element2DocumentSymbol(info, c);
+ if (ds != null) {
+ children.add(ds);
+ }
+ }
+ inner.add(new DocumentSymbol(name,
Utils.elementKind2SymbolKind(e.getKind()), range, selectionRange, null,
children));
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }.scan(path, null);
+ return inner;
}
@Override
diff --git
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
index 4dadd8e..f147945 100644
---
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
+++
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
@@ -756,16 +756,7 @@ public class ServerTest extends NbTestCase {
" line = 7\n" +
" character = 5\n" +
" ]\n" +
- "]:(Constructor:Inner:Range [\n" +
- " start = Position [\n" +
- " line = 4\n" +
- " character = 4\n" +
- " ]\n" +
- " end = Position [\n" +
- " line = 4\n" +
- " character = 4\n" +
- " ]\n" +
- "]:(), Method:innerMethod:Range [\n" +
+ "]:(Method:innerMethod():Range [\n" +
" start = Position [\n" +
" line = 5\n" +
" character = 8\n" +
@@ -774,16 +765,7 @@ public class ServerTest extends NbTestCase {
" line = 6\n" +
" character = 9\n" +
" ]\n" +
- "]:()), Constructor:Test:Range [\n" +
- " start = Position [\n" +
- " line = 0\n" +
- " character = 7\n" +
- " ]\n" +
- " end = Position [\n" +
- " line = 0\n" +
- " character = 7\n" +
- " ]\n" +
- "]:(), Field:field:Range [\n" +
+ "]:()), Field:field:Range [\n" +
" start = Position [\n" +
" line = 1\n" +
" character = 4\n" +
@@ -792,7 +774,7 @@ public class ServerTest extends NbTestCase {
" line = 1\n" +
" character = 22\n" +
" ]\n" +
- "]:(), Method:method:Range [\n" +
+ "]:(), Method:method():Range [\n" +
" start = Position [\n" +
" line = 2\n" +
" character = 4\n" +
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists