From 2d38b982f0d8e9a5aa53b2c2edd1df624f7677f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Mon, 29 Dec 2025 20:03:09 +0100 Subject: [PATCH 1/7] lsp.client: Provide a minimal implementation of LanguageClient#refreshDiagnostic Test with rust-analyzer yielded the following exception: java.lang.UnsupportedOperationException at org.eclipse.lsp4j.services.LanguageClient.refreshDiagnostics(LanguageClient.java:251) Caused: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$null$0(GenericEndpoint.java:65) Caused: java.lang.RuntimeException at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$null$0(GenericEndpoint.java:67) at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.request(GenericEndpoint.java:120) [catch] at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleRequest(RemoteEndpoint.java:261) at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:190) at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:194) at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:94) at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:113) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:840) --- .../modules/lsp/client/bindings/LanguageClientImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java index 57df7f5b33ea..b2c2bb84bd2c 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java @@ -305,6 +305,12 @@ public void notify(String method, Object parameter) { LOG.log(Level.WARNING, "Received unhandled notification: {0}: {1}", new Object[] {method, parameter}); } + @Override + public CompletableFuture refreshDiagnostics() { + bindings.getOpenedFiles().forEach(LSPBindings::scheduleBackgroundTasks); + return CompletableFuture.completedFuture(null); + } + private final class DiagnosticFixList implements LazyFixList { private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); From f080e4f5f83085da489f0dbb19f6020629622a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Sat, 17 Jan 2026 14:32:59 +0100 Subject: [PATCH 2/7] lsp.client: Extend guarded handling of invalid/null replies from LSP server --- .../lsp/client/bindings/BreadcrumbsImpl.java | 2 +- .../lsp/client/bindings/CodeActions.java | 29 ++- .../lsp/client/bindings/Formatter.java | 5 +- .../client/bindings/NavigatorPanelImpl.java | 2 +- .../client/bindings/SemanticHighlight.java | 7 +- .../bindings/refactoring/Refactoring.java | 230 +++++++++--------- 6 files changed, 148 insertions(+), 127 deletions(-) diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java index 58cd0a995c0e..57509cded5f2 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java @@ -90,7 +90,7 @@ public void run(List servers, FileObject file) { capa -> Utils.isEnabled(capa.getDocumentSymbolProvider()), () -> new DocumentSymbolParams(new TextDocumentIdentifier(Utils.toURI(file))), (server, params) -> server.getTextDocumentService().documentSymbol(params), - (server, result) -> allSymbols.add(Pair.of(server, result.stream().map(this::toDocumentSymbol).toList()))); + (server, result) -> allSymbols.add(Pair.of(server, result == null ? List.of() : result.stream().map(this::toDocumentSymbol).toList()))); this.rootElement = new RootBreadcrumbsElementImpl(file, doc, allSymbols); diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java index 6faf6676a1ba..2f5281fa84c1 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CodeActions.java @@ -22,10 +22,13 @@ import java.util.Collections; import java.util.List; import javax.swing.text.JTextComponent; +import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.CodeActionParams; +import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.modules.editor.NbEditorUtilities; import org.netbeans.modules.lsp.client.LSPBindings; @@ -63,17 +66,23 @@ public List create(Lookup context) { Utils.createPosition(component.getDocument(), component.getSelectionEnd())), new CodeActionContext(Collections.emptyList())), (server, params) -> server.getTextDocumentService().codeAction(params), - (server, result) -> result.forEach(cmd -> output.add(new CodeGenerator() { - @Override - public String getDisplayName() { - return cmd.isLeft() ? cmd.getLeft().getTitle() : cmd.getRight().getTitle(); - } + (server, result) -> { + if (result != null) { + for (Either cmd : result) { + output.add(new CodeGenerator() { + @Override + public String getDisplayName() { + return cmd.isLeft() ? cmd.getLeft().getTitle() : cmd.getRight().getTitle(); + } - @Override - public void invoke() { - Utils.applyCodeAction(server, cmd); - } - }))); + @Override + public void invoke() { + Utils.applyCodeAction(server, cmd); + } + }); + } + } + }); return output; } diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/Formatter.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/Formatter.java index e941b3e8b296..f68adfcc619e 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/Formatter.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/Formatter.java @@ -119,7 +119,10 @@ private void documentFormat(FileObject fo, LSPBindings bindings) throws BadLocat IndentUtils.isExpandTabs(ctx.document()))); List edits = new ArrayList<>(); try { - edits.addAll(bindings.getTextDocumentService().formatting(dfp).get()); + List editInputs = bindings.getTextDocumentService().formatting(dfp).get(); + if (editInputs != null) { + edits.addAll(editInputs); + } } catch (InterruptedException | ExecutionException ex) { LOG.log(Level.INFO, String.format("LSP document format failed for {0}", fo), diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java index 88cd6a65d9d8..ebc6493dc45f 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/NavigatorPanelImpl.java @@ -74,7 +74,7 @@ public void run(FileObject file) { String uri = Utils.toURI(file); List> symbols = bindings.getTextDocumentService().documentSymbol(new DocumentSymbolParams(new TextDocumentIdentifier(uri))).get(); - setKeys(symbols); + setKeys(symbols == null ? List.of() : symbols); expandAll(); } catch (ExecutionException ex) { LOG.log(Level.FINE, null, ex); diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java index 2d2b7022d88b..a461eb1ac159 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java @@ -81,7 +81,12 @@ private void convertTokenHighlights(LSPBindings server, FileObject file, FontColorSettings[] fcs, OffsetsBag target) { - List data = tokens.getData(); + List data; + if (tokens != null) { + data = tokens.getData(); + } else { + data = List.of(); + } int lastLine = 0; int lastColumn = 0; int offset = 0; diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java index 6c521df6e630..56c2f2acbafb 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java @@ -120,32 +120,34 @@ public Problem fastCheckParameters() { @Override public Problem prepare(RefactoringElementsBag refactoringElements) { BiConsumer> handleResult = (server, usages) -> { - for (Location l : usages) { - if (isCanceled()) { - break; - } - FileObject file = Utils.fromURI(l.getUri()); - if (file != null) { - PositionBounds bounds; - try { - CloneableEditorSupport es = file.getLookup().lookup(CloneableEditorSupport.class); - EditorCookie ec = file.getLookup().lookup(EditorCookie.class); - StyledDocument doc = ec.openDocument(); - - bounds = new PositionBounds(es.createPositionRef(Utils.getOffset(doc, l.getRange().getStart()), Position.Bias.Forward), - es.createPositionRef(Utils.getOffset(doc, l.getRange().getEnd()), Position.Bias.Forward)); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - bounds = null; + if (usages != null) { + for (Location l : usages) { + if (isCanceled()) { + break; } - LineCookie lc = file.getLookup().lookup(LineCookie.class); - Line startLine = lc.getLineSet().getCurrent(l.getRange().getStart().getLine()); - String lineText = startLine.getText(); - int highlightEnd = Math.min(lineText.length(), l.getRange().getEnd().getCharacter()); - String annotatedLine = lineText.substring(0, l.getRange().getStart().getCharacter()) + + FileObject file = Utils.fromURI(l.getUri()); + if (file != null) { + PositionBounds bounds; + try { + CloneableEditorSupport es = file.getLookup().lookup(CloneableEditorSupport.class); + EditorCookie ec = file.getLookup().lookup(EditorCookie.class); + StyledDocument doc = ec.openDocument(); + + bounds = new PositionBounds(es.createPositionRef(Utils.getOffset(doc, l.getRange().getStart()), Position.Bias.Forward), + es.createPositionRef(Utils.getOffset(doc, l.getRange().getEnd()), Position.Bias.Forward)); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + bounds = null; + } + LineCookie lc = file.getLookup().lookup(LineCookie.class); + Line startLine = lc.getLineSet().getCurrent(l.getRange().getStart().getLine()); + String lineText = startLine.getText(); + int highlightEnd = Math.min(lineText.length(), l.getRange().getEnd().getCharacter()); + String annotatedLine = lineText.substring(0, l.getRange().getStart().getCharacter()) + "" + lineText.substring(l.getRange().getStart().getCharacter(), highlightEnd) + "" + lineText.substring(highlightEnd); - refactoringElements.add(query, new LSPRefactoringElementImpl(annotatedLine, file, bounds)); + refactoringElements.add(query, new LSPRefactoringElementImpl(annotatedLine, file, bounds)); + } } } }; @@ -191,110 +193,112 @@ public Problem fastCheckParameters() { @Override public Problem prepare(RefactoringElementsBag refactoringElements) { BiConsumer handleResult = (server, edit) -> { - try { - List> documentChanges = edit.getDocumentChanges(); - ModificationResult result = new ModificationResult(); - Map> file2Diffs = new HashMap<>(); - Map newURI2Old = new HashMap<>(); - Map newFileURI2Content = new HashMap<>(); - - if (documentChanges != null) { - for (Either part : documentChanges) { - if (isCanceled()) { - break; - } - if (part.isLeft()) { - String uri = part.getLeft().getTextDocument().getUri(); - uri = newURI2Old.getOrDefault(uri, uri); - FileObject file = Utils.fromURI(uri); - - if (file != null) { - for (TextEdit te : part.getLeft().getEdits()) { - Difference diff = textEdit2Difference(file, te); - file2Diffs.computeIfAbsent(file, f -> new ArrayList<>()) - .add(diff); - } - } else if (newFileURI2Content.containsKey(uri)) { - FileObject temp = FileUtil.createMemoryFileSystem().getRoot().createData("temp.txt"); - try (OutputStream out = temp.getOutputStream()) { - out.write(newFileURI2Content.get(uri).getBytes()); //TODO: encoding - native, OK? - } - List diffs = new ArrayList<>(); - for (TextEdit te : part.getLeft().getEdits()) { - diffs.add(textEdit2Difference(temp, te)); - } - ModificationResult tempResult = new ModificationResult(); - tempResult.addDifferences(temp, diffs); - newFileURI2Content.put(uri, tempResult.getResultingSource(temp)); - } else { - //XXX: problem... + if(edit != null) { + try { + List> documentChanges = edit.getDocumentChanges(); + ModificationResult result = new ModificationResult(); + Map> file2Diffs = new HashMap<>(); + Map newURI2Old = new HashMap<>(); + Map newFileURI2Content = new HashMap<>(); + + if (documentChanges != null) { + for (Either part : documentChanges) { + if (isCanceled()) { + break; } - } else { - switch (part.getRight().getKind()) { - case ResourceOperationKind.Rename: { - RenameFile rename = (RenameFile) part.getRight(); - FileObject file = Utils.fromURI(rename.getOldUri()); - refactoringElements.addFileChange(refactoring, new LSPRenameFile(file, rename.getNewUri())); - newURI2Old.put(rename.getNewUri(), rename.getOldUri()); - break; - } - case ResourceOperationKind.Delete: { - DeleteFile delete = (DeleteFile) part.getRight(); - FileObject file = Utils.fromURI(delete.getUri()); - refactoringElements.addFileChange(refactoring, new LSPDeleteFile(file)); - break; + if (part.isLeft()) { + String uri = part.getLeft().getTextDocument().getUri(); + uri = newURI2Old.getOrDefault(uri, uri); + FileObject file = Utils.fromURI(uri); + + if (file != null) { + for (TextEdit te : part.getLeft().getEdits()) { + Difference diff = textEdit2Difference(file, te); + file2Diffs.computeIfAbsent(file, f -> new ArrayList<>()) + .add(diff); + } + } else if (newFileURI2Content.containsKey(uri)) { + FileObject temp = FileUtil.createMemoryFileSystem().getRoot().createData("temp.txt"); + try (OutputStream out = temp.getOutputStream()) { + out.write(newFileURI2Content.get(uri).getBytes()); //TODO: encoding - native, OK? + } + List diffs = new ArrayList<>(); + for (TextEdit te : part.getLeft().getEdits()) { + diffs.add(textEdit2Difference(temp, te)); + } + ModificationResult tempResult = new ModificationResult(); + tempResult.addDifferences(temp, diffs); + newFileURI2Content.put(uri, tempResult.getResultingSource(temp)); + } else { + //XXX: problem... } - case ResourceOperationKind.Create: { - CreateFile create = (CreateFile) part.getRight(); - String uri = create.getUri(); - newFileURI2Content.put(uri, ""); - break; + } else { + switch (part.getRight().getKind()) { + case ResourceOperationKind.Rename: { + RenameFile rename = (RenameFile) part.getRight(); + FileObject file = Utils.fromURI(rename.getOldUri()); + refactoringElements.addFileChange(refactoring, new LSPRenameFile(file, rename.getNewUri())); + newURI2Old.put(rename.getNewUri(), rename.getOldUri()); + break; + } + case ResourceOperationKind.Delete: { + DeleteFile delete = (DeleteFile) part.getRight(); + FileObject file = Utils.fromURI(delete.getUri()); + refactoringElements.addFileChange(refactoring, new LSPDeleteFile(file)); + break; + } + case ResourceOperationKind.Create: { + CreateFile create = (CreateFile) part.getRight(); + String uri = create.getUri(); + newFileURI2Content.put(uri, ""); + break; + } + default: + addProblem(new Problem(true, "Unknown file operation: " + part.getRight().getKind())); + break; } - default: - addProblem(new Problem(true, "Unknown file operation: " + part.getRight().getKind())); - break; } } - } - } else { - for (Entry> fileAndChanges : edit.getChanges().entrySet()) { - if (isCanceled()) { - break; - } - //TODO: errors: - FileObject file = Utils.fromURI(fileAndChanges.getKey()); + } else { + for (Entry> fileAndChanges : edit.getChanges().entrySet()) { + if (isCanceled()) { + break; + } + //TODO: errors: + FileObject file = Utils.fromURI(fileAndChanges.getKey()); - for (TextEdit te : fileAndChanges.getValue()) { - Difference diff = textEdit2Difference(file, te); - file2Diffs.computeIfAbsent(file, f -> new ArrayList<>()) - .add(diff); + for (TextEdit te : fileAndChanges.getValue()) { + Difference diff = textEdit2Difference(file, te); + file2Diffs.computeIfAbsent(file, f -> new ArrayList<>()) + .add(diff); + } } } - } - if (isCanceled()) { - addProblem(new Problem(false, Bundle.TXT_Canceled())); - } else { + if (isCanceled()) { + addProblem(new Problem(false, Bundle.TXT_Canceled())); + } else { - file2Diffs.entrySet() - .forEach(e -> { - e.getValue() - .forEach(diff -> refactoringElements.add(refactoring, DiffElement.create(diff, e.getKey(), result))); - result.addDifferences(e.getKey(), e.getValue()); - }); + file2Diffs.entrySet() + .forEach(e -> { + e.getValue() + .forEach(diff -> refactoringElements.add(refactoring, DiffElement.create(diff, e.getKey(), result))); + result.addDifferences(e.getKey(), e.getValue()); + }); - newFileURI2Content.entrySet() - .forEach(e -> { - refactoringElements.add(refactoring, new LSPCreateFile(e.getKey(), e.getValue())); - }); - refactoringElements.registerTransaction(new RefactoringCommit(Collections.singletonList(result))); + newFileURI2Content.entrySet() + .forEach(e -> { + refactoringElements.add(refactoring, new LSPCreateFile(e.getKey(), e.getValue())); + }); + refactoringElements.registerTransaction(new RefactoringCommit(Collections.singletonList(result))); - if (isCanceled()) { - addProblem(new Problem(false, Bundle.TXT_Canceled())); + if (isCanceled()) { + addProblem(new Problem(false, Bundle.TXT_Canceled())); + } } + } catch ( IOException ex) { + addProblem(new Problem(true, ex.getLocalizedMessage())); } - } catch ( IOException ex) { - addProblem(new Problem(true, ex.getLocalizedMessage())); } }; From b9ba18e1ed89fc629e5bfc9b0148ff87d2eef97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Sat, 17 Jan 2026 14:58:44 +0100 Subject: [PATCH 3/7] lsp.client: Warning icons not present --- ide/lsp.client/licenseinfo.xml | 29 ++++++++++++ .../client/bindings/LanguageClientImpl.java | 10 +++- .../lsp/client/resources/warning_16.png | Bin 0 -> 669 bytes .../lsp/client/resources/warning_16.svg | 43 ++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 ide/lsp.client/licenseinfo.xml create mode 100644 ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/warning_16.png create mode 100644 ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/warning_16.svg diff --git a/ide/lsp.client/licenseinfo.xml b/ide/lsp.client/licenseinfo.xml new file mode 100644 index 000000000000..24a13ec56ff1 --- /dev/null +++ b/ide/lsp.client/licenseinfo.xml @@ -0,0 +1,29 @@ + + + + + src/org/netbeans/modules/lsp/client/resources/error_16.png + src/org/netbeans/modules/lsp/client/resources/warning_16.png + + + + diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java index b2c2bb84bd2c..4b75cc3b1f59 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java @@ -65,6 +65,7 @@ import org.eclipse.lsp4j.jsonrpc.Endpoint; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClient; +import org.netbeans.api.annotations.common.StaticResource; import org.netbeans.api.progress.*; import org.netbeans.modules.lsp.client.LSPBindings; import org.netbeans.modules.lsp.client.Utils; @@ -98,6 +99,11 @@ public class LanguageClientImpl implements LanguageClient, Endpoint { private static final Logger LOG = Logger.getLogger(LanguageClientImpl.class.getName()); private static final RequestProcessor WORKER = new RequestProcessor(LanguageClientImpl.class.getName(), 1, false, false); + @StaticResource + private static final String ERROR_ICON = "org/netbeans/modules/lsp/client/resources/error_16.png"; + @StaticResource + private static final String WARNING_ICON = "org/netbeans/modules/lsp/client/resources/warning_16.png"; + private LSPBindings bindings; private boolean allowCodeActions; @@ -211,11 +217,11 @@ public void showMessage(MessageParams params) { StatusDisplayer.getDefault().setStatusText(params.getMessage()); return ; case Warning: - icon = ImageUtilities.loadImageIcon("org/netbeans/modules/lsp/client/resources/warning.png", false); + icon = ImageUtilities.loadImageIcon(WARNING_ICON, false); category = Category.WARNING; break; case Error: - icon = ImageUtilities.loadImageIcon("org/netbeans/modules/lsp/client/resources/error_16.png", false); + icon = ImageUtilities.loadImageIcon(ERROR_ICON, false); category = Category.ERROR; break; } diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/warning_16.png b/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/warning_16.png new file mode 100644 index 0000000000000000000000000000000000000000..42c67a1901f18fb402f39c39c60e33e830ea8b4a GIT binary patch literal 669 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+AuIM>3h03hE&{2`t$$4J+t11Y^ldw zY;J7!0+St;)UW9ld`d4dWnetQV8kGO&ur$x2UntY{{O#U_c#9{bN>{LW6Ssy8fFyS z{P$cxxp`x;B=h0*(|Htr{jWb3mGg>$iDNaR^e+i+lP{(}Xa2KEPx$e(h0&$?heBb= zR-lo z_`sPDvQW!BWPSJ@7!LBz2sspykhz+7i)_Z_`|lli_e*}cI#Xhv+*C%et5x`e*n{{l zu?sj9FxN3YzI!1+WW@`1gLD5IkDiPDd-B2be>^_ULU-*q*m%f#*n03NG(&ro?D<2h@sooTFnuw2y85}Sb4q9e0ES=|o&W#< literal 0 HcmV?d00001 diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/warning_16.svg b/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/warning_16.svg new file mode 100644 index 000000000000..e5932382e489 --- /dev/null +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/warning_16.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + From d549c44be64ef1464c9d3acb1a862078af584dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Sat, 17 Jan 2026 14:59:37 +0100 Subject: [PATCH 4/7] lsp.client: Don't block LSP server while diagnostics is applied to document It was observed, that with the rust-analyzer language server inserting a completion led to a dead-lock. Checking the stacks of running threads shows, that the LS tries to publish diagostics, while in parallel a documentFormat is called. The assumption is, that the rust side block execution of the `documentFormat` request until the `publishDiagnostics` call from the LS finishes. The latter though needs access to the document lock and that is already taken by the `documentFormat` task. "AWT-EventQueue-0" #35 prio=6 os_prio=0 cpu=8224,39ms elapsed=119,39s tid=0x00007f5384156f60 nid=0x2b0da7 waiting on condition [0x00007f537e7f9000] java.lang.Thread.State: WAITING (parking) at jdk.internal.misc.Unsafe.park(java.base@17.0.9/Native Method) - parking to wait for <0x00000006448493e8> (a java.util.concurrent.CompletableFuture$Signaller) at java.util.concurrent.locks.LockSupport.park(java.base@17.0.9/LockSupport.java:211) at java.util.concurrent.CompletableFuture$Signaller.block(java.base@17.0.9/CompletableFuture.java:1864) at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@17.0.9/ForkJoinPool.java:3465) at java.util.concurrent.ForkJoinPool.managedBlock(java.base@17.0.9/ForkJoinPool.java:3436) at java.util.concurrent.CompletableFuture.waitingGet(java.base@17.0.9/CompletableFuture.java:1898) at java.util.concurrent.CompletableFuture.get(java.base@17.0.9/CompletableFuture.java:2072) at org.netbeans.modules.lsp.client.bindings.Formatter.documentFormat(Formatter.java:122) at org.netbeans.modules.lsp.client.bindings.Formatter.reformat(Formatter.java:87) at org.netbeans.modules.editor.indent.TaskHandler$MimeItem.runTask(TaskHandler.java:550) at org.netbeans.modules.editor.indent.TaskHandler.runTasks(TaskHandler.java:309) at org.netbeans.modules.editor.indent.IndentImpl.reformat(IndentImpl.java:349) at org.netbeans.modules.editor.indent.api.Reformat.reformat(Reformat.java:129) at org.netbeans.lib.editor.codetemplates.CodeTemplateInsertHandler.run(CodeTemplateInsertHandler.java:352) at org.netbeans.editor.GuardedDocument.runAtomicAsUser(GuardedDocument.java:333) at org.netbeans.lib.editor.codetemplates.CodeTemplateInsertHandler.insertTemplate(CodeTemplateInsertHandler.java:258) at org.netbeans.lib.editor.codetemplates.CodeTemplateInsertHandler.processTemplate(CodeTemplateInsertHandler.java:229) at org.netbeans.lib.editor.codetemplates.CodeTemplateManagerOperation.insert(CodeTemplateManagerOperation.java:273) at org.netbeans.lib.editor.codetemplates.api.CodeTemplate.insert(CodeTemplate.java:82) at org.netbeans.modules.lsp.client.bindings.CompletionProviderImpl$3.lambda$commit$0(CompletionProviderImpl.java:307) at org.netbeans.modules.lsp.client.bindings.CompletionProviderImpl$3$$Lambda$825/0x00007f53e4ae8498.run(Unknown Source) at org.netbeans.editor.GuardedDocument.runAtomic(GuardedDocument.java:296) at org.openide.text.NbDocument.runAtomic(NbDocument.java:411) at org.netbeans.modules.lsp.client.bindings.CompletionProviderImpl$3.commit(CompletionProviderImpl.java:254) at org.netbeans.modules.lsp.client.bindings.CompletionProviderImpl$3.defaultAction(CompletionProviderImpl.java:231) at org.netbeans.modules.editor.completion.CompletionImpl.dispatchKeyEvent(CompletionImpl.java:785) at org.netbeans.modules.editor.completion.CompletionImpl.keyPressed(CompletionImpl.java:386) at java.awt.AWTEventMulticaster.keyPressed(java.desktop@17.0.9/AWTEventMulticaster.java:258) [...] "pool-4-thread-1" #75 prio=5 os_prio=0 cpu=136,37ms elapsed=76,96s tid=0x00007f5380ae1080 nid=0x2b0e84 in Object.wait() [0x00007f52e99fc000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(java.base@17.0.9/Native Method) - waiting on at java.lang.Object.wait(java.base@17.0.9/Object.java:338) at javax.swing.text.AbstractDocument.readLock(java.desktop@17.0.9/AbstractDocument.java:1421) - locked <0x000000063c6af680> (a org.netbeans.modules.csl.core.GsfDocument) at org.netbeans.editor.BaseDocument.render(BaseDocument.java:1405) at org.openide.text.PositionRef$Manager$DocumentRenderer.render(PositionRef.java:927) at org.openide.text.PositionRef$Manager$DocumentRenderer.renderToObject(PositionRef.java:948) at org.openide.text.PositionRef$Manager.addPosition(PositionRef.java:389) at org.openide.text.PositionRef.init(PositionRef.java:105) at org.openide.text.PositionRef.(PositionRef.java:90) at org.openide.text.PositionRef.(PositionRef.java:68) at org.openide.text.CloneableEditorSupport.createPositionRef(CloneableEditorSupport.java:1264) at org.netbeans.modules.editor.hints.HintsControllerImpl.linePart(HintsControllerImpl.java:231) at org.netbeans.spi.editor.hints.ErrorDescriptionFactory.createErrorDescription(ErrorDescriptionFactory.java:245) at org.netbeans.spi.editor.hints.ErrorDescriptionFactory.createErrorDescription(ErrorDescriptionFactory.java:218) at org.netbeans.modules.lsp.client.bindings.LanguageClientImpl.lambda$publishDiagnostics$0(LanguageClientImpl.java:176) at org.netbeans.modules.lsp.client.bindings.LanguageClientImpl$$Lambda$705/0x00007f53e4ac73a8.apply(Unknown Source) at java.util.stream.ReferencePipeline$3$1.accept(java.base@17.0.9/ReferencePipeline.java:197) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(java.base@17.0.9/ArrayList.java:1625) at java.util.stream.AbstractPipeline.copyInto(java.base@17.0.9/AbstractPipeline.java:509) at java.util.stream.AbstractPipeline.wrapAndCopyInto(java.base@17.0.9/AbstractPipeline.java:499) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(java.base@17.0.9/ReduceOps.java:921) at java.util.stream.AbstractPipeline.evaluate(java.base@17.0.9/AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(java.base@17.0.9/ReferencePipeline.java:682) at org.netbeans.modules.lsp.client.bindings.LanguageClientImpl.publishDiagnostics(LanguageClientImpl.java:177) at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@17.0.9/Native Method) at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@17.0.9/NativeMethodAccessorImpl.java:77) at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.9/DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(java.base@17.0.9/Method.java:568) at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$recursiveFindRpcMethods$0(GenericEndpoint.java:65) at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint$$Lambda$648/0x00007f53e4a4dd50.apply(Unknown Source) at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.notify(GenericEndpoint.java:160) at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleNotification(RemoteEndpoint.java:231) [...] --- .../client/bindings/LanguageClientImpl.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java index 4b75cc3b1f59..410fe835fcb3 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/LanguageClientImpl.java @@ -167,24 +167,26 @@ public void notifyProgress(ProgressParams params) { }); } - @Override + @Override public void publishDiagnostics(PublishDiagnosticsParams pdp) { - try { - FileObject file = URLMapper.findFileObject(new URI(pdp.getUri()).toURL()); - EditorCookie ec = file != null ? file.getLookup().lookup(EditorCookie.class) : null; - Document doc = ec != null ? ec.getDocument() : null; - if (doc == null) { - return ; //ignore... + WORKER.execute(() -> { + try { + FileObject file = URLMapper.findFileObject(new URI(pdp.getUri()).toURL()); + EditorCookie ec = file != null ? file.getLookup().lookup(EditorCookie.class) : null; + Document doc = ec != null ? ec.getDocument() : null; + if (doc == null) { + return; //ignore... + } + assert file != null; + List diags = pdp.getDiagnostics().stream().map(d -> { + LazyFixList fixList = allowCodeActions ? new DiagnosticFixList(doc, pdp.getUri(), d) : ErrorDescriptionFactory.lazyListForFixes(Collections.emptyList()); + return ErrorDescriptionFactory.createErrorDescription(severityMap.get(d.getSeverity()), d.getMessage(), fixList, file, Utils.getOffset(doc, d.getRange().getStart()), Utils.getOffset(doc, d.getRange().getEnd())); + }).collect(Collectors.toList()); + HintsController.setErrors(doc, LanguageClientImpl.class.getName(), diags); + } catch (URISyntaxException | MalformedURLException ex) { + LOG.log(Level.FINE, null, ex); } - assert file != null; - List diags = pdp.getDiagnostics().stream().map(d -> { - LazyFixList fixList = allowCodeActions ? new DiagnosticFixList(doc, pdp.getUri(), d) : ErrorDescriptionFactory.lazyListForFixes(Collections.emptyList()); - return ErrorDescriptionFactory.createErrorDescription(severityMap.get(d.getSeverity()), d.getMessage(), fixList, file, Utils.getOffset(doc, d.getRange().getStart()), Utils.getOffset(doc, d.getRange().getEnd())); - }).collect(Collectors.toList()); - HintsController.setErrors(doc, LanguageClientImpl.class.getName(), diags); - } catch (URISyntaxException | MalformedURLException ex) { - LOG.log(Level.FINE, null, ex); - } + }); } private static final Map severityMap = new EnumMap<>(DiagnosticSeverity.class); From 23f55a9137bb82fd76625e64375ecfe6139784e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Wed, 31 Dec 2025 13:47:30 +0100 Subject: [PATCH 5/7] CSL: Don't run BreadCrumbsTask if no structure scanner is present --- .../netbeans/modules/csl/navigation/BreadCrumbsTask.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ide/csl.api/src/org/netbeans/modules/csl/navigation/BreadCrumbsTask.java b/ide/csl.api/src/org/netbeans/modules/csl/navigation/BreadCrumbsTask.java index 397043d745d9..964840400f94 100644 --- a/ide/csl.api/src/org/netbeans/modules/csl/navigation/BreadCrumbsTask.java +++ b/ide/csl.api/src/org/netbeans/modules/csl/navigation/BreadCrumbsTask.java @@ -206,7 +206,14 @@ public TaskFactoryImpl() { @Override protected Collection createTasks(Language language, Snapshot snapshot) { - return Collections.singletonList(new BreadCrumbsTask()); + if (language.getStructure() != null) { + // Language#hasStructureScanner has a broken/unusable + // implementation, so support is deduced from the (non-)presence + // of a structure scanner. + return List.of(new BreadCrumbsTask()); + } else { + return List.of(); + } } } From 600863521da841b1db1b7de738e5e833c2be27e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Wed, 31 Dec 2025 00:42:44 +0100 Subject: [PATCH 6/7] rust/cargo: Switch from tomlj to updated version of toml-java to support modern TOML --- rust/rust.cargo/nbproject/project.xml | 6 +- .../rust/cargo/impl/CargoTOMLParser.java | 62 +++++++++---------- .../dependencies/RustAddDependencyAction.java | 1 + .../RustAddDependencyWizardPanel1.java | 2 + .../RustRemoveDependencyAction.java | 1 + 5 files changed, 35 insertions(+), 37 deletions(-) diff --git a/rust/rust.cargo/nbproject/project.xml b/rust/rust.cargo/nbproject/project.xml index 693c95386684..045e96136c1f 100644 --- a/rust/rust.cargo/nbproject/project.xml +++ b/rust/rust.cargo/nbproject/project.xml @@ -51,12 +51,12 @@ - org.netbeans.libs.tomlj + org.netbeans.libs.tomljava - 1 - 1.2 + 5 + 4.0 diff --git a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/CargoTOMLParser.java b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/CargoTOMLParser.java index aadfaeba4bdd..4241e4ba5882 100644 --- a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/CargoTOMLParser.java +++ b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/CargoTOMLParser.java @@ -18,12 +18,9 @@ */ package org.netbeans.modules.rust.cargo.impl; -import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -31,16 +28,12 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; +import net.vieiro.toml.TOML; +import net.vieiro.toml.TOMLParser; import org.netbeans.modules.rust.cargo.api.CargoTOML; import org.netbeans.modules.rust.cargo.api.RustPackage; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; -import org.tomlj.Toml; -import org.tomlj.TomlArray; -import org.tomlj.TomlParseError; -import org.tomlj.TomlParseResult; -import org.tomlj.TomlTable; /** * @@ -63,24 +56,23 @@ public static void parseCargoToml(FileObject cargoTomlFile, CargoTOML cargotoml) } long start = System.currentTimeMillis(); // As per the specification, .toml files are always UTF-8 - try (final BufferedReader fileContent = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { - TomlParseResult parseResult = Toml.parse(fileContent); - List errors = parseResult.errors().stream().collect(Collectors.toList()); - if (!errors.isEmpty()) { + try (InputStream cargoInputStream = cargoTomlFile.getInputStream()) { + TOML parseResult = TOMLParser.parseFromInputStream(cargoInputStream); + if (!parseResult.getErrors().isEmpty()) { final String fileName = file.getAbsolutePath(); - errors.forEach(e -> { - LOG.warning(String.format("Error parsing '%s': '%s'", fileName, e.getMessage())); // NOI18N + parseResult.getErrors().forEach(e -> { + LOG.warning(String.format("Error parsing '%s': '%s'", fileName, e)); // NOI18N }); throw new IOException(String.format("Errors parsing '%s'. See log for details", fileName)); // NOI18N } - String packageName = parseResult.getString("package.name"); // NOI18N - String version = parseResult.getString("package.version"); // NOI18N - String edition = parseResult.getString("package.edition"); // NOI18N + String packageName = parseResult.getString("package/name").orElse(null); // NOI18N + String version = parseResult.getString("package/version").orElse(null); // NOI18N + String edition = parseResult.getString("package/edition").orElse(null); // NOI18N edition = edition == null ? "2015" : edition; - String rustVersion = parseResult.getString("package.rust-version"); // NOI18N - String description = parseResult.getString("package.description"); // NOI18N - String documentation = parseResult.getString("package.documentation"); // NOI18N - String homepage = parseResult.getString("package.homepage"); + String rustVersion = parseResult.getString("package/rust-version").orElse(null); // NOI18N + String description = parseResult.getString("package/description").orElse(null); // NOI18N + String documentation = parseResult.getString("package/documentation").orElse(null); // NOI18N + String homepage = parseResult.getString("package/homepage").orElse(null); // TODO: Read more stuff... // TODO: Fire property change only if required. @@ -111,17 +103,18 @@ public static void parseCargoToml(FileObject cargoTomlFile, CargoTOML cargotoml) } // Workspaces https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html - TomlArray members = parseResult.getArray("workspace.members"); + @SuppressWarnings("unchecked") + List members = parseResult.getArray("workspace/members").orElse(null); if (members != null) { TreeMap workspacesByName = new TreeMap<>(); FileObject root = cargotoml.getFileObject().getParent(); for (int i = 0; i < members.size(); i++) { - String memberName = members.getString(i); + String memberName = members.get(i); FileObject memberCargoFO = root.getFileObject(memberName); if (memberCargoFO != null) { memberCargoFO = memberCargoFO.getFileObject("Cargo.toml"); // NOI18N } - if (memberCargoFO != null) { + if (memberCargoFO != null && ! cargoTomlFile.equals(memberCargoFO)) { CargoTOML memberCargo = new CargoTOML(memberCargoFO); workspacesByName.put(memberName, memberCargo); } @@ -135,8 +128,8 @@ public static void parseCargoToml(FileObject cargoTomlFile, CargoTOML cargotoml) LOG.info(String.format("Parsing '%s' took %5.2g ms.", file.getAbsolutePath(), (end - start) / 1000.0)); //NOI18N } - private static final List getDependencies(CargoTOML cargotoml, TomlParseResult parseResult, String propertyKey) { - TomlTable dependencies = parseResult.getTable(propertyKey); + private static List getDependencies(CargoTOML cargotoml, TOML parseResult, String propertyKey) { + Map dependencies = parseResult.getTable(propertyKey).orElse(null); if (dependencies == null || dependencies.isEmpty()) { return Collections.emptyList(); } @@ -147,16 +140,17 @@ private static final List getDependencies(CargoTOML cargotoml, Toml if (value instanceof String) { String stringValue = (String) value; packages.add(RustPackage.withNameAndVersion(cargotoml, key, stringValue)); - } else if (value instanceof TomlTable) { + } else if (value instanceof Map) { String dependencyName = key.replaceAll("^dependencies\\.", ""); // NOI18N - TomlTable dependencyInfo = (TomlTable) value; - String version = dependencyInfo.getString("version"); // NOI18N - String git = dependencyInfo.getString("git"); // NOI18N + @SuppressWarnings("unchecked") + Map dependencyInfo = (Map) value; + String version = (String) dependencyInfo.get("version"); // NOI18N + String git = (String) dependencyInfo.get("git"); // NOI18N if (version != null) { // Examples: // [dependencies] // some-crate = { version = "1.0", registry = "my-registry" } - Boolean optional = dependencyInfo.getBoolean("optional"); // NOI18N + Boolean optional = (Boolean) dependencyInfo.get("optional"); // NOI18N RustPackage rp = RustPackage.withNameAndVersion(cargotoml, dependencyName, version, optional); packages.add(rp); } else if (git != null) { @@ -164,7 +158,7 @@ private static final List getDependencies(CargoTOML cargotoml, Toml // [dependencies] // regex = { git = "https://github.com/rust-lang/regex" } // regex = { git = "https://github.com/rust-lang/regex", branch = "next" } - String branch = dependencyInfo.getString("branch"); + String branch = (String) dependencyInfo.get("branch"); RustPackage rp = RustPackage.withGit(cargotoml, dependencyName, git, branch); packages.add(rp); } diff --git a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyAction.java b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyAction.java index 0e6192424498..c31d85d04f06 100644 --- a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyAction.java +++ b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyAction.java @@ -104,6 +104,7 @@ public void actionPerformed(ActionEvent e) { break; } if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) { + @SuppressWarnings("unchecked") List packages = (List) wiz.getProperty(RustAddDependencyWizardPanel1.PROP_SELECTED_PACKAGES); List names = packages.stream().map((p) -> String.format("%s@%s", p.getName(), p.getVersion())).collect(Collectors.toList()); switch(dependencyType) { diff --git a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyWizardPanel1.java b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyWizardPanel1.java index 788f63af0d5d..e5adc7f0e0ca 100644 --- a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyWizardPanel1.java +++ b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustAddDependencyWizardPanel1.java @@ -42,6 +42,7 @@ public class RustAddDependencyWizardPanel1 implements WizardDescriptor.Panel packages = (List) wiz.getProperty(PROP_PACKAGES); component.setPackages(packages); } diff --git a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustRemoveDependencyAction.java b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustRemoveDependencyAction.java index ecbcd0652d2c..dd8d161838ab 100644 --- a/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustRemoveDependencyAction.java +++ b/rust/rust.cargo/src/org/netbeans/modules/rust/cargo/impl/nodes/actions/dependencies/RustRemoveDependencyAction.java @@ -42,6 +42,7 @@ public class RustRemoveDependencyAction extends AbstractAction { private final RustPackage rustPackage; private final RustProjectDependenciesNode.DependencyType dependencyType; + @SuppressWarnings({"this-escape"}) public RustRemoveDependencyAction(CargoTOML cargotoml, RustPackage rustPackage, RustProjectDependenciesNode.DependencyType dependencyType) { super(CargoCommand.CARGO_REMOVE.getDisplayName()); putValue(Action.SHORT_DESCRIPTION, CargoCommand.CARGO_REMOVE.getDescription()); From 373499df89c7aeb72e3cdca2f6953ae061e1153b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Thu, 7 Dec 2023 23:33:13 +0100 Subject: [PATCH 7/7] Integrate rust-analyzer - add support for rust LSP implementation rust-analyzer - align rust icons more with existing NB icons - show hint if rust analyser is not configured --- ide/lsp.client/licenseinfo.xml | 27 ++ rust/rust.grammar/licenseinfo.xml | 23 +- .../rust.grammar/nbproject/project.properties | 2 +- rust/rust.grammar/nbproject/project.xml | 61 +++ .../rust/grammar/LayerProviderImpl.java | 67 ++++ .../rust/grammar/RustLanguageConfig.java | 16 +- .../netbeans/modules/rust/grammar/layer.xml | 171 ++++++++ .../modules/rust/grammar/lsp-layer.xml | 34 ++ .../modules/rust/grammar/lsp/RustLSP.java | 55 +++ .../rust/grammar/lsp/UnconfiguredHint.java | 91 +++++ .../modules/rust/grammar/non-lsp-layer.xml | 57 +++ .../grammar/structure/RustStructureItem.java | 15 +- .../rust/grammar/structure/resources/enum.png | Bin 0 -> 830 bytes .../rust/grammar/structure/resources/enum.svg | 71 ++++ .../grammar/structure/resources/function.png | Bin 0 -> 556 bytes .../grammar/structure/resources/function.svg | 39 ++ .../grammar/structure/resources/interface.png | Bin 0 -> 541 bytes .../grammar/structure/resources/interface.svg | 47 +++ .../grammar/structure/resources/module.png | Bin 0 -> 1474 bytes .../grammar/structure/resources/snippet.png | Bin 0 -> 613 bytes .../grammar/structure/resources/snippet.svg | 64 +++ .../grammar/structure/resources/struct.png | Bin 0 -> 785 bytes .../grammar/structure/resources/struct.svg | 61 +++ .../structure/resources/structure-enum.png | Bin 1186 -> 0 bytes .../resources/structure-function.png | Bin 1007 -> 0 bytes .../structure/resources/structure-impl.png | Bin 1096 -> 0 bytes .../structure/resources/structure-macro.png | Bin 1126 -> 0 bytes .../structure/resources/structure-module.png | Bin 1173 -> 0 bytes .../structure/resources/structure-struct.png | Bin 1196 -> 0 bytes .../grammar/structure/resources/trait.png | Bin 0 -> 530 bytes .../rust/options/api/RustAnalyzerOptions.java | 50 +++ .../options/impl/RustAnalyzerOptionsImpl.java | 124 ++++++ .../options/rustanalyzer/Bundle.properties | 27 ++ .../RustAnalyzerOptionsPanelController.java | 111 ++++++ .../rustanalyzer/RustAnalyzerPanel.form | 139 +++++++ .../rustanalyzer/RustAnalyzerPanel.java | 373 ++++++++++++++++++ .../rust/sources/rs/RustFileDataObject.java | 11 + 37 files changed, 1704 insertions(+), 32 deletions(-) create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/LayerProviderImpl.java create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp-layer.xml create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/RustLSP.java create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/UnconfiguredHint.java create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/non-lsp-layer.xml create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/enum.png create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/enum.svg create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/function.png create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/function.svg create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/interface.png create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/interface.svg create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/module.png create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/snippet.png create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/snippet.svg create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/struct.png create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/struct.svg delete mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-enum.png delete mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-function.png delete mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-impl.png delete mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-macro.png delete mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-module.png delete mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-struct.png create mode 100644 rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/trait.png create mode 100644 rust/rust.options/src/org/netbeans/modules/rust/options/api/RustAnalyzerOptions.java create mode 100644 rust/rust.options/src/org/netbeans/modules/rust/options/impl/RustAnalyzerOptionsImpl.java create mode 100644 rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/Bundle.properties create mode 100644 rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerOptionsPanelController.java create mode 100644 rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.form create mode 100644 rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.java diff --git a/ide/lsp.client/licenseinfo.xml b/ide/lsp.client/licenseinfo.xml index 24a13ec56ff1..63d1496bfb22 100644 --- a/ide/lsp.client/licenseinfo.xml +++ b/ide/lsp.client/licenseinfo.xml @@ -21,6 +21,33 @@ --> + src/org/netbeans/modules/lsp/client/bindings/icons/attribute.png + src/org/netbeans/modules/lsp/client/bindings/icons/class.png + src/org/netbeans/modules/lsp/client/bindings/icons/color.gif + src/org/netbeans/modules/lsp/client/bindings/icons/constant.png + src/org/netbeans/modules/lsp/client/bindings/icons/constructor.png + src/org/netbeans/modules/lsp/client/bindings/icons/empty.png + src/org/netbeans/modules/lsp/client/bindings/icons/enummember.png + src/org/netbeans/modules/lsp/client/bindings/icons/enum.png + src/org/netbeans/modules/lsp/client/bindings/icons/event.png + src/org/netbeans/modules/lsp/client/bindings/icons/field.png + src/org/netbeans/modules/lsp/client/bindings/icons/file.png + src/org/netbeans/modules/lsp/client/bindings/icons/folder.gif + src/org/netbeans/modules/lsp/client/bindings/icons/function.png + src/org/netbeans/modules/lsp/client/bindings/icons/interface.png + src/org/netbeans/modules/lsp/client/bindings/icons/keyword.png + src/org/netbeans/modules/lsp/client/bindings/icons/method.png + src/org/netbeans/modules/lsp/client/bindings/icons/module.png + src/org/netbeans/modules/lsp/client/bindings/icons/operator.png + src/org/netbeans/modules/lsp/client/bindings/icons/property.png + src/org/netbeans/modules/lsp/client/bindings/icons/reference.png + src/org/netbeans/modules/lsp/client/bindings/icons/snippet.png + src/org/netbeans/modules/lsp/client/bindings/icons/struct.png + src/org/netbeans/modules/lsp/client/bindings/icons/text.png + src/org/netbeans/modules/lsp/client/bindings/icons/typeparameter.gif + src/org/netbeans/modules/lsp/client/bindings/icons/unit.png + src/org/netbeans/modules/lsp/client/bindings/icons/value.png + src/org/netbeans/modules/lsp/client/bindings/icons/variable.gif src/org/netbeans/modules/lsp/client/resources/error_16.png src/org/netbeans/modules/lsp/client/resources/warning_16.png diff --git a/rust/rust.grammar/licenseinfo.xml b/rust/rust.grammar/licenseinfo.xml index 4852540b3791..4741b1647e36 100644 --- a/rust/rust.grammar/licenseinfo.xml +++ b/rust/rust.grammar/licenseinfo.xml @@ -20,23 +20,14 @@ --> - - - src/org/netbeans/modules/rust/grammar/structure/resources/structure-macro.png - src/org/netbeans/modules/rust/grammar/structure/resources/structure-enum.png - src/org/netbeans/modules/rust/grammar/structure/resources/structure-struct.png - src/org/netbeans/modules/rust/grammar/structure/resources/structure-impl.png - src/org/netbeans/modules/rust/grammar/structure/resources/structure-function.png - src/org/netbeans/modules/rust/grammar/structure/resources/structure-module.png + src/org/netbeans/modules/rust/grammar/structure/resources/enum.png + src/org/netbeans/modules/rust/grammar/structure/resources/function.png + src/org/netbeans/modules/rust/grammar/structure/resources/interface.png + src/org/netbeans/modules/rust/grammar/structure/resources/module.png + src/org/netbeans/modules/rust/grammar/structure/resources/snippet.png + src/org/netbeans/modules/rust/grammar/structure/resources/struct.png + src/org/netbeans/modules/rust/grammar/structure/resources/trait.png diff --git a/rust/rust.grammar/nbproject/project.properties b/rust/rust.grammar/nbproject/project.properties index 2ad5789cf71f..0c0c4ac904e0 100644 --- a/rust/rust.grammar/nbproject/project.properties +++ b/rust/rust.grammar/nbproject/project.properties @@ -15,6 +15,6 @@ # specific language governing permissions and limitations # under the License. -javac.source=1.8 +javac.release=21 javac.compilerargs=-Xlint -Xlint:-serial build.generated.sources.dir=${build.dir}/generated-sources diff --git a/rust/rust.grammar/nbproject/project.xml b/rust/rust.grammar/nbproject/project.xml index 3a8db0587598..a3837bcdf62e 100644 --- a/rust/rust.grammar/nbproject/project.xml +++ b/rust/rust.grammar/nbproject/project.xml @@ -61,6 +61,15 @@ 1.20 + + org.netbeans.modules.editor + + + + 3 + 1.119 + + org.netbeans.modules.editor.fold @@ -70,6 +79,15 @@ 1.65 + + org.netbeans.modules.editor.lib2 + + + + 1 + 2.52 + + org.netbeans.modules.editor.mimelookup @@ -96,6 +114,24 @@ 1.1 + + org.netbeans.modules.lsp.client + + + + 0 + 1.24 + + + + org.netbeans.modules.options.api + + + + 1 + 1.77 + + org.netbeans.modules.parsing.api @@ -105,6 +141,23 @@ 9.27 + + org.netbeans.modules.rust.options + + + + 1.11 + + + + org.netbeans.spi.editor.hints + + + + 0 + 1.74 + + org.openide.filesystems @@ -113,6 +166,14 @@ 9.33 + + org.openide.modules + + + + 7.79 + + org.openide.util diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/LayerProviderImpl.java b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/LayerProviderImpl.java new file mode 100644 index 000000000000..30b4d534b0d8 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/LayerProviderImpl.java @@ -0,0 +1,67 @@ +/* + * 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.rust.grammar; + +import java.net.URL; +import java.util.Collection; +import java.util.logging.Logger; +import org.netbeans.modules.rust.options.api.RustAnalyzerOptions; +import org.openide.filesystems.Repository.LayerProvider; +import org.openide.util.lookup.ServiceProvider; + +/** + * The rust.grammer module can provide services directly or using rust-analyzer + * (the rust lsp). If the user configured rust-analyzer, that should get + * priority as it also can provide code completion and improved structure + * scanning. + * + * For this to work, the normal NetBeans behavior to generate layer entries from + * annotations (MimeRegistration of LSP, LanguageRegistration) has to be + * customized as the two options can't coexist completely. + * + * Therefore the layer entries from org.netbeans.modules.rust.grammar.RustLanguageConfig + * and org.netbeans.modules.rust.grammar.lsp.RustLSP are removed from the + * generated-layer.xml (build/classes/META-INF/generated-layer.xml), the + * annotations are commented out and the entries from the generated layer are + * checked whether they were created by one of the mentioned classes (see XML + * comments for reference) and are distributed into: + * + * - layer.xml holds everything that applies to both LSP and non-LSP mode + * - lsp-layer.xml holds everything specific to the LSP + * - non-lsp-layer.xml hold everything specific to the non-LSP run + * + * `layer.xml` is loaded by the module system unconditionally. The two variants + * `lsp-layer.xml` and `non-lsp-layer.xml` are loaded using this class. + */ +@ServiceProvider(service = LayerProvider.class) +public class LayerProviderImpl extends LayerProvider { + + private static final Logger LOG = Logger.getLogger(LayerProviderImpl.class.getName()); + + @Override + protected void registerLayers(Collection context) { + URL layerUrl; + if (RustAnalyzerOptions.getRustAnalyzerLocation(false, false) != null) { + layerUrl = LayerProviderImpl.class.getResource("lsp-layer.xml"); + } else { + layerUrl = LayerProviderImpl.class.getResource("non-lsp-layer.xml"); + } + context.add(layerUrl); + } +} diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/RustLanguageConfig.java b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/RustLanguageConfig.java index e8b6cf564901..07664647883a 100644 --- a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/RustLanguageConfig.java +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/RustLanguageConfig.java @@ -23,13 +23,13 @@ import org.netbeans.modules.csl.api.DeclarationFinder; import org.netbeans.modules.csl.api.StructureScanner; import org.netbeans.modules.csl.spi.DefaultLanguageConfig; -import org.netbeans.modules.csl.spi.LanguageRegistration; import org.netbeans.modules.parsing.spi.Parser; +import org.netbeans.modules.rust.options.api.RustAnalyzerOptions; -/** - * - */ -@LanguageRegistration(mimeType = "text/x-rust", useMultiview = true) +// See LayerProviderImpl for information about layer handling and when this +// needs to be uncommented +// +//@LanguageRegistration(mimeType = "text/x-rust", useMultiview = true) public class RustLanguageConfig extends DefaultLanguageConfig { private static final Language RUST_LANGUAGE = new RustLanguage().language(); @@ -46,12 +46,12 @@ public String getDisplayName() { @Override public StructureScanner getStructureScanner() { - return new RustStructureScanner(); + return RustAnalyzerOptions.getRustAnalyzerLocation(false, false) == null ? new RustStructureScanner() : null; } @Override public boolean hasStructureScanner() { - return true; + return RustAnalyzerOptions.getRustAnalyzerLocation(false, false) == null; } @Override @@ -66,7 +66,7 @@ public String getLineCommentPrefix() { @Override public DeclarationFinder getDeclarationFinder() { - return new RustDeclarationFinder(); + return RustAnalyzerOptions.getRustAnalyzerLocation(false, false) == null ? new RustDeclarationFinder() : null; } } diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/layer.xml b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/layer.xml index 0fb1e98858ca..353bc8cc918a 100644 --- a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/layer.xml +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/layer.xml @@ -50,6 +50,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp-layer.xml b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp-layer.xml new file mode 100644 index 000000000000..c23e12d00189 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp-layer.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/RustLSP.java b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/RustLSP.java new file mode 100644 index 000000000000..0a8bdda40cb2 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/RustLSP.java @@ -0,0 +1,55 @@ +/* + * 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.rust.grammar.lsp; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.modules.lsp.client.spi.LanguageServerProvider; +import org.netbeans.modules.rust.options.api.RustAnalyzerOptions; +import org.openide.util.Lookup; + +// See LayerProviderImpl for information about layer handling and when this +// needs to be uncommented +// +//@MimeRegistration(mimeType = "text/x-rust", service = LanguageServerProvider.class) +public class RustLSP implements LanguageServerProvider { + + private static final Logger LOG = Logger.getLogger(RustLSP.class.getName()); + + @Override + public LanguageServerDescription startServer(Lookup lookup) { + Path rustAnalyzerPath = RustAnalyzerOptions.getRustAnalyzerLocation(true, true); + if(rustAnalyzerPath == null || ! Files.isExecutable(rustAnalyzerPath)) { + return null; + } + try { + Process p = new ProcessBuilder(new String[]{rustAnalyzerPath.toAbsolutePath().toString()}) + .directory(rustAnalyzerPath.getParent().toFile()) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start(); + return LanguageServerDescription.create(p.getInputStream(), p.getOutputStream(), p); + } catch (IOException ex) { + LOG.log(Level.FINE, null, ex); + return null; + } + } +} diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/UnconfiguredHint.java b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/UnconfiguredHint.java new file mode 100644 index 000000000000..dcfa157dd995 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/lsp/UnconfiguredHint.java @@ -0,0 +1,91 @@ +/* + * 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.rust.grammar.lsp; + +import java.beans.PropertyChangeEvent; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.EditorRegistry; +import org.netbeans.api.options.OptionsDisplayer; +import org.netbeans.modules.editor.NbEditorUtilities; +import org.netbeans.modules.rust.options.api.RustAnalyzerOptions; + +import org.netbeans.spi.editor.hints.ChangeInfo; +import org.netbeans.spi.editor.hints.ErrorDescription; +import org.netbeans.spi.editor.hints.ErrorDescriptionFactory; +import org.netbeans.spi.editor.hints.Fix; +import org.netbeans.spi.editor.hints.HintsController; +import org.netbeans.spi.editor.hints.Severity; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.modules.OnStart; + + +@OnStart +public class UnconfiguredHint implements Runnable { + + private static final Set RUST_MIME_TYPES = Collections.singleton("text/x-rust"); + + @Override + public void run() { + EditorRegistry.addPropertyChangeListener((PropertyChangeEvent evt) -> { + updateFocused(); + }); + updateFocused(); + } + + private void updateFocused() { + JTextComponent selectedComponent = EditorRegistry.focusedComponent(); + if (selectedComponent == null) { + return; + } + Document doc = selectedComponent.getDocument(); + FileObject file = NbEditorUtilities.getFileObject(doc); + if (file == null || !RUST_MIME_TYPES.contains(FileUtil.getMIMEType(file))) { + return; + } + List errors = new ArrayList<>(); + Path rustLS = RustAnalyzerOptions.getRustAnalyzerLocation(true, false); + if (rustLS == null) { + errors.add(ErrorDescriptionFactory.createErrorDescription(Severity.WARNING, "For improved Rust support please install and configure rust-analyzer", Collections.singletonList(new ConfigureRustAnalyzer()), doc, 0)); + } + HintsController.setErrors(doc, UnconfiguredHint.class.getName(), errors); + } + + private static final class ConfigureRustAnalyzer implements Fix { + + @Override + public String getText() { + return "Configure rust-analyzer"; + } + + @Override + public ChangeInfo implement() throws Exception { + OptionsDisplayer.getDefault().open("Rust/RustAnalyzer"); + return null; + } + + } + +} diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/non-lsp-layer.xml b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/non-lsp-layer.xml new file mode 100644 index 000000000000..86985859b2a7 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/non-lsp-layer.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/RustStructureItem.java b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/RustStructureItem.java index 3a3c3ae138c9..d51b94ea5f31 100644 --- a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/RustStructureItem.java +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/RustStructureItem.java @@ -42,20 +42,21 @@ private static final String getIconBase(RustASTNode node) { switch (node.getKind()) { case CRATE: case ENUM: - return "org/netbeans/modules/rust/grammar/structure/resources/structure-enum.png"; // NOI18N + return "org/netbeans/modules/rust/grammar/structure/resources/enum.png"; // NOI18N case FUNCTION: - return "org/netbeans/modules/rust/grammar/structure/resources/structure-function.png"; // NOI18N + return "org/netbeans/modules/rust/grammar/structure/resources/function.png"; // NOI18N case STRUCT: - return "org/netbeans/modules/rust/grammar/structure/resources/structure-struct.png"; // NOI18N + return "org/netbeans/modules/rust/grammar/structure/resources/struct.png"; // NOI18N case IMPL: + return "org/netbeans/modules/rust/grammar/structure/resources/trait.png"; // NOI18N case TRAIT: - return "org/netbeans/modules/rust/grammar/structure/resources/structure-impl.png"; // NOI18N + return "org/netbeans/modules/rust/grammar/structure/resources/interface.png"; // NOI18N case MACRO: - return "org/netbeans/modules/rust/grammar/structure/resources/structure-macro.png"; // NOI18N + return "org/netbeans/modules/rust/grammar/structure/resources/snippet.png"; // NOI18N case MODULE: - return "org/netbeans/modules/rust/grammar/structure/resources/structure-module.png"; // NOI18N + return "org/netbeans/modules/rust/grammar/structure/resources/module.png"; // NOI18N default: - return "org/netbeans/modules/rust/grammar/structure/resources/structure-struct.png"; // NOI18N + return "org/netbeans/modules/rust/grammar/structure/resources/structure.png"; // NOI18N } } diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/enum.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/enum.png new file mode 100644 index 0000000000000000000000000000000000000000..f54c89b9cfb44a1114edf1ae4e5b40a1e2b73d2f GIT binary patch literal 830 zcmV-E1Ht@>P)E2M8c07-L`->Q!ywPQd=ip^v z`1uo#<^KL+IP&p}<5MQqCV&89RuAnu`urz9!?VM47))*K8JL)v!Fcw>Zibr-t_-$} zLJS(Bf(#7oY$*84&tD8TFJ1l*5I`&}tn3WAWfK@KA2MQ?&{4rqP+!k5p|O!c%DaO> zc*S;xE3E$*uH1agfCJP41Q5&Lzke7$GjcMB7)CIB`1+fnv!<3o(6t%N|NQt5gV2|k z3|bae7&@<=IL2_{{KYu{0mSm>_iu)8zy2{W0sSd$9?ifcWz8Tds{rPUzJJc(;b6lc zBqD-gfTA@p%wE580|+3X0l$F$`3sFZ9tH_z9R^U^1d086`;tM38JJYwzr!#9B>v~` zUj={w0vqt-&p)gl{L0SF@c#L820=jq47vC3KL9QH`4}L8m_PdRM`00IbPwJ`&J0LpMrfLIua*?}0C92wpM@hhMOe*ppv04#GT(S1NSRR91007*qo IM6N<$f + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/function.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/function.png new file mode 100644 index 0000000000000000000000000000000000000000..7be8a378a3747d5a82416effd17e4b7fab5af00a GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>?0v z(btiIVPjv-@4(4GzCyA`kS_y6l_~>6Lo)-z&;LOBB?CjL0RzLU1O^7H84L{K`IF+0 zx-l>?F7kA745_%4^ymM7duCMzg@zdt8Y~QqE*lx4#E~ZdBKNW`vk9k|A7v;!V|%pT zVUI|MdZo|YV$mgR3Jns4tdFjpSrVYfcrZ2S)-Ii-RjYXZ2u@Yyb5G^E}~_dGPl4_k>cQD@=LfYJYC} z`|-0l56>AN3qudNC434E8>9-}9$hUd{@(sa_5YW~Y;0v^JUl||C!4P@vIvCfud%8B z$sw+tef52P{q3H6`_scFOidLK6HKf4{Y>|8^YMPkb9ZE|4Wt@rK!{@MBW&gAFX-|p_*&VHhvU%;W~$0lPB;R!;_Ns1Z! mezG5V?{JXChzA(J2N*Js?XY=Uzpw!qvkacDelF{r5}E+&8r}5( literal 0 HcmV?d00001 diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/function.svg b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/function.svg new file mode 100644 index 000000000000..3188ced38012 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/function.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/interface.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/interface.png new file mode 100644 index 0000000000000000000000000000000000000000..ae582f77a865f782849599afd143a6326f5799cf GIT binary patch literal 541 zcmV+&0^k-9M6m$?0mMSA!i?NPdmxnw#Nj}^5{ReInB0F5Ab=Rt za|(EYoJOeRbRez=N_W(w z1Q0_|Smckd-@gC<{O#v|5FHd6@&Ch@?ex6pTAMWKzxumKmaid3X5p``1w~) zK}nTi$)2B{E0R#{uP@WlxrGQuzh+hNoGoTHBkquyEWaL#)Qi3 literal 0 HcmV?d00001 diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/interface.svg b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/interface.svg new file mode 100644 index 000000000000..670c9e62f565 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/interface.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/module.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/module.png new file mode 100644 index 0000000000000000000000000000000000000000..505f4b7f812319106884ce66b111bde60c9422c5 GIT binary patch literal 1474 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+mc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxR5#hc$WX!DQqR!T z#M01EN5ROz&{*HlK;Otx*U-?)#N5izOaTg%fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD#PU%0_}#n6BP2AO_EVu8M)o`HUDF34YC)x{-2sR(CaRb3oXS&*t9 zlvJ zOih!`jdW8}%u{tu(@e~DEi8-@bxjk^4Gc`pOwEnW%wgtW({EtxV(DVwXkqMP=wfK- zYUXHeZ0=%Y?rLFVYT@i`W&qRcnO9trn3tRivpW-LH&m}VUcFY%MX8A;`9&f5`8lvu z5Rj2yl3$#WU!dUZte_E|nU|7ZUaSd{2l&r$vI-3+A}#NeO%QvHfE(IMv7*vHFRMOwsNQkIz)+ zUpyV_c*r~LdFuV&b!X1#85-wpn)TID`KM)w)@B8!sHg`2$05bp6}}-M9df5{#+;p5 zc;WjT?xi#CdpyJOx38Mdl_209LA7fZ(*yzqS z=l7|I+KC?n^;jjMQbVu(y;O9qev`7e&HBEEEiAisA8PyiMnj@@#z*x$VZEmxf(%xz zd0?RS=IK4=`g6e<*}9LmOqx`e6Vmr|f^OG;jx+O4KIzDfd9gd@USHrf{psAd`Y&i^xr8Si z>*tp~@Lu@NED2|ggpFTT+$^m+AShC}N4#UzdQ*3%RYi-u%D9-gx@AAl`uO&$@$-i6 zRXvY`E?Cd~xVF^n)I@E?By(0q&o^8d(Q~7jK5f`B;n-!_Zy+-5 zno}8{&YW1qWW=ylF8cOF>7{1Aty2HEfBaOPamwtcSBhSx-j-)6>tC@)J3i*`T;cxh n?)4u(zo~kavY1EKvq~`Ztr7XuzQGSvN;7!6`njxgN@xNA?ExFr literal 0 HcmV?d00001 diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/snippet.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/snippet.png new file mode 100644 index 0000000000000000000000000000000000000000..b56c19cee5d575480a31f2a3d433a240d9de649e GIT binary patch literal 613 zcmV-r0-F7aP)>!O+57f@4x>j z`1hZG|9?R-h<)|hkN;LV3;*}5I1x2()ph}pK7asXV!#8Km>3zDp%}zoa^Mcb{Mtx{ zbw_S4WfzcD1*riDAiM^kg9K+ah6Q_Xf!XKoJof_800Bgj0V<-bU_PgAb_yCpl4YDgG2(<&A=Ffs|5%ktSr0faLQSpWQF z;9{H3@a@SrhEs0?8TgqufyMsr)mnh#d)HVEq1#;fus( z20^CX44elX7z!e%G5q-RAB + + + + + + + + + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/struct.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/struct.png new file mode 100644 index 0000000000000000000000000000000000000000..220e6f385e0aef0c0aa7ab28d6623b2a2b7d0374 GIT binary patch literal 785 zcmV+s1Md8ZP)FfD0{{dNhKZ}H zL;^MyGyMPg^7Q|&w^o8_5FaFt%>@7f#0Vl_U`d&9Am`6_>%2>LGyHyY87|H$qRX(g z$CcqbyHHR}*W2~T4gd%sxB+tugu*#~zF!ekx}M?J%hMPxWfM1LSl1oM@Pl12B6-5Q zl`wID0D^}B)7SSa0*h8L{Cs+t;s2i>7#JiDk_V|nHW(m)7~|?^fSthdY!8F+k2MU@ zX~ifu|M~Tw;l-_=46pD1V)*gpAH%PIzuvJiu}uO>P4t*L=L0|hF~-zP{a>Av!0_qg z2ZrlMmN2~AQ_hf@UjfzpkKy{Ej|`lGnhc7j<_!FjQVbuTKV!Im`Xs~SOBXgXGBCse z1Q65Tzke8>efq`lg^`OvP(Pf3$*PrMN_PhX13NpzgX=#SSh*A#3>+L8__?_l7(RUf z(;&X2x`yxf-+u}K0*K|$@81mHfHr}{frFPp!X%R6!JmH&z4f&Wx^l4$4vv-#3~ydx z1ges}Jj0Fi=i>na2xtJ%_Md-|f}4jy)G&hK@sFPjpDy2E5Ml;~^ZR!g27tu>{Qau{ z5I{^I1AYK)M8-^*cjeFf6l=0<2wchiGTU|^D#gGfem10X2HOG0>TWx z6!jP`-@VJgz{!DuLE`2F`UgQ1oNgOG>_ z!@DzI=5FAb>zt8#CYspw#*Vh=qZe9f*Gd@mnB%1+?HVK!5=N6-#svo^p^a P00000NkvXXu0mjf*;Qhm literal 0 HcmV?d00001 diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/struct.svg b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/struct.svg new file mode 100644 index 000000000000..8c0a9dca7298 --- /dev/null +++ b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/struct.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-enum.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-enum.png deleted file mode 100644 index 624f23b9d000db65a58dfa48438c3a59790504d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1186 zcmV;T1YP@yP)EX>4Tx04R}tkxeK>Q5c3F7m>)qEHqi%#jv7;WXVvIqNYL3VBy|7W~jN>bni?y zmdaLH_}LgNY^>Q@*ea#`lx%IRh>di8XJ%?98K+L?={@It`@ZjhR>)Yk*U%5PlXGK{ zupUp$>rF3c1Zg1vhUI0)qR}a8uFpH_)Op*dvTDEkbG0RxJqzk|;ZZB==7g7ohu3pi z#e2d5YsE+kp9%N6QlRjI(#;aTDoz(!Q<(vGHa0FS)uX4Y*sNk}#kGZpghNT&k$f#) z;-umY#daodmFrX;wk|t!GfJ1;MTAMFiK1hYXO#>&`s8#(@rcQNxQ0Ji^hd>Iifa{1 z922ZzV-)?UKlnXcFEuz&N@@*5R5;6(lTAnzVHn5%`_AZo&9bYr>wf0CBtZuaX(Nfm zKqRm-=n_HS*(neNN%Go3>=JrP{D?wCj5=f{5<%i+g)J+(xT53gtefeqlQTOrE;e=u zR(DIJ|LuLB=lAkF&-;H2$kW)<$ySxDHm4-P_fwH50I*wZ+eH=^Z_?e}7n*7w@69)4 z!QSaAh2lr%f6SGa2FwTrt#Gm?0KmGSfs3cH#H% zkKdBE)?4k{^cKC+@~7wBvR}NMMEPMq#z#A$X&DSW8;9TTg3rrlD_LAhL)lrqM>do- ze<*0p!n&?wZk|WY=}OQv1%@%B@%$;w#+LKiu|kjpH!aF(;$$7!Kqj+|zJUq!4NT+! z{ZCqQ&Q8_@QBDy$e^7;m1-O2-4jm7hKnTG>A`Zr)uM67l1u>wu<`{;C-$N8-==u&c zEdyQGbHH^$gWYQVMOd42m5Zlyf@kYb!0j$VTYC>Gj^D=3rcP{Z$a$VfgJqma+PvfG zjwRw9TwKcj1Vs_l*M(7E7v9%UOT3B-x2MYh0Ivpzmy;qNILJrEX>4Tx04R}tkxeK>Q5c3F7m>)qEHqi%#jv7;WXVvIqNYL3VBy|7W~jN>bni?y zmdaLH_}LgNY^>Q@*ea#`lx%IRh>di8XJ%?98K+L?={@It`@ZjhR>)Yk*U%5PlXGK{ zupUp$>rF3c1Zg1vhUI0)qR}a8uFpH_)Op*dvTDEkbG0RxJqzk|;ZZB==7g7ohu3pi z#e2d5YsE+kp9%N6QlRjI(#;aTDoz(!Q<(vGHa0FS)uX4Y*sNk}#kGZpghNT&k$f#) z;-umY#daodmFrX;wk|t!GfJ1;MTAMFiK1hYXO#>&`s8#(@rcQNxQ0Ji^hd>Iifa{1 z922ZzV-)?UKlnXcFEuz&N@@*5(R5;76lRaS2LNzSI zVkSCn6I96d<+WQ+ws*EpVgSIx^BM~r^?vpda*5mc9SYSzq5@h@U5#;k4Zyv~q0!hq z9W~xO$UR=DY#hA8>;0Ekl6*e6z}N3Re6&B{PO9`g=7(c_UDTG!Zog$%Bk`q5zIYn^ zaIAAI9Of0NfK^eX0$4cAbKcAq=F?tet|R8^&7 zyW=)tduM+Qw!JfS+$L11*e>g(DhB|RWGYn1_FGQEX>4Tx04R}tkxeK>Q5c3F7m>)qEHqi%#jv7;WXVvIqNYL3VBy|7W~jN>bni?y zmdaLH_}LgNY^>Q@*ea#`lx%IRh>di8XJ%?98K+L?={@It`@ZjhR>)Yk*U%5PlXGK{ zupUp$>rF3c1Zg1vhUI0)qR}a8uFpH_)Op*dvTDEkbG0RxJqzk|;ZZB==7g7ohu3pi z#e2d5YsE+kp9%N6QlRjI(#;aTDoz(!Q<(vGHa0FS)uX4Y*sNk}#kGZpghNT&k$f#) z;-umY#daodmFrX;wk|t!GfJ1;MTAMFiK1hYXO#>&`s8#(@rcQNxQ0Ji^hd>Iifa{1 z922ZzV-)?UKlnXcFEuz&N@@*5e$gT6~l=Yn%6GlT9_` z@p&@MYCwYj)4dfj#~_nc80;LQHo;S3 zQoeHoDG!eK$kfApcUe*alX4xvIk2PxPt`X|0O0<@LcWkp@H7+v>2~4K^ew!9`a%N@ zjE&&@%r&r70ga6v@ZEvjhV^Br6WU;iEvEwObKUV zx$y0a(D5_1YVq;w8(EUPtjZZ@tym@ipm*39$9ok|-FN5ox#63w^}hhZ<=+<=Cv#8$ O0000EX>4Tx04R}tkxeK>Q5c3F7m>)qEHqi%#jv7;WXVvIqNYL3VBy|7W~jN>bni?y zmdaLH_}LgNY^>Q@*ea#`lx%IRh>di8XJ%?98K+L?={@It`@ZjhR>)Yk*U%5PlXGK{ zupUp$>rF3c1Zg1vhUI0)qR}a8uFpH_)Op*dvTDEkbG0RxJqzk|;ZZB==7g7ohu3pi z#e2d5YsE+kp9%N6QlRjI(#;aTDoz(!Q<(vGHa0FS)uX4Y*sNk}#kGZpghNT&k$f#) z;-umY#daodmFrX;wk|t!GfJ1;MTAMFiK1hYXO#>&`s8#(@rcQNxQ0Ji^hd>Iifa{1 z922ZzV-)?UKlnXcFEuz&N@@*54_-V=-P11XD8(pKnq?NtvmprS(Q;Q=AoC05PW`x-8m7TU5sS<1Cp!hP}NXauV@l$ z35LM9y*LQ1kcX1ZfV10vj5plV7q3U&kpQ3>%ZZ96v3xiRv&)Tnj{|RBuJwc8KV~uS zaiDE9fe%Mft4N~?Bd!C$vBcBVom@F{#Tt&n>=b}r4;4{Fx6{GIR1^e3VB=L1XQra? zOa#D=J3tzYC~v+uEZw|sAa_mDUCJp8UE3Et2U*<4UaN&}rvtOa($^Tb7nDY7UI|MnmjyB(#iulV#f4c96EkZ!c{ z&^47H@ejO#v)F4kVKABg>KQSyY_D0BiP7WqkT@Bi!DM{qz+kf~gJReo$yxZ_dNGfq zQZMGgvAjZ#b@?ZHsR*rb_#s;1S1XyeZH1h>dSTh+^D9a=gGO2WZ@5twp=2`Y5)KL07*qoM6N<$f(Bdx=>Px# diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-module.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-module.png deleted file mode 100644 index 65dd9681ca968945952aa26ea6987a930cb415ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1173 zcmV;G1Zw+EX>4Tx04R}tkv&MmKpe$iQ>7vm2P=qF$WS|35EXIMDionYsTEpvFuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UwP&La) zC*oo@w<-o+As~!FL@^>UQ=dzvlJFc~_we!cF2=LG&;2?2l)T9RpGZ8*bi*RvAfDc| zbk6(4Ay$$U;&b9LgDyz?$aUG}H_kh$D)sQNECM zS>e3JS*_Gq>z@3D!MwJz%ypV!B(R7jq#!~@4P{hdAx5i4iitGs$36VRjz2{%nOtQs zax9<<6_Voz|AXJ%nuW;;Hz^ngx?gPjV-yJN0?oQ@e;?a+^91le16NwxUu^(0pQP8@ zTI2}m-v%zO+nT%wTeSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00M1EL_t(I%axNqOcQYw$G`Wdg_i5x{hHGDO8HamSz{MS zFdYnOh>0dbTyQbEXyW3AI%&{EM|IOUkl=!YCK@7OVo20NR2Y;27gMBQ=^b|k?yi)U zwy`)E2`XUtOz-8ryqEXQ_fhzZ;lYXPmR-MU8d*}vc{Znn0QyKoA{@UO=$U%@r)cKqMfOV|^D`{fwTON(FY>BJE&aO+b>8$~y{Znnr zj`?@X`3&!i)q}_cJ7FXZXj3{wum6W+g=42K#6GbAU^$iH<+ThSi2K1e^4JX`6EL+5 zl!VIIrJ4i)j(Pcb;@Ye--4lrWA%;Z!j~bx{jDtaDa>3qv`DQKCb-sIK(_MB*F$ro) z#e?35*sNAT(KHOrz|f;1G#_b3Hl2li)MaHZ2+Js3Q|Y{YhwWb ztlfs-p~kJ6;NejzRZ!=1qG;qn?{p_?XaC3skR0(Y49Ci8pU0 zmpBp@p)F{bo0&slt%$|h3@W8E^t29}-3GZe3^k>qPT(-}eg+X=1U@ljQ6taBZ-1Hl z&=)+}u&aS~GSuSh45GnfHz)vj^5VIWhXL{T;0EjEW zPU7)bl!OW+Y3#XKNvI$me?>Hc$NzaI)4ljMolFY*ex~h=JRQGfvig-(L+6V|9suAV n9Nfg46U)AP-Df-QZ14XGn+o_Q9%$}z00000NkvXXu0mjf)ejZb diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-struct.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/structure-struct.png deleted file mode 100644 index d4b4000f1ed920fb431057ade07166d3b9dd0eb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1196 zcmV;d1XKHoP)EX>4Tx04R}tkxeK>Q5c3F7m>)qEHqi%#jv7;WXVvIqNYL3VBy|7W~jN>bni?y zmdaLH_}LgNY^>Q@*ea#`lx%IRh>di8XJ%?98K+L?={@It`@ZjhR>)Yk*U%5PlXGK{ zupUp$>rF3c1Zg1vhUI0)qR}a8uFpH_)Op*dvTDEkbG0RxJqzk|;ZZB==7g7ohu3pi z#e2d5YsE+kp9%N6QlRjI(#;aTDoz(!Q<(vGHa0FS)uX4Y*sNk}#kGZpghNT&k$f#) z;-umY#daodmFrX;wk|t!GfJ1;MTAMFiK1hYXO#>&`s8#(@rcQNxQ0Ji^hd>Iifa{1 z922ZzV-)?UKlnXcFEuz&N@@*5mu z5?X5HQ$jstJ{3|B^wLYOy@V3>Ad-m?36`2d5{VBJGE!Nf)x%tCm;K!G?(TNo4Cn00 z;Zljt)5muX-yi2w;V+Co^WRO8Vz(>{w)A?E#5Pb$m1mhUGf!vry=|S|N4xc^onWee zz9y*=M)>&hVXBG)sEYc!27oao- z?IZvjIX>XOvnniH;k+I&P8$F~7A1^69zf6S`$GWH+;jhQgp28b+HnuL-|>58XB!Q%mcK zit>}yg}eS-Lxrmn1W7_ml5o0@?pcR%*^yjWwGbIuB6hC5gN92N@TzYRVq7@z9;IeI zHe`uV??+Z2cY`tNF*`a5f+W$@?gMKw?puf`Vc1++5cd!U&VtJ~ufyBgjF){+_e_%( zM3m^*ErOvfiByn3a8#qsit`=q__7cv$ftrl7@Z|XFk6ORB@}}cjO590D_@aO*0G(- z;deTN;HMQ(3|(L-B@_db)%U0XVCeZoXc8a00L3B=|CFfq=^wT_lUc`L`M$gCvAX!EImTbX4!*{V32d|kCm1<$lb2@}!q zvi-lGX*b4({r6I&*qxFS)(u%A0HBy=a|~^Xvsryl+g0zw-TFV}6$#Cf9+{Q^0000< KMNUMnLSTZ2{u|%` diff --git a/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/trait.png b/rust/rust.grammar/src/org/netbeans/modules/rust/grammar/structure/resources/trait.png new file mode 100644 index 0000000000000000000000000000000000000000..9cc56d48e2dd7afb271f65aedb6292598a6b5c86 GIT binary patch literal 530 zcmV+t0`2{YP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iprB z5g9vHVR2*t00ELoL_t(I%hi+5E5mUZ$KQ{Ai&hJ*r8XBS^^sbwEH@O&4v5vn#bFm) zYHkig*ug*G#|;-*eHAVW2a?i~QsSVMVoI~ww|)2RyUo|*;_!7N2YKqbJkRrfJ?EuCB1OUVVtc)cRC$!euY6CF$*<1lIJC;b~&Eh4Gr@h5) zKW+?#;Og&3YIhgOx+n~DcmkDP9E*;(M2#AF$0uxdAlJPgwyAP zQ7FLa^WnWz!dWafff*t6Vm8bA%L^B?S&j<9dOH0jqe5_5Rln5As>)FzSh`bH`7#_H zZJwUS&B6k%6=nZ)XsFkGK~h!EldG$3uE9YVng)y2iu;WXJZRbh)m2sscTG*yCQ1JM z+naoq%f+6HMXMRm>2Nd+xLljven0910X)XzIFx1Vq*5aUzyiPtzyTl);E9Eo2h%UX U9!>p9a{vGU07*qoM6N<$f-nl^k^lez literal 0 HcmV?d00001 diff --git a/rust/rust.options/src/org/netbeans/modules/rust/options/api/RustAnalyzerOptions.java b/rust/rust.options/src/org/netbeans/modules/rust/options/api/RustAnalyzerOptions.java new file mode 100644 index 000000000000..60ef1f197ca5 --- /dev/null +++ b/rust/rust.options/src/org/netbeans/modules/rust/options/api/RustAnalyzerOptions.java @@ -0,0 +1,50 @@ +/* + * 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.rust.options.api; + +import java.nio.file.Path; +import org.netbeans.modules.rust.options.impl.RustAnalyzerOptionsImpl; + +/** + * Returns the options for rust-analyzer + */ +public final class RustAnalyzerOptions { + + /** + * Returns the Path where rust-analyzer is installed, or null. + * + * @param verifying If true then the path is checked for validity (the path + * exists and is executable) and if it is incorrect then a notification is + * shown to the user. + * @param showNotificationIfVerifyFail If true then a user notifiation is + * generated if rust-analyzer is not executeable + * @return The Path where rust-analyzer is installed, or null. + */ + public static final Path getRustAnalyzerLocation(boolean verifying, boolean showNotificationIfVerifyFail) { + return RustAnalyzerOptionsImpl.getRustAnalyzerLocation(verifying, true); + } + + /** + * Opens the rust-analyzer options panel. + */ + public static void showRustAnalyzerOptions() { + RustAnalyzerOptionsImpl.showRustAnalyzerOptions(); + } + +} diff --git a/rust/rust.options/src/org/netbeans/modules/rust/options/impl/RustAnalyzerOptionsImpl.java b/rust/rust.options/src/org/netbeans/modules/rust/options/impl/RustAnalyzerOptionsImpl.java new file mode 100644 index 000000000000..739be4768ca7 --- /dev/null +++ b/rust/rust.options/src/org/netbeans/modules/rust/options/impl/RustAnalyzerOptionsImpl.java @@ -0,0 +1,124 @@ +/* + * 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.rust.options.impl; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.prefs.Preferences; +import javax.swing.ImageIcon; +import javax.swing.SwingUtilities; +import org.netbeans.api.options.OptionsDisplayer; +import org.netbeans.modules.rust.project.api.RustProjectAPI; +import org.openide.awt.NotificationDisplayer; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle; +import org.openide.util.NbPreferences; + +/** + * RustAnalyzerOptions implementation. + */ +public final class RustAnalyzerOptionsImpl { + + private static final String RUST_ANALYZER_LOCATION_KEY = "rust-analyzer-location"; // NOI18N + + private static Preferences getPreferences() { + return NbPreferences.forModule(RustAnalyzerOptionsImpl.class); + } + + /** + * Returns the full path to the "rust-analyzer" executable, or null if none + * exists. + * + * @param verify true to verify that the path is indeed executable + * @param showNotificationIfVerifyFail true to generate notification if rust analyzer is not executeable + * @return The full path to the rust-analyzer executable, or null. + */ + public static Path getRustAnalyzerLocation(boolean verify, boolean showNotificationIfVerifyFail) { + String rustAnalyzer = getPreferences().get(RUST_ANALYZER_LOCATION_KEY, null); + // Check if rust-analyzer is valid, if not then reset from preferences + if (rustAnalyzer != null && !rustAnalyzer.trim().isEmpty() && verify) { + File rustAnalyzerExecutable = new File(rustAnalyzer); + if (rustAnalyzerExecutable.canExecute()) { + return Paths.get(rustAnalyzer); + } + // Reset from prefernces + deleteRustAnalyzerLocation(); + rustAnalyzer = null; + // Warn the user if rust-analyzer cannot be found + if (showNotificationIfVerifyFail) { + showRustAnalyzerNotFoundNotification(); + } + } + return rustAnalyzer == null ? null : Paths.get(rustAnalyzer); + } + + /** + * Removes the previously saved rust-analyzer location. + */ + public static void deleteRustAnalyzerLocation() { + getPreferences().remove(RUST_ANALYZER_LOCATION_KEY); + } + + /** + * Sets a new rust-analyzer location. It is ignored if this is not a valid + * rust-analyzer location. + * + * @param location The location (possibly an absolute path). + */ + public static void setRustAnalyzerLocation(String location) { + if (location == null || location.trim().isEmpty()) { + deleteRustAnalyzerLocation(); + } else { + File rustAnalyzer = new File(location); + if (rustAnalyzer.canExecute()) { + getPreferences().put(RUST_ANALYZER_LOCATION_KEY, rustAnalyzer.getAbsolutePath()); + } + } + } + + /** + * Opens the "Rust" options dialog, focused in the "rust-analyzer" tab. + */ + public static void showRustAnalyzerOptions() { + SwingUtilities.invokeLater(() -> { + OptionsDisplayer.getDefault().open("Rust/RustAnalyser"); // NOI18N + }); + } + + @NbBundle.Messages({ + "MISSING_RUSTANALYZER_TITLE=rust-analyzer command was not found.", + "MISSING_RUSTANALYZER_DETAILS=rust-analyzer could not be found in your PATH. Please select the rust-analyzer executable location" + }) + public static void showRustAnalyzerNotFoundNotification() { + NotificationDisplayer.Priority priority = NotificationDisplayer.Priority.HIGH; + String title = Bundle.MISSING_RUSTANALYZER_TITLE(); + String details = Bundle.MISSING_RUSTANALYZER_DETAILS(); + ImageIcon icon = new ImageIcon(ImageUtilities.loadImage(RustProjectAPI.ICON)); + NotificationDisplayer.getDefault().notify( + title, + icon, + details, + actionEvent -> showRustAnalyzerOptions(), + priority + ); + } + +} diff --git a/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/Bundle.properties b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/Bundle.properties new file mode 100644 index 000000000000..0a9b93959a97 --- /dev/null +++ b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/Bundle.properties @@ -0,0 +1,27 @@ +# 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. +RustAnalyzerPanel.cmdGetVersion.text=Get version +RustAnalyzerPanel.cmdBrowse.toolTipText=Browse rust-analyzer in your filesystem +RustAnalyzerPanel.cmdBrowse.text=Browse... +RustAnalyzerPanel.filterRustAnalyser=rust-analyser binary +RustAnalyzerPanel.infoPanel.text=\n \n\n \n \n

About

\n

\n rust-analyzer is a LSP server for the Rust programming language.\n

\n

\n LSP server provide IDE independend language support. This includes\n semantic analysis, error checking, formatting and code completion.\n NetBeans can make use of such the rust-analyzer LSP to provide\n language services.\n

\n

Download

\n

\n Please follow the instructions on the website of the project:\n https://rust-analyzer.github.io/.\n

\n

Notice

\n

Switching from LSP/to LSP requires a restart of the IDE.

\n \n\n +RustAnalyzerPanel.restartTitle=Restart IDE +RustAnalyzerPanel.restartDetails=Click here to restart IDE and apply your rust-analyzer settings change. +RustAnalyzerPanel.lblRustAnalyzerPath.text=rust-analyzer executable location: +RustAnalyzerPanel.txtRustAnalyzerPath.toolTipText=The full path pointing to rust-analyzer executable +RustAnalyzerPanel.txtRustAnalyzerPath.text= +RustAnalyzerPanel.lblRustAnalyzerVersion.text= diff --git a/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerOptionsPanelController.java b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerOptionsPanelController.java new file mode 100644 index 000000000000..3bb3b2284240 --- /dev/null +++ b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerOptionsPanelController.java @@ -0,0 +1,111 @@ +/* + * 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.rust.options.rustanalyzer; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; + +@OptionsPanelController.SubRegistration( + id="RustAnalyzer", + location = "Rust", + displayName = "#AdvancedOption_DisplayName_RustAnalyzer", + keywords = "#AdvancedOption_Keywords_RustAnalyzer", + keywordsCategory = "Rust/RustAnalyzer", + position = 1500 +) +@org.openide.util.NbBundle.Messages({ + "AdvancedOption_DisplayName_RustAnalyzer=rust-analyzer", + "AdvancedOption_Keywords_RustAnalyzer=rust analyser rust-analyzer" +}) +public final class RustAnalyzerOptionsPanelController extends OptionsPanelController { + + private RustAnalyzerPanel panel; + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private boolean changed; + + @Override + public void update() { + getPanel().load(); + changed = false; + } + + @Override + public void applyChanges() { + SwingUtilities.invokeLater(() -> { + getPanel().store(); + changed = false; + }); + } + + @Override + public void cancel() { + // need not do anything special, if no changes have been persisted yet + } + + @Override + public boolean isValid() { + return getPanel().valid(); + } + + @Override + public boolean isChanged() { + return changed; + } + + @Override + public HelpCtx getHelpCtx() { + return null; // new HelpCtx("...ID") if you have a help set + } + + @Override + public JComponent getComponent(Lookup masterLookup) { + return getPanel(); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener l) { + pcs.addPropertyChangeListener(l); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener l) { + pcs.removePropertyChangeListener(l); + } + + private RustAnalyzerPanel getPanel() { + if (panel == null) { + panel = new RustAnalyzerPanel(this); + } + return panel; + } + + void changed() { + if (!changed) { + changed = true; + pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true); + } + pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null); + } + +} diff --git a/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.form b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.form new file mode 100644 index 000000000000..e7ac43da735d --- /dev/null +++ b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.form @@ -0,0 +1,139 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.java b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.java new file mode 100644 index 000000000000..6ba3edce2212 --- /dev/null +++ b/rust/rust.options/src/org/netbeans/modules/rust/options/rustanalyzer/RustAnalyzerPanel.java @@ -0,0 +1,373 @@ +/* + * 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.rust.options.rustanalyzer; + +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import javax.swing.JFileChooser; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.filechooser.FileFilter; +import org.netbeans.modules.rust.options.impl.RustAnalyzerOptionsImpl; +import org.openide.LifecycleManager; +import org.openide.awt.Notification; +import org.openide.awt.NotificationDisplayer; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle; + +final class RustAnalyzerPanel extends javax.swing.JPanel implements DocumentListener { + + private final RustAnalyzerOptionsPanelController controller; + private final ComponentListener resizeListener = new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + updateInfoPaneWidth(); + } + }; + private final HyperlinkListener hyperlinkListener = (HyperlinkEvent he) -> { + try { + if (he.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + URI uri = he.getURL().toURI(); + Desktop.getDesktop().browse(uri); + } + } catch (IOException | URISyntaxException ex) { + ex.printStackTrace(); + } + }; + private JFileChooser fileChooser; + private SwingWorker versionWorker; + + RustAnalyzerPanel(RustAnalyzerOptionsPanelController controller) { + this.controller = controller; + initComponents(); + txtRustAnalyzerPath.getDocument().addDocumentListener(this); + infoPanel.setEditable(false); + infoPanel.addHyperlinkListener(hyperlinkListener); + } + + private void updateInfoPaneWidth() { + int width = getParent().getWidth(); + setMaximumSize(new Dimension(width, Integer.MAX_VALUE)); + setPreferredSize(new Dimension(width, super.getMinimumSize().height)); + validate(); + } + + @Override + public void addNotify() { + super.addNotify(); + SwingUtilities.getUnwrappedParent(this).addComponentListener(resizeListener); + updateInfoPaneWidth(); + } + + @Override + public void removeNotify() { + SwingUtilities.getUnwrappedParent(this).removeComponentListener(resizeListener); + super.removeNotify(); + } + + private SwingWorker newWorker() { + return new SwingWorker() { + @Override + protected String doInBackground() throws Exception { + String rustAnalyser = txtRustAnalyzerPath.getText(); + return getVersionOf(rustAnalyser); + } + + @Override + protected void done() { + versionWorker = null; + String version; + try { + version = get(); + lblRustAnalyzerVersion.setText(version); + } catch (Exception ex) { + lblRustAnalyzerVersion.setText(""); + } + } + }; + } + + private String getVersionOf(String aPossibleRustAnalyserPath) throws Exception { + File rustAnalyser = Paths.get(aPossibleRustAnalyserPath).toFile(); + if (rustAnalyser.isFile() && rustAnalyser.canExecute()) { + return getVersionOf(rustAnalyser); + } + return null; + } + + private String getVersionOf(File rustAnalyser) throws Exception { + Process p = Runtime.getRuntime().exec(new String[]{rustAnalyser.getAbsolutePath(), "--version"}); + int ok = p.waitFor(); + if (ok == 0) { + String version = null; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) { + do { + String line = reader.readLine(); + if (line == null) { + break; + } + version = line; + } while (true); + } + p.destroy(); + return version; + } + return null; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + lblRustAnalyzerPath = new javax.swing.JLabel(); + txtRustAnalyzerPath = new javax.swing.JTextField(); + cmdBrowse = new javax.swing.JButton(); + cmdGetVersion = new javax.swing.JButton(); + lblRustAnalyzerVersion = new javax.swing.JLabel(); + infoPanel = new javax.swing.JTextPane(); + + setLayout(new java.awt.GridBagLayout()); + + lblRustAnalyzerPath.setLabelFor(txtRustAnalyzerPath); + org.openide.awt.Mnemonics.setLocalizedText(lblRustAnalyzerPath, org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.lblRustAnalyzerPath.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(8, 8, 8, 8); + add(lblRustAnalyzerPath, gridBagConstraints); + + txtRustAnalyzerPath.setText(org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.txtRustAnalyzerPath.text")); // NOI18N + txtRustAnalyzerPath.setToolTipText(org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.txtRustAnalyzerPath.toolTipText")); // NOI18N + txtRustAnalyzerPath.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + txtRustAnalyzerPathActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.BASELINE; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 0); + add(txtRustAnalyzerPath, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(cmdBrowse, org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.cmdBrowse.text")); // NOI18N + cmdBrowse.setToolTipText(org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.cmdBrowse.toolTipText")); // NOI18N + cmdBrowse.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cmdBrowseActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.BASELINE; + gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 0); + add(cmdBrowse, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(cmdGetVersion, org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.cmdGetVersion.text")); // NOI18N + cmdGetVersion.setEnabled(false); + cmdGetVersion.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cmdGetVersionActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 1; + gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 8); + add(cmdGetVersion, gridBagConstraints); + + lblRustAnalyzerVersion.setFont(lblRustAnalyzerVersion.getFont().deriveFont(lblRustAnalyzerVersion.getFont().getStyle() | java.awt.Font.BOLD)); + org.openide.awt.Mnemonics.setLocalizedText(lblRustAnalyzerVersion, org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.lblRustAnalyzerVersion.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(8, 8, 8, 8); + add(lblRustAnalyzerVersion, gridBagConstraints); + + infoPanel.setContentType("text/html"); // NOI18N + infoPanel.setText(org.openide.util.NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.infoPanel.text")); // NOI18N + infoPanel.setOpaque(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + add(infoPanel, gridBagConstraints); + }// //GEN-END:initComponents + + private void txtRustAnalyzerPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtRustAnalyzerPathActionPerformed + controller.changed(); + }//GEN-LAST:event_txtRustAnalyzerPathActionPerformed + + private void cmdBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmdBrowseActionPerformed + + showFileChooser(); + + }//GEN-LAST:event_cmdBrowseActionPerformed + + private void cmdGetVersionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmdGetVersionActionPerformed + if (versionWorker == null) { + versionWorker = newWorker(); + versionWorker.execute(); + } + }//GEN-LAST:event_cmdGetVersionActionPerformed + + void showFileChooser() { + if (fileChooser == null) { + fileChooser = new JFileChooser(System.getProperty("user.home")); // NOI18N + FileFilter rustAnalyserFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.isFile() && f.exists() && f.canExecute() && f.getName().startsWith("rust-analyzer")); + } + + @Override + public String getDescription() { + return NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.filterRustAnalyser"); + } + }; + fileChooser.addChoosableFileFilter(rustAnalyserFilter); + fileChooser.addChoosableFileFilter(fileChooser.getAcceptAllFileFilter()); + fileChooser.setFileFilter(rustAnalyserFilter); + fileChooser.setDialogType(JFileChooser.OPEN_DIALOG); + fileChooser.setFileHidingEnabled(false); + } + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + int result = fileChooser.showDialog(this, null); + if (result == JFileChooser.APPROVE_OPTION) { + txtRustAnalyzerPath.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + + void load() { + Path rustAnalyzerLocation = RustAnalyzerOptionsImpl.getRustAnalyzerLocation(true, false); + txtRustAnalyzerPath.setText(rustAnalyzerLocation == null ? "" : rustAnalyzerLocation.toString()); + } + + void store() { + String path = txtRustAnalyzerPath.getText(); + Path origPath = RustAnalyzerOptionsImpl.getRustAnalyzerLocation(false, false); + RustAnalyzerOptionsImpl.setRustAnalyzerLocation(path.trim().isEmpty() ? null : path); + Path rustAnalyzerLocation = RustAnalyzerOptionsImpl.getRustAnalyzerLocation(true, false); + txtRustAnalyzerPath.setText(rustAnalyzerLocation == null ? "" : rustAnalyzerLocation.toString()); + if( + (nullOrBlank(path) && origPath != null) + || (!nullOrBlank(path) && origPath == null) + ) { + askForRestart(); + } + } + + private boolean nullOrBlank(String input) { + return input == null || input.trim().isEmpty(); + } + + boolean valid() { + String path = txtRustAnalyzerPath.getText(); + if(path.trim().isEmpty()) { + return true; + } + File rustAnalyzer = new File(txtRustAnalyzerPath.getText()); + return rustAnalyzer.exists() && rustAnalyzer.isFile() && rustAnalyzer.canExecute(); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cmdBrowse; + private javax.swing.JButton cmdGetVersion; + private javax.swing.JTextPane infoPanel; + private javax.swing.JLabel lblRustAnalyzerPath; + private javax.swing.JLabel lblRustAnalyzerVersion; + private javax.swing.JTextField txtRustAnalyzerPath; + // End of variables declaration//GEN-END:variables + + @Override + public void insertUpdate(DocumentEvent e) { + documentChanged(e); + } + + @Override + public void removeUpdate(DocumentEvent e) { + documentChanged(e); + } + + @Override + public void changedUpdate(DocumentEvent e) { + documentChanged(e); + } + + private void documentChanged(DocumentEvent e) { + controller.changed(); + File file = Paths.get(txtRustAnalyzerPath.getText()).toFile(); + boolean executable = file.exists() && file.canExecute(); + cmdGetVersion.setEnabled(executable); + } + + private Notification restartNotification; + + private void askForRestart() { + if(restartNotification != null) { + restartNotification.clear(); + } + restartNotification = NotificationDisplayer.getDefault().notify( + NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.restartTitle"), + ImageUtilities.loadImageIcon( "org/netbeans/core/windows/resources/restart.png", true ), //NOI18N + NbBundle.getMessage(RustAnalyzerPanel.class, "RustAnalyzerPanel.restartDetails"), + e -> { + if(restartNotification != null) { + restartNotification.clear(); + restartNotification = null; + } + LifecycleManager.getDefault().markForRestart(); + LifecycleManager.getDefault().exit(); + }, + NotificationDisplayer.Priority.NORMAL, NotificationDisplayer.Category.INFO); + } +} diff --git a/rust/rust.sources/src/org/netbeans/modules/rust/sources/rs/RustFileDataObject.java b/rust/rust.sources/src/org/netbeans/modules/rust/sources/rs/RustFileDataObject.java index 816d17c8666e..fb5057f97063 100644 --- a/rust/rust.sources/src/org/netbeans/modules/rust/sources/rs/RustFileDataObject.java +++ b/rust/rust.sources/src/org/netbeans/modules/rust/sources/rs/RustFileDataObject.java @@ -99,6 +99,17 @@ path = "Loaders/text/x-rust/Actions", id = @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"), position = 1400 + ), + @ActionReference( + path = "Editors/text/x-rust/Popup", + id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.WhereUsedAction"), + position = 1400 + ), + @ActionReference( + path = "Editors/text/x-rust/Popup", + id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.RenameAction"), + position = 1500, + separatorAfter = 1550 ) })