This is an automated email from the ASF dual-hosted git repository.
jlahoda 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 aa742a6 Improving code completion appearance for both LSP client and
Java LSP server.
aa742a6 is described below
commit aa742a673a21c13de008f5f30621a936997e478f
Author: Jan Lahoda <[email protected]>
AuthorDate: Mon Sep 16 07:17:33 2019 +0200
Improving code completion appearance for both LSP client and Java LSP
server.
---
.../client/bindings/CompletionProviderImpl.java | 121 ++++++++++++++-------
.../lsp/server/text/TextDocumentServiceImpl.java | 36 +++---
.../modules/java/lsp/server/ServerTest.java | 4 +-
3 files changed, 99 insertions(+), 62 deletions(-)
diff --git
a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
index 75a017f..8c873b4 100644
---
a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
+++
b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
@@ -37,8 +37,11 @@ import javax.swing.text.StyledDocument;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.CompletionList;
+import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.CompletionParams;
+import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.ParameterInformation;
+import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.SignatureHelp;
import org.eclipse.lsp4j.SignatureInformation;
import org.eclipse.lsp4j.TextDocumentIdentifier;
@@ -62,6 +65,7 @@ import org.openide.filesystems.FileObject;
import org.openide.text.NbDocument;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
+import org.openide.xml.XMLUtil;
/**
*
@@ -162,20 +166,13 @@ public class CompletionProviderImpl implements
CompletionProvider {
for (CompletionItem i : items) {
String insert = i.getInsertText() != null ?
i.getInsertText() : i.getLabel();
String leftLabel = encode(i.getLabel());
- String rightLabel = encode(""); //TODO: anything we
could show there?
- String sortText = i.getSortText() != null ?
i.getSortText() : i.getLabel();
- String header = "<html>" + "<b>" + (i.getDetail() !=
null ? i.getDetail() : i.getLabel()) + "</b>";
- String documentation;
- if (i.getDocumentation() != null) {
- header += "<br><br>";
- if (i.getDocumentation().isLeft()) {
- documentation = header +
i.getDocumentation().getLeft();
- } else {
- documentation = header +
i.getDocumentation().getRight().getValue(); //TODO: convert markup!
- }
+ String rightLabel;
+ if (i.getDetail() != null) {
+ rightLabel = encode(i.getDetail());
} else {
- documentation = header;
+ rightLabel = null;
}
+ String sortText = i.getSortText() != null ?
i.getSortText() : i.getLabel();
CompletionItemKind kind = i.getKind();
Icon ic = Icons.getCompletionIcon(kind);
ImageIcon icon = new
ImageIcon(ImageUtilities.icon2Image(ic));
@@ -226,36 +223,66 @@ public class CompletionProviderImpl implements
CompletionProvider {
@Override
public CompletionTask createDocumentationTask() {
- return new CompletionTask() {
+ return new AsyncCompletionTask(new
AsyncCompletionQuery() {
@Override
- public void query(CompletionResultSet
resultSet) {
- resultSet.setDocumentation(new
CompletionDocumentation() {
- @Override
- public String getText() {
- return documentation;
- }
- @Override
- public URL getURL() {
- return null;
- }
- @Override
- public CompletionDocumentation
resolveLink(String link) {
- return null;
+ protected void query(CompletionResultSet
resultSet, Document doc, int caretOffset) {
+ CompletionItem resolved;
+ if ((i.getDetail() == null ||
i.getDocumentation() == null) && hasCompletionResolve(server)) {
+ CompletionItem temp;
+ try {
+ temp =
server.getTextDocumentService().resolveCompletionItem(i).get();
+ } catch (InterruptedException |
ExecutionException ex) {
+ Exceptions.printStackTrace(ex);
+ temp = i;
}
- @Override
- public Action
getGotoSourceAction() {
- return null;
- }
- });
+ resolved = temp;
+ } else {
+ resolved = i;
+ }
+ if (resolved.getDocumentation() !=
null || resolved.getDetail() != null) {
+ resultSet.setDocumentation(new
CompletionDocumentation() {
+ @Override
+ public String getText() {
+ StringBuilder
documentation = new StringBuilder();
+
documentation.append("<html>\n");
+ if (resolved.getDetail()
!= null) {
+
documentation.append("<b>").append(escape(resolved.getDetail())).append("</b>");
+
documentation.append("\n<p>");
+ }
+ if
(resolved.getDocumentation() != null) {
+ MarkupContent content;
+ if
(resolved.getDocumentation().isLeft()) {
+ content = new
MarkupContent();
+
content.setKind("plaintext");
+
content.setValue(resolved.getDocumentation().getLeft());
+ } else {
+ content =
resolved.getDocumentation().getRight();
+ }
+ switch
(content.getKind()) {
+ case "markdown":
+ default:
+ case "plaintext":
documentation.append("<pre>\n").append(content.getValue()).append("\n</pre>");
break;
+ }
+ }
+ return
documentation.toString();
+ }
+ @Override
+ public URL getURL() {
+ return null;
+ }
+ @Override
+ public CompletionDocumentation
resolveLink(String link) {
+ return null;
+ }
+ @Override
+ public Action
getGotoSourceAction() {
+ return null;
+ }
+ });
+ }
resultSet.finish();
}
-
- @Override
- public void refresh(CompletionResultSet
resultSet) {}
-
- @Override
- public void cancel() {}
- };
+ });
}
@Override
@@ -295,6 +322,24 @@ public class CompletionProviderImpl implements
CompletionProvider {
}, component);
}
+ private boolean hasCompletionResolve(LSPBindings server) {
+ ServerCapabilities capabilities =
server.getInitResult().getCapabilities();
+ if (capabilities == null) return false;
+ CompletionOptions completionProvider =
capabilities.getCompletionProvider();
+ if (completionProvider == null) return false;
+ Boolean resolveProvider = completionProvider.getResolveProvider();
+ return resolveProvider != null && resolveProvider;
+ }
+
+ private static String escape(String s) {
+ if (s != null) {
+ try {
+ return XMLUtil.toAttributeValue(s);
+ } catch (Exception ex) {}
+ }
+ return s;
+ }
+
private String encode(String str) {
return str.replace("&", "&")
.replace("<", "<");
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java
index 3c46336..3f3ebe1 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/text/TextDocumentServiceImpl.java
@@ -29,7 +29,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
-import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
@@ -40,8 +39,6 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.BiFunction;
-import java.util.function.Function;
import java.util.prefs.Preferences;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
@@ -109,9 +106,6 @@ import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.ModificationResult;
import org.netbeans.api.java.source.support.ReferencesCount;
-import org.netbeans.api.java.source.ui.ElementOpen;
-import org.netbeans.api.lexer.TokenHierarchy;
-import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.modules.editor.java.GoToSupport;
import org.netbeans.modules.editor.java.GoToSupport.Context;
import org.netbeans.modules.editor.java.GoToSupport.GoToTarget;
@@ -136,16 +130,12 @@ import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.Fix;
import org.netbeans.spi.editor.hints.LazyFixList;
import org.netbeans.spi.java.hints.JavaFix;
-import org.netbeans.spi.lexer.MutableTextInput;
import org.openide.cookies.EditorCookie;
-import org.openide.cookies.LineCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.modules.Places;
-import org.openide.text.Line;
import org.openide.text.NbDocument;
-import org.openide.text.PositionRef;
import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;
@@ -167,7 +157,8 @@ public class TextDocumentServiceImpl implements
TextDocumentService, LanguageCli
@Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>>
completion(CompletionParams params) {
try {
- FileObject file = fromUri(params.getTextDocument().getUri());
+ String uri = params.getTextDocument().getUri();
+ FileObject file = fromUri(uri);
EditorCookie ec = file.getLookup().lookup(EditorCookie.class);
Document doc = ec.openDocument();
int caret = getOffset(doc, params.getPosition());
@@ -248,28 +239,29 @@ public class TextDocumentServiceImpl implements
TextDocumentService, LanguageCli
@Override
public CompletionItem createExecutableItem(CompilationInfo info,
ExecutableElement elem, ExecutableType type, int substitutionOffset,
ReferencesCount referencesCount, boolean isInherited, boolean isDeprecated,
boolean inImport, boolean addSemicolon, boolean smartType, int
assignToVarOffset, boolean memberRef) {
- CompletionItem item = new
CompletionItem(elem.getSimpleName().toString());
- item.setKind(elementKind2CompletionItemKind(elem.getKind()));
Iterator<? extends VariableElement> it =
elem.getParameters().iterator();
Iterator<? extends TypeMirror> tIt =
type.getParameterTypes().iterator();
- StringBuilder details = new StringBuilder();
+ StringBuilder label = new StringBuilder();
String sep = "";
- details.append("(");
+ label.append(elem.getSimpleName().toString());
+ label.append("(");
while(it.hasNext() && tIt.hasNext()) {
TypeMirror tm = tIt.next();
if (tm == null) {
break;
}
- details.append(sep);
- details.append(Utilities.getTypeName(info, tm, false,
elem.isVarArgs() && !tIt.hasNext()).toString());
- details.append(' ');
- details.append(it.next().getSimpleName().toString());
+ label.append(sep);
+ label.append(Utilities.getTypeName(info, tm, false,
elem.isVarArgs() && !tIt.hasNext()).toString());
+ label.append(' ');
+ label.append(it.next().getSimpleName().toString());
sep = ", ";
}
- details.append(")");
+ label.append(") : ");
TypeMirror retType = type.getReturnType();
- details.append(Utilities.getTypeName(info, retType,
false).toString());
- item.setDetail(details.toString());
+ label.append(Utilities.getTypeName(info, retType,
false).toString());
+ CompletionItem item = new CompletionItem(label.toString());
+ item.setKind(elementKind2CompletionItemKind(elem.getKind()));
+ item.setInsertText(elem.getSimpleName().toString());
return item;
}
diff --git
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java
index 58151ee..b2545bd 100644
---
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java
+++
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ServerTest.java
@@ -188,7 +188,7 @@ public class ServerTest extends NbTestCase {
Either<List<CompletionItem>, CompletionList> completion =
server.getTextDocumentService().completion(new CompletionParams(new
TextDocumentIdentifier(src.toURI().toString()), new Position(0, hashCodeStart +
2))).get();
assertTrue(completion.isRight());
List<String> actualItems =
completion.getRight().getItems().stream().map(ci -> ci.getKind() + ":" +
ci.getLabel()).collect(Collectors.toList());
- assertEquals(Arrays.asList("Method:hashCode"), actualItems);
+ assertEquals(Arrays.asList("Method:hashCode() : int"), actualItems);
VersionedTextDocumentIdentifier id = new
VersionedTextDocumentIdentifier(1);
id.setUri(src.toURI().toString());
server.getTextDocumentService().didChange(new
DidChangeTextDocumentParams(id, Arrays.asList(new
TextDocumentContentChangeEvent(new Range(new Position(0, hashCodeStart), new
Position(0, hashCodeStart + "hashCode".length())), "hashCode".length(),
"equ"))));
@@ -196,7 +196,7 @@ public class ServerTest extends NbTestCase {
assertDiags(diags, "Error:0:31-0:34");//hints
completion = server.getTextDocumentService().completion(new
CompletionParams(new TextDocumentIdentifier(src.toURI().toString()), new
Position(0, hashCodeStart + 2))).get();
actualItems = completion.getRight().getItems().stream().map(ci ->
ci.getKind() + ":" + ci.getLabel()).collect(Collectors.toList());
- assertEquals(Arrays.asList("Method:equals",
"Method:equalsIgnoreCase"), actualItems);
+ assertEquals(Arrays.asList("Method:equals(Object anObject) : boolean",
"Method:equalsIgnoreCase(String anotherString) : boolean"), actualItems);
completion = server.getTextDocumentService().completion(new
CompletionParams(new TextDocumentIdentifier(src.toURI().toString()), new
Position(0, 0))).get();
actualItems = completion.getRight().getItems().stream().map(ci ->
ci.getKind() + ":" + ci.getLabel()).collect(Collectors.toList());
assertTrue(actualItems.contains("Keyword:interface"));
---------------------------------------------------------------------
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