Skip to content

Commit

Permalink
Support for showing diagnostics from multiple language servers
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Mäder <tmader@redhat.com>
  • Loading branch information
tsmaeder committed Jul 4, 2017
1 parent 522f7d5 commit 18067e6
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.che.ide.api.editor.text.Position;
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.ide.api.editor.text.annotation.Annotation;
import org.eclipse.che.ide.util.loging.Log;

import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -70,6 +71,7 @@ protected void addAnnotation(final Annotation annotation, final Position positio
try {
addPosition(position);
} catch (BadLocationException ignore) {
Log.error(getClass(), "BadLocation for "+annotation);
//ignore invalid location
}
getAnnotationModelEvent().annotationAdded(annotation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
import org.eclipse.che.requirejs.ModuleHolder;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -687,14 +688,16 @@ public void scrollToLine(int line) {
}

public void showErrors(AnnotationModelEvent event) {
List<Annotation> addedAnnotations = event.getAddedAnnotations();
JsArray<OrionProblemOverlay> jsArray = JsArray.createArray().cast();
AnnotationModel annotationModel = event.getAnnotationModel();
OrionAnnotationSeverityProvider severityProvider = null;
if (annotationModel instanceof OrionAnnotationSeverityProvider) {
severityProvider = (OrionAnnotationSeverityProvider)annotationModel;
}
for (Annotation annotation : addedAnnotations) {

Iterator<Annotation> annotationIterator = annotationModel.getAnnotationIterator();
while (annotationIterator.hasNext()) {
Annotation annotation = annotationIterator.next();
Position position = annotationModel.getPosition(annotation);

OrionProblemOverlay problem = JavaScriptObject.createObject().cast();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ public interface DiagnosticCollector {
* @param diagnostic
* Diagnostic - The discovered diagnostic.
*/
void acceptDiagnostic(Diagnostic diagnostic);
void acceptDiagnostic(String diagnosticsCollection, Diagnostic diagnostic);

/**
* Notification sent before starting the diagnostic process.
* Typically, this would tell a diagnostic collector to clear previously recorded diagnostic.
*/
void beginReporting();
void beginReporting(String diagnosticsCollection);

/**
* Notification sent after having completed diagnostic process.
* Typically, this would tell a diagnostic collector that no more diagnostics should be expected in this
* iteration.
*/
void endReporting();
void endReporting(String diagnosticsCollection);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;

import org.eclipse.che.ide.api.editor.annotation.AnnotationModelImpl;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.document.DocumentHandle;
Expand All @@ -21,11 +20,13 @@
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.ide.api.editor.texteditor.EditorResources;
import org.eclipse.che.ide.editor.orion.client.OrionAnnotationSeverityProvider;
import org.eclipse.che.ide.util.loging.Log;
import org.eclipse.che.plugin.languageserver.ide.LanguageServerResources;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -34,10 +35,11 @@
* @author Evgen Vidolob
*/
public class LanguageServerAnnotationModel extends AnnotationModelImpl implements DiagnosticCollector, OrionAnnotationSeverityProvider {
private final LanguageServerResources.LSCss lsCss;
private final EditorResources.EditorCss editorCss;
private List<Diagnostic> diagnostics;
private List<DiagnosticAnnotation> generatedAnnotations = new ArrayList<>();
private final LanguageServerResources.LSCss lsCss;
private final EditorResources.EditorCss editorCss;
private final Map<String, List<Diagnostic>> diagnostics = new HashMap<>();
private final Map<String, List<Diagnostic>> collectedDiagnostics = new HashMap<>();
private final Map<Diagnostic, DiagnosticAnnotation> generatedAnnotations = new HashMap<>();

@AssistedInject
public LanguageServerAnnotationModel(@Assisted final DocumentPositionMap docPositionMap,
Expand Down Expand Up @@ -75,49 +77,66 @@ protected Position createPositionFromDiagnostic(final Diagnostic diagnostic) {
}

@Override
public void acceptDiagnostic(final Diagnostic problem) {
diagnostics.add(problem);
public void acceptDiagnostic(String diagnosticCollection, final Diagnostic problem) {
collectedDiagnostics.get(diagnosticCollection).add(problem);
}

@Override
public void beginReporting() {
diagnostics = new ArrayList<>();
public void beginReporting(String diagnosticCollection) {
collectedDiagnostics.put(diagnosticCollection, new ArrayList<>());
}

@Override
public void endReporting() {
reportDiagnostic();
public void endReporting(String diagnosticCollection) {
reportDiagnostic(diagnosticCollection, collectedDiagnostics.remove(diagnosticCollection));
}

private void reportDiagnostic() {
private void reportDiagnostic(String diagnosticCollection, List<Diagnostic> newDiagnostics) {
boolean temporaryProblemsChanged = false;

if (!generatedAnnotations.isEmpty()) {
temporaryProblemsChanged = true;
super.clear();
generatedAnnotations.clear();
}

if (diagnostics != null && !diagnostics.isEmpty()) {

for (final Diagnostic diagnostic : diagnostics) {
final Position position = createPositionFromDiagnostic(diagnostic);

if (position != null) {
final DiagnosticAnnotation annotation = new DiagnosticAnnotation(diagnostic);
addAnnotation(annotation, position, false);
generatedAnnotations.add(annotation);

temporaryProblemsChanged = true;
}
List<Diagnostic> currentDiagnostics = diagnostics.getOrDefault(diagnosticCollection, Collections.emptyList());
// new list becomes previous list, but make a copy; the collection is modified below.
diagnostics.put(diagnosticCollection, new ArrayList<>(newDiagnostics));

for (Diagnostic diagnostic : currentDiagnostics) {
// go through the current set and remove those that are not present
// in the new set.
if (!newDiagnostics.contains(diagnostic)) {
removeAnnotationFor(diagnostic);
temporaryProblemsChanged= true;
} else {
newDiagnostics.remove(diagnostic);
}
}

for (Diagnostic diagnostic : newDiagnostics) {
// now go through the ones left in the new set: they are new
addAnnotationFor(diagnostic);
temporaryProblemsChanged= true;
}

if (temporaryProblemsChanged) {
fireModelChanged();
}
}

private void addAnnotationFor(Diagnostic diagnostic) {
Log.debug(getClass(), "adding annotation for "+diagnostic);
DiagnosticAnnotation annotation = new DiagnosticAnnotation(diagnostic);
generatedAnnotations.put(diagnostic, annotation);
Position position = createPositionFromDiagnostic(diagnostic);
if (position != null) {
addAnnotation(annotation, position, false);
} else {
Log.error(getClass(), "Position is null for "+diagnostic);
}
}

private void removeAnnotationFor(Diagnostic diagnostic) {
Log.debug(getClass(), "removing annotation for "+diagnostic);
DiagnosticAnnotation annotation = generatedAnnotations.remove(diagnostic);
removeAnnotation(annotation, false);
}

@Override
public Map<String, String> getAnnotationDecorations() {
final Map<String, String> decorations = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@

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

import org.eclipse.che.api.languageserver.shared.model.ExtendedPublishDiagnosticsParams;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.editor.annotation.AnnotationModel;
import org.eclipse.che.ide.api.editor.editorconfig.TextEditorConfiguration;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.resource.Path;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.PublishDiagnosticsParams;

/**
* @author Anatolii Bazko
Expand All @@ -35,8 +34,8 @@ public PublishDiagnosticsProcessor(EditorAgent editorAgent) {
this.editorAgent = editorAgent;
}

public void processDiagnostics(PublishDiagnosticsParams diagnosticsMessage) {
EditorPartPresenter openedEditor = editorAgent.getOpenedEditor(new Path(diagnosticsMessage.getUri()));
public void processDiagnostics(ExtendedPublishDiagnosticsParams diagnosticsMessage) {
EditorPartPresenter openedEditor = editorAgent.getOpenedEditor(new Path(diagnosticsMessage.getParams().getUri()));
//TODO add markers
if (openedEditor == null) {
return;
Expand All @@ -47,13 +46,14 @@ public void processDiagnostics(PublishDiagnosticsParams diagnosticsMessage) {
AnnotationModel annotationModel = editorConfiguration.getAnnotationModel();
if (annotationModel != null && annotationModel instanceof DiagnosticCollector) {
DiagnosticCollector collector = (DiagnosticCollector)annotationModel;
collector.beginReporting();
String languageServerId = diagnosticsMessage.getLanguageServerId();
collector.beginReporting(languageServerId);
try {
for (Diagnostic diagnostic : diagnosticsMessage.getDiagnostics()) {
collector.acceptDiagnostic(diagnostic);
for (Diagnostic diagnostic : diagnosticsMessage.getParams().getDiagnostics()) {
collector.acceptDiagnostic(languageServerId, diagnostic);
}
} finally {
collector.endReporting();
collector.endReporting(languageServerId);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator;
import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter;
import org.eclipse.che.api.languageserver.shared.model.ExtendedPublishDiagnosticsParams;
import org.eclipse.che.plugin.languageserver.ide.editor.PublishDiagnosticsProcessor;
import org.eclipse.lsp4j.PublishDiagnosticsParams;

/**
* Subscribes and receives JSON-RPC messages related to 'textDocument/publishDiagnostics' events
Expand All @@ -28,7 +27,7 @@ public class PublishDiagnosticsReceiver {
private void configureReceiver(Provider<PublishDiagnosticsProcessor> provider, RequestHandlerConfigurator configurator) {
configurator.newConfiguration()
.methodName("textDocument/publishDiagnostics")
.paramsAsDto(PublishDiagnosticsParams.class)
.paramsAsDto(ExtendedPublishDiagnosticsParams.class)
.noResult()
.withConsumer(params -> provider.get().processDiagnostics(params));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Red Hat
* 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:
* Red Hat - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.languageserver.shared.model;

import org.eclipse.lsp4j.PublishDiagnosticsParams;

/**
* Extends diagnostics notification with a server id, to keep diagnostics in
* different name spaces with a different update cycle
*
* @author Thomas Mäder
*
*/
public class ExtendedPublishDiagnosticsParams {
private PublishDiagnosticsParams params;
private String languageServerId;

public ExtendedPublishDiagnosticsParams() {
}

public ExtendedPublishDiagnosticsParams(String serverId, PublishDiagnosticsParams diagnostics) {
this.languageServerId = serverId;
this.params = diagnostics;
}

public PublishDiagnosticsParams getParams() {
return params;
}

public void setParams(PublishDiagnosticsParams params) {
this.params = params;
}

public String getLanguageServerId() {
return languageServerId;
}

public void setLanguageServerId(String languageServerId) {
this.languageServerId = languageServerId;
}
}
1 change: 1 addition & 0 deletions wsagent/che-core-api-languageserver/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
<exclude>**/LanguageDescription.java</exclude>
<exclude>**/ServerCapabilitiesOverlay.java</exclude>
<exclude>**/DocumentFilter.java</exclude>
<exclude>**/CheLanguageClient.java</exclude>
<exclude>**/LanguageServerDescription.java</exclude>
<exclude>**/LSOperation.java</exclude>
<exclude>**/OperationUtil.java</exclude>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator;
import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.PublishDiagnosticsParamsDto;
import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.ExtendedPublishDiagnosticsParamsDto;
import org.eclipse.che.api.languageserver.shared.model.ExtendedPublishDiagnosticsParams;
import org.eclipse.lsp4j.PublishDiagnosticsParams;

import javax.inject.Inject;
import javax.inject.Singleton;

import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

Expand All @@ -31,15 +33,16 @@ public class PublishDiagnosticsParamsJsonRpcTransmitter {
@Inject
private void subscribe(EventService eventService, RequestTransmitter requestTransmitter) {
eventService.subscribe(event -> {
if(event.getUri() != null) {
event.setUri(event.getUri().substring(16));
PublishDiagnosticsParams params = event.getParams();
if(params.getUri() != null) {
params.setUri(params.getUri().substring(16));
}
endpointIds.forEach(endpointId -> requestTransmitter.newRequest()
.endpointId(endpointId)
.methodName("textDocument/publishDiagnostics")
.paramsAsDto(new PublishDiagnosticsParamsDto(event))
.paramsAsDto(new ExtendedPublishDiagnosticsParamsDto(event))
.sendAndSkipResult());
}, PublishDiagnosticsParams.class);
}, ExtendedPublishDiagnosticsParams.class);
}

@Inject
Expand Down
Loading

0 comments on commit 18067e6

Please sign in to comment.