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 <jlah...@netbeans.org> 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: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists