Skip to content

Commit

Permalink
Issue 1793: Language Server Protocol: ShowMessage Notification feature (
Browse files Browse the repository at this point in the history
#3124)

Added a ShowMessageProcessor and a ShowMessageMessager classes to process
incoming `window/showMessage` notification and display a notification
in `float` mode in the UI if the message type is `error` or `warning`, in
the events panel otherwise. Note that the notification type for `error`
messages is incorrectly set to `Log` because of a bug in the typefox
dependency: `io.typefox.lsapi.MessageType#Log` has the value `1` instead of
`4`.

This issue depends on #3113
(Add a 'warning' state for the notifications)

To test the pull request, please follow the instructions on
#3123 to run the 'test-lang' server.
Once in the workspace, create a project, add a `foo.test` file (the
Language Server support for the 'test-lang' will be activated), then
type the following line
> window/showMessage:error: a message
and wait for the editor to save the changes. This will trigger a
`window/showMessage` notification from the 'test-lang' server in the Che UI.

Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
  • Loading branch information
xcoulon authored and Vitalii Parfonov committed Dec 5, 2016
1 parent c7fda4f commit a346454
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ config/
######################
*.log
*.sqlite
npm-debug.log.*

# OS generated files #
######################
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/

package org.eclipse.che.plugin.languageserver.ide.editor;

import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;

import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.util.loging.Log;

import com.google.inject.Inject;
import com.google.inject.Singleton;

import io.typefox.lsapi.MessageParams;

/**
* A processor for incoming <code>window/showMessage</code> notifications sent
* by a language server.
*
* @author xcoulon
*/
@Singleton
public class ShowMessageProcessor {

private final NotificationManager notificationManager;

@Inject
public ShowMessageProcessor(final NotificationManager notificationManager) {
this.notificationManager = notificationManager;
}

public void processNotification(final MessageParams messageParams) {
Log.debug(getClass(), "Received a 'ShowMessage' message: " + messageParams.getMessage());
switch(messageParams.getType()) {
case Error:
this.notificationManager.notify(messageParams.getMessage(), StatusNotification.Status.FAIL, FLOAT_MODE);
break;
case Warning:
this.notificationManager.notify(messageParams.getMessage(), StatusNotification.Status.WARNING, FLOAT_MODE);
break;
case Info:
case Log:
default:
this.notificationManager.notify(messageParams.getMessage(), StatusNotification.Status.SUCCESS, FLOAT_MODE);
break;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.eclipse.che.api.languageserver.shared.lsapi.LocationDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.PublishDiagnosticsParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.ReferenceParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.ShowMessageRequestParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.SignatureHelpDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.SymbolInformationDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.TextDocumentPositionParamsDTO;
Expand All @@ -49,6 +50,7 @@
import org.eclipse.che.ide.websocket.WebSocketException;
import org.eclipse.che.ide.websocket.rest.SubscriptionHandler;
import org.eclipse.che.plugin.languageserver.ide.editor.PublishDiagnosticsProcessor;
import org.eclipse.che.plugin.languageserver.ide.editor.ShowMessageProcessor;

import java.util.List;

Expand All @@ -68,6 +70,7 @@ public class TextDocumentServiceClient {
private final AppContext appContext;
private final NotificationManager notificationManager;
private final PublishDiagnosticsProcessor publishDiagnosticsProcessor;
private final ShowMessageProcessor showMessageProcessor;

@Inject
public TextDocumentServiceClient(
Expand All @@ -76,18 +79,21 @@ public TextDocumentServiceClient(
final AppContext appContext,
final AsyncRequestFactory asyncRequestFactory,
final WsAgentStateController wsAgentStateController,
final PublishDiagnosticsProcessor publishDiagnosticsProcessor) {
final PublishDiagnosticsProcessor publishDiagnosticsProcessor,
final ShowMessageProcessor showMessageProcessor) {
this.unmarshallerFactory = unmarshallerFactory;
this.notificationManager = notificationManager;
this.appContext = appContext;
this.asyncRequestFactory = asyncRequestFactory;
this.publishDiagnosticsProcessor = publishDiagnosticsProcessor;
wsAgentStateController.getMessageBus().then(new Operation<MessageBus>() {
@Override
public void apply(MessageBus arg) throws OperationException {
subscribeToPublishDiagnostics(arg);
public void apply(MessageBus messageBus) throws OperationException {
subscribeToPublishDiagnostics(messageBus);
subscribeToShowMessages(messageBus);
}
});
this.showMessageProcessor = showMessageProcessor;
}

/**
Expand Down Expand Up @@ -310,4 +316,28 @@ protected void onErrorReceived(Throwable exception) {
}
}

/**
* Subscribes to websocket for 'window/showMessage' notifications.
*/
private void subscribeToShowMessages(final MessageBus messageBus) {
final org.eclipse.che.ide.websocket.rest.Unmarshallable<ShowMessageRequestParamsDTO> unmarshaller =
unmarshallerFactory.newWSUnmarshaller(ShowMessageRequestParamsDTO.class);
try {
messageBus.subscribe("languageserver/window/showMessage",
new SubscriptionHandler<ShowMessageRequestParamsDTO>(unmarshaller) {
@Override
protected void onMessageReceived(ShowMessageRequestParamsDTO showMessageRequestParamsDTO) {
showMessageProcessor.processNotification(showMessageRequestParamsDTO);
}

@Override
protected void onErrorReceived(Throwable exception) {
notificationManager.notify(exception.getMessage(), StatusNotification.Status.FAIL,
StatusNotification.DisplayMode.NOT_EMERGE_MODE);
}
});
} catch (WebSocketException exception) {
Log.error(getClass(), exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.eclipse.che.ide.rest.AsyncRequestFactory;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
import org.eclipse.che.ide.rest.Unmarshallable;

import java.util.List;

import static org.eclipse.che.ide.MimeType.APPLICATION_JSON;
Expand All @@ -35,11 +34,13 @@ public class WorkspaceServiceClient {
private final DtoUnmarshallerFactory unmarshallerFactory;
private final AppContext appContext;
private final AsyncRequestFactory asyncRequestFactory;



@Inject
public WorkspaceServiceClient(final DtoUnmarshallerFactory unmarshallerFactory,
final AppContext appContext,
final AsyncRequestFactory asyncRequestFactory) {
final AsyncRequestFactory asyncRequestFactory
) {
this.unmarshallerFactory = unmarshallerFactory;
this.appContext = appContext;
this.asyncRequestFactory = asyncRequestFactory;
Expand All @@ -59,4 +60,5 @@ public Promise<List<SymbolInformationDTO>> symbol(WorkspaceSymbolParamsDTO param
.header(CONTENT_TYPE, APPLICATION_JSON)
.send(unmarshaller);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@
*******************************************************************************/
package org.eclipse.che.api.languageserver;

import com.google.inject.AbstractModule;

import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsMessenger;
import org.eclipse.che.api.languageserver.registry.ServerInitializer;
import org.eclipse.che.api.languageserver.service.LanguageRegistryService;
import org.eclipse.che.api.languageserver.service.TextDocumentService;
import org.eclipse.che.inject.DynaModule;
import org.eclipse.che.api.languageserver.messager.InitializeEventMessenger;
import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsMessenger;
import org.eclipse.che.api.languageserver.messager.ShowMessageMessenger;
import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry;
import org.eclipse.che.api.languageserver.registry.LanguageServerRegistryImpl;
import org.eclipse.che.api.languageserver.registry.ServerInitializer;
import org.eclipse.che.api.languageserver.registry.ServerInitializerImpl;
import org.eclipse.che.api.languageserver.service.LanguageRegistryService;
import org.eclipse.che.api.languageserver.service.TextDocumentService;
import org.eclipse.che.api.languageserver.service.WorkspaceService;
import org.eclipse.che.inject.DynaModule;

import com.google.inject.AbstractModule;

@DynaModule
public class LanguageServerModule extends AbstractModule {
Expand All @@ -30,11 +31,11 @@ public class LanguageServerModule extends AbstractModule {
protected void configure() {
bind(LanguageServerRegistry.class).to(LanguageServerRegistryImpl.class);
bind(ServerInitializer.class).to(ServerInitializerImpl.class);

bind(LanguageRegistryService.class);
bind(TextDocumentService.class);
bind(WorkspaceService.class);
bind(PublishDiagnosticsParamsMessenger.class);
bind(ShowMessageMessenger.class);
bind(InitializeEventMessenger.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.languageserver.messager;

import java.io.IOException;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.websocket.EncodeException;

import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.core.notification.EventSubscriber;
import org.everrest.websockets.WSConnectionContext;
import org.everrest.websockets.message.ChannelBroadcastMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

import io.typefox.lsapi.MessageParams;

/**
* {@link EventSubscriber} for incoming <code>window/showMessage</code> notifications.
* @author xcoulon
*/
@Singleton
public class ShowMessageMessenger implements EventSubscriber<MessageParams> {

private final static Logger LOG = LoggerFactory.getLogger(ShowMessageMessenger.class);

private final EventService eventService;

@Inject
public ShowMessageMessenger(EventService eventService) {
this.eventService = eventService;
}

public void onEvent(final MessageParams event) {
try {
final ChannelBroadcastMessage bm = new ChannelBroadcastMessage();
bm.setChannel("languageserver/window/showMessage");
bm.setBody(new Gson().toJson(event));
WSConnectionContext.sendMessage(bm);
} catch (EncodeException | IOException e) {
LOG.error(e.getMessage(), e);
}
}

@PostConstruct
public void subscribe() {
eventService.subscribe(this);
}

@PreDestroy
public void unsubscribe() {
eventService.unsubscribe(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.che.api.languageserver.exception.LanguageServerException;
import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher;
import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsMessenger;
import org.eclipse.che.api.languageserver.messager.ShowMessageMessenger;
import org.eclipse.che.api.languageserver.shared.model.LanguageDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -35,6 +36,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
* @author Anatoliy Bazko
Expand All @@ -48,16 +50,19 @@ public class ServerInitializerImpl implements ServerInitializer {

private final List<ServerInitializerObserver> observers;
private final PublishDiagnosticsParamsMessenger publishDiagnosticsParamsMessenger;

private final ShowMessageMessenger showMessageMessenger;

private final ConcurrentHashMap<String, LanguageServer> languageIdToServers;
private final ConcurrentHashMap<LanguageServer, LanguageServerDescription> serversToInitResult;

@Inject
public ServerInitializerImpl(PublishDiagnosticsParamsMessenger publishDiagnosticsParamsMessenger) {
public ServerInitializerImpl(final PublishDiagnosticsParamsMessenger publishDiagnosticsParamsMessenger,
final ShowMessageMessenger showMessageMessenger) {
this.observers = new ArrayList<>();
this.languageIdToServers = new ConcurrentHashMap<>();
this.serversToInitResult = new ConcurrentHashMap<>();
this.publishDiagnosticsParamsMessenger = publishDiagnosticsParamsMessenger;
this.showMessageMessenger = showMessageMessenger;
}

private static int getProcessId() {
Expand Down Expand Up @@ -137,7 +142,8 @@ protected LanguageServer doInitialize(LanguageServerLauncher launcher, String pr

protected void registerCallbacks(LanguageServer server) {
server.getTextDocumentService().onPublishDiagnostics(publishDiagnosticsParamsMessenger::onEvent);
server.getWindowService().onLogMessage(messageParams -> LOG.error(messageParams.getType() + " " + messageParams.getMessage()));
server.getWindowService().onLogMessage(messageParams -> LOG.error(messageParams.getType() + " " + messageParams.getMessage()));
server.getWindowService().onShowMessage(showMessageMessenger::onEvent);
server.onTelemetryEvent(o -> LOG.error(o.toString()));

if (server instanceof ServerInitializerObserver) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher;
import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsMessenger;
import org.eclipse.che.api.languageserver.messager.ShowMessageMessenger;
import org.eclipse.che.api.languageserver.shared.model.LanguageDescription;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
Expand Down Expand Up @@ -47,6 +48,8 @@ public class ServerInitializerImplTest {
@Mock
private PublishDiagnosticsParamsMessenger publishDiagnosticsParamsMessenger;
@Mock
private ShowMessageMessenger showMessageParamsMessenger;
@Mock
private LanguageDescription languageDescription;
@Mock
private LanguageServerLauncher launcher;
Expand All @@ -59,7 +62,7 @@ public class ServerInitializerImplTest {

@BeforeMethod
public void setUp() throws Exception {
initializer = spy(new ServerInitializerImpl(publishDiagnosticsParamsMessenger));
initializer = spy(new ServerInitializerImpl(publishDiagnosticsParamsMessenger, showMessageParamsMessenger));
}

@Test
Expand Down

0 comments on commit a346454

Please sign in to comment.