You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by lk...@apache.org on 2020/10/23 01:11:00 UTC

[netbeans] 13/18: StatusDisplayer messages remoted to LSP client. (#2478)

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 <sv...@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-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists