/******************************************************************************* * Copyright (c) 2012-2017 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.service; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.che.api.languageserver.shared.model.ExtendedCompletionItem; import org.eclipse.che.api.languageserver.shared.model.ExtendedCompletionList; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.machine.WsAgentStateController; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.notification.StatusNotification; import org.eclipse.che.ide.dto.JsonSerializable; import org.eclipse.che.ide.rest.AsyncRequestFactory; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.rest.Unmarshallable; import org.eclipse.che.ide.util.loging.Log; import org.eclipse.che.ide.websocket.MessageBus; 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 org.eclipse.lsp4j.CodeActionParams; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.DidChangeTextDocumentParams; import org.eclipse.lsp4j.DidCloseTextDocumentParams; import org.eclipse.lsp4j.DidOpenTextDocumentParams; import org.eclipse.lsp4j.DidSaveTextDocumentParams; import org.eclipse.lsp4j.DocumentFormattingParams; import org.eclipse.lsp4j.DocumentHighlight; import org.eclipse.lsp4j.DocumentOnTypeFormattingParams; import org.eclipse.lsp4j.DocumentRangeFormattingParams; import org.eclipse.lsp4j.DocumentSymbolParams; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.ReferenceParams; import org.eclipse.lsp4j.ShowMessageRequestParams; import org.eclipse.lsp4j.SignatureHelp; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.TextEdit; import java.util.List; import static org.eclipse.che.ide.MimeType.APPLICATION_JSON; import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT; import static org.eclipse.che.ide.rest.HTTPHeader.CONTENT_TYPE; /** * @author Anatolii Bazko */ @Singleton public class TextDocumentServiceClient { private final DtoUnmarshallerFactory unmarshallerFactory; private final AsyncRequestFactory asyncRequestFactory; private final AppContext appContext; private final NotificationManager notificationManager; private final PublishDiagnosticsProcessor publishDiagnosticsProcessor; private final ShowMessageProcessor showMessageProcessor; @Inject public TextDocumentServiceClient( final DtoUnmarshallerFactory unmarshallerFactory, final NotificationManager notificationManager, final AppContext appContext, final AsyncRequestFactory asyncRequestFactory, final WsAgentStateController wsAgentStateController, 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 messageBus) throws OperationException { subscribeToPublishDiagnostics(messageBus); subscribeToShowMessages(messageBus); } }); this.showMessageProcessor = showMessageProcessor; } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#completion(io.typefox.lsapi.TextDocumentPositionParams)} * * @param position * @return */ public Promise<ExtendedCompletionList> completion(TextDocumentPositionParams position) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/completion"; Unmarshallable<ExtendedCompletionList> unmarshaller = unmarshallerFactory.newUnmarshaller(ExtendedCompletionList.class); return asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)position).toJson()).send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#resolveCompletionItem(CompletionItem)} * * @param completionItem * @return */ public Promise<ExtendedCompletionItem> resolveCompletionItem(CompletionItem completionItem) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/completionItem/resolve"; Unmarshallable<ExtendedCompletionItem> unmarshaller = unmarshallerFactory.newUnmarshaller(ExtendedCompletionItem.class); return asyncRequestFactory.createPostRequest(requestUrl, completionItem) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#documentSymbol(io.typefox.lsapi.DocumentSymbolParams)} * * @param params * @return */ public Promise<List<SymbolInformation>> documentSymbol(DocumentSymbolParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/documentSymbol"; Unmarshallable<List<SymbolInformation>> unmarshaller = unmarshallerFactory.newListUnmarshaller(SymbolInformation.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#references(io.typefox.lsapi.ReferenceParams)} * * @param params * @return */ public Promise<List<Location>> references(ReferenceParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/references"; Unmarshallable<List<Location>> unmarshaller = unmarshallerFactory.newListUnmarshaller(Location.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#references(io.typefox.lsapi.ReferenceParams)} * * @param params * @return */ public Promise<List<Location>> definition(TextDocumentPositionParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/definition"; Unmarshallable<List<Location>> unmarshaller = unmarshallerFactory.newListUnmarshaller(Location.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#hover(io.typefox.lsapi.TextDocumentPositionParams)} * * @param params * @return */ public Promise<Hover> hover(TextDocumentPositionParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/hover"; Unmarshallable<Hover> unmarshaller = unmarshallerFactory.newUnmarshaller(Hover.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#signatureHelp(io.typefox.lsapi.TextDocumentPositionParams)} * * @param params * @return */ public Promise<SignatureHelp> signatureHelp(TextDocumentPositionParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/signatureHelp"; Unmarshallable<SignatureHelp> unmarshaller = unmarshallerFactory.newUnmarshaller(SignatureHelp.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#formatting(io.typefox.lsapi.DocumentFormattingParams)} * * @param params * @return */ public Promise<List<TextEdit>> formatting(DocumentFormattingParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/formatting"; Unmarshallable<List<TextEdit>> unmarshaller = unmarshallerFactory.newListUnmarshaller(TextEdit.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#formatting(io.typefox.lsapi.DocumentRangeFormattingParams)} * * @param params * @return */ public Promise<List<TextEdit>> rangeFormatting(DocumentRangeFormattingParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/rangeFormatting"; Unmarshallable<List<TextEdit>> unmarshaller = unmarshallerFactory.newListUnmarshaller(TextEdit.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#formatting(io.typefox.lsapi.DocumentFormattingParams)} * * @param params * @return */ public Promise<List<TextEdit>> onTypeFormatting(DocumentOnTypeFormattingParams params) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/onTypeFormatting"; Unmarshallable<List<TextEdit>> unmarshaller = unmarshallerFactory.newListUnmarshaller(TextEdit.class); return asyncRequestFactory.createPostRequest(requestUrl, params) .header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON) .send(unmarshaller); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didChange(io.typefox.lsapi.DidChangeTextDocumentParams)} * * @param change * @return */ public void didChange(DidChangeTextDocumentParams change) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didChange"; asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)change).toJson()).send(); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didOpen(io.typefox.lsapi.DidOpenTextDocumentParams)} * * @param openEvent * @return */ public void didOpen(DidOpenTextDocumentParams openEvent) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didOpen"; asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)openEvent).toJson()).send(); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didClose(io.typefox.lsapi.DidCloseTextDocumentParams)} * * @param closeEvent * @return */ public void didClose(DidCloseTextDocumentParams closeEvent) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didClose"; asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)closeEvent).toJson()).send(); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didSave(io.typefox.lsapi.DidSaveTextDocumentParams)} * * @param saveEvent * @return */ public void didSave(DidSaveTextDocumentParams saveEvent) { String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didSave"; asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)saveEvent).toJson()).send(); } /** * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#documentHighlight(io.typefox.lsapi.TextDocumentPositionParams * position)} * * @param position * @return a {@link Promise} of an array of {@link DocumentHighlightDTO} which will be computed by the language server. */ public Promise<DocumentHighlight> documentHighlight(TextDocumentPositionParams position) { final String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/documentHighlight"; final Unmarshallable<DocumentHighlight> unmarshaller = unmarshallerFactory.newUnmarshaller(DocumentHighlight.class); return asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)position).toJson()).send(unmarshaller); } public Promise<List<Command>> codeAction(CodeActionParams params) { final String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/codeAction"; final Unmarshallable<List<Command>> unmarshaller = unmarshallerFactory.newListUnmarshaller(Command.class); return asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)params).toJson()).send(unmarshaller); } /** * Subscribes to websocket for 'textDocument/publishDiagnostics' notifications. */ private void subscribeToPublishDiagnostics(final MessageBus messageBus) { org.eclipse.che.ide.websocket.rest.Unmarshallable<PublishDiagnosticsParams> unmarshaller = unmarshallerFactory.newWSUnmarshaller(PublishDiagnosticsParams.class); try { messageBus.subscribe("languageserver/textDocument/publishDiagnostics", new SubscriptionHandler<PublishDiagnosticsParams>(unmarshaller) { @Override protected void onMessageReceived(PublishDiagnosticsParams statusEvent) { publishDiagnosticsProcessor.processDiagnostics(statusEvent); } @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); } } /** * Subscribes to websocket for 'window/showMessage' notifications. */ private void subscribeToShowMessages(final MessageBus messageBus) { final org.eclipse.che.ide.websocket.rest.Unmarshallable<ShowMessageRequestParams> unmarshaller = unmarshallerFactory.newWSUnmarshaller(ShowMessageRequestParams.class); try { messageBus.subscribe("languageserver/window/showMessage", new SubscriptionHandler<ShowMessageRequestParams>(unmarshaller) { @Override protected void onMessageReceived(ShowMessageRequestParams ShowMessageRequestParams) { showMessageProcessor.processNotification(ShowMessageRequestParams); } @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); } } }