Skip to content

Commit

Permalink
Issue eclipse-che#1802 - Document highlightings (eclipse-che#3343)
Browse files Browse the repository at this point in the history
Providing support for occurrences highlighting, with a
restriction due a bug in the io.typefox.lsapi.services 0.3.0
bundle, which assumes that the language server will return
a single occurrence to highlight, instead of a list of
occurrences.

Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
  • Loading branch information
xcoulon authored and Vitalii Parfonov committed Feb 21, 2017
1 parent 6b3f692 commit 9cc4fe5
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
import com.google.gwt.core.client.JsArrayString;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import static com.google.common.collect.Lists.newArrayList;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.che.api.languageserver.shared.lsapi.LanguageDescriptionDTO;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
Expand All @@ -25,33 +33,41 @@
import org.eclipse.che.ide.api.filetypes.FileTypeRegistry;
import org.eclipse.che.ide.editor.orion.client.OrionContentTypeRegistrant;
import org.eclipse.che.ide.editor.orion.client.OrionHoverRegistrant;
import org.eclipse.che.ide.editor.orion.client.OrionOccurrencesRegistrant;
import org.eclipse.che.ide.editor.orion.client.jso.OrionContentTypeOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionHighlightingConfigurationOverlay;
import org.eclipse.che.plugin.languageserver.ide.editor.LanguageServerEditorProvider;
import org.eclipse.che.plugin.languageserver.ide.highlighting.OccurrencesProvider;
import org.eclipse.che.plugin.languageserver.ide.hover.HoverProvider;
import org.eclipse.che.plugin.languageserver.ide.service.LanguageServerRegistryServiceClient;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.google.common.collect.Lists.newArrayList;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.JsArrayString;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
* @author Evgen Vidolob
*/
@Singleton
public class LanguageServerFileTypeRegister implements WsAgentComponent {




private final LanguageServerRegistryServiceClient serverLanguageRegistry;
private final FileTypeRegistry fileTypeRegistry;
private final LanguageServerResources resources;
private final EditorRegistry editorRegistry;
private final OrionContentTypeRegistrant contentTypeRegistrant;
private final OrionHoverRegistrant orionHoverRegistrant;
private final LanguageServerEditorProvider editorProvider;
private final HoverProvider hoverProvider;
private final FileTypeRegistry fileTypeRegistry;
private final LanguageServerResources resources;
private final EditorRegistry editorRegistry;
private final OrionContentTypeRegistrant contentTypeRegistrant;
private final OrionHoverRegistrant orionHoverRegistrant;
private final OrionOccurrencesRegistrant orionOccurrencesRegistrant;
private final LanguageServerEditorProvider editorProvider;
private final HoverProvider hoverProvider;
private final OccurrencesProvider occurrencesProvider;

private final Map<String, String> ext2langId = new HashMap<>();

Expand All @@ -62,16 +78,20 @@ public LanguageServerFileTypeRegister(LanguageServerRegistryServiceClient server
EditorRegistry editorRegistry,
OrionContentTypeRegistrant contentTypeRegistrant,
OrionHoverRegistrant orionHoverRegistrant,
OrionOccurrencesRegistrant orionOccurrencesRegistrant,
LanguageServerEditorProvider editorProvider,
HoverProvider hoverProvider) {
HoverProvider hoverProvider,
OccurrencesProvider occurrencesProvider) {
this.serverLanguageRegistry = serverLanguageRegistry;
this.fileTypeRegistry = fileTypeRegistry;
this.resources = resources;
this.editorRegistry = editorRegistry;
this.contentTypeRegistrant = contentTypeRegistrant;
this.orionHoverRegistrant = orionHoverRegistrant;
this.orionOccurrencesRegistrant = orionOccurrencesRegistrant;
this.editorProvider = editorProvider;
this.hoverProvider = hoverProvider;
this.occurrencesProvider = occurrencesProvider;
}

@Override
Expand Down Expand Up @@ -108,11 +128,12 @@ public void apply(List<LanguageDescriptionDTO> langs) throws OperationException
config.setId(lang.getLanguageId() + ".highlighting");
config.setContentTypes(contentTypeId);
config.setPatterns(lang.getHighlightingConfiguration());

Logger logger = Logger.getLogger(LanguageServerFileTypeRegister.class.getName());
contentTypeRegistrant.registerFileType(contentType, config);
}
}
orionHoverRegistrant.registerHover(contentTypes, hoverProvider);
orionOccurrencesRegistrant.registerOccurrencesHandler(contentTypes, occurrencesProvider);
}
callback.onSuccess(LanguageServerFileTypeRegister.this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.eclipse.che.plugin.languageserver.ide.highlighting;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.che.api.languageserver.shared.lsapi.DocumentHighlightDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.TextDocumentPositionParamsDTO;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.FunctionException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.js.JsPromise;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.editor.orion.client.OrionOccurrencesHandler;
import org.eclipse.che.ide.editor.orion.client.jso.OrionOccurrenceContextOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionOccurrenceOverlay;
import org.eclipse.che.plugin.languageserver.ide.editor.LanguageServerEditorConfiguration;
import org.eclipse.che.plugin.languageserver.ide.service.TextDocumentServiceClient;
import org.eclipse.che.plugin.languageserver.ide.util.DtoBuildHelper;

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

/**
* Provides occurrences highlights for the Orion Editor.
*
* @author Xavier Coulon, Red Hat
*/
@Singleton
public class OccurrencesProvider implements OrionOccurrencesHandler {

private static final Logger LOGGER = Logger.getLogger(OccurrencesProvider.class.getName());
private final EditorAgent editorAgent;
private final TextDocumentServiceClient client;
private final DtoBuildHelper helper;

/**
* Constructor.
* @param editorAgent
* @param client
* @param helper
*/
@Inject
public OccurrencesProvider(EditorAgent editorAgent, TextDocumentServiceClient client, DtoBuildHelper helper) {
this.editorAgent = editorAgent;
this.client = client;
this.helper = helper;
}

@Override
public JsPromise<OrionOccurrenceOverlay[]> computeOccurrences(
OrionOccurrenceContextOverlay context) {
final EditorPartPresenter activeEditor = editorAgent.getActiveEditor();
if (activeEditor == null || !(activeEditor instanceof TextEditor)) {
return null;
}
final TextEditor editor = ((TextEditor)activeEditor);
if (!(editor.getConfiguration() instanceof LanguageServerEditorConfiguration)) {
return null;
}
final LanguageServerEditorConfiguration configuration = (LanguageServerEditorConfiguration)editor.getConfiguration();
if (configuration.getServerCapabilities().isDocumentHighlightProvider() == null || !configuration.getServerCapabilities().isDocumentHighlightProvider()) {
return null;
}
final Document document = editor.getDocument();
final TextDocumentPositionParamsDTO paramsDTO = helper.createTDPP(document, context.getStart());
// FIXME: the result should be a Promise<List<DocumentHighlightDTO>> but the typefox API returns a single DocumentHighlightDTO
Promise<DocumentHighlightDTO> promise = client.documentHighlight(paramsDTO);
Promise<OrionOccurrenceOverlay[]> then = promise.then(new Function<DocumentHighlightDTO, OrionOccurrenceOverlay[]>() {
@Override
public OrionOccurrenceOverlay[] apply(DocumentHighlightDTO highlight) throws FunctionException {
if(highlight == null) {
return new OrionOccurrenceOverlay[0];
}
final OrionOccurrenceOverlay[] occurrences = new OrionOccurrenceOverlay[1];
final OrionOccurrenceOverlay occurrence = OrionOccurrenceOverlay.create();
// FIXME: this assumes that the language server will
// compute a range based on 'line 1', ie, the whole
// file content is on line 1 and the location to
// highlight is given by the 'character' position
// only.
occurrence.setStart(highlight.getRange().getStart().getCharacter());
occurrence.setEnd(highlight.getRange().getEnd().getCharacter() + 1);
occurrences[0] = occurrence;
return occurrences;
}
});
return (JsPromise<OrionOccurrenceOverlay[]>)then;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
*******************************************************************************/
package org.eclipse.che.plugin.languageserver.ide.service;

import io.typefox.lsapi.CompletionItem;
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;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;

import org.eclipse.che.api.languageserver.shared.lsapi.CompletionItemDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.CompletionListDTO;
Expand All @@ -22,6 +23,7 @@
import org.eclipse.che.api.languageserver.shared.lsapi.DidOpenTextDocumentParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.DidSaveTextDocumentParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.DocumentFormattingParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.DocumentHighlightDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.DocumentOnTypeFormattingParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.DocumentRangeFormattingParamsDTO;
import org.eclipse.che.api.languageserver.shared.lsapi.DocumentSymbolParamsDTO;
Expand Down Expand Up @@ -52,11 +54,10 @@
import org.eclipse.che.plugin.languageserver.ide.editor.PublishDiagnosticsProcessor;
import org.eclipse.che.plugin.languageserver.ide.editor.ShowMessageProcessor;

import java.util.List;
import com.google.inject.Inject;
import com.google.inject.Singleton;

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;
import io.typefox.lsapi.CompletionItem;


/**
Expand Down Expand Up @@ -291,6 +292,19 @@ public void didSave(DidSaveTextDocumentParamsDTO saveEvent) {
.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<DocumentHighlightDTO> documentHighlight(TextDocumentPositionParamsDTO position) {
final String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/documentHighlight";
final Unmarshallable<DocumentHighlightDTO> unmarshaller = unmarshallerFactory.newUnmarshaller(DocumentHighlightDTO.class);
return asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON)
.header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)position).toJson()).send(unmarshaller);
}
/**
* Subscribes to websocket for 'textDocument/publishDiagnostics' notifications.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*******************************************************************************
* 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.ide.editor.orion.client;

import org.eclipse.che.api.promises.client.js.JsPromise;
import org.eclipse.che.ide.editor.orion.client.jso.OrionOccurrenceContextOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionOccurrenceOverlay;

/**
* @author Xavier Coulon, Red Hat
*/
public interface OrionOccurrencesHandler {
JsPromise<OrionOccurrenceOverlay[]> computeOccurrences(OrionOccurrenceContextOverlay context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*******************************************************************************
* 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.ide.editor.orion.client;

import com.google.gwt.core.client.JsArrayString;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.ide.editor.orion.client.jso.OrionCodeEditWidgetOverlay;
import org.eclipse.che.ide.editor.orion.client.jso.OrionServiceRegistryOverlay;

/**
* @author Xavier Coulon,Red Hat
*/
@Singleton
public class OrionOccurrencesRegistrant {

private final Provider<OrionCodeEditWidgetOverlay> codeEditWidgetProvider;
private final EditorInitializePromiseHolder editorModule;

@Inject
public OrionOccurrencesRegistrant(Provider<OrionCodeEditWidgetOverlay> codeEditWidgetProvider,
EditorInitializePromiseHolder editorModule) {
this.codeEditWidgetProvider = codeEditWidgetProvider;
this.editorModule = editorModule;
}

public void registerOccurrencesHandler(final JsArrayString contentTypes, final OrionOccurrencesHandler handler) {
editorModule.getInitializerPromise().then(new Operation<Void>() {
@Override
public void apply(Void arg) throws OperationException {
registerOccurrencesHandler(codeEditWidgetProvider.get().getServiceRegistry(), contentTypes, handler);
}
});
}

private final native void registerOccurrencesHandler(OrionServiceRegistryOverlay serviceRegistry, JsArrayString contentTypes,
OrionOccurrencesHandler handler) /*-{
serviceRegistry.registerService("orion.edit.occurrences", {
computeOccurrences: function(editorContext, context) {
return handler.@OrionOccurrencesHandler::computeOccurrences(*)(context);
}
}, {
name: "Occurrences",
contentType: contentTypes
});
}-*/;


}
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@ protected void configure() {
install(new GinFactoryModuleBuilder().build(ContentAssistWidgetFactory.class));

GinMultibinder.newSetBinder(binder(), OrionPlugin.class).addBinding().to(JavaHighlightingOrionPlugin.class);

//GinMultibinder.newSetBinder(binder(), OrionPlugin.class).addBinding().to(LanguageServerHighlightingOrionPlugin.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
* 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.ide.editor.orion.client.jso;

import com.google.gwt.core.client.JavaScriptObject;

/**
* The 'Occurrence Object' for Orion occurrences
* See <a href="https://wiki.eclipse.org/Orion/Documentation/Developer_Guide/Plugging_into_the_editor#orion.edit.occurrences">Orion Occurrences</a>
*
* @author Xavier Coulon, Red Hat
*/
public class OrionOccurrenceContextOverlay extends JavaScriptObject{
protected OrionOccurrenceContextOverlay() {}

public final native String getContentType() /*-{
return this.contentType;
}-*/;

/**
* @return The offset into the file for the start of the occurrence.
*/
public final native int getStart() /*-{
return this.selection.start;
}-*/;

/**
* @return The offset into the file for the end of the occurrence.
*/
public final native int getEnd() /*-{
return this.selection.end;
}-*/;
}

Loading

0 comments on commit 9cc4fe5

Please sign in to comment.