blob: 1aa187a5a4adb8cb2dbd3a28ee21adb3f0337e4b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Red Hat Inc. and others.
* 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:
* Mickael Istria (Red Hat Inc.) - initial implementation
*******************************************************************************/
package org.eclipse.lsp4e.operations.diagnostics;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServerPlugin;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.ui.texteditor.MarkerUtilities;
public class LSPDiagnosticsToMarkers implements Consumer<PublishDiagnosticsParams> {
public static final String LSP_DIAGNOSTIC = "lspDiagnostic"; //$NON-NLS-1$
public static final String LANGUAGE_SERVER_ID = "languageServerId"; //$NON-NLS-1$
public static final String LS_DIAGNOSTIC_MARKER_TYPE = "org.eclipse.lsp4e.diagnostic"; //$NON-NLS-1$
private final @NonNull IProject project;
private final @NonNull String languageServerId;
public LSPDiagnosticsToMarkers(@NonNull IProject project, @NonNull String serverId) {
this.project = project;
this.languageServerId = serverId;
}
@Override
public void accept(PublishDiagnosticsParams diagnostics) {
try {
// fix issue with file:/// vs file:/
String uri = diagnostics.getUri();
IResource resource = LSPEclipseUtils.findResourceFor(uri);
if (resource == null || !resource.exists()) {
resource = project;
}
Set<IMarker> remainingMarkers = new HashSet<>(
Arrays.asList(resource.findMarkers(LS_DIAGNOSTIC_MARKER_TYPE, false, IResource.DEPTH_ONE)));
remainingMarkers.removeIf(marker -> !Objects.equals(marker.getAttribute(LANGUAGE_SERVER_ID, ""), languageServerId)); //$NON-NLS-1$
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
IMarker associatedMarker = getExistingMarkerFor(resource, diagnostic, remainingMarkers);
if (associatedMarker == null) {
associatedMarker = resource.createMarker(LS_DIAGNOSTIC_MARKER_TYPE);
} else {
remainingMarkers.remove(associatedMarker);
}
updateMarker(resource, diagnostic, associatedMarker);
}
for (IMarker marker : remainingMarkers) {
marker.delete();
}
} catch (CoreException ex) {
LanguageServerPlugin.logError(ex);
}
}
protected void updateMarker(IResource resource, Diagnostic diagnostic, IMarker marker) {
try {
marker.setAttribute(LSP_DIAGNOSTIC, diagnostic);
marker.setAttribute(LANGUAGE_SERVER_ID, this.languageServerId);
marker.setAttribute(IMarker.MESSAGE, diagnostic.getMessage());
marker.setAttribute(IMarker.SEVERITY, LSPEclipseUtils.toEclipseMarkerSeverity(diagnostic.getSeverity()));
if (resource.getType() != IResource.FILE) {
return;
}
IFile file = (IFile) resource;
ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager();
ITextFileBuffer textFileBuffer = manager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE);
if (textFileBuffer == null) {
manager.connect(file.getFullPath(), LocationKind.IFILE, new NullProgressMonitor());
textFileBuffer = manager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE);
}
IDocument document = textFileBuffer.getDocument();
int start = Math.min(LSPEclipseUtils.toOffset(diagnostic.getRange().getStart(), document),
document.getLength());
int end = Math.min(LSPEclipseUtils.toOffset(diagnostic.getRange().getEnd(), document),
document.getLength());
marker.setAttribute(IMarker.CHAR_START, start);
marker.setAttribute(IMarker.CHAR_END, end);
marker.setAttribute(IMarker.LINE_NUMBER, document.getLineOfOffset(start) + 1);
} catch (CoreException | BadLocationException e) {
LanguageServerPlugin.logError(e);
}
}
private IMarker getExistingMarkerFor(IResource resource, Diagnostic diagnostic, Set<IMarker> remainingMarkers) {
ITextFileBuffer textFileBuffer = FileBuffers.getTextFileBufferManager()
.getTextFileBuffer(resource.getFullPath(), LocationKind.IFILE);
if (textFileBuffer == null) {
return null;
}
IDocument document = textFileBuffer.getDocument();
for (IMarker marker : remainingMarkers) {
int startOffset = MarkerUtilities.getCharStart(marker);
int endOffset = MarkerUtilities.getCharEnd(marker);
try {
if (marker.getResource().getProjectRelativePath().toString().equals(diagnostic.getSource())
&& LSPEclipseUtils.toOffset(diagnostic.getRange().getStart(), document) == startOffset + 1
&& LSPEclipseUtils.toOffset(diagnostic.getRange().getEnd(), document) == endOffset + 1
&& Objects.equals(marker.getAttribute(IMarker.MESSAGE), diagnostic.getMessage())
&& Objects.equals(marker.getAttribute(LANGUAGE_SERVER_ID), this.languageServerId)) {
return marker;
}
} catch (CoreException | BadLocationException e) {
LanguageServerPlugin.logError(e);
}
}
return null;
}
}