From 9b90fa7c6de5fbed31911520116b1743a117a0de Mon Sep 17 00:00:00 2001 From: Sebastian Thomschke Date: Mon, 22 Jul 2024 19:15:06 +0200 Subject: [PATCH] refact: enable NonNullByDefault for lsp4e base package --- ...umentToLanguageServerSetupParticipant.java | 3 - .../ContentTypeToLSPLaunchConfigEntry.java | 8 +- ...ContentTypeToLanguageServerDefinition.java | 12 +- .../lsp4e/DocumentContentSynchronizer.java | 42 ++-- .../HasLanguageServerPropertyTester.java | 5 +- .../lsp4e/IMarkerAttributeComputer.java | 5 +- .../org/eclipse/lsp4e/LSPEclipseUtils.java | 136 +++++------ .../org/eclipse/lsp4e/LanguageClientImpl.java | 20 +- .../eclipse/lsp4e/LanguageServerPlugin.java | 13 +- .../eclipse/lsp4e/LanguageServerWrapper.java | 142 ++++++----- .../org/eclipse/lsp4e/LanguageServers.java | 111 ++++----- .../lsp4e/LanguageServersRegistry.java | 53 ++--- .../lsp4e/LanguageServiceAccessor.java | 222 +++++++++--------- .../LaunchConfigurationStreamProvider.java | 50 ++-- .../LoggingStreamConnectionProviderProxy.java | 30 +-- .../eclipse/lsp4e/ServerMessageHandler.java | 3 +- .../CallHierarchyContentProvider.java | 25 +- .../CodeActionMarkerResolution.java | 4 +- .../codeactions/CommandMarkerResolution.java | 4 +- .../LSPCodeActionMarkerResolution.java | 11 +- .../LSPCodeActionQuickAssistProcessor.java | 4 +- .../codeactions/LSPCodeActionsMenu.java | 6 +- .../operations/codelens/LSPCodeMining.java | 14 +- .../color/ColorInformationMining.java | 2 +- .../completion/LSCompletionProposal.java | 20 +- .../completion/LSContentAssistProcessor.java | 31 ++- .../diagnostics/LSPDiagnosticsToMarkers.java | 9 +- .../documentLink/DocumentLinkDetector.java | 3 +- ...ntLinkPresentationReconcilingStrategy.java | 1 + .../lsp4e/operations/format/LSPFormatter.java | 2 +- .../inlayhint/LSPLineContentCodeMining.java | 56 +++-- .../operations/references/LSSearchQuery.java | 4 +- .../lsp4e/operations/references/URIMatch.java | 4 +- .../operations/rename/LSPRenameProcessor.java | 10 +- .../LSPSelectionRangeAbstractHandler.java | 21 +- .../SemanticHighlightReconcilerStrategy.java | 18 +- .../WorkspaceSymbolQuickAccessElement.java | 3 +- .../TypeHierarchyContentProvider.java | 6 +- .../typeHierarchy/TypeHierarchyHandler.java | 10 +- .../typeHierarchy/TypeHierarchyView.java | 54 +++-- .../TypeHierarchyViewContentProvider.java | 8 +- .../outline/LSSymbolsContentProvider.java | 2 +- .../src/org/eclipse/lsp4e/package-info.java | 6 + 43 files changed, 611 insertions(+), 582 deletions(-) create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/package-info.java diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ConnectDocumentToLanguageServerSetupParticipant.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ConnectDocumentToLanguageServerSetupParticipant.java index d39432b3c..c3785e892 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ConnectDocumentToLanguageServerSetupParticipant.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ConnectDocumentToLanguageServerSetupParticipant.java @@ -49,9 +49,6 @@ public void setup(IDocument document) { @Override public void setup(final IDocument document, IPath location, LocationKind locationKind) { - if (document == null) { - return; - } // Force document connect CompletableFuture.runAsync( () -> PENDING_CONNECTIONS.add(LanguageServers.forDocument(document).collectAll(ls -> CompletableFuture.completedFuture(null))), diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLSPLaunchConfigEntry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLSPLaunchConfigEntry.java index edfb8301e..a7e896ffc 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLSPLaunchConfigEntry.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLSPLaunchConfigEntry.java @@ -21,7 +21,7 @@ import org.eclipse.core.runtime.content.IContentType; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServersRegistry.LaunchConfigurationLanguageServerDefinition; public class ContentTypeToLSPLaunchConfigEntry extends ContentTypeToLanguageServerDefinition { @@ -31,8 +31,8 @@ public class ContentTypeToLSPLaunchConfigEntry extends ContentTypeToLanguageServ private final ILaunchConfiguration launchConfiguration; private final Set launchModes; - public ContentTypeToLSPLaunchConfigEntry(@NonNull IContentType contentType, @NonNull ILaunchConfiguration launchConfig, - @NonNull Set launchModes) { + public ContentTypeToLSPLaunchConfigEntry(IContentType contentType, ILaunchConfiguration launchConfig, + Set launchModes) { super(contentType, new LaunchConfigurationLanguageServerDefinition(launchConfig, launchModes), null); this.launchConfiguration = launchConfig; this.launchModes = Collections.unmodifiableSet(launchModes); @@ -64,7 +64,7 @@ public Set getLaunchModes() { return launchModes; } - static ContentTypeToLSPLaunchConfigEntry readFromPreference(String preferenceEntry) { + static @Nullable ContentTypeToLSPLaunchConfigEntry readFromPreference(String preferenceEntry) { String[] parts = preferenceEntry.split(":"); //$NON-NLS-1$ if (parts.length != 2) { return null; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLanguageServerDefinition.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLanguageServerDefinition.java index 0393bde68..d7a253901 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLanguageServerDefinition.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ContentTypeToLanguageServerDefinition.java @@ -16,7 +16,6 @@ import java.util.AbstractMap.SimpleEntry; import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; import org.eclipse.lsp4e.enablement.EnablementTester; @@ -24,16 +23,15 @@ public class ContentTypeToLanguageServerDefinition extends SimpleEntry { private static final long serialVersionUID = 6002703726009331762L; - private final EnablementTester enablement; + private final @Nullable EnablementTester enablement; - public ContentTypeToLanguageServerDefinition(@NonNull IContentType contentType, - @NonNull LanguageServerDefinition provider, + public ContentTypeToLanguageServerDefinition(IContentType contentType, LanguageServerDefinition provider, @Nullable EnablementTester enablement) { super(contentType, provider); this.enablement = enablement; } - public boolean isEnabled(URI uri) { + public boolean isEnabled(@Nullable URI uri) { return isUserEnabled() && isExtensionEnabled(uri); } @@ -48,11 +46,11 @@ public boolean isUserEnabled() { return true; } - public boolean isExtensionEnabled(URI uri) { + public boolean isExtensionEnabled(@Nullable URI uri) { return enablement != null ? enablement.evaluate(uri) : true; } - public EnablementTester getEnablementCondition() { + public @Nullable EnablementTester getEnablementCondition() { return enablement; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java index 043625cd1..974645e42 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java @@ -35,7 +35,6 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; @@ -76,20 +75,19 @@ final class DocumentContentSynchronizer implements IDocumentListener { - private final @NonNull LanguageServerWrapper languageServerWrapper; - private final @NonNull IDocument document; - private final @NonNull URI fileUri; + private final LanguageServerWrapper languageServerWrapper; + private final IDocument document; + private final URI fileUri; private final TextDocumentSyncKind syncKind; private int version = 0; - private DidChangeTextDocumentParams changeParams; + private @Nullable DidChangeTextDocumentParams changeParams; private long openSaveStamp; private IPreferenceStore store; - private IFormatRegionsProvider formatRegionsProvider; + private @Nullable IFormatRegionsProvider formatRegionsProvider; - public DocumentContentSynchronizer(@NonNull LanguageServerWrapper languageServerWrapper, - @NonNull LanguageServer languageServer, - @NonNull IDocument document, TextDocumentSyncKind syncKind) { + public DocumentContentSynchronizer(LanguageServerWrapper languageServerWrapper, LanguageServer languageServer, + IDocument document, @Nullable TextDocumentSyncKind syncKind) { this.languageServerWrapper = languageServerWrapper; URI uri = LSPEclipseUtils.toUri(document); if (uri == null) { @@ -127,7 +125,7 @@ public DocumentContentSynchronizer(@NonNull LanguageServerWrapper languageServer languageId = path.lastSegment(); } } - if (languageId == null && this.fileUri.getSchemeSpecificPart() != null) { + if (languageId == null && !this.fileUri.getSchemeSpecificPart().isEmpty()) { String part = this.fileUri.getSchemeSpecificPart(); int lastSeparatorIndex = Math.max(part.lastIndexOf('.'), part.lastIndexOf('/')); languageId = part.substring(lastSeparatorIndex + 1); @@ -180,7 +178,7 @@ public void documentAboutToBeChanged(DocumentEvent event) { */ private boolean createChangeEvent(DocumentEvent event) { Assert.isTrue(changeParams == null); - changeParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(), + final var changeParams = this.changeParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(), Collections.singletonList(new TextDocumentContentChangeEvent())); changeParams.getTextDocument().setUri(fileUri.toASCIIString()); @@ -220,8 +218,7 @@ private boolean serverSupportsWillSaveWaitUntil() { if(serverCapabilities != null ) { Either textDocumentSync = serverCapabilities.getTextDocumentSync(); if(textDocumentSync.isRight()) { - TextDocumentSyncOptions saveOptions = textDocumentSync.getRight(); - return saveOptions != null && Boolean.TRUE.equals(saveOptions.getWillSaveWaitUntil()); + return Boolean.TRUE.equals(textDocumentSync.getRight().getWillSaveWaitUntil()); } } return false; @@ -238,7 +235,7 @@ private boolean serverSupportsWillSaveWaitUntil() { * * @return language server's preference ID to define a timeout for willSaveWaitUntil */ - private static @NonNull String lsToWillSaveWaitUntilTimeoutKey(String serverId) { + private static String lsToWillSaveWaitUntilTimeoutKey(String serverId) { return serverId + '.' + WILL_SAVE_WAIT_UNTIL_TIMEOUT__KEY; } @@ -263,7 +260,6 @@ public void documentAboutToBeSaved() { // Use @link{TextDocumentSaveReason.Manual} as the platform does not give enough information to be accurate final var params = new WillSaveTextDocumentParams(identifier, TextDocumentSaveReason.Manual); - try { List edits = languageServerWrapper.executeImpl(ls -> ls.getTextDocumentService().willSaveWaitUntil(params)) .get(lsToWillSaveWaitUntilTimeout(), TimeUnit.SECONDS); @@ -290,7 +286,7 @@ public void documentAboutToBeSaved() { private void formatDocument() { var regions = getFormatRegions(); - if (regions != null && document != null) { + if (regions != null) { try { var textSelection = new MultiTextSelection(document, regions); var edits = requestFormatting(document, textSelection).get(lsToWillSaveWaitUntilTimeout(), TimeUnit.SECONDS); @@ -299,11 +295,12 @@ private void formatDocument() { edits.apply(); } catch (final ConcurrentModificationException ex) { ServerMessageHandler.showMessage(Messages.LSPFormatHandler_DiscardedFormat, new MessageParams(MessageType.Error, Messages.LSPFormatHandler_DiscardedFormatResponse)); - } catch (BadLocationException e) { - LanguageServerPlugin.logError(e); } - }; - } catch (BadLocationException | InterruptedException | ExecutionException | TimeoutException e) { + } + } catch (InterruptedException e) { + LanguageServerPlugin.logError(e); + Thread.currentThread().interrupt(); + } catch (Exception e) { LanguageServerPlugin.logError(e); } } @@ -341,14 +338,15 @@ private void formatDocument() { return null; } - private CompletableFuture requestFormatting(@NonNull IDocument document, @NonNull ITextSelection textSelection) throws BadLocationException { + private CompletableFuture<@Nullable VersionedEdits> requestFormatting(IDocument document, ITextSelection textSelection) throws BadLocationException { long modificationStamp = DocumentUtil.getDocumentModificationStamp(document); FormattingOptions formatOptions = LSPFormatter.getFormatOptions(); TextDocumentIdentifier docId = new TextDocumentIdentifier(fileUri.toString()); final ServerCapabilities capabilities = languageServerWrapper.getServerCapabilities(); - if (LSPFormatter.isDocumentRangeFormattingSupported(capabilities) + if (capabilities != null + && LSPFormatter.isDocumentRangeFormattingSupported(capabilities) && !(LSPFormatter.isDocumentFormattingSupported(capabilities) && textSelection.getLength() == 0)) { var rangeParams = LSPFormatter.getRangeFormattingParams(document, textSelection, formatOptions, docId); return languageServerWrapper.executeImpl(ls -> ls.getTextDocumentService().rangeFormatting(rangeParams).thenApply(edits -> new VersionedEdits(modificationStamp, edits, document))); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/HasLanguageServerPropertyTester.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/HasLanguageServerPropertyTester.java index caa013ea4..090555a34 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/HasLanguageServerPropertyTester.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/HasLanguageServerPropertyTester.java @@ -14,6 +14,7 @@ import org.eclipse.core.expressions.PropertyTester; import org.eclipse.core.resources.IFile; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.lsp4e.ui.UI; @@ -23,7 +24,7 @@ public class HasLanguageServerPropertyTester extends PropertyTester { @Override - public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + public boolean test(@Nullable Object receiver, String property, Object[] args, @Nullable Object expectedValue) { if (receiver instanceof IFile file) { return LanguageServersRegistry.getInstance().canUseLanguageServer(file); } else if (receiver instanceof IEditorInput editorInput) { @@ -38,7 +39,7 @@ public boolean test(Object receiver, String property, Object[] args, Object expe return false; } - private boolean test(ITextViewer viewer) { + private boolean test(@Nullable ITextViewer viewer) { if (viewer != null) { IDocument document = viewer.getDocument(); if (document != null) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IMarkerAttributeComputer.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IMarkerAttributeComputer.java index a99b9bb72..d9e224612 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IMarkerAttributeComputer.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IMarkerAttributeComputer.java @@ -14,7 +14,6 @@ import java.util.Map; import org.eclipse.core.resources.IResource; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4j.Diagnostic; @@ -40,6 +39,6 @@ public interface IMarkerAttributeComputer { * the map with the attributes for the marker, where the * implementation can add attributes */ - public void addMarkerAttributesForDiagnostic(@NonNull Diagnostic diagnostic, @Nullable IDocument document, - @NonNull IResource resource, @NonNull Map attributes); + public void addMarkerAttributesForDiagnostic(Diagnostic diagnostic, @Nullable IDocument document, + IResource resource, Map attributes); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java index c9e6d0e49..92b1351c6 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java @@ -21,6 +21,8 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -94,7 +96,6 @@ import org.eclipse.lsp4e.refactoring.LSPTextChange; import org.eclipse.lsp4e.ui.Messages; import org.eclipse.lsp4e.ui.UI; -import org.eclipse.lsp4j.CallHierarchyPrepareParams; import org.eclipse.lsp4j.Color; import org.eclipse.lsp4j.CompletionContext; import org.eclipse.lsp4j.CompletionParams; @@ -188,7 +189,7 @@ private LSPEclipseUtils() { // this class shouldn't be instantiated } - public static @NonNull Position toPosition(int offset, IDocument document) throws BadLocationException { + public static Position toPosition(int offset, IDocument document) throws BadLocationException { final var res = new Position(); res.setLine(document.getLineOfOffset(offset)); res.setCharacter(offset - document.getLineInformationOfOffset(offset).getOffset()); @@ -255,7 +256,7 @@ public static CompletionParams toCompletionParams(URI fileUri, int offset, IDocu return param; } - public static ISelection toSelection(Range range, IDocument document) { + public static @Nullable ISelection toSelection(Range range, IDocument document) { try { int offset = toOffset(range.getStart(), document); int endOffset = toOffset(range.getEnd(), document); @@ -270,7 +271,6 @@ public static ISelection toSelection(Range range, IDocument document) { * @param fileUri * @param offset * @param document - * @return * @throws BadLocationException * @deprecated Use {@link #toTextDocumentPosistionParams(int, IDocument)} * instead @@ -337,7 +337,7 @@ public static LinkedEditingRangeParams toLinkedEditingRangeParams(TextDocumentPo * only be used for T where T adds no new fields. */ private static T toTextDocumentPositionParamsCommon( - @NonNull T specificParams, TextDocumentPositionParams genericParams) { + T specificParams, TextDocumentPositionParams genericParams) { if (genericParams.getPosition() != null) { specificParams.setPosition(genericParams.getPosition()); } @@ -347,36 +347,31 @@ private static T toTextDocumentPositionPa return specificParams; } - @NonNull - public static TextDocumentIdentifier toTextDocumentIdentifier(@NonNull final IDocument document) { - return toTextDocumentIdentifier(toUri(document)); + public static @Nullable TextDocumentIdentifier toTextDocumentIdentifier(final IDocument document) { + final var uri = toUri(document); + return uri == null ? null : toTextDocumentIdentifier(uri); } - @NonNull - public static TextDocumentIdentifier toTextDocumentIdentifier(@NonNull final IResource res) { - return toTextDocumentIdentifier(toUri(res)); + public static @Nullable TextDocumentIdentifier toTextDocumentIdentifier(final IResource res) { + final var uri = toUri(res); + return uri == null ? null : toTextDocumentIdentifier(uri); } - @NonNull public static TextDocumentIdentifier toTextDocumentIdentifier(final URI uri) { return new TextDocumentIdentifier(uri.toASCIIString()); } - public static CallHierarchyPrepareParams toCallHierarchyPrepareParams(int offset, final @NonNull IDocument document) throws BadLocationException { - Position position = LSPEclipseUtils.toPosition(offset, document); - TextDocumentIdentifier documentIdentifier = toTextDocumentIdentifier(document); - return new CallHierarchyPrepareParams(documentIdentifier, position); - - } - - public static ITextFileBuffer toBuffer(IDocument document) { + public static @Nullable ITextFileBuffer toBuffer(@Nullable IDocument document) { + if(document == null) { + return null; + } ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); if (bufferManager == null) return null; return bufferManager.getTextFileBuffer(document); } - public static URI toUri(@Nullable IDocument document) { + public static @Nullable URI toUri(@Nullable IDocument document) { if(document == null) { return null; } @@ -393,18 +388,18 @@ public static URI toUri(@Nullable IDocument document) { return null; } - private static IPath toPath(IFileBuffer buffer) { + private static @Nullable IPath toPath(@Nullable IFileBuffer buffer) { if (buffer != null) { return buffer.getLocation(); } return null; } - public static IPath toPath(IDocument document) { + public static @Nullable IPath toPath(@Nullable IDocument document) { return toPath(toBuffer(document)); } - public static int toEclipseMarkerSeverity(DiagnosticSeverity lspSeverity) { + public static int toEclipseMarkerSeverity(@Nullable DiagnosticSeverity lspSeverity) { if (lspSeverity == null) { // if severity is empty it is up to the client to interpret diagnostics return IMarker.SEVERITY_ERROR; @@ -484,7 +479,7 @@ public static IResource findResourceFor(@Nullable URI uri) { } } - public static IFile findMostNested(IFile[] files) { + public static @Nullable IFile findMostNested(IFile[] files) { int shortestLen = Integer.MAX_VALUE; IFile shortest = null; for (IFile file : files) { @@ -521,7 +516,7 @@ public static void applyEdit(TextEdit textEdit, IDocument document) throws BadLo * list of LSP TextEdits * @throws BadLocationException */ - public static void applyEdits(IDocument document, List edits) throws BadLocationException { + public static void applyEdits(@Nullable IDocument document, @Nullable List edits) throws BadLocationException { if (document == null || edits == null || edits.isEmpty()) { return; } @@ -604,8 +599,7 @@ public static IDocument getDocument(@Nullable IResource resource) { return document; } - @Nullable - public static IDocument getExistingDocument(@Nullable IResource resource) { + public static @Nullable IDocument getExistingDocument(@Nullable IResource resource) { if (resource == null) { return null; } @@ -621,8 +615,7 @@ public static IDocument getExistingDocument(@Nullable IResource resource) { } } - @Nullable - public static IDocument getDocument(URI uri) { + public static @Nullable IDocument getDocument(@Nullable URI uri) { if (uri == null) { return null; } @@ -661,7 +654,7 @@ public static IDocument getDocument(URI uri) { } public static void openInEditor(Location location) { - openInEditor(location, UI.getActivePage()); + openInEditor(location, castNonNull(UI.getActivePage())); } public static void openInEditor(Location location, IWorkbenchPage page) { @@ -669,22 +662,26 @@ public static void openInEditor(Location location, IWorkbenchPage page) { } public static void openInEditor(LocationLink link) { - openInEditor(link, UI.getActivePage()); + openInEditor(link, castNonNull(UI.getActivePage())); } public static void openInEditor(LocationLink link, IWorkbenchPage page) { open(link.getTargetUri(), page, link.getTargetSelectionRange()); } - public static void open(String uri, Range optionalRange) { - open(uri, UI.getActivePage(), optionalRange); + public static void open(String uri, @Nullable Range optionalRange) { + open(uri, castNonNull(UI.getActivePage()), optionalRange); } - public static void open(String uri, IWorkbenchPage page, Range optionalRange) { + public static void open(String uri, IWorkbenchPage page, @Nullable Range optionalRange) { open(uri, page, optionalRange, false); } - public static void open(String uri, IWorkbenchPage page, Range optionalRange, boolean createFile) { + public static void open(String uri, @Nullable Range optionalRange, boolean createFile) { + open(uri, castNonNull(UI.getActivePage()), optionalRange, createFile); + } + + public static void open(String uri, IWorkbenchPage page, @Nullable Range optionalRange, boolean createFile) { if (uri.startsWith(HTTP)) { if (uri.startsWith(INTRO_URL)) { openIntroURL(uri); @@ -714,7 +711,7 @@ public static void open(String uri, IWorkbenchPage page, Range optionalRange, bo * the uri to parse * @return a range object containing the information */ - public static Range parseRange(String location) { + public static @Nullable Range parseRange(String location) { try { if (!location.startsWith(LSPEclipseUtils.FILE_URI)) return null; @@ -777,7 +774,7 @@ protected static void openHttpLocationInBrowser(final String uri, IWorkbenchPage }); } - protected static void openFileLocationInEditor(String uri, IWorkbenchPage page, Range optionalRange, + protected static void openFileLocationInEditor(String uri, IWorkbenchPage page, @Nullable Range optionalRange, boolean createFile) { IEditorPart part = openEditor(uri, page, createFile); @@ -802,7 +799,7 @@ protected static void openFileLocationInEditor(String uri, IWorkbenchPage page, } } - private static IEditorPart openEditor(String uri, IWorkbenchPage page, boolean createFile) { + private static @Nullable IEditorPart openEditor(String uri, @Nullable IWorkbenchPage page, boolean createFile) { if (page == null) { return null; } @@ -862,7 +859,7 @@ private static IEditorPart openEditor(String uri, IWorkbenchPage page, boolean c return null; } - public static IDocument getDocument(ITextEditor editor) { + public static @Nullable IDocument getDocument(@Nullable ITextEditor editor) { if (editor == null) return null; final IEditorInput editorInput = editor.getEditorInput(); @@ -891,7 +888,7 @@ public static IDocument getDocument(ITextEditor editor) { return null; } - public static IDocument getDocument(IEditorInput editorInput) { + public static @Nullable IDocument getDocument(IEditorInput editorInput) { if (!editorInput.exists()) { // Shouldn't happen too often, but happens rather a lot in testing when // teardown runs when there are document setup actions still pending @@ -917,7 +914,7 @@ public static IDocument getDocument(IEditorInput editorInput) { * * @param wsEdit */ - public static void applyWorkspaceEdit(WorkspaceEdit wsEdit) { + public static void applyWorkspaceEdit(@Nullable WorkspaceEdit wsEdit) { applyWorkspaceEdit(wsEdit, null); } @@ -928,7 +925,7 @@ public static void applyWorkspaceEdit(WorkspaceEdit wsEdit) { * @param wsEdit * @param label */ - public static void applyWorkspaceEdit(WorkspaceEdit wsEdit, String label) { + public static void applyWorkspaceEdit(@Nullable WorkspaceEdit wsEdit, @Nullable String label) { if (wsEdit == null) { return; } @@ -998,13 +995,10 @@ public Change createChange(IProgressMonitor pm) throws CoreException, OperationC RefactoringWizard.DIALOG_BASED_USER_INTERFACE | RefactoringWizard.NO_BACK_BUTTON_ON_STATUS_DIALOG ) { - - @Override protected void addUserInputPages() { //no inputs required } - }; UI.runOnUIThread(() -> { try { @@ -1048,8 +1042,8 @@ private static boolean applyWorkspaceEditIfSingleOpenFile(WorkspaceEdit wsEdit) return false; } URI singleDocumentUri = documentUris.iterator().next(); - Set editors = LSPEclipseUtils.findOpenEditorsFor(singleDocumentUri); - if (editors == null || editors.isEmpty()) { + Set editors = findOpenEditorsFor(singleDocumentUri); + if (editors.isEmpty()) { return false; } Optional doc = editors.stream().map(editor -> { @@ -1093,7 +1087,7 @@ public static CompositeChange toCompositeChange(WorkspaceEdit wsEdit, String nam * @param collector A map of URI to Range entries collected from WorkspaceEdit * @return a ltk {@link CompositeChange} from a lsp {@link WorkspaceEdit}. */ - private static CompositeChange toCompositeChange(WorkspaceEdit wsEdit, String name, Map collector) { + private static CompositeChange toCompositeChange(WorkspaceEdit wsEdit, String name, @Nullable Map collector) { final var change = new CompositeChange(name); List> documentChanges = wsEdit.getDocumentChanges(); if (documentChanges != null) { @@ -1211,7 +1205,7 @@ private static CompositeChange toCompositeChange(WorkspaceEdit wsEdit, String na * * @param textEdits A list of textEdits sorted in reversed order */ - private static void collectChangedURI(URI uri, List textEdits, Map collector) { + private static void collectChangedURI(URI uri, @Nullable List textEdits, @Nullable Map collector) { if (collector == null) { return; } @@ -1227,7 +1221,7 @@ private static void collectChangedURI(URI uri, List textEdits, Map 1 ? ResourcesPlugin.getWorkspace().getRoot().getFile(buffer.getLocation()) : null; if (res != null) { @@ -1305,17 +1299,17 @@ public static URI toUri(File file) { } } - @Nullable public static IFile getFile(@NonNull IDocument document) { + public static @Nullable IFile getFile(@Nullable IDocument document) { IPath path = toPath(document); return getFile(path); } - @Nullable public static IFile getFile(IPath path) { + public static @Nullable IFile getFile(@Nullable IPath path) { if(path == null) { return null; } IFile res = ResourcesPlugin.getWorkspace().getRoot().getFile(path); - if (res != null && res.exists()) { + if (res.exists()) { return res; } else { return null; @@ -1325,16 +1319,14 @@ public static URI toUri(File file) { /** * @return a list of folder objects for all open projects of the current workspace */ - @NonNull - public static List<@NonNull WorkspaceFolder> getWorkspaceFolders() { + public static List getWorkspaceFolders() { return Arrays.stream(ResourcesPlugin.getWorkspace().getRoot().getProjects()) .filter(IProject::isAccessible) // .map(LSPEclipseUtils::toWorkspaceFolder) // .toList(); } - @NonNull - public static WorkspaceFolder toWorkspaceFolder(@NonNull IProject project) { + public static WorkspaceFolder toWorkspaceFolder(IProject project) { final var folder = new WorkspaceFolder(); URI folderUri = toUri(project); folder.setUri(folderUri != null ? folderUri.toASCIIString() : ""); //$NON-NLS-1$ @@ -1342,8 +1334,7 @@ public static WorkspaceFolder toWorkspaceFolder(@NonNull IProject project) { return folder; } - @NonNull - public static List getFileContentTypes(@NonNull IFile file) { + public static List getFileContentTypes(IFile file) { IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); final var contentTypes = new ArrayList(); if (file.exists()) { @@ -1376,8 +1367,7 @@ private static String getFileName(@Nullable ITextFileBuffer buffer) { return null; } - @NonNull - public static List getDocumentContentTypes(@NonNull IDocument document) { + public static List getDocumentContentTypes(IDocument document) { final var contentTypes = new ArrayList(); ITextFileBuffer buffer = toBuffer(document); @@ -1419,7 +1409,7 @@ public static List getDocumentContentTypes(@NonNull IDocument docu * @deprecated */ @Deprecated - public static String getDocString(Either documentation) { + public static @Nullable String getDocString(@Nullable Either documentation) { if (documentation != null) { if (documentation.isLeft()) { return documentation.getLeft(); @@ -1430,7 +1420,7 @@ public static String getDocString(Either documentation) { return null; } - public static String getHtmlDocString(Either documentation) { + public static @Nullable String getHtmlDocString(Either<@Nullable String, MarkupContent> documentation) { return documentation.map(text -> { if (text != null && !text.isEmpty()) { return htmlParagraph(text); @@ -1457,7 +1447,7 @@ public static String getHtmlDocString(Either documentatio }); } - public static ITextViewer getTextViewer(@Nullable final IEditorPart editorPart) { + public static @Nullable ITextViewer getTextViewer(@Nullable final IEditorPart editorPart) { final @Nullable ITextViewer textViewer = Adapters.adapt(editorPart, ITextViewer.class); if (textViewer != null) { return textViewer; @@ -1504,7 +1494,7 @@ public static RGBA toRGBA(Color color) { (int) color.getAlpha()); } - public static Set findOpenEditorsFor(URI uri) { + public static Set findOpenEditorsFor(@Nullable URI uri) { if (uri == null) { return Collections.emptySet(); } @@ -1524,7 +1514,7 @@ public static Set findOpenEditorsFor(URI uri) { .collect(Collectors.toSet()); } - public static URI toUri(IEditorInput editorInput) { + public static @Nullable URI toUri(IEditorInput editorInput) { if (editorInput instanceof FileEditorInput fileEditorInput) { return toUri(fileEditorInput.getFile()); } @@ -1560,24 +1550,24 @@ public static File fromUri(URI uri) { return Paths.get(uri).toFile(); } - public static boolean hasCapability(final @Nullable Either eitherCapability) { + public static boolean hasCapability(final @Nullable Either eitherCapability) { if(eitherCapability == null) { return false; } return eitherCapability.isRight() || eitherCapability.getLeft(); } - public static boolean isReadOnly(final @NonNull URI uri) { + public static boolean isReadOnly(final URI uri) { IResource resource = findResourceFor(uri); return resource != null && isReadOnly(resource); } - public static boolean isReadOnly(final @NonNull IDocument document) { + public static boolean isReadOnly(final IDocument document) { IFile file = getFile(document); return file != null && isReadOnly(file); } - public static boolean isReadOnly(final @NonNull IResource resource) { + public static boolean isReadOnly(final IResource resource) { ResourceAttributes attributes = resource.getResourceAttributes(); return attributes != null && attributes.isReadOnly(); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageClientImpl.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageClientImpl.java index d46b97ef9..ff86948a9 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageClientImpl.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageClientImpl.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -23,7 +25,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.lsp4e.progress.LSPProgressManager; @@ -51,11 +53,11 @@ public class LanguageClientImpl implements LanguageClient { - private Consumer diagnosticConsumer; + private Consumer diagnosticConsumer = lateNonNull(); private final LSPProgressManager progressManager = new LSPProgressManager(); - private LanguageServer server; - private LanguageServerWrapper wrapper; + private LanguageServer server = lateNonNull(); + private LanguageServerWrapper wrapper = lateNonNull(); public final void connect(LanguageServer server, LanguageServerWrapper wrapper) { this.server = server; @@ -63,7 +65,7 @@ public final void connect(LanguageServer server, LanguageServerWrapper wrapper) progressManager.connect(server, wrapper.serverDefinition); } - protected void setDiagnosticsConsumer(@NonNull Consumer diagnosticConsumer) { + protected void setDiagnosticsConsumer(Consumer diagnosticConsumer) { this.diagnosticConsumer = diagnosticConsumer; } @@ -72,9 +74,9 @@ protected final LanguageServer getLanguageServer() { } @Override - public CompletableFuture> configuration(ConfigurationParams configurationParams) { + public CompletableFuture> configuration(ConfigurationParams configurationParams) { // override as needed - List list = new ArrayList<>(configurationParams.getItems().size()); + List<@Nullable Object> list = new ArrayList<>(configurationParams.getItems().size()); for (int i = 0; i < configurationParams.getItems().size(); i++) { list.add(null); } @@ -82,7 +84,7 @@ public CompletableFuture> configuration(ConfigurationParams configu } @Override - public void telemetryEvent(Object object) { + public void telemetryEvent(@Nullable Object object) { // TODO } @@ -106,13 +108,11 @@ public final void logMessage(MessageParams message) { CompletableFuture.runAsync(() -> ServerMessageHandler.logMessage(wrapper, message)); } - @SuppressWarnings("null") @Override public CompletableFuture createProgress(final WorkDoneProgressCreateParams params) { return progressManager.createProgress(params); } - @SuppressWarnings("null") @Override public void notifyProgress(final ProgressParams params) { progressManager.notifyProgress(params); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java index b5e2bd6cd..f3e616add 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerPlugin.java @@ -11,9 +11,11 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.lsp4e.ui.LSPImages; import org.eclipse.ui.plugin.AbstractUIPlugin; @@ -31,7 +33,7 @@ public class LanguageServerPlugin extends AbstractUIPlugin { public static final boolean DEBUG = Boolean.parseBoolean(Platform.getDebugOption("org.eclipse.lsp4e/debug")); //$NON-NLS-1$ // The shared instance - private static LanguageServerPlugin plugin; + private static volatile @Nullable LanguageServerPlugin plugin; public LanguageServerPlugin() { } @@ -55,6 +57,7 @@ public void stop(BundleContext context) throws Exception { * @return the shared instance */ public static LanguageServerPlugin getDefault() { + Assert.isNotNull(plugin); return plugin; } @@ -70,7 +73,6 @@ protected void initializeImageRegistry(ImageRegistry registry) { * The exception through which we noticed the error */ public static void logError(final Throwable thr) { - LanguageServerPlugin plugin = getDefault(); if (plugin != null) { plugin.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, 0, thr.getMessage(), thr)); } @@ -84,8 +86,7 @@ public static void logError(final Throwable thr) { * @param thr * The exception through which we noticed the error */ - public static void logError(final String message, final Throwable thr) { - LanguageServerPlugin plugin = getDefault(); + public static void logError(final @Nullable String message, final @Nullable Throwable thr) { if (plugin != null) { plugin.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, 0, message, thr)); } @@ -97,7 +98,6 @@ public static void logError(final String message, final Throwable thr) { * @param message */ public static void logInfo(final String message) { - LanguageServerPlugin plugin = getDefault(); if (plugin != null) { plugin.getLog().log(new Status(IStatus.INFO, PLUGIN_ID, 0, message, null)); } @@ -111,8 +111,7 @@ public static void logInfo(final String message) { * @param thr * The exception through which we noticed the warning */ - public static void logWarning(final String message, final Throwable thr) { - LanguageServerPlugin plugin = getDefault(); + public static void logWarning(final @Nullable String message, final @Nullable Throwable thr) { if (plugin != null) { plugin.getLog().log(new Status(IStatus.WARNING, PLUGIN_ID, 0, message, thr)); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java index 17babc9cb..55874494f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java @@ -68,7 +68,6 @@ import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; @@ -122,7 +121,10 @@ public class LanguageServerWrapper { private final IFileBufferListener fileBufferListener = new FileBufferListenerAdapter() { @Override public void bufferDisposed(IFileBuffer buffer) { - disconnect(LSPEclipseUtils.toUri(buffer)); + final var uri = LSPEclipseUtils.toUri(buffer); + if (uri != null) { + disconnect(uri); + } } @Override @@ -148,50 +150,45 @@ public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) { }; - @NonNull public final LanguageServerDefinition serverDefinition; - @Nullable - public final IProject initialProject; - @NonNull - protected Map<@NonNull URI, @NonNull DocumentContentSynchronizer> connectedDocuments; - @Nullable - protected final IPath initialPath; + public final @Nullable IProject initialProject; + protected Map connectedDocuments; + protected final @Nullable IPath initialPath; protected final InitializeParams initParams = new InitializeParams(); - protected StreamConnectionProvider lspStreamProvider; - private Future launcherFuture; - private CompletableFuture initializeFuture; - private final AtomicReference initializeFutureMonitorRef = new AtomicReference<>(); + protected @Nullable StreamConnectionProvider lspStreamProvider; + private @Nullable Future launcherFuture; + private @Nullable CompletableFuture initializeFuture; + private final AtomicReference<@Nullable IProgressMonitor> initializeFutureMonitorRef = new AtomicReference<>(); private final int initializeFutureNumberOfStages = 7; - private LanguageServer languageServer; - private LanguageClientImpl languageClient; - private ServerCapabilities serverCapabilities; + private @Nullable LanguageServer languageServer; + private @Nullable LanguageClientImpl languageClient; + private @Nullable ServerCapabilities serverCapabilities; private final Timer timer = new Timer("Stop Language Server Task Processor"); //$NON-NLS-1$ - private TimerTask stopTimerTask; + private @Nullable TimerTask stopTimerTask; private AtomicBoolean stopping = new AtomicBoolean(false); private final ExecutorService dispatcher; - private final ExecutorService listener; /** * Map containing unregistration handlers for dynamic capability registrations. */ - private final @NonNull Map<@NonNull String, @NonNull Runnable> dynamicRegistrations = new HashMap<>(); + private final Map dynamicRegistrations = new HashMap<>(); private boolean initiallySupportsWorkspaceFolders = false; - private final @NonNull IResourceChangeListener workspaceFolderUpdater = new WorkspaceFolderListener(); + private final IResourceChangeListener workspaceFolderUpdater = new WorkspaceFolderListener(); /* Backwards compatible constructor */ - public LanguageServerWrapper(@NonNull IProject project, @NonNull LanguageServerDefinition serverDefinition) { + public LanguageServerWrapper(IProject project, LanguageServerDefinition serverDefinition) { this(project, serverDefinition, null); } - public LanguageServerWrapper(@NonNull LanguageServerDefinition serverDefinition, @Nullable IPath initialPath) { + public LanguageServerWrapper(LanguageServerDefinition serverDefinition, @Nullable IPath initialPath) { this(null, serverDefinition, initialPath); } /** Unified private constructor to set sensible defaults in all cases */ - private LanguageServerWrapper(@Nullable IProject project, @NonNull LanguageServerDefinition serverDefinition, + private LanguageServerWrapper(@Nullable IProject project, LanguageServerDefinition serverDefinition, @Nullable IPath initialPath) { this.initialProject = project; this.initialPath = initialPath; @@ -287,13 +284,14 @@ private synchronized void start(boolean forceRestart) { this.launcherFuture = new CompletableFuture<>(); this.initializeFuture = CompletableFuture.supplyAsync(() -> { advanceInitializeFutureMonitor(); + final StreamConnectionProvider lspStreamProvider; if (LoggingStreamConnectionProviderProxy.shouldLog(serverDefinition.id)) { - this.lspStreamProvider = new LoggingStreamConnectionProviderProxy( + lspStreamProvider = this.lspStreamProvider = new LoggingStreamConnectionProviderProxy( serverDefinition.createConnectionProvider(), serverDefinition.id); } else { - this.lspStreamProvider = serverDefinition.createConnectionProvider(); + lspStreamProvider = this.lspStreamProvider = serverDefinition.createConnectionProvider(); } - initParams.setInitializationOptions(this.lspStreamProvider.getInitializationOptions(rootURI)); + initParams.setInitializationOptions(lspStreamProvider.getInitializationOptions(rootURI)); try { lspStreamProvider.start(); } catch (IOException e) { @@ -302,7 +300,7 @@ private synchronized void start(boolean forceRestart) { return null; }).thenRun(() -> { advanceInitializeFutureMonitor(); - languageClient = serverDefinition.createLanguageClient(); + final var languageClient = this.languageClient = serverDefinition.createLanguageClient(); initParams.setProcessId((int) ProcessHandle.current().pid()); @@ -314,21 +312,21 @@ private synchronized void start(boolean forceRestart) { UnaryOperator wrapper = consumer -> (message -> { logMessage(message); consumer.consume(message); - final StreamConnectionProvider currentConnectionProvider = this.lspStreamProvider; - if (currentConnectionProvider != null && isActive()) { - currentConnectionProvider.handleMessage(message, this.languageServer, rootURI); + final var lspStreamProvider = this.lspStreamProvider; + if (lspStreamProvider != null && isActive()) { + lspStreamProvider.handleMessage(message, castNonNull(languageServer), rootURI); } }); initParams.setWorkspaceFolders(getRelevantWorkspaceFolders()); Launcher launcher = serverDefinition.createLauncherBuilder() // .setLocalService(languageClient)// .setRemoteInterface(serverDefinition.getServerInterface())// - .setInput(lspStreamProvider.getInputStream())// - .setOutput(lspStreamProvider.getOutputStream())// + .setInput(castNonNull(lspStreamProvider).getInputStream())// + .setOutput(castNonNull(lspStreamProvider).getOutputStream())// .setExecutorService(listener)// .wrapMessages(wrapper)// .create(); - this.languageServer = launcher.getRemoteProxy(); + final var languageServer = this.languageServer = launcher.getRemoteProxy(); languageClient.connect(languageServer, this); this.launcherFuture = launcher.startListening(); }) @@ -342,18 +340,14 @@ private synchronized void start(boolean forceRestart) { this.initiallySupportsWorkspaceFolders = supportsWorkspaceFolders(serverCapabilities); }).thenRun(() -> { advanceInitializeFutureMonitor(); - this.languageServer.initialized(new InitializedParams()); + castNonNull(languageServer).initialized(new InitializedParams()); }).thenRun(() -> { advanceInitializeFutureMonitor(); final Map toReconnect = filesToReconnect; - initializeFuture.thenRunAsync(() -> { + castNonNull(initializeFuture).thenRunAsync(() -> { watchProjects(); for (Entry fileToReconnect : toReconnect.entrySet()) { - try { - connect(fileToReconnect.getKey(), fileToReconnect.getValue()); - } catch (IOException e) { - throw new RuntimeException(e); - } + connect(fileToReconnect.getKey(), fileToReconnect.getValue()); } }); FileBuffers.getTextFileBufferManager().addFileBufferListener(fileBufferListener); @@ -411,13 +405,13 @@ protected IStatus run(IProgressMonitor monitor) { } @Override - public boolean belongsTo(Object family) { + public boolean belongsTo(@Nullable Object family) { return LanguageServerPlugin.FAMILY_INITIALIZE_LANGUAGE_SERVER == family; } }; } - private CompletableFuture initServer(final URI rootURI) { + private CompletableFuture initServer(final @Nullable URI rootURI) { final IProduct product = Platform.getProduct(); final String name = product != null ? product.getName() : "Eclipse IDE"; //$NON-NLS-1$ @@ -429,12 +423,12 @@ private CompletableFuture initServer(final URI rootURI) { workspaceClientCapabilities, textDocumentClientCapabilities, windowClientCapabilities, - lspStreamProvider.getExperimentalFeaturesPOJO())); + castNonNull(lspStreamProvider).getExperimentalFeaturesPOJO())); initParams.setClientInfo(getClientInfo(name)); - initParams.setTrace(this.lspStreamProvider.getTrace(rootURI)); + initParams.setTrace(castNonNull(lspStreamProvider).getTrace(rootURI)); // no then...Async future here as we want this chain of operation to be sequential and "atomic"-ish - return languageServer.initialize(initParams); + return castNonNull(languageServer).initialize(initParams); } @Nullable @@ -459,14 +453,14 @@ private URI getRootURI() { if (path != null) { File projectDirectory = path.toFile(); if (projectDirectory.isFile()) { - projectDirectory = projectDirectory.getParentFile(); + projectDirectory = castNonNull(projectDirectory.getParentFile()); } return LSPEclipseUtils.toUri(projectDirectory); } return null; } - private static boolean supportsWorkspaceFolders(ServerCapabilities serverCapabilities) { + private static boolean supportsWorkspaceFolders(@Nullable ServerCapabilities serverCapabilities) { return serverCapabilities != null && serverCapabilities.getWorkspace() != null && serverCapabilities.getWorkspace().getWorkspaceFolders() != null @@ -487,7 +481,8 @@ private void logMessage(Message message) { * @return whether the underlying connection to language server is still active */ public synchronized boolean isActive() { - return this.launcherFuture != null && !this.launcherFuture.isDone() && !this.launcherFuture.isCancelled(); + final var launcherFuture = this.launcherFuture; + return launcherFuture != null && !launcherFuture.isDone() && !launcherFuture.isCancelled(); } private void removeStopTimerTask() { @@ -587,7 +582,7 @@ public synchronized void stop() { FileBuffers.getTextFileBufferManager().removeFileBufferListener(fileBufferListener); } - public @Nullable CompletableFuture<@NonNull LanguageServerWrapper> connect(IDocument document, @NonNull IFile file) + public @Nullable CompletableFuture connect(IDocument document, IFile file) throws IOException { final URI uri = LSPEclipseUtils.toUri(file); if (uri != null) { @@ -614,7 +609,7 @@ private void watchProjects() { final LanguageServer currentLS = this.languageServer; new WorkspaceJob("Setting watch projects on server " + serverDefinition.label) { //$NON-NLS-1$ @Override - public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + public IStatus runInWorkspace(@Nullable IProgressMonitor monitor) throws CoreException { WorkspaceFoldersChangeEvent wsFolderEvent = new WorkspaceFoldersChangeEvent(); wsFolderEvent.getAdded().addAll(getRelevantWorkspaceFolders()); if (currentLS != null && currentLS == LanguageServerWrapper.this.languageServer) { @@ -640,7 +635,7 @@ public boolean canOperate(@Nullable IProject project) { || supportsWorkspaceFolderCapability(); } - public boolean canOperate(@NonNull IDocument document) { + public boolean canOperate(IDocument document) { URI documentUri = LSPEclipseUtils.toUri(document); if (documentUri == null) { return false; @@ -684,7 +679,7 @@ private boolean supportsWorkspaceFolderCapability() { * @return null if not connection has happened, a future that completes when file is initialized otherwise * @noreference internal so far */ - private @Nullable CompletableFuture<@NonNull LanguageServerWrapper> connect(@NonNull URI uri, IDocument document) throws IOException { + private @Nullable CompletableFuture connect(URI uri, @Nullable IDocument document) { removeStopTimerTask(); if (this.connectedDocuments.containsKey(uri)) { return CompletableFuture.completedFuture(this); @@ -701,14 +696,14 @@ private boolean supportsWorkspaceFolderCapability() { return null; } final IDocument theDocument = document; - return initializeFuture.thenAcceptAsync(theVoid -> { + return castNonNull(initializeFuture).thenAcceptAsync(theVoid -> { synchronized (connectedDocuments) { if (this.connectedDocuments.containsKey(uri)) { return; } TextDocumentSyncKind syncKind = initializeFuture == null ? null - : serverCapabilities.getTextDocumentSync().map(Functions.identity(), TextDocumentSyncOptions::getChange); - final var listener = new DocumentContentSynchronizer(this, languageServer, theDocument, syncKind); + : castNonNull(serverCapabilities).getTextDocumentSync().map(Functions.identity(), TextDocumentSyncOptions::getChange); + final var listener = new DocumentContentSynchronizer(this, castNonNull(languageServer), theDocument, syncKind); theDocument.addPrenotifiedDocumentListener(listener); LanguageServerWrapper.this.connectedDocuments.put(uri, listener); } @@ -719,7 +714,7 @@ private boolean supportsWorkspaceFolderCapability() { * @param uri * @return null if not disconnection has happened, a future tracking the disconnection state otherwise */ - public CompletableFuture disconnect(URI uri) { + public @Nullable CompletableFuture disconnect(URI uri) { DocumentContentSynchronizer documentListener = this.connectedDocuments.remove(uri); CompletableFuture documentClosedFuture = null; if (documentListener != null) { @@ -736,7 +731,7 @@ public CompletableFuture disconnect(URI uri) { return documentClosedFuture; } - public void disconnectContentType(@NonNull IContentType contentType) { + public void disconnectContentType(IContentType contentType) { final var urisToDisconnect = new ArrayList(); for (URI uri : connectedDocuments.keySet()) { IFile[] foundFiles = ResourcesPlugin.getWorkspace().getRoot() @@ -781,15 +776,13 @@ protected LanguageServer getServer() { * notifications are sent). *

If done in the UI thread, a job will be created * displaying that the server is being initialized

- * */ - @NonNull protected CompletableFuture getInitializedServer() { start(); final CompletableFuture currentInitializeFuture = initializeFuture; if (currentInitializeFuture != null && !currentInitializeFuture.isDone()) { - return currentInitializeFuture.thenApply(r -> this.languageServer); + return currentInitializeFuture.thenApply(r -> castNonNull(this.languageServer)); } return CompletableFuture.completedFuture(this.languageServer); } @@ -799,7 +792,7 @@ protected CompletableFuture getInitializedServer() { * * @param fn LS notification to send */ - public void sendNotification(@NonNull Consumer fn) { + public void sendNotification(Consumer fn) { // Enqueues a notification on the dispatch thread associated with the wrapped language server. This // ensures the interleaving of document updates and other requests in the UI is mirrored in the // order in which they get dispatched to the server @@ -824,7 +817,7 @@ public void sendNotification(@NonNull Consumer fn) { * * @return Async result */ - public @NonNull CompletableFuture execute(@NonNull Function> fn) { + public <@Nullable T> CompletableFuture execute(Function> fn) { // Send the request on the dispatch thread CompletableFuture lsRequest = executeImpl(fn); // then additionally make sure the response is delivered on a thread from the default ForkJoinPool. @@ -838,7 +831,7 @@ public void sendNotification(@NonNull Consumer fn) { if (t instanceof CancellationException) { lsRequest.cancel(true); } - return (T)null; + return null; }); return future; } @@ -860,16 +853,15 @@ public void sendNotification(@NonNull Consumer fn) { * * @return Async result */ - @NonNull - CompletableFuture executeImpl(@NonNull Function> fn) { + <@Nullable T> CompletableFuture executeImpl(Function> fn) { // Run the supplied function, ensuring that it is enqueued on the dispatch thread associated with the - // wrapped language server, and is thus guarannteed to be seen in the correct order with respect + // wrapped language server, and is thus guaranteed to be seen in the correct order with respect // to e.g. previous document changes // // Note this doesn't get the .thenApplyAsync(Function.identity()) chained on additionally, unlike // the public-facing version of this method, because we trust the LSPExecutor implementations to // make sure the server response thread doesn't get blocked by any further work - AtomicReference> request = new AtomicReference<>(); + AtomicReference<@Nullable CompletableFuture> request = new AtomicReference<>(); Function> cancelWrapper = ls -> { CompletableFuture res = fn.apply(ls); request.set(res); @@ -894,7 +886,7 @@ CompletableFuture executeImpl(@NonNull Function { switch (reg.getMethod()) { case "workspace/didChangeWorkspaceFolders": //$NON-NLS-1$ - Assert.isNotNull(serverCapabilities, - "Dynamic capability registration failed! Server not yet initialized?"); //$NON-NLS-1$ if (initiallySupportsWorkspaceFolders) { // Can treat this as a NOP since nothing can disable it dynamically if it was // enabled on initialization. @@ -1006,7 +999,7 @@ void registerCapability(RegistrationParams params) { }}); } - private void addRegistration(@NonNull Registration reg, @NonNull Runnable unregistrationHandler) { + private void addRegistration(Registration reg, Runnable unregistrationHandler) { String regId = reg.getId(); synchronized (dynamicRegistrations) { Assert.isLegal(!dynamicRegistrations.containsKey(regId), "Registration id is not unique"); //$NON-NLS-1$ @@ -1018,8 +1011,9 @@ synchronized void setWorkspaceFoldersEnablement(boolean enable) { if (enable == supportsWorkspaceFolderCapability()) { return; } + var serverCapabilities = this.serverCapabilities; if (serverCapabilities == null) { - this.serverCapabilities = new ServerCapabilities(); + serverCapabilities = this.serverCapabilities = new ServerCapabilities(); } WorkspaceServerCapabilities workspace = serverCapabilities.getWorkspace(); if (workspace == null) { @@ -1216,7 +1210,7 @@ private boolean isProjectOpenCloseEvent(IResourceDelta delta) { * * @return True if this workspace folder is non-null and has non-empty content */ - private boolean isValid(WorkspaceFolder wsFolder) { + private boolean isValid(@Nullable WorkspaceFolder wsFolder) { return wsFolder != null && wsFolder.getUri() != null && !wsFolder.getUri().isEmpty(); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServers.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServers.java index 37a3807f9..0d314c50e 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServers.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServers.java @@ -12,6 +12,8 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -30,7 +32,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Assert; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; @@ -44,6 +46,7 @@ */ public abstract class LanguageServers> { + @SuppressWarnings("null") private static void forwardCancellation(CompletableFuture from, CompletableFuture... to) { from.exceptionally(t -> { if (t instanceof CancellationException) { @@ -53,7 +56,7 @@ private static void forwardCancellation(CompletableFuture from, CompletableFu }); } - /** creates a future that is running on het common async pool, ensuring it's not blocking UI Thread */ + /** creates a future that is running on the common async pool, ensuring it's not blocking UI Thread */ private static CompletableFuture onCommonPool(CompletableFuture source) { CompletableFuture res = source.thenApplyAsync(Function.identity()); forwardCancellation(res, source); @@ -70,8 +73,7 @@ private static CompletableFuture onCommonPool(CompletableFuture source * * @return Async result */ - @NonNull - public CompletableFuture<@NonNull List<@NonNull T>> collectAll(Function> fn) { + public CompletableFuture> collectAll(Function> fn) { return collectAll((w, ls) -> fn.apply(ls)); } @@ -87,9 +89,8 @@ private static CompletableFuture onCommonPool(CompletableFuture source * * @return Async result */ - @NonNull - public CompletableFuture<@NonNull List<@NonNull T>> collectAll(BiFunction> fn) { - final CompletableFuture<@NonNull List> init = CompletableFuture.completedFuture(new ArrayList()); + public CompletableFuture> collectAll(BiFunction> fn) { + final CompletableFuture> init = CompletableFuture.completedFuture(new ArrayList()); return onCommonPool(executeOnServers(fn).reduce(init, LanguageServers::add, LanguageServers::addAll)); } @@ -104,8 +105,7 @@ private static CompletableFuture onCommonPool(CompletableFuture source * * @return A list of pending results (note that these may be null or empty) */ - @NonNull - public List<@NonNull CompletableFuture<@Nullable T>> computeAll(Function> fn) { + public List> computeAll(Function> fn) { return computeAll((w, ls) -> fn.apply(ls)); } @@ -122,8 +122,7 @@ private static CompletableFuture onCommonPool(CompletableFuture source * * @return A list of pending results (note that these may be null or empty) */ - @NonNull - public List<@NonNull CompletableFuture<@Nullable T>> computeAll(BiFunction> fn) { + public List> computeAll(BiFunction> fn) { return getServers().stream().map(serverFuture -> { CompletableFuture> requestFuture = serverFuture .thenApply(w -> w == null ? CompletableFuture.completedFuture(null) : w.executeImpl(ls -> fn.apply(w, ls))); @@ -144,7 +143,7 @@ private static CompletableFuture onCommonPool(CompletableFuture source * @return An asynchronous result that will complete with a populated Optional<T> from the first * non-empty response, and with an empty Optional if none of the servers returned a non-empty result. */ - public CompletableFuture> computeFirst(Function> queryLS) { + public CompletableFuture> computeFirst(Function> queryLS) { return computeFirst((w, ls) -> queryLS.apply(ls)); } @@ -161,7 +160,7 @@ public CompletableFuture> computeFirst(FunctionOptional<T> from the first * non-empty response, and with an empty Optional if none of the servers returned a non-empty result. */ - public CompletableFuture> computeFirst(BiFunction> queryLS) { + public CompletableFuture> computeFirst(BiFunction> queryLS) { final CompletableFuture> result = new CompletableFuture<>(); // Dispatch the request to the servers, appending a step to each such that @@ -172,7 +171,7 @@ public CompletableFuture> computeFirst(BiFunction { CompletableFuture populateFuture = lsRequest.thenApply(t -> { - if (!isEmpty(t)) { // some LS methods return null objects when they have nothing to report, and some return an empty List + if (t instanceof Collection c && !c.isEmpty() || t != null) { // some LS methods return null objects when they have nothing to report, and some return an empty List result.complete(Optional.of(t)); } return t; @@ -185,43 +184,44 @@ public CompletableFuture> computeFirst(BiFunction filter) { + @SuppressWarnings("unchecked") + public E withFilter(final Predicate filter) { Assert.isLegal(this.filter == NO_FILTER); this.filter = filter; - return (E)this; + return (E) this; } /** * Specifies the capabilities that a server must have to process this request * @param serverCapabilities - * @return */ - public @NonNull E withCapability(final @NonNull Function> serverCapabilities) { + @SuppressWarnings("unchecked") + public E withCapability(final Function> serverCapabilities) { Assert.isLegal(this.filter == NO_FILTER); this.filter = f -> LSPEclipseUtils.hasCapability(serverCapabilities.apply(f)); - return (E)this; + return (E) this; } /** * * @return Predicate that will be used to determine which servers this executor will use */ - public @NonNull Predicate getFilter() { + public Predicate getFilter() { return this.filter; } - protected Boolean matches(@NonNull CompletableFuture<@Nullable LanguageServerWrapper> wrapperFuture) { + protected Boolean matches(CompletableFuture<@Nullable LanguageServerWrapper> wrapperFuture) { try { return wrapperFuture.thenApply(Objects::nonNull).get(50, TimeUnit.MILLISECONDS); } catch (java.util.concurrent.ExecutionException e) { @@ -253,20 +253,21 @@ public boolean anyMatching() { @SuppressWarnings("null") public static class LanguageServerDocumentExecutor extends LanguageServers { - private final @NonNull IDocument document; + private final IDocument document; - protected LanguageServerDocumentExecutor(final @NonNull IDocument document) { + protected LanguageServerDocumentExecutor(final IDocument document) { this.document = document; } - public @NonNull IDocument getDocument() { + public IDocument getDocument() { return this.document; } - @NonNull CompletableFuture<@Nullable LanguageServerWrapper> connect(@NonNull CompletableFuture<@Nullable LanguageServerWrapper> wrapperFuture) { + CompletableFuture<@Nullable LanguageServerWrapper> connect(CompletableFuture<@Nullable LanguageServerWrapper> wrapperFuture) { return wrapperFuture.thenCompose(wrapper -> { if (wrapper != null) { try { + @NonNullByDefault({}) CompletableFuture serverFuture = wrapper.connectDocument(document); if (serverFuture != null) { return serverFuture; @@ -283,15 +284,15 @@ protected LanguageServerDocumentExecutor(final @NonNull IDocument document) { /** * Test whether this server supports the requested ServerCapabilities. */ - private @NonNull CompletableFuture<@Nullable LanguageServerWrapper> filter(@NonNull LanguageServerWrapper wrapper) { + private CompletableFuture<@Nullable LanguageServerWrapper> filter(LanguageServerWrapper wrapper) { return wrapper.getInitializedServer() .thenCompose(server -> CompletableFuture - .completedFuture(server != null && getFilter().test(wrapper.getServerCapabilities()))) + .completedFuture(server != null && getFilter().test(castNonNull(wrapper.getServerCapabilities())))) .thenApply(matches -> matches ? wrapper: null); } @Override - protected @NonNull List<@NonNull CompletableFuture<@Nullable LanguageServerWrapper>> getServers() { + protected List> getServers() { // Compute list of servers from document & filter Collection wrappers = LanguageServiceAccessor.getLSWrappers(document); return order(wrappers).stream().map(this::filter).map(this::connect).toList(); @@ -327,18 +328,17 @@ public static class LanguageServerProjectExecutor extends LanguageServers> getServers() { + protected List> getServers() { // Compute list of servers from project & filter - Collection<@NonNull LanguageServerWrapper> startedWrappers = order(LanguageServiceAccessor.getStartedWrappers(project, getFilter(), !restartStopped)); - List<@NonNull CompletableFuture> wrappers = new ArrayList<>(startedWrappers.size()); + Collection startedWrappers = order(LanguageServiceAccessor.getStartedWrappers(project, getFilter(), !restartStopped)); + List> wrappers = new ArrayList<>(startedWrappers.size()); for (LanguageServerWrapper wrapper : startedWrappers) { wrappers.add(wrapper.getInitializedServer().thenApply(ls -> wrapper)); } @@ -346,27 +346,23 @@ public static class LanguageServerProjectExecutor extends LanguageServers boolean isEmpty(final T t) { - return t == null || (t instanceof Collection c && c.isEmpty()); - } - protected Collection order(Collection wrappers) { if (serverDefinition != null && wrappers.size() > 1) { List temp = new ArrayList<>(wrappers); for (int i = 0; i < temp.size(); i++) { LanguageServerWrapper wrapper = temp.get(i); - if (wrapper != null && wrapper.serverDefinition != null && Objects.equals(serverDefinition, wrapper.serverDefinition)) { + if (Objects.equals(serverDefinition, wrapper.serverDefinition)) { Collections.swap(temp, 0, i); return temp; } - }; + } } return wrappers; } - // Pluggable strategy for getting the set of LSWrappers to dispatch operations on - protected abstract @NonNull List<@NonNull CompletableFuture<@Nullable LanguageServerWrapper>> getServers(); + /** Pluggable strategy for getting the set of LSWrappers to dispatch operations on */ + protected abstract List> getServers(); /** * Hook called when requests are scheduled - for subclasses to implement optimistic locking @@ -381,7 +377,6 @@ protected void computeVersion() {} * @param col * @return A stream (empty if col is null) */ - @NonNull public static Stream streamSafely(@Nullable Collection col) { return col == null ? Stream.of() : col.stream(); } @@ -392,12 +387,9 @@ public static Stream streamSafely(@Nullable Collection col) { * @param Result type * @param accumulator One async result * @param element Another async result - * @return */ - @SuppressWarnings("null") - @NonNull - private static CompletableFuture<@NonNull List<@NonNull T>> add(@NonNull CompletableFuture> accumulator, @NonNull CompletableFuture<@Nullable T> element) { - CompletableFuture<@NonNull List<@NonNull T>> res = accumulator.thenCombine(element, (a, b) -> { + private static CompletableFuture> add(CompletableFuture> accumulator, CompletableFuture<@Nullable T> element) { + CompletableFuture> res = accumulator.thenCombine(element, (a, b) -> { if (b != null) { a.add(b); } @@ -414,10 +406,8 @@ public static Stream streamSafely(@Nullable Collection col) { * @param another Another async result * @return Async combined result */ - @SuppressWarnings("null") - @NonNull - public static CompletableFuture<@NonNull List> addAll(@NonNull CompletableFuture<@NonNull List> accumulator, @NonNull CompletableFuture<@NonNull List> another) { - CompletableFuture<@NonNull List> res = accumulator.thenCombine(another, (a, b) -> { + public static CompletableFuture> addAll(CompletableFuture> accumulator, CompletableFuture> another) { + CompletableFuture> res = accumulator.thenCombine(another, (a, b) -> { a.addAll(b); return a; }); @@ -431,13 +421,12 @@ public static Stream streamSafely(@Nullable Collection col) { * (not chained with other futures) so cancelling the futures in * this stream will send a cancellation event to the LSs.

*/ - @NonNull private Stream> executeOnServers( BiFunction> fn) { return getServers().stream().map(serverFuture -> { // wrap in AtomicReference to allow dereferencing in downstream future CompletableFuture> lsRequestFuture = serverFuture.thenApply(w -> w == null - ? CompletableFuture.completedFuture((T) null) + ? CompletableFuture.completedFuture(null) : w.executeImpl(ls -> fn.apply(w, ls))); CompletableFuture res = lsRequestFuture.thenCompose(Function.identity()); lsRequestFuture.thenAccept(request -> forwardCancellation(res, request)); @@ -450,7 +439,7 @@ private Stream> executeOnServers( * then we give up and supply an empty result rather than potentially waiting * forever... */ - private void completeEmptyOrWithException(final CompletableFuture> completableFuture, final Throwable t) { + private void completeEmptyOrWithException(final CompletableFuture> completableFuture, final @Nullable Throwable t) { if (t != null) { completableFuture.completeExceptionally(t); } else { @@ -463,7 +452,7 @@ private void completeEmptyOrWithException(final CompletableFuture void completeEmptyOrWithException(final CompletableFuture NO_FILTER = s -> true; - private @NonNull Predicate filter = NO_FILTER; + private static final Predicate NO_FILTER = s -> true; + private Predicate filter = NO_FILTER; protected @Nullable LanguageServerDefinition serverDefinition; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java index 9905741f1..9957eea63 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java @@ -36,7 +36,6 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.debug.core.ILaunchConfiguration; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.preference.IPersistentPreferenceStore; import org.eclipse.jface.preference.IPreferenceStore; @@ -89,13 +88,13 @@ public class LanguageServersRegistry { private static final String ENABLED_WHEN_DESC = "description"; //$NON-NLS-1$ public abstract static class LanguageServerDefinition { - public final @NonNull String id; - public final @NonNull String label; + public final String id; + public final String label; public final boolean isSingleton; public final int lastDocumentDisconnectedTimeout; - public final @NonNull Map languageIdMappings; + public final Map languageIdMappings; - LanguageServerDefinition(@NonNull String id, @NonNull String label, boolean isSingleton, int lastDocumentDisconnectedTimeout) { + LanguageServerDefinition(String id, String label, boolean isSingleton, int lastDocumentDisconnectedTimeout) { this.id = id; this.label = label; this.isSingleton = isSingleton; @@ -103,7 +102,7 @@ public abstract static class LanguageServerDefinition { this.languageIdMappings = new ConcurrentHashMap<>(); } - public void registerAssociation(@NonNull IContentType contentType, @NonNull String languageId) { + public void registerAssociation(IContentType contentType, String languageId) { this.languageIdMappings.put(contentType, languageId); } @@ -156,7 +155,7 @@ private static int getLastDocumentDisconnectedTimeout(IConfigurationElement elem return lastDocumentisconnectedTiemoutAttribute == null ? DEFAULT_LAST_DOCUMENTED_DISCONNECTED_TIEMOUT : Integer.parseInt(lastDocumentisconnectedTiemoutAttribute); } - public ExtensionLanguageServerDefinition(@NonNull IConfigurationElement element) { + public ExtensionLanguageServerDefinition(IConfigurationElement element) { super(element.getAttribute(ID_ATTRIBUTE), element.getAttribute(LABEL_ATTRIBUTE), getIsSingleton(element), getLastDocumentDisconnectedTimeout(element)); this.extension = element; } @@ -265,7 +264,7 @@ private LanguageServersRegistry() { private void initialize() { String prefs = preferenceStore.getString(CONTENT_TYPE_TO_LSP_LAUNCH_PREF_KEY); - if (prefs != null && !prefs.isEmpty()) { + if (!prefs.isEmpty()) { String[] entries = prefs.split(","); //$NON-NLS-1$ for (String entry : entries) { ContentTypeToLSPLaunchConfigEntry mapping = ContentTypeToLSPLaunchConfigEntry.readFromPreference(entry); @@ -286,7 +285,7 @@ private void initialize() { IContentType contentType = Platform.getContentTypeManager().getContentType(extension.getAttribute(CONTENT_TYPE_ATTRIBUTE)); String languageId = extension.getAttribute(LANGUAGE_ID_ATTRIBUTE); EnablementTester expression = null; - if (extension.getChildren(ENABLED_WHEN_ATTRIBUTE) != null) { + if (extension.getChildren(ENABLED_WHEN_ATTRIBUTE).length > 0) { IConfigurationElement[] enabledWhenElements = extension.getChildren(ENABLED_WHEN_ATTRIBUTE); if (enabledWhenElements.length == 1) { IConfigurationElement enabledWhen = enabledWhenElements[0]; @@ -320,7 +319,7 @@ private void initialize() { } } - private IEvaluationContext evaluationContext() { + private @Nullable IEvaluationContext evaluationContext() { final var handlerService = PlatformUI.getWorkbench().getService(IHandlerService.class); return handlerService == null ? null @@ -351,7 +350,7 @@ private void persistContentTypeToLaunchConfigurationMapping() { * @return the {@link LanguageServerDefinition}s directly associated to the given content-type. * This does not include the one that match transitively as per content-type hierarchy */ - List<@NonNull ContentTypeToLanguageServerDefinition> findProviderFor(final @NonNull IContentType contentType) { + List findProviderFor(final IContentType contentType) { return connections.stream() .filter(entry -> entry.getKey().equals(contentType)) .sorted((mapping1, mapping2) -> { @@ -367,16 +366,15 @@ private void persistContentTypeToLaunchConfigurationMapping() { }).toList(); } - public void registerAssociation(@NonNull IContentType contentType, @NonNull ILaunchConfiguration launchConfig, @NonNull Set launchMode) { + public void registerAssociation(IContentType contentType, ILaunchConfiguration launchConfig, Set launchMode) { final var mapping = new ContentTypeToLSPLaunchConfigEntry(contentType, launchConfig, launchMode); connections.add(mapping); persistContentTypeToLaunchConfigurationMapping(); } - public void registerAssociation(@NonNull IContentType contentType, - @NonNull LanguageServerDefinition serverDefinition, @Nullable String languageId, - EnablementTester enablement) { + public void registerAssociation(IContentType contentType, LanguageServerDefinition serverDefinition, + @Nullable String languageId, @Nullable EnablementTester enablement) { if (languageId != null) { serverDefinition.registerAssociation(contentType, languageId); } @@ -398,7 +396,7 @@ public List getContentTypeToLSPExtensions return this.connections.stream().filter(mapping -> mapping.getValue() instanceof ExtensionLanguageServerDefinition).toList(); } - public @Nullable LanguageServerDefinition getDefinition(@NonNull String languageServerId) { + public @Nullable LanguageServerDefinition getDefinition(String languageServerId) { for (ContentTypeToLanguageServerDefinition mapping : this.connections) { if (mapping.getValue().id.equals(languageServerId)) { return mapping.getValue(); @@ -412,13 +410,12 @@ public List getContentTypeToLSPExtensions */ private static final class ContentTypeMapping { - @NonNull public final String id; - @NonNull public final IContentType contentType; - @Nullable public final String languageId; - @Nullable - public final EnablementTester enablement; + public final String id; + public final IContentType contentType; + public final @Nullable String languageId; + public final @Nullable EnablementTester enablement; - public ContentTypeMapping(@NonNull IContentType contentType, @NonNull String id, @Nullable String languageId, + public ContentTypeMapping(IContentType contentType, String id, @Nullable String languageId, @Nullable EnablementTester enablement) { this.contentType = contentType; this.id = id; @@ -433,7 +430,7 @@ public ContentTypeMapping(@NonNull IContentType contentType, @NonNull String id, * @param serverDefinition * @return whether the given serverDefinition is suitable for the file */ - public boolean matches(@NonNull IFile file, @NonNull LanguageServerDefinition serverDefinition) { + public boolean matches(IFile file, LanguageServerDefinition serverDefinition) { return getAvailableLSFor(LSPEclipseUtils.getFileContentTypes(file), file.getLocationURI()).contains(serverDefinition); } @@ -442,16 +439,16 @@ public boolean matches(@NonNull IFile file, @NonNull LanguageServerDefinition se * @param serverDefinition * @return whether the given serverDefinition is suitable for the file */ - public boolean matches(@NonNull IDocument document, @NonNull LanguageServerDefinition serverDefinition) { + public boolean matches(IDocument document, LanguageServerDefinition serverDefinition) { return getAvailableLSFor(LSPEclipseUtils.getDocumentContentTypes(document), LSPEclipseUtils.toUri(document)).contains(serverDefinition); } - public boolean canUseLanguageServer(@NonNull IEditorInput editorInput) { + public boolean canUseLanguageServer(IEditorInput editorInput) { return !getAvailableLSFor( Arrays.asList(Platform.getContentTypeManager().findContentTypesFor(editorInput.getName())), LSPEclipseUtils.toUri(editorInput)).isEmpty(); } - public boolean canUseLanguageServer(@NonNull IDocument document) { + public boolean canUseLanguageServer(IDocument document) { List contentTypes = LSPEclipseUtils.getDocumentContentTypes(document); if (contentTypes.isEmpty()) { @@ -461,7 +458,7 @@ public boolean canUseLanguageServer(@NonNull IDocument document) { return !getAvailableLSFor(contentTypes, LSPEclipseUtils.toUri(document)).isEmpty(); } - public boolean canUseLanguageServer(@NonNull IFile file) { + public boolean canUseLanguageServer(IFile file) { return !getAvailableLSFor(LSPEclipseUtils.getFileContentTypes(file), file.getLocationURI()).isEmpty(); } @@ -470,7 +467,7 @@ public boolean canUseLanguageServer(@NonNull IFile file) { * @param contentTypes content-types to check against LS registry. Base types are checked too. * @return definitions that can support the following content-types */ - private Set getAvailableLSFor(Collection contentTypes, URI uri) { + private Set getAvailableLSFor(Collection contentTypes, @Nullable URI uri) { final var res = new HashSet(); contentTypes = expandToSuperTypes(contentTypes); for (ContentTypeToLanguageServerDefinition mapping : this.connections) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java index 43e33dce5..f47e02e00 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java @@ -14,6 +14,8 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.io.IOException; import java.net.URI; import java.util.ArrayDeque; @@ -51,8 +53,8 @@ import org.eclipse.ui.part.FileEditorInput; /** - * The entry-point to retrieve a Language Server Wrapper for a given resource/project. - * Deals with instantiations and caching of underlying + * The entry-point to retrieve a Language Server Wrapper for a given + * resource/project. Deals with instantiations and caching of underlying * {@link LanguageServerWrapper}. * */ @@ -62,7 +64,7 @@ private LanguageServiceAccessor() { // this class shouldn't be instantiated } - private static final Set<@NonNull LanguageServerWrapper> startedServers = new CopyOnWriteArraySet<>(); + private static final Set startedServers = new CopyOnWriteArraySet<>(); private static final Map providersToLSDefinitions = new HashMap<>(); /** @@ -79,23 +81,23 @@ public static void clearStartedServers() { /** * A bean storing association of a Document/File with a language server wrapper. + * * @deprecated use {@link LanguageServers#forDocument(IDocument)} instead. */ @Deprecated(forRemoval = true) public static class LSPDocumentInfo { - private final @NonNull URI fileUri; - private final @NonNull IDocument document; - private final @NonNull LanguageServerWrapper wrapper; + private final URI fileUri; + private final IDocument document; + private final LanguageServerWrapper wrapper; - private LSPDocumentInfo(@NonNull URI fileUri, @NonNull IDocument document, - @NonNull LanguageServerWrapper wrapper) { + private LSPDocumentInfo(URI fileUri, IDocument document, LanguageServerWrapper wrapper) { this.fileUri = fileUri; this.document = document; this.wrapper = wrapper; } - public @NonNull IDocument getDocument() { + public IDocument getDocument() { return this.document; } @@ -104,7 +106,7 @@ private LSPDocumentInfo(@NonNull URI fileUri, @NonNull IDocument document, * * @return the file URI */ - public @NonNull URI getFileUri() { + public URI getFileUri() { return this.fileUri; } @@ -126,35 +128,28 @@ public boolean isActive() { } public static void disableLanguageServerContentType( - @NonNull ContentTypeToLanguageServerDefinition contentTypeToLSDefinition) { + ContentTypeToLanguageServerDefinition contentTypeToLSDefinition) { Optional result = startedServers.stream() .filter(server -> server.serverDefinition.equals(contentTypeToLSDefinition.getValue())).findFirst(); if (result.isPresent()) { IContentType contentType = contentTypeToLSDefinition.getKey(); - if (contentType != null) { - result.get().disconnectContentType(contentType); - } + result.get().disconnectContentType(contentType); } } public static void enableLanguageServerContentType( - @NonNull final ContentTypeToLanguageServerDefinition contentTypeToLSDefinition, - @NonNull final IEditorReference[] editors) { + final ContentTypeToLanguageServerDefinition contentTypeToLSDefinition, final IEditorReference[] editors) { final IContentType contentType = contentTypeToLSDefinition.getKey(); - if(contentType == null) - return; final LanguageServerDefinition lsDefinition = contentTypeToLSDefinition.getValue(); - if(lsDefinition == null) - return; - for (final IEditorReference editor : editors) { try { if (editor.getEditorInput() instanceof FileEditorInput editorInput) { final IFile editorFile = editorInput.getFile(); final IContentDescription contentDesc = editorFile.getContentDescription(); - if(contentDesc == null) + if (contentDesc == null) continue; - if (contentType.equals(contentDesc.getContentType()) && contentTypeToLSDefinition.isEnabled(editorFile.getLocationURI())) { + if (contentType.equals(contentDesc.getContentType()) + && contentTypeToLSDefinition.isEnabled(editorFile.getLocationURI())) { getInitializedLanguageServer(editorFile, lsDefinition, capabilities -> true); } } @@ -164,18 +159,21 @@ public static void enableLanguageServerContentType( } } - /** - * Get the requested language server instance for the given file. Starts the language server if not already started. + * Get the requested language server instance for the given file. Starts the + * language server if not already started. + * * @param resource * @param lsDefinition - * @param capabilitiesPredicate a predicate to check capabilities - * @return a LanguageServer for the given file, which is defined with provided server ID and conforms to specified request. - * If {@code capabilitesPredicate} does not test positive for the server's capabilities, {@code null} is returned. + * @param capabilitiesPredicate + * a predicate to check capabilities + * @return a LanguageServer for the given file, which is defined with provided + * server ID and conforms to specified request. If + * {@code capabilitesPredicate} does not test positive for the server's + * capabilities, {@code null} is returned. */ - private static CompletableFuture getInitializedLanguageServer(@NonNull IResource resource, - @NonNull LanguageServerDefinition lsDefinition, Predicate capabilitiesPredicate) - throws IOException { + private static @Nullable CompletableFuture getInitializedLanguageServer(IResource resource, + LanguageServerDefinition lsDefinition, @Nullable Predicate capabilitiesPredicate) { LanguageServerWrapper wrapper = getLSWrapper(resource.getProject(), lsDefinition, resource.getFullPath()); if (capabilitiesComply(wrapper, capabilitiesPredicate)) { return wrapper.getInitializedServer(); @@ -183,7 +181,6 @@ private static CompletableFuture getInitializedLanguageServer(@N return null; } - /** * Checks if the given {@code wrapper}'s capabilities comply with the given * {@code capabilitiesPredicate}. @@ -199,11 +196,13 @@ private static CompletableFuture getInitializedLanguageServer(@N * {@code wrapper.getServerCapabilities() == null} */ private static boolean capabilitiesComply(LanguageServerWrapper wrapper, - Predicate capabilitiesPredicate) { + @Nullable Predicate capabilitiesPredicate) { return capabilitiesPredicate == null - /* next null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */ + /* + * next null check is workaround for https://github.com/TypeFox/ls-api/issues/47 + */ || wrapper.getServerCapabilities() == null - || capabilitiesPredicate.test(wrapper.getServerCapabilities()); + || capabilitiesPredicate.test(castNonNull(wrapper.getServerCapabilities())); } /** @@ -212,13 +211,11 @@ private static boolean capabilitiesComply(LanguageServerWrapper wrapper, * @param file * @param request * @return the matching LS wrappers - * @throws IOException * @noreference This method is currently internal and should only be referenced * for testing */ - @NonNull - public static List getLSWrappers(@NonNull final IFile file, - @Nullable final Predicate request) throws IOException { + public static List getLSWrappers(final IFile file, + final @Nullable Predicate request) { final var project = file.getProject(); if (project == null) { return Collections.emptyList(); @@ -226,9 +223,13 @@ public static List getLSWrappers(@NonNull final IFile fil final var lsRegistry = LanguageServersRegistry.getInstance(); final var fileURI = file.getLocationURI(); + if (fileURI == null) { + return Collections.emptyList(); + } - List<@NonNull LanguageServerWrapper> wrappers = getStartedWrappers(file.getProject(), request, true); - wrappers.removeIf(wrapper -> !wrapper.isConnectedTo(fileURI) || !lsRegistry.matches(file, wrapper.serverDefinition)); + List wrappers = getStartedWrappers(file.getProject(), request, true); + wrappers.removeIf( + wrapper -> !wrapper.isConnectedTo(fileURI) || !lsRegistry.matches(file, wrapper.serverDefinition)); // look for running language servers via content-type final var directContentTypes = LSPEclipseUtils.getFileContentTypes(file); @@ -246,10 +247,6 @@ public static List getLSWrappers(@NonNull final IFile fil continue; } final LanguageServerDefinition serverDefinition = mapping.getValue(); - if (serverDefinition == null) { - continue; - } - final var wrapper = getLSWrapper(project, serverDefinition, file.getFullPath()); if (!wrappers.contains(wrapper) && capabilitiesComply(wrapper, request)) { wrappers.add(wrapper); @@ -264,8 +261,7 @@ public static List getLSWrappers(@NonNull final IFile fil return wrappers; } - @NonNull - protected static Collection getLSWrappers(@NonNull final IDocument document) { + protected static Collection getLSWrappers(final IDocument document) { final URI uri = LSPEclipseUtils.toUri(document); if (uri == null) { return Collections.emptyList(); @@ -304,11 +300,8 @@ protected static Collection getLSWrappers(@NonNull final continue; } final LanguageServerDefinition serverDefinition = mapping.getValue(); - if (serverDefinition == null) { - continue; - } - final Predicate selectServersWithEqualDefinition = wrapper -> - wrapper.serverDefinition .equals(serverDefinition); + final Predicate selectServersWithEqualDefinition = wrapper -> wrapper.serverDefinition + .equals(serverDefinition); if (res.stream().anyMatch(selectServersWithEqualDefinition)) { // we already found a compatible LS with this definition continue; @@ -346,17 +339,16 @@ protected static Collection getLSWrappers(@NonNull final * * @param project * @param serverDefinition - * @return a new or existing {@link LanguageServerWrapper} for the given definition. + * @return a new or existing {@link LanguageServerWrapper} for the given + * definition. */ - @NonNull public static LanguageServerWrapper getLSWrapper(@Nullable IProject project, - @NonNull LanguageServerDefinition serverDefinition) { + LanguageServerDefinition serverDefinition) { return getLSWrapper(project, serverDefinition, null); } - @NonNull private static LanguageServerWrapper getLSWrapper(@Nullable IProject project, - @NonNull LanguageServerDefinition serverDefinition, @Nullable IPath initialPath) { + LanguageServerDefinition serverDefinition, @Nullable IPath initialPath) { final Predicate serverSelector = wrapper -> wrapper.canOperate(project) && wrapper.serverDefinition.equals(serverDefinition); @@ -383,13 +375,14 @@ private static LanguageServerWrapper getLSWrapper(@Nullable IProject project, } } - public static @NonNull LanguageServerWrapper startLanguageServer(@NonNull LanguageServerDefinition serverDefinition) { + public static LanguageServerWrapper startLanguageServer(LanguageServerDefinition serverDefinition) { synchronized (startedServers) { - LanguageServerWrapper wrapper = startedServers.stream().filter(w -> w.serverDefinition == serverDefinition).findFirst().orElseGet(() -> { - LanguageServerWrapper w = new LanguageServerWrapper(serverDefinition, null); - startedServers.add(w); - return w; - }); + LanguageServerWrapper wrapper = startedServers.stream().filter(w -> w.serverDefinition == serverDefinition) + .findFirst().orElseGet(() -> { + LanguageServerWrapper w = new LanguageServerWrapper(serverDefinition, null); + startedServers.add(w); + return w; + }); if (!wrapper.isActive()) { wrapper.start(); } @@ -406,49 +399,51 @@ private static interface ServerSupplier { LanguageServerWrapper get() throws IOException; } - @NonNull - public static List<@NonNull LanguageServerWrapper> getStartedWrappers(Predicate request, boolean onlyActiveLS) { + public static List getStartedWrappers(@Nullable Predicate request, + boolean onlyActiveLS) { return getStartedWrappers(w -> true, request, onlyActiveLS); } - @NonNull - public static List<@NonNull LanguageServerWrapper> getStartedWrappers(@Nullable IProject project, Predicate request, boolean onlyActiveLS) { + public static List getStartedWrappers(@Nullable IProject project, + @Nullable Predicate request, boolean onlyActiveLS) { return getStartedWrappers(w -> w.canOperate(project), request, onlyActiveLS); } - @NonNull - public static List<@NonNull LanguageServerWrapper> getStartedWrappers(@NonNull IDocument document, Predicate request, boolean onlyActiveLS) { + public static List getStartedWrappers(IDocument document, + Predicate request, boolean onlyActiveLS) { return getStartedWrappers(w -> w.canOperate(document), request, onlyActiveLS); } - @NonNull - private static List<@NonNull LanguageServerWrapper> getStartedWrappers(Predicate canOperatePredicate, Predicate capabilitiesPredicate, boolean onlyActiveLS) { - List<@NonNull LanguageServerWrapper> result = new ArrayList<>(); + private static List getStartedWrappers(Predicate canOperatePredicate, + @Nullable Predicate capabilitiesPredicate, boolean onlyActiveLS) { + List result = new ArrayList<>(); for (LanguageServerWrapper wrapper : startedServers) { - if ((!onlyActiveLS || wrapper.isActive()) && canOperatePredicate.test(wrapper) && capabilitiesComply(wrapper, capabilitiesPredicate)) { - result.add(wrapper); + if ((!onlyActiveLS || wrapper.isActive()) && canOperatePredicate.test(wrapper) + && capabilitiesComply(wrapper, capabilitiesPredicate)) { + result.add(wrapper); } } return result; } /** - * Returns {@code true} if there are running language servers satisfying a capability predicate. - * This does not start any matching language servers. + * Returns {@code true} if there are running language servers satisfying a + * capability predicate. This does not start any matching language servers. * * @param request - * @return {@code true} if there are running language servers satisfying a capability predicate + * @return {@code true} if there are running language servers satisfying a + * capability predicate */ - public static boolean hasActiveLanguageServers(@NonNull Predicate request) { + public static boolean hasActiveLanguageServers(Predicate request) { return !getLanguageServers(request).isEmpty(); } - public static boolean hasActiveLanguageServers(@Nullable IFile file, @NonNull Predicate request) { + public static boolean hasActiveLanguageServers(@Nullable IFile file, Predicate request) { final IProject project = file != null ? file.getProject() : null; return !getLanguageServers(project, request).isEmpty(); } - public static boolean hasActiveLanguageServers(@NonNull IDocument document, @NonNull Predicate request) { + public static boolean hasActiveLanguageServers(IDocument document, Predicate request) { return !getLanguageServers(document, request).isEmpty(); } @@ -456,14 +451,13 @@ public static boolean hasActiveLanguageServers(@NonNull IDocument document, @Non * Gets list of LS initialized for any project * * @param onlyActiveLS - * true if this method should return only the already running - * language servers, otherwise previously started language servers - * will be re-activated + * true if this method should return only the already running + * language servers, otherwise previously started language servers + * will be re-activated * @return list of Language Servers */ - @NonNull - private static List<@NonNull LanguageServer> getLanguageServers(Predicate capabilitiesPredicate) { - List<@NonNull LanguageServerWrapper> wrappers = getStartedWrappers(capabilitiesPredicate, true); + private static List getLanguageServers(Predicate capabilitiesPredicate) { + List wrappers = getStartedWrappers(capabilitiesPredicate, true); return getLanguageServersFromWrappers(wrappers); } @@ -471,14 +465,14 @@ public static boolean hasActiveLanguageServers(@NonNull IDocument document, @Non * Gets list of LS initialized for given project * * @param onlyActiveLS - * true if this method should return only the already running - * language servers, otherwise previously started language servers - * will be re-activated + * true if this method should return only the already running + * language servers, otherwise previously started language servers + * will be re-activated * @return list of Language Servers */ - @NonNull - private static List<@NonNull LanguageServer> getLanguageServers(@Nullable IProject project, Predicate capabilitiesPredicate) { - List<@NonNull LanguageServerWrapper> wrappers = getStartedWrappers(project, capabilitiesPredicate, true); + private static List getLanguageServers(@Nullable IProject project, + Predicate capabilitiesPredicate) { + List wrappers = getStartedWrappers(project, capabilitiesPredicate, true); return getLanguageServersFromWrappers(wrappers); } @@ -491,14 +485,14 @@ public static boolean hasActiveLanguageServers(@NonNull IDocument document, @Non * will be re-activated * @return list of Language Servers */ - @NonNull - private static List<@NonNull LanguageServer> getLanguageServers(@NonNull IDocument document, Predicate capabilitiesPredicate) { - List<@NonNull LanguageServerWrapper> wrappers = getStartedWrappers(document, capabilitiesPredicate, true); + private static List getLanguageServers(IDocument document, + Predicate capabilitiesPredicate) { + List wrappers = getStartedWrappers(document, capabilitiesPredicate, true); return getLanguageServersFromWrappers(wrappers); } - private static List<@NonNull LanguageServer> getLanguageServersFromWrappers(List<@NonNull LanguageServerWrapper> wrappers) { - final var servers = new ArrayList<@NonNull LanguageServer>(wrappers.size()); + private static List getLanguageServersFromWrappers(List wrappers) { + final var servers = new ArrayList(wrappers.size()); for (LanguageServerWrapper wrapper : wrappers) { @Nullable LanguageServer server = wrapper.getServer(); @@ -509,7 +503,7 @@ public static boolean hasActiveLanguageServers(@NonNull IDocument document, @Non return servers; } - protected static LanguageServerDefinition getLSDefinition(@NonNull StreamConnectionProvider provider) { + protected static @Nullable LanguageServerDefinition getLSDefinition(StreamConnectionProvider provider) { return providersToLSDefinitions.get(provider); } @@ -517,21 +511,26 @@ protected static LanguageServerDefinition getLSDefinition(@NonNull StreamConnect * @deprecated use {@link LanguageServers#forDocument(IDocument)} instead. */ @Deprecated(forRemoval = true) - @NonNull public static List<@NonNull LSPDocumentInfo> getLSPDocumentInfosFor(@NonNull IDocument document, @NonNull Predicate capabilityRequest) { + public static List getLSPDocumentInfosFor(IDocument document, + Predicate capabilityRequest) { URI fileUri = LSPEclipseUtils.toUri(document); final var res = new ArrayList(); - try { - getLSWrappers(document).stream().filter(wrapper -> wrapper.getServerCapabilities() == null - || capabilityRequest.test(wrapper.getServerCapabilities())).forEach(wrapper -> { - try { - wrapper.connectDocument(document); - } catch (IOException e) { - LanguageServerPlugin.logError(e); - } - res.add(new LSPDocumentInfo(fileUri, document, wrapper)); - }); - } catch (final Exception e) { - LanguageServerPlugin.logError(e); + if (fileUri != null) { + try { + getLSWrappers(document).stream() // + .filter(wrapper -> wrapper.getServerCapabilities() == null + || capabilityRequest.test(castNonNull(wrapper.getServerCapabilities()))) + .forEach(wrapper -> { + try { + wrapper.connectDocument(document); + } catch (IOException e) { + LanguageServerPlugin.logError(e); + } + res.add(new LSPDocumentInfo(fileUri, document, wrapper)); + }); + } catch (final Exception e) { + LanguageServerPlugin.logError(e); + } } return res; } @@ -540,4 +539,3 @@ static void shutdownAllDispatchers() { startedServers.forEach(LanguageServerWrapper::stopDispatcher); } } - diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LaunchConfigurationStreamProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LaunchConfigurationStreamProvider.java index 136b243b4..4d344a551 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LaunchConfigurationStreamProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LaunchConfigurationStreamProvider.java @@ -41,6 +41,7 @@ import org.eclipse.debug.core.model.RuntimeProcess; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.core.Preferences; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.server.StreamConnectionProvider; /** @@ -49,11 +50,11 @@ */ public class LaunchConfigurationStreamProvider implements StreamConnectionProvider, IAdaptable { - private StreamProxyInputStream inputStream; - private StreamProxyInputStream errorStream; - private OutputStream outputStream; - private ILaunch launch; - private IProcess process; + private @Nullable StreamProxyInputStream inputStream; + private @Nullable StreamProxyInputStream errorStream; + private @Nullable OutputStream outputStream; + private @Nullable ILaunch launch; + private @Nullable IProcess process; private final ILaunchConfiguration launchConfiguration; private Set launchModes; @@ -98,7 +99,7 @@ public int available() throws IOException { } - public LaunchConfigurationStreamProvider(ILaunchConfiguration launchConfig, Set launchModes) { + public LaunchConfigurationStreamProvider(ILaunchConfiguration launchConfig, @Nullable Set launchModes) { super(); Assert.isNotNull(launchConfig); this.launchConfiguration = launchConfig; @@ -110,7 +111,7 @@ public LaunchConfigurationStreamProvider(ILaunchConfiguration launchConfig, Set< } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } @@ -124,7 +125,7 @@ public int hashCode() { return this.launchConfiguration.hashCode() ^ this.launchModes.hashCode(); } - public static ILaunchConfiguration findLaunchConfiguration(String typeId, String name) { + public static @Nullable ILaunchConfiguration findLaunchConfiguration(String typeId, String name) { ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType type = manager.getLaunchConfigurationType(typeId); ILaunchConfiguration res = null; @@ -146,10 +147,10 @@ public void start() throws IOException { // the IDE when Outline is displayed. boolean statusHandlerToUpdate = disableStatusHandler(); try { - this.launch = this.launchConfiguration.launch(this.launchModes.iterator().next(), new NullProgressMonitor(), + final var launch = this.launch = this.launchConfiguration.launch(this.launchModes.iterator().next(), new NullProgressMonitor(), false); long initialTimestamp = System.currentTimeMillis(); - while (this.launch.getProcesses().length == 0 && System.currentTimeMillis() - initialTimestamp < 5000) { + while (launch.getProcesses().length == 0 && System.currentTimeMillis() - initialTimestamp < 5000) { try { Thread.sleep(50); } catch (InterruptedException e) { @@ -157,10 +158,10 @@ public void start() throws IOException { Thread.currentThread().interrupt(); } } - if (this.launch.getProcesses().length > 0) { - this.process = this.launch.getProcesses()[0]; - this.inputStream = new StreamProxyInputStream(process); - process.getStreamsProxy().getOutputStreamMonitor().addListener(this.inputStream); + if (launch.getProcesses().length > 0) { + final var process = this.process = launch.getProcesses()[0]; + final var inputStream = this.inputStream = new StreamProxyInputStream(process); + process.getStreamsProxy().getOutputStreamMonitor().addListener(inputStream); // TODO: Ugly hack, find something better to retrieve stream! try { Method systemProcessGetter = RuntimeProcess.class.getDeclaredMethod("getSystemProcess"); //$NON-NLS-1$ @@ -170,8 +171,8 @@ public void start() throws IOException { } catch (ReflectiveOperationException ex) { LanguageServerPlugin.logError(ex); } - this.errorStream = new StreamProxyInputStream(process); - process.getStreamsProxy().getErrorStreamMonitor().addListener(this.errorStream); + final var errorStream = this.errorStream = new StreamProxyInputStream(process); + process.getStreamsProxy().getErrorStreamMonitor().addListener(errorStream); } } catch (Exception e) { LanguageServerPlugin.logError(e); @@ -210,36 +211,37 @@ private void setStatusHandler(boolean enabled) { } @Override - public T getAdapter(Class adapter) { - if(adapter == ProcessHandle.class) { + public @Nullable T getAdapter(@Nullable Class adapter) { + if(adapter == ProcessHandle.class && process != null) { return process.getAdapter(adapter); } return null; } @Override - public InputStream getInputStream() { + public @Nullable InputStream getInputStream() { return this.inputStream; } @Override - public OutputStream getOutputStream() { + public @Nullable OutputStream getOutputStream() { return this.outputStream; } @Override - public InputStream getErrorStream() { + public @Nullable InputStream getErrorStream() { return this.errorStream; } @Override public void stop() { - if (this.launch == null) { + final var launch = this.launch; + if (launch == null) { return; } try { - this.launch.terminate(); - for (IProcess p : this.launch.getProcesses()) { + launch.terminate(); + for (IProcess p : launch.getProcesses()) { p.terminate(); } } catch (DebugException e1) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LoggingStreamConnectionProviderProxy.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LoggingStreamConnectionProviderProxy.java index ee5433b12..1c10b3bae 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LoggingStreamConnectionProviderProxy.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LoggingStreamConnectionProviderProxy.java @@ -41,7 +41,7 @@ public class LoggingStreamConnectionProviderProxy implements StreamConnectionProvider, IAdaptable { - public static File getLogDirectory() { + public static @Nullable File getLogDirectory() { IPath root = ResourcesPlugin.getWorkspace().getRoot().getLocation(); if (root == null) { return null; @@ -57,11 +57,11 @@ public static File getLogDirectory() { private static final String STDERR_KEY = "stderr.logging.enabled"; //$NON-NLS-1$ private final StreamConnectionProvider provider; - private InputStream inputStream; - private OutputStream outputStream; - private InputStream errorStream; + private @Nullable InputStream inputStream; + private @Nullable OutputStream outputStream; + private @Nullable InputStream errorStream; private final String id; - private final File logFile; + private final @Nullable File logFile; private boolean logToFile; private boolean logToConsole; @@ -134,7 +134,7 @@ private String errorMessage(byte[] payload) { } @Override - public InputStream getInputStream() { + public @Nullable InputStream getInputStream() { if (inputStream != null) { return inputStream; } @@ -162,7 +162,7 @@ public int read(byte[] b, int off, int len) throws IOException { } @Override - public T getAdapter(Class adapter) { + public @Nullable T getAdapter(@Nullable Class adapter) { if(adapter == ProcessHandle.class) { return Adapters.adapt(provider, adapter); } @@ -170,7 +170,7 @@ public T getAdapter(Class adapter) { } @Override - public InputStream getErrorStream() { + public @Nullable InputStream getErrorStream() { if (errorStream != null) { return errorStream; } @@ -198,7 +198,7 @@ public int read(byte[] b, int off, int len) throws IOException { } @Override - public OutputStream getOutputStream() { + public @Nullable OutputStream getOutputStream() { if (outputStream != null) { return outputStream; } @@ -228,12 +228,12 @@ public void start() throws IOException { } @Override - public InputStream forwardCopyTo(InputStream input, OutputStream output) { + public @Nullable InputStream forwardCopyTo(@Nullable InputStream input, @Nullable OutputStream output) { return provider.forwardCopyTo(input, output); } @Override - public Object getInitializationOptions(@Nullable URI rootUri) { + public @Nullable Object getInitializationOptions(@Nullable URI rootUri) { return provider.getInitializationOptions(rootUri); } @@ -269,13 +269,14 @@ public void stop() { } private void logToConsole(String string) { + var consoleStream = this.consoleStream; if (consoleStream == null || consoleStream.isClosed()) { - consoleStream = findConsole().newMessageStream(); + consoleStream = this.consoleStream = findConsole().newMessageStream(); } consoleStream.println(string); } - private MessageConsoleStream consoleStream; + private @Nullable MessageConsoleStream consoleStream; private MessageConsole findConsole() { ConsolePlugin plugin = ConsolePlugin.getDefault(); IConsoleManager conMan = plugin.getConsoleManager(); @@ -290,6 +291,7 @@ private MessageConsole findConsole() { } private void logToFile(String string) { + final var logFile = this.logFile; if (logFile == null) { return; } @@ -309,7 +311,7 @@ private void logToFile(String string) { } } - private File getLogFile() { + private @Nullable File getLogFile() { if (logFile != null) { return logFile; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ServerMessageHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ServerMessageHandler.java index 1389d2760..1f2c8d560 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ServerMessageHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ServerMessageHandler.java @@ -13,6 +13,7 @@ import java.util.concurrent.CompletableFuture; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.notifications.AbstractNotificationPopup; import org.eclipse.lsp4e.ui.LSPImages; @@ -59,7 +60,7 @@ protected void createContentArea(Composite parent) { } @Override - public Image getPopupShellImage(int maximumHeight) { + public @Nullable Image getPopupShellImage(int maximumHeight) { return switch (messageParams.getType()) { case Error -> LSPImages.getSharedImage(ISharedImages.IMG_OBJS_ERROR_TSK); case Warning -> LSPImages.getSharedImage(ISharedImages.IMG_OBJS_WARN_TSK); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyContentProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyContentProvider.java index ca46aa843..8fc5bf4dc 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyContentProvider.java @@ -12,6 +12,8 @@ package org.eclipse.lsp4e.callhierarchy; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -33,8 +35,10 @@ import org.eclipse.lsp4j.CallHierarchyIncomingCallsParams; import org.eclipse.lsp4j.CallHierarchyItem; import org.eclipse.lsp4j.CallHierarchyPrepareParams; +import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.ui.PlatformUI; /** @@ -86,19 +90,14 @@ public void inputChanged(final Viewer viewer, final @Nullable Object oldInput, f rootItems = null; IDocument document = viewInput.getDocument(); - if (document != null) { - try { - initialise(document, viewInput.getOffset()); - } catch (BadLocationException e) { - handleRootError(); - } - } else { + try { + initialise(document, viewInput.getOffset()); + } catch (Exception e) { handleRootError(); } } else { handleRootError(); } - } private void initialise(final IDocument document, final int offset) throws BadLocationException { @@ -108,7 +107,7 @@ private void initialise(final IDocument document, final int offset) throws BadLo handleRootError(); return; } - CallHierarchyPrepareParams prepareParams = LSPEclipseUtils.toCallHierarchyPrepareParams(offset, document); + CallHierarchyPrepareParams prepareParams = toCallHierarchyPrepareParams(offset, document); executor.computeFirst((w, ls) -> ls.getTextDocumentService().prepareCallHierarchy(prepareParams) .thenApply(result -> new Pair<>(w, result))).thenAccept(o -> o.ifPresentOrElse(p -> { languageServerWrapper = p.first(); @@ -134,6 +133,12 @@ private void initialise(final IDocument document, final int offset) throws BadLo } return result; }); + } + + private CallHierarchyPrepareParams toCallHierarchyPrepareParams(int offset, final IDocument document) throws BadLocationException { + Position position = LSPEclipseUtils.toPosition(offset, document); + TextDocumentIdentifier documentIdentifier = castNonNull(LSPEclipseUtils.toTextDocumentIdentifier(document)); + return new CallHierarchyPrepareParams(documentIdentifier, position); } @@ -167,6 +172,8 @@ private void updateCallers(final CallHierarchyViewTreeNode callee) { final var incomingCallParams = new CallHierarchyIncomingCallsParams(callee.getCallContainer()); languageServerWrapper.execute(languageServer -> languageServer.getTextDocumentService() .callHierarchyIncomingCalls(incomingCallParams)).thenApply(incomingCalls -> { + if (incomingCalls == null) + return new ArrayList(0); List children = new ArrayList<>(incomingCalls.size()); for (CallHierarchyIncomingCall call : incomingCalls) { CallHierarchyItem callContainer = call.getFrom(); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionMarkerResolution.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionMarkerResolution.java index c7f1f3c64..18ae578cb 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionMarkerResolution.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionMarkerResolution.java @@ -35,6 +35,7 @@ import org.eclipse.lsp4j.ExecuteCommandOptions; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.MessageType; +import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.ShowMessageRequestParams; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.IMarkerResolution; @@ -93,7 +94,8 @@ public void run(IMarker marker) { } if (codeAction.getCommand() != null) { Command command = codeAction.getCommand(); - ExecuteCommandOptions provider = wrapper.getServerCapabilities().getExecuteCommandProvider(); + ServerCapabilities cap = wrapper.getServerCapabilities(); + ExecuteCommandOptions provider = cap == null ? null : cap.getExecuteCommandProvider(); if (provider != null && provider.getCommands().contains(command.getCommand())) { final LanguageServerDefinition serverDefinition = wrapper.serverDefinition; wrapper.execute(ls -> ls.getWorkspaceService() diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CommandMarkerResolution.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CommandMarkerResolution.java index 4810ee86e..d41179ae0 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CommandMarkerResolution.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CommandMarkerResolution.java @@ -24,6 +24,7 @@ import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.ExecuteCommandOptions; import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.IMarkerResolution; import org.eclipse.ui.views.markers.WorkbenchMarkerResolution; @@ -57,7 +58,8 @@ public void run(IMarker marker) { } LanguageServerWrapper wrapper = LanguageServiceAccessor.getLSWrapper(resource.getProject(), definition); - ExecuteCommandOptions provider = wrapper.getServerCapabilities().getExecuteCommandProvider(); + ServerCapabilities cap = wrapper.getServerCapabilities(); + ExecuteCommandOptions provider = cap == null ? null : cap.getExecuteCommandProvider(); if (provider != null && provider.getCommands().contains(command.getCommand())) { wrapper.execute(ls -> ls.getWorkspaceService() .executeCommand(new ExecuteCommandParams(command.getCommand(), command.getArguments()))); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java index 0e4d480f3..04989a3cf 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java @@ -13,7 +13,6 @@ import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; -import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URI; @@ -104,13 +103,13 @@ public IMarkerResolution[] getResolutions(IMarker marker) { checkMarkerResolution(marker); att = marker.getAttribute(LSP_REMEDIATION); } - } catch (IOException | CoreException | ExecutionException e) { - LanguageServerPlugin.logError(e); - return new IMarkerResolution[0]; } catch (InterruptedException e) { LanguageServerPlugin.logError(e); Thread.currentThread().interrupt(); return new IMarkerResolution[0]; + } catch (Exception e) { + LanguageServerPlugin.logError(e); + return new IMarkerResolution[0]; } if (att == COMPUTING) { return new IMarkerResolution[] { COMPUTING }; @@ -122,7 +121,7 @@ public IMarkerResolution[] getResolutions(IMarker marker) { .toArray(IMarkerResolution[]::new); } - private void checkMarkerResolution(IMarker marker) throws IOException, CoreException, InterruptedException, ExecutionException { + private void checkMarkerResolution(IMarker marker) throws CoreException, InterruptedException, ExecutionException { IResource res = marker.getResource(); if (res instanceof IFile file) { Object[] attributes = marker.getAttributes(new String[]{LSPDiagnosticsToMarkers.LANGUAGE_SERVER_ID, LSPDiagnosticsToMarkers.LSP_DIAGNOSTIC}); @@ -135,7 +134,7 @@ private void checkMarkerResolution(IMarker marker) throws IOException, CoreExcep final var context = new CodeActionContext(Collections.singletonList(diagnostic)); final var params = new CodeActionParams(); params.setContext(context); - params.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(res)); + params.setTextDocument(castNonNull(LSPEclipseUtils.toTextDocumentIdentifier(res))); params.setRange(diagnostic.getRange()); if (marker.exists()) { // otherwise the marker has been removed by now marker.setAttribute(LSP_REMEDIATION, COMPUTING); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionQuickAssistProcessor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionQuickAssistProcessor.java index 46ef8843c..ebd25093c 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionQuickAssistProcessor.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionQuickAssistProcessor.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.codeactions; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -192,7 +194,7 @@ private void refreshProposals(IQuickAssistInvocationContext invocationContext) { private static CodeActionParams prepareCodeActionParams(final IDocument doc, int offset, int length) { final var context = new CodeActionContext(Collections.emptyList()); final var params = new CodeActionParams(); - params.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(doc)); + params.setTextDocument(castNonNull(LSPEclipseUtils.toTextDocumentIdentifier(doc))); try { params.setRange(new Range(LSPEclipseUtils.toPosition(offset, doc), LSPEclipseUtils .toPosition(offset + (length > 0 ? length : 0), doc))); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionsMenu.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionsMenu.java index 4a46132ec..aafc037bd 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionsMenu.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionsMenu.java @@ -12,9 +12,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.codeactions; -import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.*; -import java.net.URI; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -84,11 +83,10 @@ public void fill(final Menu menu, int index) { item.setText(Messages.computing); final IDocument document = this.document; - final URI fileUri = LSPEclipseUtils.toUri(document); final var context = new CodeActionContext(Collections.emptyList()); final var params = new CodeActionParams(); - params.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(fileUri)); + params.setTextDocument(castNonNull(LSPEclipseUtils.toTextDocumentIdentifier(document))); params.setRange(this.range); params.setContext(context); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/LSPCodeMining.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/LSPCodeMining.java index fa3890fc9..bd2a0cb66 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/LSPCodeMining.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/LSPCodeMining.java @@ -23,6 +23,7 @@ import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.ExecuteCommandOptions; import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.swt.events.MouseEvent; public class LSPCodeMining extends LineHeaderCodeMining { @@ -50,15 +51,19 @@ public LSPCodeMining(CodeLens codeLens, IDocument document, LanguageServerWrappe @Override protected CompletableFuture doResolve(ITextViewer viewer, IProgressMonitor monitor) { - final Boolean resolveProvider = this.languageServerWrapper.getServerCapabilities().getCodeLensProvider().getResolveProvider(); + final ServerCapabilities serverCapabilities = languageServerWrapper.getServerCapabilities(); + if (serverCapabilities == null) { + return CompletableFuture.completedFuture(null); + } + final Boolean resolveProvider = serverCapabilities.getCodeLensProvider().getResolveProvider(); if (resolveProvider == null || !resolveProvider) { return CompletableFuture.completedFuture(null); } - return this.languageServerWrapper.execute(languageServer -> languageServer.getTextDocumentService().resolveCodeLens(this.codeLens)) + return languageServerWrapper.execute(languageServer -> languageServer.getTextDocumentService().resolveCodeLens(this.codeLens)) .thenAccept(resolvedCodeLens -> { - codeLens = resolvedCodeLens; if (resolvedCodeLens != null) { + codeLens = resolvedCodeLens; setLabel(getCodeLensString(resolvedCodeLens)); } }); @@ -75,7 +80,8 @@ protected CompletableFuture doResolve(ITextViewer viewer, IProgressMonitor } private void performAction(MouseEvent mouseEvent) { - ExecuteCommandOptions provider = languageServerWrapper.getServerCapabilities().getExecuteCommandProvider(); + final ServerCapabilities serverCapabilities = languageServerWrapper.getServerCapabilities(); + ExecuteCommandOptions provider = serverCapabilities == null ? null : serverCapabilities.getExecuteCommandProvider(); Command command = codeLens.getCommand(); if (provider != null && provider.getCommands().contains(command.getCommand())) { languageServerWrapper.execute(ls -> ls.getWorkspaceService() diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/ColorInformationMining.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/ColorInformationMining.java index 408b83a4e..22a67f4a3 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/ColorInformationMining.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/ColorInformationMining.java @@ -83,7 +83,7 @@ public void accept(MouseEvent event) { LSPEclipseUtils.toColor(rgb), colorInformation.getRange()); this.languageServerWrapper.execute(ls -> ls.getTextDocumentService().colorPresentation(params)) .thenAcceptAsync(presentations -> { - if (presentations.isEmpty()) { + if (presentations == null || presentations.isEmpty()) { return; } // As ColorDialog cannot be customized (to choose the color presentation (rgb, diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSCompletionProposal.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSCompletionProposal.java index 801261440..87a097d15 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSCompletionProposal.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSCompletionProposal.java @@ -566,7 +566,8 @@ protected void apply(IDocument document, char trigger, int stateMask, int offset if (item.getCommand() != null) { Command command = item.getCommand(); - ExecuteCommandOptions provider = languageServerWrapper.getServerCapabilities().getExecuteCommandProvider(); + ServerCapabilities serverCapabilities = languageServerWrapper.getServerCapabilities(); + ExecuteCommandOptions provider = serverCapabilities == null ? null : serverCapabilities.getExecuteCommandProvider(); if (provider != null && provider.getCommands().contains(command.getCommand())) { languageServerWrapper.execute(ls -> ls.getWorkspaceService() .executeCommand(new ExecuteCommandParams(command.getCommand(), command.getArguments()))); @@ -617,16 +618,23 @@ private int computeNewOffset(@Nullable List additionalTextEdits, int i private String getVariableValue(String variableName) { return switch (variableName) { case TM_FILENAME_BASE -> { - IPath pathBase = LSPEclipseUtils.toPath(document).removeFileExtension(); - String fileName = pathBase.lastSegment(); + IPath path = LSPEclipseUtils.toPath(document); + String fileName = path == null ? null : path.removeFileExtension().lastSegment(); yield fileName != null ? fileName : ""; //$NON-NLS-1$ } case TM_FILENAME -> { - String fileName = LSPEclipseUtils.toPath(document).lastSegment(); + IPath path = LSPEclipseUtils.toPath(document); + String fileName = path == null ? null : path.lastSegment(); yield fileName != null ? fileName : ""; //$NON-NLS-1$ } - case TM_FILEPATH -> getAbsoluteLocation(LSPEclipseUtils.toPath(document)); - case TM_DIRECTORY -> getAbsoluteLocation(LSPEclipseUtils.toPath(document).removeLastSegments(1)); + case TM_FILEPATH -> { + IPath path = LSPEclipseUtils.toPath(document); + yield path == null ? "" : getAbsoluteLocation(path); //$NON-NLS-1$ + } + case TM_DIRECTORY -> { + IPath path = LSPEclipseUtils.toPath(document); + yield path == null ? "" : getAbsoluteLocation(path.removeLastSegments(1)); //$NON-NLS-1$ + } case TM_LINE_INDEX -> { try { yield Integer.toString(getTextEditRange().getStart().getLine()); // TODO probably wrong, should use viewer state diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSContentAssistProcessor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSContentAssistProcessor.java index 662b10407..558e9ba15 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSContentAssistProcessor.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/LSContentAssistProcessor.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.completion; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.net.URI; import java.util.ArrayList; import java.util.Arrays; @@ -79,7 +81,10 @@ public class LSContentAssistProcessor implements IContentAssistProcessor { private char[] contextTriggerChars = new char[0]; private final boolean incompleteAsCompletionItem; - /** The cancellation support used to cancel previous LSP requests 'textDocument/completion' when completion is retriggered */ + /** + * The cancellation support used to cancel previous LSP requests + * 'textDocument/completion' when completion is retriggered + */ private CancellationSupport cancellationSupport; public LSContentAssistProcessor() { @@ -124,15 +129,16 @@ public LSContentAssistProcessor(boolean errorAsCompletionItem, boolean incomplet List proposals = Collections.synchronizedList(new ArrayList<>()); AtomicBoolean anyIncomplete = new AtomicBoolean(false); try { - // Cancel the previous LSP requests 'textDocument/completions' and completionLanguageServersFuture + // Cancel the previous LSP requests 'textDocument/completions' and + // completionLanguageServersFuture this.cancellationSupport.cancel(); // Initialize a new cancel support to register: // - LSP requests 'textDocument/completions' // - completionLanguageServersFuture CancellationSupport cancellationSupport = new CancellationSupport(); - final var completionLanguageServersFuture = this.completionLanguageServersFuture = LanguageServers.forDocument(document) - .withFilter(capabilities -> capabilities.getCompletionProvider() != null) // + final var completionLanguageServersFuture = this.completionLanguageServersFuture = LanguageServers + .forDocument(document).withFilter(capabilities -> capabilities.getCompletionProvider() != null) // .collectAll((w, ls) -> cancellationSupport.execute(ls.getTextDocumentService().completion(param)) // .thenAccept(completion -> { boolean isIncomplete = completion != null && completion.isRight() @@ -232,8 +238,9 @@ private void initiateLanguageServers(IDocument document) { this.contextTriggerChars = new char[0]; this.completionLanguageServersFuture = LanguageServers.forDocument(document) - .withFilter(capabilities -> capabilities.getCompletionProvider() != null).collectAll((w, ls) -> { - CompletionOptions provider = w.getServerCapabilities().getCompletionProvider(); + .withFilter(capabilities -> capabilities.getCompletionProvider() != null) // + .collectAll((w, ls) -> { + CompletionOptions provider = castNonNull(w.getServerCapabilities()).getCompletionProvider(); synchronized (completionTriggerCharsSemaphore) { completionTriggerChars = mergeTriggers(completionTriggerChars, provider.getTriggerCharacters()); @@ -241,8 +248,9 @@ private void initiateLanguageServers(IDocument document) { return CompletableFuture.completedFuture(null); }); this.contextInformationLanguageServersFuture = LanguageServers.forDocument(document) - .withFilter(capabilities -> capabilities.getSignatureHelpProvider() != null).collectAll((w, ls) -> { - SignatureHelpOptions provider = w.getServerCapabilities().getSignatureHelpProvider(); + .withFilter(capabilities -> capabilities.getSignatureHelpProvider() != null) // + .collectAll((w, ls) -> { + SignatureHelpOptions provider = castNonNull(w.getServerCapabilities()).getSignatureHelpProvider(); synchronized (contextTriggerCharsSemaphore) { contextTriggerChars = mergeTriggers(contextTriggerChars, provider.getTriggerCharacters()); } @@ -263,8 +271,8 @@ private void initiateLanguageServers() { } private static List toProposals(IDocument document, int offset, - @Nullable Either, CompletionList> completionList, LanguageServerWrapper languageServerWrapper, - CancelChecker cancelChecker, boolean isIncomplete) { + @Nullable Either, CompletionList> completionList, + LanguageServerWrapper languageServerWrapper, CancelChecker cancelChecker, boolean isIncomplete) { if (completionList == null) { return Collections.emptyList(); } @@ -357,7 +365,8 @@ private void getFuture(@Nullable CompletableFuture> future) { } } - private static char[] mergeTriggers(char @Nullable [] initialArray, @Nullable Collection additionalTriggers) { + private static char[] mergeTriggers(char @Nullable [] initialArray, + @Nullable Collection additionalTriggers) { if (initialArray == null) { initialArray = new char[0]; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java index 140371370..2f12e9728 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java @@ -119,9 +119,12 @@ private void updateEditorAnnotations(ISourceViewer sourceViewer, PublishDiagnost final var toAdd = new HashMap(diagnostics.getDiagnostics().size(), 1.f); diagnostics.getDiagnostics().forEach(diagnostic -> { try { - int startOffset = LSPEclipseUtils.toOffset(diagnostic.getRange().getStart(), sourceViewer.getDocument()); - int endOffset = LSPEclipseUtils.toOffset(diagnostic.getRange().getEnd(), sourceViewer.getDocument()); - toAdd.put(new DiagnosticAnnotation(diagnostic), new Position(startOffset, endOffset - startOffset)); + final var doc = sourceViewer.getDocument(); + if (doc != null) { + int startOffset = LSPEclipseUtils.toOffset(diagnostic.getRange().getStart(), doc); + int endOffset = LSPEclipseUtils.toOffset(diagnostic.getRange().getEnd(), doc); + toAdd.put(new DiagnosticAnnotation(diagnostic), new Position(startOffset, endOffset - startOffset)); + } } catch (BadLocationException ex) { LanguageServerPlugin.logError(ex); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/DocumentLinkDetector.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/DocumentLinkDetector.java index e44ad4ef4..a3c677b20 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/DocumentLinkDetector.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/DocumentLinkDetector.java @@ -31,7 +31,6 @@ import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.LanguageServers; -import org.eclipse.lsp4e.ui.UI; import org.eclipse.lsp4j.DocumentLink; import org.eclipse.lsp4j.DocumentLinkParams; @@ -64,7 +63,7 @@ public String getHyperlinkText() { @Override public void open() { - LSPEclipseUtils.open(uri, UI.getActivePage(), null, true); + LSPEclipseUtils.open(uri, null, true); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/LSPDocumentLinkPresentationReconcilingStrategy.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/LSPDocumentLinkPresentationReconcilingStrategy.java index 48af0b91a..2bc8359e3 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/LSPDocumentLinkPresentationReconcilingStrategy.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/LSPDocumentLinkPresentationReconcilingStrategy.java @@ -89,6 +89,7 @@ private void underline() { private void underline(@Nullable List links) { final var viewer = this.viewer; + final var document = this.document; if (document == null || links == null || viewer == null) { return; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatter.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatter.java index 9dc3a3445..eaeb43ec2 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatter.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatter.java @@ -57,7 +57,7 @@ public CompletableFuture> requestFormatting(IDocument d long modificationStamp = DocumentUtil.getDocumentModificationStamp(document); return executor.computeFirst((w, ls) -> { final ServerCapabilities capabilities = w.getServerCapabilities(); - if (isDocumentRangeFormattingSupported(capabilities) + if (capabilities != null && isDocumentRangeFormattingSupported(capabilities) && !(isDocumentFormattingSupported(capabilities) && textSelection.getLength() == 0)) { return ls.getTextDocumentService().rangeFormatting(rangeParams).thenApply(edits -> new VersionedEdits(modificationStamp, edits, document)); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/LSPLineContentCodeMining.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/LSPLineContentCodeMining.java index 4291d589f..11a37dd1f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/LSPLineContentCodeMining.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/LSPLineContentCodeMining.java @@ -54,8 +54,8 @@ public class LSPLineContentCodeMining extends LineContentCodeMining { private @Nullable Point location; private FontData @Nullable [] fontData; - public LSPLineContentCodeMining(InlayHint inlayHint, IDocument document, LanguageServerWrapper languageServerWrapper, - InlayHintProvider provider) throws BadLocationException { + public LSPLineContentCodeMining(InlayHint inlayHint, IDocument document, + LanguageServerWrapper languageServerWrapper, InlayHintProvider provider) throws BadLocationException { super(toPosition(inlayHint.getPosition(), document), provider); this.inlayHint = inlayHint; this.wrapper = languageServerWrapper; @@ -82,12 +82,12 @@ public void setLabel(final @Nullable String label) { } @Override - protected CompletableFuture doResolve(ITextViewer viewer, IProgressMonitor monitor) { + protected CompletableFuture<@Nullable Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { if (wrapper.isActive() && canResolveInlayHint(wrapper.getServerCapabilities())) { - return wrapper.execute(ls -> ls.getTextDocumentService().resolveInlayHint(this.inlayHint) - .thenAcceptAsync(resolvedInlayHint -> { - inlayHint = resolvedInlayHint; + return wrapper.execute( + ls -> ls.getTextDocumentService().resolveInlayHint(inlayHint).thenAcceptAsync(resolvedInlayHint -> { if (resolvedInlayHint != null) { + inlayHint = resolvedInlayHint; setLabel(getInlayHintString(resolvedInlayHint)); } })); @@ -95,7 +95,9 @@ protected CompletableFuture doResolve(ITextViewer viewer, IProgressMonitor return CompletableFuture.completedFuture(null); } - private static boolean canResolveInlayHint(ServerCapabilities capabilities) { + private static boolean canResolveInlayHint(@Nullable ServerCapabilities capabilities) { + if (capabilities == null) + return false; Either inlayProvider = capabilities.getInlayHintProvider(); if (inlayProvider != null && inlayProvider.isRight()) { InlayHintRegistrationOptions options = inlayProvider.getRight(); @@ -127,24 +129,28 @@ private static org.eclipse.jface.text.Position toPosition(Position position, IDo private @Nullable Consumer labelPartAction(List labelParts) { String title = getLabel(); - if (title != null && !title.isEmpty() && labelParts.stream().map(InlayHintLabelPart::getCommand).anyMatch(Objects::nonNull)) { - return me -> { - findLabelPart(me, labelParts).map(InlayHintLabelPart::getCommand).filter(Objects::nonNull).ifPresent(command -> { - ExecuteCommandOptions provider = wrapper.getServerCapabilities().getExecuteCommandProvider(); - String commandId = command.getCommand(); - if (provider != null && provider.getCommands().contains(commandId)) { - LanguageServers.forDocument(document).computeAll((w, ls) -> { - if (w == wrapper) { - return ls.getWorkspaceService() - .executeCommand(new ExecuteCommandParams(commandId, command.getArguments())); - } - return CompletableFuture.completedFuture(null); - }); - } else { - CommandExecutor.executeCommandClientSide(command, document); - } - }); - }; + if (title != null && !title.isEmpty() + && labelParts.stream().map(InlayHintLabelPart::getCommand).anyMatch(Objects::nonNull)) { + return me -> findLabelPart(me, labelParts) // + .map(InlayHintLabelPart::getCommand) // + .filter(Objects::nonNull) // + .ifPresent(command -> { + ServerCapabilities serverCapabilities = wrapper.getServerCapabilities(); + ExecuteCommandOptions provider = serverCapabilities == null ? null + : serverCapabilities.getExecuteCommandProvider(); + String commandId = command.getCommand(); + if (provider != null && provider.getCommands().contains(commandId)) { + LanguageServers.forDocument(document).computeAll((w, ls) -> { + if (w == wrapper) { + return ls.getWorkspaceService().executeCommand( + new ExecuteCommandParams(commandId, command.getArguments())); + } + return CompletableFuture.completedFuture(null); + }); + } else { + CommandExecutor.executeCommandClientSide(command, document); + } + }); } return null; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchQuery.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchQuery.java index 725d054d2..6f296e1e5 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchQuery.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchQuery.java @@ -14,6 +14,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.references; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -79,7 +81,7 @@ public IStatus run(@Nullable IProgressMonitor monitor) throws OperationCanceledE // Execute LSP "references" service final var params = new ReferenceParams(); params.setContext(new ReferenceContext(false)); - params.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(document)); + params.setTextDocument(castNonNull(LSPEclipseUtils.toTextDocumentIdentifier(document))); params.setPosition(LSPEclipseUtils.toPosition(offset, document)); List>> requests = LanguageServers.forDocument(document).withCapability(ServerCapabilities::getReferencesProvider) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/URIMatch.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/URIMatch.java index ff93e74c0..3924b91bb 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/URIMatch.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/URIMatch.java @@ -8,6 +8,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.references; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.net.URI; import java.net.URISyntaxException; @@ -21,7 +23,7 @@ public class URIMatch extends Match { public static URIMatch create(final Location location) throws BadLocationException, URISyntaxException { final URI uri = new URI(location.getUri()); - final IDocument doc = LSPEclipseUtils.getDocument(uri); + final IDocument doc = castNonNull(LSPEclipseUtils.getDocument(uri)); final int offset = LSPEclipseUtils.toOffset(location.getRange().getStart(), doc); final int length = LSPEclipseUtils.toOffset(location.getRange().getEnd(), doc) - LSPEclipseUtils.toOffset(location.getRange().getStart(), doc); return new URIMatch(location, uri, offset, length); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameProcessor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameProcessor.java index 478619448..3ad861ee5 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameProcessor.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameProcessor.java @@ -13,6 +13,8 @@ */ package org.eclipse.lsp4e.operations.rename; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.List; import java.util.Objects; import java.util.Optional; @@ -101,9 +103,8 @@ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) final var status = new RefactoringStatus(); try { - final var identifier = LSPEclipseUtils.toTextDocumentIdentifier(document); final var params = new PrepareRenameParams(); - params.setTextDocument(identifier); + params.setTextDocument(castNonNull(LSPEclipseUtils.toTextDocumentIdentifier(document))); params.setPosition(LSPEclipseUtils.toPosition(offset, document)); @SuppressWarnings("null") @@ -178,9 +179,7 @@ public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditio try { final var params = new RenameParams(); params.setPosition(LSPEclipseUtils.toPosition(offset, document)); - final var identifier = LSPEclipseUtils.toTextDocumentIdentifier(document); - identifier.setUri(LSPEclipseUtils.toUri(document).toString()); - params.setTextDocument(identifier); + params.setTextDocument(castNonNull(LSPEclipseUtils.toTextDocumentIdentifier(document))); params.setNewName(newName); // TODO: how to manage ltk with CompletableFuture? Is 1000 ms is enough? @@ -218,6 +217,7 @@ private String getErrorMessage(Throwable e) { @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { + final var rename = this.rename; if (rename == null) { throw new CoreException( new Status(IStatus.ERROR, LanguageServerPlugin.PLUGIN_ID, Messages.rename_processor_required)); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/LSPSelectionRangeAbstractHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/LSPSelectionRangeAbstractHandler.java index df098975c..938bf046a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/LSPSelectionRangeAbstractHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/LSPSelectionRangeAbstractHandler.java @@ -129,14 +129,16 @@ public void updateSelection(ISelectionProvider provider, IDocument document, Dir SelectionRange selectionRange = getSelectionRange(direction); if (selectionRange != null) { ISelection selection = LSPEclipseUtils.toSelection(selectionRange.getRange(), document); - styledText.getDisplay().execute(() -> { - try { - updating = true; - provider.setSelection(selection); - } finally { - updating = false; - } - }); + if (selection != null) { + styledText.getDisplay().execute(() -> { + try { + updating = true; + provider.setSelection(selection); + } finally { + updating = false; + } + }); + } } } @@ -209,6 +211,9 @@ private CompletableFuture>> collectSelectionRanges try { Position position = LSPEclipseUtils.toPosition(offset, document); TextDocumentIdentifier identifier = LSPEclipseUtils.toTextDocumentIdentifier(document); + if (identifier == null) { + return CompletableFuture.completedFuture(null); + } List positions = Collections.singletonList(position); SelectionRangeParams params = new SelectionRangeParams(identifier, positions); return LanguageServers.forDocument(document).withCapability(ServerCapabilities::getSelectionRangeProvider) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticHighlightReconcilerStrategy.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticHighlightReconcilerStrategy.java index 78714a818..35fe8dcde 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticHighlightReconcilerStrategy.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticHighlightReconcilerStrategy.java @@ -13,9 +13,7 @@ import java.net.URI; import java.util.List; import java.util.Optional; -import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.function.Function; import org.eclipse.core.runtime.IProgressMonitor; @@ -47,7 +45,6 @@ import org.eclipse.lsp4j.SemanticTokensParams; import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions; import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; @@ -169,14 +166,11 @@ private Function offsetMapper() { }; } - private @Nullable SemanticTokensParams getSemanticTokensParams() { - URI uri = LSPEclipseUtils.toUri(document); - if (uri != null) { - final var semanticTokensParams = new SemanticTokensParams(); - semanticTokensParams.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(uri)); - return semanticTokensParams; - } - return null; + private SemanticTokensParams getSemanticTokensParams() { + URI uri = castNonNull(LSPEclipseUtils.toUri(document)); + final var semanticTokensParams = new SemanticTokensParams(); + semanticTokensParams.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(uri)); + return semanticTokensParams; } private void saveStyle(final Pair<@Nullable SemanticTokens, @Nullable SemanticTokensLegend> pair) { @@ -280,7 +274,7 @@ private void fullReconcile() { } catch (InterruptedException e) { LanguageServerPlugin.logError(e); Thread.currentThread().interrupt(); - } catch (ResponseErrorException | ExecutionException | CancellationException e) { + } catch (Exception e) { if (!CancellationUtil.isRequestCancelledException(e)) { // do not report error if the server has cancelled the request LanguageServerPlugin.logError(e); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/WorkspaceSymbolQuickAccessElement.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/WorkspaceSymbolQuickAccessElement.java index e92740baf..5a7a52a36 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/WorkspaceSymbolQuickAccessElement.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/WorkspaceSymbolQuickAccessElement.java @@ -17,7 +17,6 @@ import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.outline.SymbolsLabelProvider; -import org.eclipse.lsp4e.ui.UI; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.WorkspaceSymbol; @@ -63,7 +62,7 @@ public String getId() { public void execute() { String locationUri = symbol.getLocation().map(Location::getUri, WorkspaceSymbolLocation::getUri); @Nullable Range range = symbol.getLocation().map(Location::getRange, s -> null); - LSPEclipseUtils.open(locationUri, UI.getActivePage(), range); + LSPEclipseUtils.open(locationUri, range); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyContentProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyContentProvider.java index 049961111..c3209420f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyContentProvider.java @@ -47,8 +47,12 @@ public TypeHierarchyContentProvider(LanguageServerDefinition lsDefinition, IDocu public Object[] getElements(@Nullable Object inputElement) { if (inputElement instanceof ITextSelection textSelection) { try { + final var identifier = LSPEclipseUtils.toTextDocumentIdentifier(document); + if (identifier == null) { + return new Object[0]; + } Position position = LSPEclipseUtils.toPosition(textSelection.getOffset(), document); - TypeHierarchyPrepareParams prepare = new TypeHierarchyPrepareParams(LSPEclipseUtils.toTextDocumentIdentifier(document), position); + TypeHierarchyPrepareParams prepare = new TypeHierarchyPrepareParams(identifier, position); return LanguageServers.forDocument(document).withPreferredServer(lsDefinition) .computeFirst((wrapper, ls) -> ls.getTextDocumentService().prepareTypeHierarchy(prepare).thenApply(items -> new SimpleEntry<>(wrapper, items))) .thenApply(entry -> { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyHandler.java index 91d9313ef..e051cfff2 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyHandler.java @@ -25,10 +25,12 @@ public class TypeHierarchyHandler extends LSPDocumentAbstractHandler { @Override protected void execute(ExecutionEvent event, ITextEditor editor) { IDocument document = LSPEclipseUtils.getDocument(editor); - LanguageServers.forDocument(document) - .withCapability(ServerCapabilities::getTypeHierarchyProvider) - .computeFirst((wrapper, ls) -> CompletableFuture.completedFuture(wrapper.serverDefinition)) - .thenAcceptAsync(definition -> definition.ifPresent(def -> new TypeHierarchyDialog(editor.getSite().getShell(), (ITextSelection)editor.getSelectionProvider().getSelection(), document, def).open()), editor.getSite().getShell().getDisplay()); + if (document != null) { + LanguageServers.forDocument(document) + .withCapability(ServerCapabilities::getTypeHierarchyProvider) + .computeFirst((wrapper, ls) -> CompletableFuture.completedFuture(wrapper.serverDefinition)) + .thenAcceptAsync(definition -> definition.ifPresent(def -> new TypeHierarchyDialog(editor.getSite().getShell(), (ITextSelection)editor.getSelectionProvider().getSelection(), document, def).open()), editor.getSite().getShell().getDisplay()); + } } @Override diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyView.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyView.java index 2a2bd8018..62cfb002f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyView.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyView.java @@ -380,33 +380,37 @@ private synchronized void refreshSymbols(@Nullable SymbolsContainer symbolsConta } final IDocument document = symbolsContainer.getDocument(); try { - if (document != null) { - CompletableFuture>> symbols; - final var params = new DocumentSymbolParams( - LSPEclipseUtils.toTextDocumentIdentifier(document)); - CompletableFuture> languageServer = LanguageServers - .forDocument(document) - .withCapability(ServerCapabilities::getDocumentSymbolProvider) - .computeFirst((w, ls) -> CompletableFuture.completedFuture(w)); - try { - symbols = languageServer.get(500, TimeUnit.MILLISECONDS).filter(Objects::nonNull) - .filter(LanguageServerWrapper::isActive) - .map(s -> s.execute(ls -> ls.getTextDocumentService().documentSymbol(params))) - .orElse(CompletableFuture.completedFuture(null)); - } catch (TimeoutException | ExecutionException | InterruptedException e) { - LanguageServerPlugin.logError(e); - symbols = CompletableFuture.completedFuture(null); - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - } - symbols.thenAcceptAsync(response -> { - symbolsContainer.symbolsModel.update(response); - symbolsContainer.isDirty = false; - }).join(); - } else { + if (document == null) { + symbolsContainer.symbolsModel.update(null); + return; + } + final var identifier = LSPEclipseUtils.toTextDocumentIdentifier(document); + if(identifier == null) { symbolsContainer.symbolsModel.update(null); + return; + } + CompletableFuture>> symbols; + final var params = new DocumentSymbolParams(identifier); + CompletableFuture> languageServer = LanguageServers + .forDocument(document) + .withCapability(ServerCapabilities::getDocumentSymbolProvider) + .computeFirst((w, ls) -> CompletableFuture.completedFuture(w)); + try { + symbols = languageServer.get(500, TimeUnit.MILLISECONDS).filter(Objects::nonNull) + .filter(LanguageServerWrapper::isActive) + .map(s -> s.execute(ls -> ls.getTextDocumentService().documentSymbol(params))) + .orElse(CompletableFuture.completedFuture(null)); + } catch (TimeoutException | ExecutionException | InterruptedException e) { + LanguageServerPlugin.logError(e); + symbols = CompletableFuture.completedFuture(null); + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } } + symbols.thenAcceptAsync(response -> { + symbolsContainer.symbolsModel.update(response); + symbolsContainer.isDirty = false; + }).join(); } catch (Exception e) { LanguageServerPlugin.logError(e); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyViewContentProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyViewContentProvider.java index 1333fff36..b5608d05f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyViewContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyViewContentProvider.java @@ -116,6 +116,9 @@ private void initialise(final IDocument document, final int offset, TreeViewer v return; } TypeHierarchyPrepareParams prepareParams = toTypeHierarchyPrepareParams(offset, document); + if (prepareParams == null) { + return; + } executor.computeFirst((w, ls) -> ls.getTextDocumentService().prepareTypeHierarchy(prepareParams) .thenApply(result -> new Pair<>(w, result))).thenAccept(o -> o.ifPresentOrElse(p -> { languageServerWrapper = p.first(); @@ -140,12 +143,13 @@ private void initialise(final IDocument document, final int offset, TreeViewer v } return result; }); - } - private static TypeHierarchyPrepareParams toTypeHierarchyPrepareParams(int offset, final IDocument document) throws BadLocationException { + private static @Nullable TypeHierarchyPrepareParams toTypeHierarchyPrepareParams(int offset, final IDocument document) throws BadLocationException { Position position = LSPEclipseUtils.toPosition(offset, document); TextDocumentIdentifier documentIdentifier = LSPEclipseUtils.toTextDocumentIdentifier(document); + if(documentIdentifier == null) + return null; return new TypeHierarchyPrepareParams(documentIdentifier, position); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java index ce24f2405..68a1ec119 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java @@ -221,7 +221,7 @@ public void resourceChanged(IResourceChangeEvent event) { private OutlineViewerInput outlineViewerInput = lateNonNull(); private final SymbolsModel symbolsModel = new SymbolsModel(); - private volatile @Nullable CompletableFuture>> symbols; + private volatile @Nullable CompletableFuture<@Nullable List>> symbols; private final boolean refreshOnResourceChanged; private boolean isQuickOutline; private @Nullable IOutlineUpdater outlineUpdater; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/package-info.java new file mode 100644 index 000000000..8dca9d2ee --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault;