Skip to content

Commit

Permalink
[eclipse-cdt#247] Add .clangd configuration file syntax checker
Browse files Browse the repository at this point in the history
- Fetch info from MarkedYAMLException to create a marker for the .clangd
file.

fixes eclipse-cdt#247
  • Loading branch information
ghentschke committed Feb 7, 2024
1 parent 64f8d69 commit c714fe7
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 196 deletions.
3 changes: 2 additions & 1 deletion bundles/org.eclipse.cdt.lsp.clangd/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ Require-Bundle: org.eclipse.cdt.lsp;bundle-version="0.0.0",
org.eclipse.ui.ide;bundle-version="0.0.0",
org.eclipse.ui.workbench;bundle-version="0.0.0",
org.eclipse.ui.workbench.texteditor;bundle-version="0.0.0",
org.eclipse.core.variables;bundle-version="0.0.0"
org.eclipse.core.variables;bundle-version="0.0.0",
org.yaml.snakeyaml;bundle-version="0.0.0"
Service-Component: OSGI-INF/org.eclipse.cdt.lsp.clangd.BuiltinClangdOptionsDefaults.xml,
OSGI-INF/org.eclipse.cdt.lsp.clangd.ClangdConfigurationFileManager.xml,
OSGI-INF/org.eclipse.cdt.lsp.internal.clangd.ClangdConfigurationAccess.xml,
Expand Down
2 changes: 1 addition & 1 deletion bundles/org.eclipse.cdt.lsp.clangd/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
</extension>
<extension
id="org.eclipse.cdt.lsp.clangd.config.marker"
name="Clangd Marker"
name=".clangd yaml Problem"
point="org.eclipse.core.resources.markers">
<super
type="org.eclipse.core.resources.problemmarker">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,21 @@
package org.eclipse.cdt.lsp.internal.clangd;

import java.io.IOException;
import java.util.Optional;
import java.util.regex.Pattern;

import org.eclipse.cdt.lsp.internal.clangd.editor.ClangdPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.MarkedYAMLException;

/**
* Checks the <code>.clangd</code> file for syntax errors and notifies the user via error markers in the file and Problems view.
*/
public class ClangdConfigFileChecker {
public static final String CLANGD_MARKER = ClangdPlugin.PLUGIN_ID + ".config.marker"; //$NON-NLS-1$
private final Pattern pattern = Pattern.compile(".*line (\\d+), column (\\d+).*"); //$NON-NLS-1$
private boolean temporaryLoadedFile = false;

/**
* Checks if the .clangd file contains valid yaml syntax. Adds error marker to the file if not.
Expand All @@ -46,17 +41,23 @@ public void checkConfigFile(IFile configFile) {
removeMarkerFromClangdConfig(configFile);
//throws ScannerException and ParserException:
yaml.load(inputStream);
} catch (Exception yamlException) {
addMarkerToClangdConfig(configFile, yamlException);
} catch (Exception exception) {
if (exception instanceof MarkedYAMLException yamlException) {
addMarkerToClangdConfig(configFile, yamlException);
} else {
//log unexpected exception:
Platform.getLog(getClass())
.error("Expected MarkedYAMLException, but was: " + exception.getMessage(), exception); //$NON-NLS-1$
}
}
} catch (IOException | CoreException e) {
Platform.getLog(getClass()).error(e.getMessage(), e);
}
}

private void addMarkerToClangdConfig(IFile configFile, Exception e) {
private void addMarkerToClangdConfig(IFile configFile, MarkedYAMLException yamlException) {
try {
var configMarker = parseYamlException(e, configFile);
var configMarker = parseYamlException(yamlException);
var marker = configFile.createMarker(CLANGD_MARKER);
marker.setAttribute(IMarker.MESSAGE, configMarker.message);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
Expand All @@ -68,6 +69,14 @@ private void addMarkerToClangdConfig(IFile configFile, Exception e) {
}
}

private void removeMarkerFromClangdConfig(IFile configFile) {
try {
configFile.deleteMarkers(CLANGD_MARKER, false, IResource.DEPTH_INFINITE);
} catch (CoreException e) {
Platform.getLog(getClass()).log(e.getStatus());
}
}

private class ClangdConfigMarker {
public String message;
public int line = 1;
Expand All @@ -77,96 +86,30 @@ private class ClangdConfigMarker {

/**
* Fetch line and char position information from exception to create a marker for the .clangd file.
* @param e
* @param exception
* @param file
* @return
*/
private ClangdConfigMarker parseYamlException(Exception e, IFile file) {
private ClangdConfigMarker parseYamlException(MarkedYAMLException exception) {
var marker = new ClangdConfigMarker();
marker.message = getErrorMessage(e);
var doc = getDocument(file);
if (doc == null) {
return marker;
}
int startLine = -1;
int endLine = -1;
for (var line : toLines(e.getMessage())) {
var matcher = pattern.matcher(line);
if (matcher.matches()) {
var lineInt = Integer.parseInt(matcher.replaceAll("$1")); //$NON-NLS-1$
var column = Integer.parseInt(matcher.replaceAll("$2")); //$NON-NLS-1$
if (startLine == -1) {
startLine = lineInt;
} else if (endLine == -1) {
endLine = lineInt;
}
try {
if (marker.charStart == -1 && startLine > -1) {
var lineOffset = doc.getLineOffset(startLine - 1);
marker.charStart = lineOffset + column - 1;
} else if (marker.charEnd == -1 && endLine > -1) {
var lineOffset = doc.getLineOffset(endLine - 1);
marker.charEnd = lineOffset + column - 1;
}
} catch (BadLocationException bl) {
Platform.getLog(getClass()).error(bl.getMessage(), bl);
}
if (startLine > -1 && endLine > -1)
break;
}
}
//check if endChar has been found:
if (marker.charEnd == -1) {
if (marker.charStart < doc.getLength() - 1) {
marker.charEnd = marker.charStart + 1;
} else if (marker.charStart == doc.getLength() - 1 && marker.charStart > 0) {
marker.charEnd = marker.charStart;
marker.charStart--;
} else {
marker.charStart = 0;
marker.charEnd = 1;
}
}
cleanUp(file);
if (startLine > -1) {
marker.line = startLine;
marker.message = exception.getProblem();
marker.line = exception.getProblemMark().getLine() + 1; //getLine() is zero based, IMarker wants 1-based
int index = exception.getProblemMark().getIndex();
var buffer = exception.getProblemMark().getBuffer();
if (index == buffer.length) {
index = getIndexOfLastPrintableChar(buffer);
}
marker.charStart = index;
marker.charEnd = index + 1;
return marker;
}

private String[] toLines(String message) {
return Optional.ofNullable(message).map(m -> m.lines().toArray(String[]::new)).orElse(new String[] {});
}

private String getErrorMessage(Exception e) {
return Optional.ofNullable(e.getLocalizedMessage())
.map(m -> m.replaceAll("[" + System.lineSeparator() + "]", " ")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
.orElse("Unknown yaml error"); //$NON-NLS-1$
}

private void removeMarkerFromClangdConfig(IFile configFile) {
try {
configFile.deleteMarkers(CLANGD_MARKER, false, IResource.DEPTH_INFINITE);
} catch (CoreException e) {
Platform.getLog(getClass()).log(e.getStatus());
}
}

private IDocument getDocument(IFile file) {
IDocument document = FileUtils.getDocumentFromBuffer(file);
if (document != null)
return document;
document = FileUtils.loadFileTemporary(file);
if (document != null)
temporaryLoadedFile = true;
return document;
}

private void cleanUp(IFile file) {
if (temporaryLoadedFile) {
FileUtils.disconnectTemporaryLoadedFile(file);
temporaryLoadedFile = false;
private int getIndexOfLastPrintableChar(int[] buffer) {
for (int i = buffer.length - 1; i >= 0; i--) {
if ('\r' != ((char) buffer[i]) && '\n' != ((char) buffer[i])) {
return i;
}
}
return Math.max(0, buffer.length - 2);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import java.util.concurrent.ConcurrentLinkedQueue;

import org.eclipse.cdt.lsp.internal.clangd.editor.ClangdPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
Expand All @@ -25,8 +24,8 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.statushandlers.StatusManager;

/**
* Monitor changes in <code>.clangd</code> files in the workspace and triggers a yaml checker
Expand All @@ -44,7 +43,7 @@ public void resourceChanged(IResourceChangeEvent event) {
if (event.getDelta() != null && event.getType() == IResourceChangeEvent.POST_CHANGE) {
try {
event.getDelta().accept(delta -> {
if ((delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.REMOVED
if ((delta.getKind() == IResourceDelta.ADDED
|| (delta.getFlags() & IResourceDelta.CONTENT) != 0)
&& CLANGD_CONFIG_FILE.equals(delta.getResource().getName())) {
if (delta.getResource() instanceof IFile file) {
Expand All @@ -55,7 +54,7 @@ public void resourceChanged(IResourceChangeEvent event) {
return true;
});
} catch (CoreException e) {
StatusManager.getManager().handle(e, ClangdPlugin.PLUGIN_ID);
Platform.getLog(getClass()).log(e.getStatus());
}
}
}
Expand Down

This file was deleted.

0 comments on commit c714fe7

Please sign in to comment.