This is an automated email from the ASF dual-hosted git repository. lkishalmi pushed a commit to branch release122 in repository https://gitbox.apache.org/repos/asf/netbeans.git
commit 15099a794af75e5343b188517bbc652172cc3699 Author: Svatopluk Dedic <svatopluk.de...@oracle.com> AuthorDate: Thu Oct 22 20:25:18 2020 +0200 StatusDisplayer messages remoted to LSP client. (#2478) * StatusDisplayer messages remoted to vscode. * Extended capabilities negotiated with the client. --- .../server/protocol/NbCodeClientCapabilities.java | 87 +++++++++++++++ .../lsp/server/protocol/NbCodeClientWrapper.java | 120 +++++++++++++++++++++ .../lsp/server/protocol/NbCodeLanguageClient.java | 49 +++++++++ .../modules/java/lsp/server/protocol/Server.java | 83 ++++++++++++-- .../server/protocol/ShowStatusMessageParams.java | 78 ++++++++++++++ .../server/protocol/TextDocumentServiceImpl.java | 5 +- .../lsp/server/protocol/WorkspaceServiceImpl.java | 33 +----- .../lsp/server/protocol/WorkspaceUIContext.java | 62 +++++++++++ .../lsp/server/ui/AbstractLspStatusDisplayer.java | 5 +- .../modules/java/lsp/server/ui/UIContext.java | 15 +++ .../java/lsp/server/protocol/ServerTest.java | 24 +++-- java/java.lsp.server/vscode/src/extension.ts | 38 ++++++- java/java.lsp.server/vscode/src/protocol.ts | 35 ++++++ 13 files changed, 583 insertions(+), 51 deletions(-) diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientCapabilities.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientCapabilities.java new file mode 100644 index 0000000..41f7e0a --- /dev/null +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientCapabilities.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.lsp.server.protocol; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import org.eclipse.lsp4j.InitializeParams; + +/** + * Encapsulates all nbcode-specific client capabilities. Need to be passed in + * an object: + * <code><pre> + * "nbcodeCapabilities" : { + * "statusBarMessageSupport"? : boolean + * ... + * } + * </pre></code> + * @author sdedic + */ +public final class NbCodeClientCapabilities { + /** + * Supports status bar messages: + * <ul> + * <li>window/showStatusBarMessage + * </ul> + */ + private Boolean statusBarMessageSupport; + + public Boolean getStatusBarMessageSupport() { + return statusBarMessageSupport; + } + + public boolean hasStatusBarMessageSupport() { + return statusBarMessageSupport != null && statusBarMessageSupport.booleanValue(); + } + + public void setStatusBarMessageSupport(Boolean statusBarMessageSupport) { + this.statusBarMessageSupport = statusBarMessageSupport; + } + + public static NbCodeClientCapabilities get(InitializeParams initParams) { + if (initParams == null) { + return null; + } + Object ext = initParams.getInitializationOptions(); + if (!(ext instanceof JsonElement)) { + return null; + } + InitializationExtendedCapabilities root = new GsonBuilder(). + /* + hypothetically needed for formatting options with Either type + registerTypeAdapterFactory(new EitherTypeAdapter.Factory()). + */ + create(). + fromJson((JsonElement)ext, InitializationExtendedCapabilities.class); + return root == null ? null : root.getNbcodeCapabilities(); + + } + + static final class InitializationExtendedCapabilities { + private NbCodeClientCapabilities nbcodeCapabilities; + + public NbCodeClientCapabilities getNbcodeCapabilities() { + return nbcodeCapabilities; + } + + public void setNbcodeCapabilities(NbCodeClientCapabilities nbcodeCapabilities) { + this.nbcodeCapabilities = nbcodeCapabilities; + } + } +} diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientWrapper.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientWrapper.java new file mode 100644 index 0000000..c4ba4b5 --- /dev/null +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientWrapper.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.lsp.server.protocol; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.ApplyWorkspaceEditParams; +import org.eclipse.lsp4j.ApplyWorkspaceEditResponse; +import org.eclipse.lsp4j.ConfigurationParams; +import org.eclipse.lsp4j.MessageActionItem; +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.RegistrationParams; +import org.eclipse.lsp4j.SemanticHighlightingParams; +import org.eclipse.lsp4j.ShowMessageRequestParams; +import org.eclipse.lsp4j.UnregistrationParams; +import org.eclipse.lsp4j.WorkspaceFolder; + +/** + * Convenience wrapper that binds language client's remote proxy together with + * other useful methods. Will be sent out as THE client by the server core code. + * + * @author sdedic + */ +class NbCodeClientWrapper implements NbCodeLanguageClient { + private final NbCodeLanguageClient remote; + private volatile NbCodeClientCapabilities clientCaps; + + public NbCodeClientWrapper(NbCodeLanguageClient remote) { + this.remote = remote; + this.clientCaps = new NbCodeClientCapabilities(); + } + + public void setClientCaps(NbCodeClientCapabilities clientCaps) { + if (clientCaps != null) { + this.clientCaps = clientCaps; + } + } + + @Override + public NbCodeClientCapabilities getNbCodeCapabilities() { + return clientCaps; + } + + @Override + public void showStatusBarMessage(ShowStatusMessageParams params) { + remote.showStatusBarMessage(params); + } + + @Override + public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) { + return remote.applyEdit(params); + } + + @Override + public CompletableFuture<Void> registerCapability(RegistrationParams params) { + return remote.registerCapability(params); + } + + @Override + public CompletableFuture<Void> unregisterCapability(UnregistrationParams params) { + return remote.unregisterCapability(params); + } + + @Override + public void telemetryEvent(Object object) { + remote.telemetryEvent(object); + } + + @Override + public void publishDiagnostics(PublishDiagnosticsParams diagnostics) { + remote.publishDiagnostics(diagnostics); + } + + @Override + public void showMessage(MessageParams messageParams) { + remote.showMessage(messageParams); + } + + @Override + public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams requestParams) { + return remote.showMessageRequest(requestParams); + } + + @Override + public void logMessage(MessageParams message) { + remote.logMessage(message); + } + + @Override + public CompletableFuture<List<WorkspaceFolder>> workspaceFolders() { + return remote.workspaceFolders(); + } + + @Override + public CompletableFuture<List<Object>> configuration(ConfigurationParams configurationParams) { + return remote.configuration(configurationParams); + } + + @Override + public void semanticHighlighting(SemanticHighlightingParams params) { + remote.semanticHighlighting(params); + } +} diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java new file mode 100644 index 0000000..10ad5a6 --- /dev/null +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeLanguageClient.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.lsp.server.protocol; + +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; +import org.eclipse.lsp4j.services.LanguageClient; + +/** + * An extension to the standard LanguageClient that adds several messages missing + * from the official LSP protocol.s + * @author sdedic + */ +public interface NbCodeLanguageClient extends LanguageClient { + + /** + * Shows a message in the status bar. Log- and Info-type messages are shown "as is". + * The other message types can be decorated by an icon according to {@link MessageParams#getType}. + * The message will be hidden after specified number of milliseconds; 0 means the client + * controls when the message is hidden. + * + * @param params message type and text. + */ + @JsonNotification("window/showStatusBarMessage") + public void showStatusBarMessage(@NonNull ShowStatusMessageParams params); + + /** + * Returns extended code capabilities. + * @return code capabilities. + */ + public NbCodeClientCapabilities getNbCodeCapabilities(); +} diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java index 2a10c70..91bf541 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java @@ -35,12 +35,15 @@ import org.eclipse.lsp4j.CompletionOptions; import org.eclipse.lsp4j.ExecuteCommandOptions; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.MessageType; import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.TextDocumentSyncKind; import org.eclipse.lsp4j.WorkspaceFolder; +import org.eclipse.lsp4j.jsonrpc.JsonRpcException; import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.jsonrpc.MessageConsumer; +import org.eclipse.lsp4j.jsonrpc.MessageIssueException; +import org.eclipse.lsp4j.jsonrpc.messages.Message; import org.eclipse.lsp4j.launch.LSPLauncher; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClientAware; @@ -57,6 +60,11 @@ import org.netbeans.api.project.Sources; import org.netbeans.api.project.ui.OpenProjects; import org.openide.filesystems.FileObject; import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; /** * @@ -68,7 +76,7 @@ public final class Server { public static void launchServer(InputStream in, OutputStream out) { LanguageServerImpl server = new LanguageServerImpl(); - Launcher<LanguageClient> serverLauncher = LSPLauncher.createServerLauncher(server, in, out); + Launcher<NbCodeLanguageClient> serverLauncher = createLauncher(server, in, out); ((LanguageClientAware) server).connect(serverLauncher.getRemoteProxy()); Future<Void> runningServer = serverLauncher.startListening(); try { @@ -77,16 +85,60 @@ public final class Server { Exceptions.printStackTrace(ex); } } + + private static Launcher<NbCodeLanguageClient> createLauncher(LanguageServerImpl server, InputStream in, OutputStream out) { + return new LSPLauncher.Builder<NbCodeLanguageClient>() + .setLocalService(server) + .setRemoteInterface(NbCodeLanguageClient.class) + .setInput(in) + .setOutput(out) + .wrapMessages(new ConsumeWithLookup(server.getSessionLookup())::attachLookup) + .create(); + } + + /** + * Processes message while the default Lookup is set to + * {@link LanguageServerImpl#getSessionLookup()}. + */ + private static class ConsumeWithLookup { + private final Lookup sessionLookup; + public ConsumeWithLookup(Lookup sessionLookup) { + this.sessionLookup = sessionLookup; + } + + public MessageConsumer attachLookup(MessageConsumer delegate) { + return new MessageConsumer() { + @Override + public void consume(Message msg) throws MessageIssueException, JsonRpcException { + Lookups.executeWith(sessionLookup, () -> { + delegate.consume(msg); + }); + } + }; + } + } + private static class LanguageServerImpl implements LanguageServer, LanguageClientAware { private static final Logger LOG = Logger.getLogger(LanguageServerImpl.class.getName()); - private LanguageClient client; + private NbCodeClientWrapper client; private final TextDocumentService textDocumentService = new TextDocumentServiceImpl(); private final WorkspaceService workspaceService = new WorkspaceServiceImpl(); - + private final InstanceContent sessionServices = new InstanceContent(); + private final Lookup sessionLookup = new ProxyLookup( + new AbstractLookup(sessionServices), + Lookup.getDefault() + ); + + Lookup getSessionLookup() { + return sessionLookup; + } + @Override public CompletableFuture<InitializeResult> initialize(InitializeParams init) { + NbCodeClientCapabilities capa = NbCodeClientCapabilities.get(init); + client.setClientCaps(capa); List<FileObject> projectCandidates = new ArrayList<>(); List<WorkspaceFolder> folders = init.getWorkspaceFolders(); if (folders != null) { @@ -142,7 +194,11 @@ public final class Server { try { JavaSource.create(ClasspathInfo.create(ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY)) .runWhenScanFinished(cc -> { - client.showMessage(new MessageParams(MessageType.Info, INDEXING_COMPLETED)); + if (client.getNbCodeCapabilities().hasStatusBarMessageSupport()) { + client.showStatusBarMessage(new ShowStatusMessageParams(MessageType.Info, INDEXING_COMPLETED, 0)); + } else { + client.showMessage(new ShowStatusMessageParams(MessageType.Info, INDEXING_COMPLETED, 0)); + } //todo: refresh diagnostics all open editor? }, true); } catch (IOException ex) { @@ -183,10 +239,19 @@ public final class Server { } @Override - public void connect(LanguageClient client) { - this.client = client; - ((LanguageClientAware) getTextDocumentService()).connect(client); - ((LanguageClientAware) getWorkspaceService()).connect(client); + public void connect(LanguageClient aClient) { + this.client = new NbCodeClientWrapper((NbCodeLanguageClient)aClient); + + sessionServices.add(new WorkspaceIOContext() { + @Override + protected LanguageClient client() { + return client; + } + }); + sessionServices.add(new WorkspaceUIContext(client)); + + ((LanguageClientAware) getTextDocumentService()).connect(aClient); + ((LanguageClientAware) getWorkspaceService()).connect(aClient); } } diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowStatusMessageParams.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowStatusMessageParams.java new file mode 100644 index 0000000..43af736 --- /dev/null +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ShowStatusMessageParams.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.lsp.server.protocol; + +import java.util.Objects; +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.MessageType; +import org.eclipse.xtext.xbase.lib.Pure; + +/** + * + * @author sdedic + */ +public class ShowStatusMessageParams extends MessageParams { + private Integer timeout; + + public ShowStatusMessageParams(MessageType type, String message) { + super(type, message); + } + + public ShowStatusMessageParams(MessageType type, String message, int timeout) { + super(type, message); + this.timeout = timeout; + } + + @Pure + public Integer getTimeout() { + return timeout; + } + + public ShowStatusMessageParams setTimeout(Integer timeout) { + this.timeout = timeout; + return this; + } + + @Pure + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + (timeout == null ? 7 : this.timeout.hashCode()); + return hash; + } + + @Pure + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ShowStatusMessageParams other = (ShowStatusMessageParams) obj; + if (Objects.equals(this.timeout, other.timeout)) { + return false; + } + return true; + } +} 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 e0a1df0..f612a1a 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 @@ -172,6 +172,7 @@ 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.openide.awt.StatusDisplayer; import org.openide.cookies.EditorCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -194,7 +195,7 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli private final Map<String, Document> openedDocuments = new HashMap<>(); private final Map<String, RequestProcessor.Task> diagnosticTasks = new HashMap<>(); - private LanguageClient client; + private NbCodeLanguageClient client; public TextDocumentServiceImpl() { } @@ -245,7 +246,7 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli @Override public void connect(LanguageClient client) { - this.client = client; + this.client = (NbCodeLanguageClient)client; } private static class ItemFactoryImpl implements JavaCompletionTask.ItemFactory<CompletionItem> { diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java index 696ef85..52c395b 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java @@ -23,7 +23,6 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.DidChangeConfigurationParams; import org.eclipse.lsp4j.DidChangeWatchedFilesParams; import org.eclipse.lsp4j.ExecuteCommandParams; -import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.WorkspaceSymbolParams; import org.eclipse.lsp4j.services.LanguageClient; @@ -33,11 +32,8 @@ import org.netbeans.api.debugger.ActionsManager; import org.netbeans.api.debugger.DebuggerManager; import org.netbeans.api.project.Project; import org.netbeans.api.project.ui.OpenProjects; -import org.netbeans.modules.java.lsp.server.ui.UIContext; import org.netbeans.spi.project.ActionProvider; -import org.openide.util.Lookup; import org.openide.util.lookup.Lookups; -import org.openide.util.lookup.ProxyLookup; /** * @@ -45,12 +41,9 @@ import org.openide.util.lookup.ProxyLookup; */ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageClientAware { - private LanguageClient client; - private UIContext ctx; - private final WorkspaceIOContext ioContext; + private NbCodeLanguageClient client; public WorkspaceServiceImpl() { - this.ioContext = new WorkspaceContext(); } @Override @@ -64,9 +57,7 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli for (Project prj : OpenProjects.getDefault().getOpenProjects()) { ActionProvider ap = prj.getLookup().lookup(ActionProvider.class); if (ap != null && ap.isActionEnabled(ActionProvider.COMMAND_BUILD, Lookups.fixed())) { - Lookups.executeWith(new ProxyLookup(Lookups.fixed(ctx, ioContext), Lookup.getDefault()), () -> { - ap.invokeAction(ActionProvider.COMMAND_REBUILD, Lookups.fixed()); - }); + ap.invokeAction(ActionProvider.COMMAND_REBUILD, Lookups.fixed()); } } return CompletableFuture.completedFuture(true); @@ -92,24 +83,6 @@ public final class WorkspaceServiceImpl implements WorkspaceService, LanguageCli @Override public void connect(LanguageClient client) { - this.client = client; - this.ctx = new UIContext() { - @Override - protected boolean isValid() { - return true; - } - - @Override - protected void showMessage(MessageParams msg) { - client.showMessage(msg); - } - }; - } - - private final class WorkspaceContext extends WorkspaceIOContext { - @Override - protected LanguageClient client() { - return client; - } + this.client = (NbCodeLanguageClient)client; } } diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceUIContext.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceUIContext.java new file mode 100644 index 0000000..c20575c --- /dev/null +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceUIContext.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.lsp.server.protocol; + +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.services.LanguageClient; +import org.netbeans.modules.java.lsp.server.ui.UIContext; +import org.openide.awt.StatusDisplayer; + +/** + * + * @author sdedic + */ +class WorkspaceUIContext extends UIContext { + private final NbCodeLanguageClient client; + + public WorkspaceUIContext(NbCodeLanguageClient client) { + this.client = client; + } + + @Override + protected boolean isValid() { + return true; + } + + @Override + protected void showMessage(MessageParams msg) { + client.showMessage(msg); + } + + @Override + protected void logMessage(MessageParams msg) { + client.logMessage(msg); + } + + @Override + protected StatusDisplayer.Message showStatusMessage(ShowStatusMessageParams msg) { + if (client.getNbCodeCapabilities().hasStatusBarMessageSupport()) { + client.showStatusBarMessage(msg); + } else { + client.showMessage(msg); + } + return null; + } + +} diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspStatusDisplayer.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspStatusDisplayer.java index 97953d2..aa5f7a3 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspStatusDisplayer.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspStatusDisplayer.java @@ -21,6 +21,7 @@ package org.netbeans.modules.java.lsp.server.ui; import javax.swing.event.ChangeListener; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.MessageType; +import org.netbeans.modules.java.lsp.server.protocol.ShowStatusMessageParams; import org.openide.awt.StatusDisplayer; public abstract class AbstractLspStatusDisplayer extends StatusDisplayer { @@ -56,8 +57,8 @@ public abstract class AbstractLspStatusDisplayer extends StatusDisplayer { } else { type = MessageType.Info; } - ctx.showMessage(new MessageParams(type, text)); - return (int timeInMillis) -> { + Message m = ctx.showStatusMessage(new ShowStatusMessageParams(type, text, 0)); + return m != null ? m : (int timeInMillis) -> { }; } diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java index aa4a510..781d04e 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java @@ -21,6 +21,8 @@ package org.netbeans.modules.java.lsp.server.ui; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import org.eclipse.lsp4j.MessageParams; +import org.netbeans.modules.java.lsp.server.protocol.ShowStatusMessageParams; +import org.openide.awt.StatusDisplayer.Message; import org.openide.util.Lookup; public abstract class UIContext { @@ -49,6 +51,8 @@ public abstract class UIContext { protected abstract boolean isValid(); protected abstract void showMessage(MessageParams msg); + protected abstract void logMessage(MessageParams msg); + protected abstract Message showStatusMessage(ShowStatusMessageParams msg); private static final class LogImpl extends UIContext { @@ -63,6 +67,17 @@ public abstract class UIContext { } @Override + protected void logMessage(MessageParams msg) { + System.err.println(msg.getType() + ": " + msg.getMessage()); + } + + @Override + protected Message showStatusMessage(ShowStatusMessageParams msg) { + System.out.println(msg.getType() + ": " + msg.getMessage()); + return (int timeInMillis) -> {}; + } + + @Override protected boolean isValid() { return true; } 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 4d7231c..8213c5b 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 @@ -18,6 +18,8 @@ */ package org.netbeans.modules.java.lsp.server.protocol; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -957,7 +959,19 @@ public class ServerTest extends NbTestCase { } List<Diagnostic>[] diags = new List[1]; CountDownLatch indexingComplete = new CountDownLatch(1); - Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LanguageClient() { + Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new NbCodeLanguageClient() { + @Override + public void showStatusBarMessage(ShowStatusMessageParams params) { + if (Server.INDEXING_COMPLETED.equals(params.getMessage())) { + indexingComplete.countDown(); + } + } + + @Override + public NbCodeClientCapabilities getNbCodeCapabilities() { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public void telemetryEvent(Object arg0) { throw new UnsupportedOperationException("Not supported yet."); @@ -973,11 +987,7 @@ public class ServerTest extends NbTestCase { @Override public void showMessage(MessageParams params) { - if (Server.INDEXING_COMPLETED.equals(params.getMessage())) { - indexingComplete.countDown(); - } else { - throw new UnsupportedOperationException("Unexpected message."); - } + throw new UnsupportedOperationException("Unexpected message."); } @Override @@ -993,6 +1003,8 @@ public class ServerTest extends NbTestCase { serverLauncher.startListening(); LanguageServer server = serverLauncher.getRemoteProxy(); InitializeParams initParams = new InitializeParams(); + initParams.setInitializationOptions(new JsonParser().parse( + "{ nbcodeCapabilities: { statusBarMessageSupport : true } }").getAsJsonObject()); initParams.setRootUri(getWorkDir().toURI().toString()); InitializeResult result = server.initialize(initParams).get(); indexingComplete.await(); diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts index 785741c..5ce27ce 100644 --- a/java/java.lsp.server/vscode/src/extension.ts +++ b/java/java.lsp.server/vscode/src/extension.ts @@ -23,7 +23,8 @@ import { commands, window, workspace, ExtensionContext, ProgressLocation } from import { LanguageClient, LanguageClientOptions, - StreamInfo + StreamInfo, + ShowMessageParams, MessageType, } from 'vscode-languageclient'; import * as net from 'net'; @@ -32,6 +33,7 @@ import * as path from 'path'; import { spawn, ChildProcess, SpawnOptions } from 'child_process'; import * as vscode from 'vscode'; import * as launcher from './nbcode'; +import { StatusMessageRequest, ShowStatusMessageParams } from './protocol'; let client: LanguageClient; let nbProcess : ChildProcess | null = null; @@ -182,7 +184,12 @@ export function activate(context: ExtensionContext) { ] }, outputChannelName: 'Java', - revealOutputChannelOn: 4 // never + revealOutputChannelOn: 4, + initializationOptions : { + 'nbcodeCapabilities' : { + 'statusBarMessageSupport' : true + } + } } // Create the language client and start the client. @@ -197,6 +204,7 @@ export function activate(context: ExtensionContext) { client.start(); client.onReady().then((value) => { commands.executeCommand('setContext', 'nbJavaLSReady', true); + client.onNotification(StatusMessageRequest.type, showStatusBarMessage); }); //register debugger: @@ -240,6 +248,32 @@ export function activate(context: ExtensionContext) { } +function showStatusBarMessage(params : ShowStatusMessageParams) { + let decorated : string = params.message; + let defTimeout; + + switch (params.type) { + case MessageType.Error: + decorated = '$(error) ' + params.message; + defTimeout = 0; + break; + case MessageType.Warning: + decorated = '$(warning) ' + params.message; + defTimeout = 0; + break; + default: + defTimeout = 10000; + break; + } + // params.timeout may be defined but 0 -> should be used + const timeout = params.timeout != undefined ? params.timeout : defTimeout; + if (timeout > 0) { + window.setStatusBarMessage(decorated, timeout); + } else { + window.setStatusBarMessage(decorated); + } +} + export function deactivate(): Thenable<void> { if (nbProcess != null) { nbProcess.kill(); diff --git a/java/java.lsp.server/vscode/src/protocol.ts b/java/java.lsp.server/vscode/src/protocol.ts new file mode 100644 index 0000000..4b485ba --- /dev/null +++ b/java/java.lsp.server/vscode/src/protocol.ts @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +'use strict'; + +import { + NotificationType, + ShowMessageParams +} from 'vscode-languageclient'; + +export interface ShowStatusMessageParams extends ShowMessageParams { + /** + * The timeout + */ + timeout?: number; +} + +export namespace StatusMessageRequest { + export const type = new NotificationType<ShowStatusMessageParams, void>('window/showStatusBarMessage'); +}; --------------------------------------------------------------------- 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