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/incubator-netbeans.git
The following commit(s) were added to refs/heads/master by this push: new c95b336 Various improvements to the Language Server Protocol client. c95b336 is described below commit c95b336f711c12413d69598b252c1b3cb8ab431f Author: Jan Lahoda <lah...@gmail.com> AuthorDate: Sun Jan 27 20:19:24 2019 +0100 Various improvements to the Language Server Protocol client. * Creating and using a utility to convert FileObjects to URI strings. * Track the language server process. * Sending events asynchronously. * Improving LSP code completion. * Improvements to the LSP client: code completion tooltip, stopping all server on shutdown, handling workspace/configuration request. * Some more improvements: sending client capabilities (empty now); don't try to read fixes for errors when the server does not support codeActions. --- .../netbeans/modules/lsp/client/LSPBindings.java | 52 +++++++-- .../modules/lsp/client/{bindings => }/Utils.java | 7 +- .../modules/lsp/client/bindings/CodeActions.java | 6 +- .../client/bindings/CompletionProviderImpl.java | 129 +++++++++++++++++++-- .../lsp/client/bindings/HyperlinkProviderImpl.java | 5 +- .../lsp/client/bindings/LanguageClientImpl.java | 31 ++++- .../lsp/client/bindings/NavigatorPanelImpl.java | 4 +- .../TextDocumentSyncServerCapabilityHandler.java | 54 ++++++--- 8 files changed, 244 insertions(+), 44 deletions(-) diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java index b6af96d..f68c1db 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java @@ -23,12 +23,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.WeakHashMap; @@ -36,8 +34,11 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.TextDocumentClientCapabilities; +import org.eclipse.lsp4j.WorkspaceClientCapabilities; import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.launch.LSPLauncher; import org.eclipse.lsp4j.services.LanguageServer; @@ -52,6 +53,7 @@ import org.netbeans.modules.lsp.client.spi.LanguageServerProvider; import org.netbeans.modules.lsp.client.spi.LanguageServerProvider.LanguageServerDescription; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; +import org.openide.modules.OnStop; import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.util.RequestProcessor; @@ -70,7 +72,7 @@ public class LSPBindings { private static final Map<FileObject, Map<String, LSPBindings>> workspace2Extension2Server = new HashMap<>(); private final Map<FileObject, Map<BackgroundTask, RequestProcessor.Task>> backgroundTasks = new WeakHashMap<>(); - public static LSPBindings getBindings(FileObject file) { + public static synchronized LSPBindings getBindings(FileObject file) { for (Entry<FileObject, Map<String, LSPBindings>> e : workspace2Extension2Server.entrySet()) { if (FileUtil.isParentOf(e.getKey(), file)) { LSPBindings bindings = e.getValue().get(file.getExt()); @@ -108,21 +110,26 @@ public class LSPBindings { launcher.startListening(); LanguageServer server = launcher.getRemoteProxy(); InitializeParams initParams = new InitializeParams(); - initParams.setRootUri(prj.getProjectDirectory().toURI().toString()); //XXX: what if a different root is expected???? + initParams.setRootUri(Utils.toURI(prj.getProjectDirectory())); //XXX: what if a different root is expected???? initParams.setRootPath(FileUtil.toFile(prj.getProjectDirectory()).getAbsolutePath()); //some servers still expect root path initParams.setProcessId(0); + initParams.setCapabilities(new ClientCapabilities(new WorkspaceClientCapabilities(), new TextDocumentClientCapabilities(), null)); InitializeResult result = server.initialize(initParams).get(); - LSPBindings b = new LSPBindings(server, result); + LSPBindings b = new LSPBindings(server, result, LanguageServerProviderAccessor.getINSTANCE().getProcess(desc)); lci.setBindings(b); return b; } catch (InterruptedException | ExecutionException ex) { - LOG.log(Level.FINE, null, ex); + LOG.log(Level.WARNING, null, ex); } } } - return new LSPBindings(null, null); + return new LSPBindings(null, null, null); }); + if (bindings.process != null && !bindings.process.isAlive()) { + //XXX: what now + return null; + } return bindings.server != null ? bindings : null; } private static final Logger LOG = Logger.getLogger(LSPBindings.class.getName()); @@ -147,10 +154,10 @@ public class LSPBindings { LanguageServer server = launcher.getRemoteProxy(); InitializeParams initParams = new InitializeParams(); - initParams.setRootUri(root.toURI().toString()); + initParams.setRootUri(Utils.toURI(root)); initParams.setProcessId(0); InitializeResult result = server.initialize(initParams).get(); - LSPBindings bindings = new LSPBindings(server, result); + LSPBindings bindings = new LSPBindings(server, result, null); lc.setBindings(bindings); @@ -163,10 +170,12 @@ public class LSPBindings { private final LanguageServer server; private final InitializeResult initResult; + private final Process process; - private LSPBindings(LanguageServer server, InitializeResult initResult) { + private LSPBindings(LanguageServer server, InitializeResult initResult, Process process) { this.server = server; this.initResult = initResult; + this.process = process; } public TextDocumentService getTextDocumentService() { @@ -222,4 +231,27 @@ public class LSPBindings { public interface BackgroundTask { public void run(LSPBindings bindings, FileObject file); } + + @OnStop + public static class Cleanup implements Runnable { + + @Override + public void run() { + for (Map<String, LSPBindings> mime2Bindings : project2MimeType2Server.values()) { + for (LSPBindings b : mime2Bindings.values()) { + if (b != null) { + b.process.destroy(); + } + } + } + for (Map<String, LSPBindings> mime2Bindings : workspace2Extension2Server.values()) { + for (LSPBindings b : mime2Bindings.values()) { + if (b != null) { + b.process.destroy(); + } + } + } + } + + } } diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/Utils.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java similarity index 88% rename from ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/Utils.java rename to ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java index ca0c7d9..f972bc5 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/Utils.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java @@ -16,13 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -package org.netbeans.modules.lsp.client.bindings; +package org.netbeans.modules.lsp.client; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import org.eclipse.lsp4j.Position; import org.netbeans.api.editor.document.LineDocument; import org.netbeans.api.editor.document.LineDocumentUtils; +import org.openide.filesystems.FileObject; /** * @@ -30,6 +31,10 @@ import org.netbeans.api.editor.document.LineDocumentUtils; */ public class Utils { + public static String toURI(FileObject file) { + return file.toURI().toString().replace("file:/", "file:///"); + } + public static Position createPosition(Document doc, int offset) throws BadLocationException { return new Position(LineDocumentUtils.getLineIndex((LineDocument) doc, offset), offset - LineDocumentUtils.getLineStart((LineDocument) doc, offset)); diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java index 8e7e071..982da88 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java @@ -18,7 +18,6 @@ */ package org.netbeans.modules.lsp.client.bindings; -import java.net.URI; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; @@ -34,6 +33,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.modules.editor.NbEditorUtilities; import org.netbeans.modules.lsp.client.LSPBindings; +import org.netbeans.modules.lsp.client.Utils; import org.netbeans.spi.editor.codegen.CodeGenerator; import org.openide.filesystems.FileObject; import org.openide.util.Exceptions; @@ -60,10 +60,10 @@ public class CodeActions implements CodeGenerator.Factory { if (server == null) { return Collections.emptyList(); } - URI uri = file.toURI(); + String uri = Utils.toURI(file); try { List<? extends Command> commands = - server.getTextDocumentService().codeAction(new CodeActionParams(new TextDocumentIdentifier(uri.toString()), + server.getTextDocumentService().codeAction(new CodeActionParams(new TextDocumentIdentifier(uri), new Range(Utils.createPosition(component.getDocument(), component.getSelectionStart()), Utils.createPosition(component.getDocument(), component.getSelectionEnd())), new CodeActionContext(Collections.emptyList()))).get(); 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 61d016f..75a017f 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 @@ -22,12 +22,14 @@ import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.event.KeyEvent; -import java.net.URI; +import java.net.URL; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; +import javax.swing.JToolTip; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; @@ -36,7 +38,11 @@ import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemKind; import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.CompletionParams; +import org.eclipse.lsp4j.ParameterInformation; +import org.eclipse.lsp4j.SignatureHelp; +import org.eclipse.lsp4j.SignatureInformation; import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.netbeans.api.editor.mimelookup.MimeRegistration; @@ -44,6 +50,8 @@ import org.netbeans.editor.BaseDocument; import org.netbeans.editor.Utilities; import org.netbeans.modules.editor.NbEditorUtilities; import org.netbeans.modules.lsp.client.LSPBindings; +import org.netbeans.modules.lsp.client.Utils; +import org.netbeans.spi.editor.completion.CompletionDocumentation; import org.netbeans.spi.editor.completion.CompletionProvider; import org.netbeans.spi.editor.completion.CompletionResultSet; import org.netbeans.spi.editor.completion.CompletionTask; @@ -64,6 +72,64 @@ public class CompletionProviderImpl implements CompletionProvider { @Override public CompletionTask createTask(int queryType, JTextComponent component) { + if ((queryType & TOOLTIP_QUERY_TYPE) != 0) { + return new AsyncCompletionTask(new AsyncCompletionQuery() { + @Override + protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) { + try { + FileObject file = NbEditorUtilities.getFileObject(doc); + if (file == null) { + //TODO: beep + return ; + } + LSPBindings server = LSPBindings.getBindings(file); + if (server == null) { + return ; + } + String uri = Utils.toURI(file); + TextDocumentPositionParams params; + params = new TextDocumentPositionParams(new TextDocumentIdentifier(uri), + Utils.createPosition(doc, caretOffset)); + SignatureHelp help = server.getTextDocumentService().signatureHelp(params).get(); + if (help == null || help.getSignatures().isEmpty()) { + return ; + } + //TODO: active signature? + StringBuilder signatures = new StringBuilder(); + signatures.append("<html>"); + for (SignatureInformation info : help.getSignatures()) { + if (info.getParameters().isEmpty()) { + signatures.append("No parameter."); + continue; + } + String sigSep = ""; + int idx = 0; + for (ParameterInformation pi : info.getParameters()) { + if (idx == help.getActiveParameter()) { + signatures.append("<b>"); + } + signatures.append(sigSep); + signatures.append(pi.getLabel()); + if (idx == help.getActiveParameter()) { + signatures.append("</b>"); + } + sigSep = ", "; + idx++; + } + } + JToolTip tip = new JToolTip(); + tip.setTipText(signatures.toString()); + resultSet.setToolTip(tip); + } catch (BadLocationException | InterruptedException ex) { + Exceptions.printStackTrace(ex); + } catch (ExecutionException ex) { + Exceptions.printStackTrace(ex); + } finally { + resultSet.finish(); + } + } + }, component); + } return new AsyncCompletionTask(new AsyncCompletionQuery() { @Override protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) { @@ -77,9 +143,9 @@ public class CompletionProviderImpl implements CompletionProvider { if (server == null) { return ; } - URI uri = file.toURI(); + String uri = Utils.toURI(file); CompletionParams params; - params = new CompletionParams(new TextDocumentIdentifier(uri.toString()), + params = new CompletionParams(new TextDocumentIdentifier(uri), Utils.createPosition(doc, caretOffset)); CountDownLatch l = new CountDownLatch(1); //TODO: Location or Location[] @@ -95,6 +161,21 @@ 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! + } + } else { + documentation = header; + } CompletionItemKind kind = i.getKind(); Icon ic = Icons.getCompletionIcon(kind); ImageIcon icon = new ImageIcon(ImageUtilities.icon2Image(ic)); @@ -135,17 +216,46 @@ public class CompletionProviderImpl implements CompletionProvider { @Override public int getPreferredWidth(Graphics grphcs, Font font) { - return CompletionUtilities.getPreferredWidth(insert, null, grphcs, font); + return CompletionUtilities.getPreferredWidth(leftLabel, rightLabel, grphcs, font); } @Override public void render(Graphics grphcs, Font font, Color color, Color color1, int i, int i1, boolean bln) { - CompletionUtilities.renderHtml(icon, insert, null, grphcs, font, color, i, i1, bln); + CompletionUtilities.renderHtml(icon, leftLabel, rightLabel, grphcs, font, color, i, i1, bln); } @Override public CompletionTask createDocumentationTask() { - return null; + return new CompletionTask() { + @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; + } + @Override + public Action getGotoSourceAction() { + return null; + } + }); + resultSet.finish(); + } + + @Override + public void refresh(CompletionResultSet resultSet) {} + + @Override + public void cancel() {} + }; } @Override @@ -165,7 +275,7 @@ public class CompletionProviderImpl implements CompletionProvider { @Override public CharSequence getSortText() { - return i.getSortText(); + return sortText; } @Override @@ -184,6 +294,11 @@ public class CompletionProviderImpl implements CompletionProvider { } }, component); } + + private String encode(String str) { + return str.replace("&", "&") + .replace("<", "<"); + } @Override public int getAutoQueryTypes(JTextComponent component, String typedText) { diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/HyperlinkProviderImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/HyperlinkProviderImpl.java index 4c79aa1..4f1cb70 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/HyperlinkProviderImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/HyperlinkProviderImpl.java @@ -38,6 +38,7 @@ import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt; import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType; import org.netbeans.modules.editor.NbEditorUtilities; import org.netbeans.modules.lsp.client.LSPBindings; +import org.netbeans.modules.lsp.client.Utils; import org.openide.cookies.LineCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.URLMapper; @@ -84,10 +85,10 @@ public class HyperlinkProviderImpl implements HyperlinkProviderExt { if (server == null) { return ; } - URI uri = file.toURI(); + String uri = Utils.toURI(file); try { TextDocumentPositionParams params; - params = new TextDocumentPositionParams(new TextDocumentIdentifier(uri.toString()), + params = new TextDocumentPositionParams(new TextDocumentIdentifier(uri), Utils.createPosition(doc, offset)); //TODO: Location or Location[] CompletableFuture<List<? extends Location>> def = server.getTextDocumentService().definition(params); diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java index 4ed1fd5..fe2ebdd 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; import java.util.List; @@ -42,18 +43,22 @@ import org.eclipse.lsp4j.ApplyWorkspaceEditResponse; import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.CodeActionParams; import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.ConfigurationItem; +import org.eclipse.lsp4j.ConfigurationParams; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.MessageActionItem; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.ShowMessageRequestParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextEdit; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.services.LanguageClient; import org.netbeans.modules.lsp.client.LSPBindings; +import org.netbeans.modules.lsp.client.Utils; import org.netbeans.spi.editor.hints.ChangeInfo; import org.netbeans.spi.editor.hints.ErrorDescription; import org.netbeans.spi.editor.hints.ErrorDescriptionFactory; @@ -66,6 +71,7 @@ import org.openide.filesystems.FileObject; import org.openide.filesystems.URLMapper; import org.openide.text.NbDocument; import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; /** * @@ -74,11 +80,16 @@ import org.openide.util.Exceptions; public class LanguageClientImpl implements LanguageClient { private static final Logger LOG = Logger.getLogger(LanguageClientImpl.class.getName()); + private static final RequestProcessor WORKER = new RequestProcessor(LanguageClientImpl.class.getName(), 1, false, false); private LSPBindings bindings; + private boolean allowCodeActions; public void setBindings(LSPBindings bindings) { this.bindings = bindings; + ServerCapabilities serverCapabilities = bindings.getInitResult().getCapabilities(); + Boolean codeActions = serverCapabilities.getCodeActionProvider(); + allowCodeActions = codeActions != null && codeActions; } @Override @@ -94,9 +105,10 @@ public class LanguageClientImpl implements LanguageClient { Document doc = ec != null ? ec.getDocument() : null; if (doc == null) return ; //ignore... - List<ErrorDescription> diags = pdp.getDiagnostics().stream().map(d -> - ErrorDescriptionFactory.createErrorDescription(severityMap.get(d.getSeverity()), d.getMessage(), new DiagnosticFixList(pdp.getUri(), d), file, Utils.getOffset(doc, d.getRange().getStart()), Utils.getOffset(doc, d.getRange().getEnd())) - ).collect(Collectors.toList()); + List<ErrorDescription> diags = pdp.getDiagnostics().stream().map(d -> { + LazyFixList fixList = allowCodeActions ? new DiagnosticFixList(pdp.getUri(), d) : ErrorDescriptionFactory.lazyListForFixes(Collections.emptyList()); + return ErrorDescriptionFactory.createErrorDescription(severityMap.get(d.getSeverity()), d.getMessage(), fixList, file, Utils.getOffset(doc, d.getRange().getStart()), Utils.getOffset(doc, d.getRange().getEnd())); + }).collect(Collectors.toList()); HintsController.setErrors(doc, LanguageClientImpl.class.getName(), diags); } catch (URISyntaxException | MalformedURLException ex) { LOG.log(Level.FINE, null, ex); @@ -161,6 +173,19 @@ public class LanguageClientImpl implements LanguageClient { System.err.println("logMessage: " + arg0); } + @Override + public CompletableFuture<List<Object>> configuration(ConfigurationParams configurationParams) { + CompletableFuture<List<Object>> result = new CompletableFuture<>(); + WORKER.post(() -> { + List<Object> outcome = new ArrayList<>(); + for (ConfigurationItem ci : configurationParams.getItems()) { + outcome.add(null); + } + result.complete(outcome); + }); + return result; + } + private final class DiagnosticFixList implements LazyFixList { private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java index bbc12f3..def4911 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java @@ -32,6 +32,7 @@ import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.netbeans.modules.lsp.client.LSPBindings; import org.netbeans.modules.lsp.client.LSPBindings.BackgroundTask; +import org.netbeans.modules.lsp.client.Utils; import org.netbeans.spi.navigator.NavigatorPanel; import org.openide.explorer.ExplorerManager; import org.openide.explorer.view.BeanTreeView; @@ -135,7 +136,8 @@ public class NavigatorPanelImpl extends Children.Keys<SymbolInformation> impleme public void run(LSPBindings bindings, FileObject file) { if (file.equals(this.file)) { try { - List<? extends SymbolInformation> symbols = bindings.getTextDocumentService().documentSymbol(new DocumentSymbolParams(new TextDocumentIdentifier(file.toURI().toString()))).get(); + String uri = Utils.toURI(file); + List<? extends SymbolInformation> symbols = bindings.getTextDocumentService().documentSymbol(new DocumentSymbolParams(new TextDocumentIdentifier(uri))).get(); setKeys(symbols); } catch (InterruptedException | ExecutionException ex) { diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java index c08a3b9..9ac2f60 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java @@ -39,19 +39,21 @@ import org.netbeans.api.editor.EditorRegistry; import org.netbeans.editor.BaseDocumentEvent; import org.netbeans.modules.editor.*; import org.netbeans.modules.lsp.client.LSPBindings; +import org.netbeans.modules.lsp.client.Utils; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.modules.OnStart; import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; -/** TODO: asynchronous - * TODO: follow the synchronization options +/** TODO: follow the synchronization options * TODO: close * * @author lahvac */ public class TextDocumentSyncServerCapabilityHandler { + private final RequestProcessor WORKER = new RequestProcessor(TextDocumentSyncServerCapabilityHandler.class.getName(), 1, false, false); private final Set<JTextComponent> lastOpened = Collections.newSetFromMap(new IdentityHashMap<>()); private void handleChange() { @@ -73,25 +75,34 @@ public class TextDocumentSyncServerCapabilityHandler { if (file == null) continue; //ignore - LSPBindings server = LSPBindings.getBindings(file); + Document doc = opened.getDocument(); - if (server == null) - continue; //ignore + WORKER.post(() -> { + LSPBindings server = LSPBindings.getBindings(file); + + if (server == null) + return ; //ignore + + String uri = Utils.toURI(file); + String[] text = new String[1]; - try { - //XXX: should construct events outside of AWT - TextDocumentItem textDocumentItem = new TextDocumentItem(file.toURI().toString(), + doc.render(() -> { + try { + text[0] = doc.getText(0, doc.getLength()); + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + text[0] = ""; + } + }); + + TextDocumentItem textDocumentItem = new TextDocumentItem(uri, FileUtil.getMIMEType(file), 0, - opened.getDocument().getText(0, opened.getDocument().getLength())); //XXX: should do in render! + text[0]); server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(textDocumentItem)); server.scheduleBackgroundTasks(file); - } catch (BadLocationException ex) { - Exceptions.printStackTrace(ex); - } - - Document doc = opened.getDocument(); + }); doc.addDocumentListener(new DocumentListener() { //XXX: listener int version; //XXX: proper versioning! @@ -128,9 +139,18 @@ public class TextDocumentSyncServerCapabilityHandler { oldText.length(), newText); VersionedTextDocumentIdentifier di = new VersionedTextDocumentIdentifier(++version); - di.setUri(file.toURI().toString()); - server.getTextDocumentService().didChange(new DidChangeTextDocumentParams(di, Arrays.asList(event))); - server.scheduleBackgroundTasks(file); + di.setUri(org.netbeans.modules.lsp.client.Utils.toURI(file)); + DidChangeTextDocumentParams params = new DidChangeTextDocumentParams(di, Arrays.asList(event)); + + WORKER.post(() -> { + LSPBindings server = LSPBindings.getBindings(file); + + if (server == null) + return ; //ignore + + server.getTextDocumentService().didChange(params); + server.scheduleBackgroundTasks(file); + }); } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } --------------------------------------------------------------------- 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