Skip to content

Commit

Permalink
Merge pull request #7725 from dbalek/dbalek/lsp-push-diagnostics
Browse files Browse the repository at this point in the history
LSP: Push diagnostics collected on project indexing.
  • Loading branch information
dbalek committed Sep 5, 2024
2 parents 083226f + 0d6fb1a commit c934998
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@
*/
package org.netbeans.modules.micronaut.hints;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -47,10 +41,10 @@
import org.netbeans.modules.parsing.spi.Parser;
import org.netbeans.modules.parsing.spi.indexing.CustomIndexer;
import org.netbeans.modules.parsing.spi.indexing.CustomIndexerFactory;
import org.netbeans.modules.parsing.spi.indexing.ErrorsCache;
import org.netbeans.modules.parsing.spi.indexing.Indexable;
import org.netbeans.spi.lsp.ErrorProvider;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;

Expand All @@ -61,6 +55,8 @@
public class MicronautConfigErrorProvider extends CustomIndexer implements ErrorProvider {

private static final MicronautConfigErrorProvider INSTANCE = new MicronautConfigErrorProvider();
private static final ErrorsCache.Convertor<Diagnostic> ERROR_CONVERTOR = new ErrorConvertorImpl();
private static final String ERR_CODE_PREFIX = "WARN_PropertyWithoutValue:";

@MimeRegistrations({
@MimeRegistration(mimeType = MicronautConfigUtilities.YAML_MIME, service = ErrorProvider.class),
Expand Down Expand Up @@ -90,7 +86,7 @@ protected void index(Iterable<? extends Indexable> files, org.netbeans.modules.p
for (Indexable file : files) {
FileObject fo = root.getFileObject(file.getRelativePath());
if (fo != null && MicronautConfigUtilities.isMicronautConfigFile(fo)) {
store(context.getIndexFolder(), file.getRelativePath(), computeErrors(fo));
ErrorsCache.setErrors(context.getRootURI(), file, computeErrors(fo), ERROR_CONVERTOR);
}
}
}
Expand All @@ -109,12 +105,13 @@ public void run(ResultIterator it) throws Exception {
if (language != null) {
StructureScanner scanner = language.getStructure();
if (scanner != null && result instanceof ParserResult) {
scan(snapshot.getText().toString(), scanner.scan((ParserResult) result), structure -> {
String text = snapshot.getText().toString();
scan(text, scanner.scan((ParserResult) result), structure -> {
int start = (int) structure.getPosition();
int end = (int) structure.getEndPosition();
diags.add(Diagnostic.Builder.create(() -> start, () -> end, Bundle.ERR_PropertyWithoutValue())
.setSeverity(Diagnostic.Severity.Warning)
.setCode("WARN_PropertyWithoutValue " + start + " - " + end)
.setCode(ERR_CODE_PREFIX + text.substring(0, start).split("\n").length)
.build());
});
}
Expand All @@ -127,7 +124,9 @@ public void run(ResultIterator it) throws Exception {
}
} else {
int offset = 0;
for(String line : Source.create(fo).createSnapshot().getText().toString().split("\n")) {
String[] lines = Source.create(fo).createSnapshot().getText().toString().split("\n");
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line.length() > 0 && !line.startsWith("#") && !line.startsWith("!")) {
int eqIdx = line.indexOf('=');
if (eqIdx > 0) {
Expand All @@ -136,7 +135,7 @@ public void run(ResultIterator it) throws Exception {
int end = offset + line.length();
diags.add(Diagnostic.Builder.create(() -> start, () -> end, Bundle.ERR_PropertyWithoutValue())
.setSeverity(Diagnostic.Severity.Warning)
.setCode("WARN_PropertyWithoutValue " + start + " - " + end)
.setCode(ERR_CODE_PREFIX + (i + 1))
.build());
}
}
Expand All @@ -147,29 +146,6 @@ public void run(ResultIterator it) throws Exception {
return diags;
}

private void store(FileObject indexFolder, String resourceName, List<? extends Diagnostic> diags) {
File cacheRoot = FileUtil.toFile(indexFolder);
File output = new File(cacheRoot, resourceName + ".err"); //NOI18N
if (diags.isEmpty()) {
if (output.exists()) {
output.delete();
}
} else {
output.getParentFile().mkdirs();
try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output), StandardCharsets.UTF_8))) {
for (Diagnostic diag : diags) {
pw.print(diag.getCode());
pw.print(':'); //NOI18N
pw.print(diag.getStartPosition().getOffset());
pw.print('-'); //NOI18N
pw.println(diag.getEndPosition().getOffset());
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}

private static void scan(String sourceText, List<? extends StructureItem> structures, Consumer<StructureItem> callback) {
if (structures != null) {
for (StructureItem structure : structures) {
Expand Down Expand Up @@ -221,4 +197,19 @@ public int getIndexVersion() {
return MicronautSymbolFinder.VERSION;
}
}

private static final class ErrorConvertorImpl implements ErrorsCache.Convertor<Diagnostic> {
@Override
public ErrorsCache.ErrorKind getKind(Diagnostic t) {
return t.getSeverity() == Diagnostic.Severity.Error ? ErrorsCache.ErrorKind.ERROR : ErrorsCache.ErrorKind.WARNING;
}
@Override
public int getLineNumber(Diagnostic t) {
return Integer.parseInt(t.getCode().substring(ERR_CODE_PREFIX.length()));
}
@Override
public String getMessage(Diagnostic t) {
return t.getDescription();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,6 @@ public Set<? extends Descriptor> getSymbols(Project project, String textForQuery
if (!duplicates.isEmpty()) {
control.diagnosticChanged(duplicates, "text/x-java");
}
Set<FileObject> errs = getResourcesWithErrors(project);
if (!errs.isEmpty()) {
control.diagnosticChanged(errs, null);
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
Expand Down Expand Up @@ -129,35 +125,6 @@ static Set<SymbolDescriptor> getSymbolsWithPathDuplicates(Project project, FileO
return duplicates;
}

private static Set<FileObject> getResourcesWithErrors(Project project) {
Set<FileObject> files = new HashSet<>();
for (SourceGroup sg : ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_RESOURCES)) {
try {
FileObject root = sg.getRootFolder();
FileObject cacheRoot = getCacheRoot(root.toURL());
if (cacheRoot != null) {
cacheRoot.refresh();
Enumeration<? extends FileObject> children = cacheRoot.getChildren(true);
while (children.hasMoreElements()) {
FileObject child = children.nextElement();
if (child.hasExt("err")) { //NOI18N
String path = FileUtil.getRelativePath(cacheRoot, child);
if (path != null) {
FileObject fo = root.getFileObject(path.substring(0, path.length() - 4));
if (fo != null) {
files.add(fo);
}
}
}
}
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
return files;
}

private static Set<SymbolDescriptor> getSymbols(Project project, String textForQuery) {
Set<SymbolDescriptor> symbols = new HashSet<>();
for (SourceGroup sg : ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
Expand Down
14 changes: 14 additions & 0 deletions ide/parsing.indexing/apichanges.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ is the proper place.
<!-- ACTUAL CHANGES BEGIN HERE: -->

<changes>
<change id="ErrorsCache.getAllFilesWithRecord">
<api name="IndexingAPI"/>
<summary>Added method to ErrorsCache to return all files with error or warning</summary>
<version major="9" minor="36"/>
<date day="3" month="9" year="2024"/>
<author login="dbalek"/>
<compatibility source="compatible" binary="compatible" semantic="compatible" addition="yes"/>
<description>
<p>
Added the <a href="@TOP@/org/netbeans/modules/parsing/spi/indexing/ErrorsCache.html#getAllFilesWithRecord-java.net.URL-">getAllFilesWithRecord()</a> method to return all files with error or warning.
</p>
</description>
<class package="org.netbeans.modules.parsing.spi.indexing" name="ErrorsCache"/>
</change>
<change id="IndexabilityQuery">
<api name="IndexingAPI"/>
<summary>Allow plugins to exclude files from being indexed.</summary>
Expand Down
2 changes: 1 addition & 1 deletion ide/parsing.indexing/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# under the License.
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
spec.version.base=9.35.0
spec.version.base=9.36.0
is.autoload=true
javadoc.apichanges=${basedir}/apichanges.xml
javadoc.arch=${basedir}/arch.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ public static Collection<? extends URL> getAllFilesInError(URL root) throws IOEx
return Collections.unmodifiableCollection(TaskCache.getDefault().getAllFilesInError(root));
}

/**Return all files with error or warning badge under the given source root
*
* @param root source root to test
* @return all files with error or warning badge under the given root
* @since 9.36
*/
public static Collection<? extends URL> getAllFilesWithRecord(URL root) throws IOException {
return Collections.unmodifiableCollection(TaskCache.getDefault().getAllFilesWithRecord(root));
}

/**Getter for properties of the given error description.
*/
public static interface Convertor<T> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.lsp.server.protocol;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import org.netbeans.api.lsp.Diagnostic;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.modules.java.lsp.server.LspServerState;
import org.netbeans.modules.parsing.spi.indexing.ErrorsCache;
import org.openide.filesystems.URLMapper;
import org.openide.util.Exceptions;
import org.openide.util.lookup.Lookups;

/**
*
* @author Dusan Balek
*/
public final class ErrorsNotifier {

private final Map<LspServerState, Future<Void>> servers = new WeakHashMap<>();

public void connect(LspServerState server, Future<Void> future) {
synchronized (servers) {
servers.put(server, future);
}
}

public void notifyErrors(URL root) {
List<LspServerState> toRemove = new ArrayList<>();
List<LspServerState> toProcess = new ArrayList<>();
synchronized (servers) {
for (Map.Entry<LspServerState, Future<Void>> entry : servers.entrySet()) {
if (entry.getValue().isDone()) {
toRemove.add(entry.getKey());
} else {
toProcess.add(entry.getKey());
}
}
servers.keySet().removeAll(toRemove);
}
try {
Collection<? extends URL> filesWithErrors = ErrorsCache.getAllFilesWithRecord(root);
if (!filesWithErrors.isEmpty()) {
Project project = FileOwnerQuery.getOwner(root.toURI());
for (LspServerState server : toProcess) {
for (Project p : server.openedProjects().getNow(new Project[0])) {
if (p == project) {
Diagnostic.ReporterControl control = Diagnostic.findReporterControl(Lookups.fixed(server), null);
control.diagnosticChanged(filesWithErrors.stream().map(url -> URLMapper.findFileObject(url)).filter(fo -> fo != null).collect(Collectors.toList()), null);
}
}
}
}
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import java.util.prefs.Preferences;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -161,6 +160,7 @@
public final class Server {
private static final Logger LOG = Logger.getLogger(Server.class.getName());
private static final LspServerTelemetryManager LSP_SERVER_TELEMETRY = new LspServerTelemetryManager();
private static final ErrorsNotifier ERR_NOTIFIER = new ErrorsNotifier();

private Server() {
}
Expand All @@ -183,7 +183,8 @@ public static NbLspServer launchServer(Pair<InputStream, OutputStream> io, LspSe
((LanguageClientAware) server).connect(remote);
msgProcessor.attachClient(server.client);
Future<Void> runningServer = serverLauncher.startListening();
LSP_SERVER_TELEMETRY.connect(server.client, runningServer);
LSP_SERVER_TELEMETRY.connect(server.client, runningServer);
ERR_NOTIFIER.connect(server, runningServer);
return new NbLspServer(server, runningServer);
}

Expand Down Expand Up @@ -1405,13 +1406,14 @@ private CustomIndexerTelemetryFactory() {

@Override
public synchronized boolean scanStarted(Context context) {
LSP_SERVER_TELEMETRY.sendTelemetry(new TelemetryEvent(MessageType.Info.toString(), LSP_SERVER_TELEMETRY.SCAN_START_EVT, "nbls.scanStarted"));
return true;
LSP_SERVER_TELEMETRY.sendTelemetry(new TelemetryEvent(MessageType.Info.toString(), LSP_SERVER_TELEMETRY.SCAN_START_EVT, "nbls.scanStarted"));
return true;
}

@Override
public synchronized void scanFinished(Context context) {
LSP_SERVER_TELEMETRY.sendTelemetry(new TelemetryEvent(MessageType.Info.toString(),LSP_SERVER_TELEMETRY.SCAN_END_EVT,"nbls.scanFinished"));
LSP_SERVER_TELEMETRY.sendTelemetry(new TelemetryEvent(MessageType.Info.toString(),LSP_SERVER_TELEMETRY.SCAN_END_EVT,"nbls.scanFinished"));
ERR_NOTIFIER.notifyErrors(context.getRootURI());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2175,7 +2175,11 @@ public void diagnosticChanged(Collection<FileObject> files, String mimeType) {
try {
String uriString = url.toURI().toString();
String lspUri = URITranslator.getDefault().uriToLSP(uriString);
runDiagnosticTasks(lspUri);
server.asyncOpenFileOwner(f).thenRun(() -> {
Lookups.executeWith(new ProxyLookup(Lookups.singleton(client), Lookup.getDefault()), () -> {
runDiagnosticTasks(lspUri);
});
});
} catch (URISyntaxException ex) {
// should not happen
}
Expand Down

0 comments on commit c934998

Please sign in to comment.