From 3ed68da2e2c21ee12364495b5576d609065d26fb Mon Sep 17 00:00:00 2001 From: Sebastian Thomschke Date: Tue, 13 Aug 2024 13:09:21 +0200 Subject: [PATCH] Enable NonNullByDefault for lspe4.plugin (#1009) --- .gitignore | 25 +- .project | 6 + org.eclipse.lsp4e.debug/.gitignore | 1 - org.eclipse.lsp4e.test/.gitignore | 2 - org.eclipse.lsp4e.tests.mock/.gitignore | 1 - .../META-INF/MANIFEST.MF | 2 +- org.eclipse.lsp4e/.classpath | 18 +- org.eclipse.lsp4e/.gitignore | 3 - org.eclipse.lsp4e/.project | 4 +- .../.settings/org.eclipse.jdt.core.prefs | 18 +- org.eclipse.lsp4e/META-INF/MANIFEST.MF | 1 - org.eclipse.lsp4e/build.properties | 3 + ...umentToLanguageServerSetupParticipant.java | 3 - .../ContentTypeToLSPLaunchConfigEntry.java | 8 +- ...ContentTypeToLanguageServerDefinition.java | 12 +- .../lsp4e/DocumentContentSynchronizer.java | 88 +++---- .../HasLanguageServerPropertyTester.java | 5 +- .../lsp4e/IMarkerAttributeComputer.java | 5 +- .../org/eclipse/lsp4e/LSPEclipseUtils.java | 170 +++++++------- .../org/eclipse/lsp4e/LanguageClientImpl.java | 20 +- .../eclipse/lsp4e/LanguageServerPlugin.java | 13 +- .../eclipse/lsp4e/LanguageServerWrapper.java | 152 ++++++------ .../org/eclipse/lsp4e/LanguageServers.java | 112 +++++---- .../lsp4e/LanguageServersRegistry.java | 53 ++--- .../lsp4e/LanguageServiceAccessor.java | 222 +++++++++--------- .../LaunchConfigurationStreamProvider.java | 56 +++-- .../LoggingStreamConnectionProviderProxy.java | 38 +-- .../eclipse/lsp4e/ServerMessageHandler.java | 5 +- .../CallHierarchyCommandHandler.java | 3 +- .../CallHierarchyContentProvider.java | 55 +++-- .../CallHierarchyLabelProvider.java | 7 +- .../callhierarchy/CallHierarchyView.java | 4 +- .../CallHierarchyViewTreeNode.java | 13 +- .../lsp4e/callhierarchy/package-info.java | 6 + .../lsp4e/command/CommandExecutor.java | 61 ++--- .../lsp4e/command/LSPCommandHandler.java | 6 +- .../command/internal/CommandConverter.java | 4 +- .../lsp4e/command/internal/PathConverter.java | 7 +- .../lsp4e/command/internal/package-info.java | 6 + .../eclipse/lsp4e/command/package-info.java | 6 + .../lsp4e/enablement/EnablementTester.java | 7 +- .../lsp4e/enablement/package-info.java | 6 + .../format/DefaultFormatRegionsProvider.java | 3 +- .../lsp4e/format/IFormatRegionsProvider.java | 9 +- .../eclipse/lsp4e/format/package-info.java | 6 + .../lsp4e/internal/CancellationUtil.java | 3 +- .../internal/LSPDocumentAbstractHandler.java | 18 +- .../lsp4e/internal/NullSafetyHelper.java | 79 +++++++ .../org/eclipse/lsp4e/internal/StyleUtil.java | 3 +- .../lsp4e/internal/SupportedFeatures.java | 5 +- .../eclipse/lsp4e/internal/package-info.java | 6 + .../CodeActionCompletionProposal.java | 22 +- .../CodeActionMarkerResolution.java | 10 +- .../codeactions/CommandMarkerResolution.java | 24 +- .../LSPCodeActionMarkerResolution.java | 26 +- .../LSPCodeActionQuickAssistProcessor.java | 38 +-- .../codeactions/LSPCodeActionsMenu.java | 32 +-- .../operations/codeactions/package-info.java | 6 + .../operations/codelens/CodeLensProvider.java | 22 +- .../operations/codelens/LSPCodeMining.java | 24 +- .../operations/codelens/package-info.java | 6 + .../color/ColorInformationMining.java | 5 +- .../color/DocumentColorProvider.java | 10 +- .../lsp4e/operations/color/package-info.java | 6 + .../completion/CompletionProposalTools.java | 2 +- .../completion/CompletionSnippetParser.java | 1 - .../completion/LSCompletionProposal.java | 90 ++++--- .../completion/LSContentAssistProcessor.java | 146 +++++++----- .../operations/completion/package-info.java | 6 + .../declaration/LSBasedHyperlink.java | 30 +-- .../OpenDeclarationHyperlinkDetector.java | 6 +- .../operations/declaration/package-info.java | 6 + .../diagnostics/DiagnosticAnnotation.java | 5 +- .../diagnostics/LSPDiagnosticsToMarkers.java | 39 ++- .../operations/diagnostics/package-info.java | 6 + .../documentLink/DocumentLinkDetector.java | 8 +- ...ntLinkPresentationReconcilingStrategy.java | 16 +- .../operations/documentLink/package-info.java | 6 + .../LSPFoldingReconcilingStrategy.java | 52 ++-- .../operations/folding/package-info.java | 6 + .../format/LSPFormatFilesHandler.java | 16 +- .../operations/format/LSPFormatHandler.java | 3 +- .../lsp4e/operations/format/LSPFormatter.java | 5 +- .../lsp4e/operations/format/package-info.java | 6 + .../HighlightReconcilingStrategy.java | 69 +++--- .../operations/highlight/package-info.java | 6 + .../FocusableBrowserInformationControl.java | 31 +-- .../lsp4e/operations/hover/LSPTextHover.java | 66 +++--- .../lsp4e/operations/hover/package-info.java | 6 + .../inlayhint/InlayHintProvider.java | 10 +- .../inlayhint/LSPLineContentCodeMining.java | 78 +++--- .../operations/inlayhint/package-info.java | 6 + .../linkedediting/LSPLinkedEditingBase.java | 8 +- .../LSPLinkedEditingReconcilingStrategy.java | 62 ++--- .../linkedediting/package-info.java | 6 + .../FileAndURIMatchContentProvider.java | 22 +- .../FileAndURIMatchLabelProvider.java | 7 +- .../references/LSFindReferences.java | 3 +- .../operations/references/LSSearchQuery.java | 27 ++- .../operations/references/LSSearchResult.java | 7 +- .../references/LSSearchResultPage.java | 12 +- .../lsp4e/operations/references/URIMatch.java | 4 +- .../operations/references/package-info.java | 6 + .../operations/rename/LSPRenameHandler.java | 3 +- .../operations/rename/LSPRenameProcessor.java | 86 ++++--- .../rename/LSPRenameRefactoringWizard.java | 11 +- .../lsp4e/operations/rename/package-info.java | 6 + .../LSPSelectionRangeAbstractHandler.java | 40 ++-- .../selectionRange/package-info.java | 6 + .../SemanticHighlightReconcilerStrategy.java | 104 ++++---- .../SemanticTokensDataStreamProcessor.java | 18 +- .../semanticTokens/StyleRangeHolder.java | 6 +- .../semanticTokens/TokenTypeMapper.java | 10 +- .../VersionedSemanticTokens.java | 9 +- .../semanticTokens/package-info.java | 6 + .../symbols/LSPSymbolInFileDialog.java | 16 +- .../symbols/LSPSymbolInFileHandler.java | 3 +- .../symbols/LSPSymbolInWorkspaceDialog.java | 102 ++++---- .../symbols/LSPSymbolInWorkspaceHandler.java | 5 +- .../WorkspaceSymbolQuickAccessElement.java | 3 +- .../WorkspaceSymbolsQuickAccessProvider.java | 18 +- .../operations/symbols/package-info.java | 6 + .../TypeHierarchyContentProvider.java | 15 +- .../typeHierarchy/TypeHierarchyDialog.java | 12 +- .../typeHierarchy/TypeHierarchyHandler.java | 13 +- .../TypeHierarchyItemLabelProvider.java | 7 +- .../typeHierarchy/TypeHierarchyView.java | 104 ++++---- .../TypeHierarchyViewContentProvider.java | 27 ++- .../TypeHierarchyViewHandler.java | 3 +- .../TypeMemberContentProvider.java | 6 +- .../typeHierarchy/package-info.java | 6 + .../eclipse/lsp4e/outline/CNFOutlinePage.java | 49 ++-- .../EditorToOutlineAdapterFactory.java | 6 +- .../lsp4e/outline/HasCNFOutlinePage.java | 3 +- .../outline/LSSymbolsContentProvider.java | 47 ++-- .../eclipse/lsp4e/outline/OutlineSorter.java | 10 +- .../lsp4e/outline/ShowKindHandler.java | 3 +- .../SymbolInformationPropertyTester.java | 5 +- .../lsp4e/outline/SymbolsLabelProvider.java | 27 ++- .../eclipse/lsp4e/outline/SymbolsModel.java | 46 ++-- .../lsp4e/outline/ToggleLinkingHandler.java | 3 +- .../outline/ToggleSortOutlineHandler.java | 3 +- .../eclipse/lsp4e/outline/package-info.java | 6 + .../src/org/eclipse/lsp4e/package-info.java | 6 + .../lsp4e/progress/LSPProgressManager.java | 18 +- .../eclipse/lsp4e/progress/package-info.java | 6 + .../lsp4e/refactoring/CreateFileChange.java | 21 +- .../lsp4e/refactoring/DeleteExternalFile.java | 8 +- .../lsp4e/refactoring/LSPTextChange.java | 34 +-- .../lsp4e/refactoring/package-info.java | 6 + ...essOverSocketStreamConnectionProvider.java | 16 +- .../ProcessStreamConnectionProvider.java | 20 +- .../server/StreamConnectionProvider.java | 10 +- .../eclipse/lsp4e/server/package-info.java | 6 + .../eclipse/lsp4e/ui/EnableDisableLSJob.java | 7 +- .../src/org/eclipse/lsp4e/ui/LSPImages.java | 18 +- .../ui/LanguageServerPreferencePage.java | 24 +- .../eclipse/lsp4e/ui/LanguageServersView.java | 17 +- .../lsp4e/ui/LoggingPreferencePage.java | 29 ++- .../src/org/eclipse/lsp4e/ui/Messages.java | 2 + .../ui/NewContentTypeLSPLaunchDialog.java | 55 ++--- .../org/eclipse/lsp4e/ui/package-info.java | 6 + .../eclipse/lsp4e/ui/views/package-info.java | 6 + pom.xml | 31 ++- 164 files changed, 2110 insertions(+), 1586 deletions(-) delete mode 100644 org.eclipse.lsp4e.debug/.gitignore delete mode 100644 org.eclipse.lsp4e.test/.gitignore delete mode 100644 org.eclipse.lsp4e.tests.mock/.gitignore create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/NullSafetyHelper.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/package-info.java create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/views/package-info.java diff --git a/.gitignore b/.gitignore index 59a2d8f8b..f0d5b0e6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,26 @@ -target/ -bin/ -.settings/de.loskutov.anyedit.AnyEditTools.prefs +# Local work folder that is not checked in +_LOCAL/ + +# Eclipse +/bin/ +/*/bin/ +**/.settings/* +!**/.settings/org.eclipse.core.resoures.prefs +!**/.settings/org.eclipse.jdt.core.prefs +!**/.settings/org.eclipse.pde.core.prefs +**/.pydevproject + +# Maven +/target/ +/*/target/ .polyglot.* .META-INF_MANIFEST.MF dash-licenses/ *.log + +# OSX +.DS_Store + +# Vim +*.swo +*.swp diff --git a/.project b/.project index ee42d3571..4ab6a997e 100644 --- a/.project +++ b/.project @@ -5,7 +5,13 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature diff --git a/org.eclipse.lsp4e.debug/.gitignore b/org.eclipse.lsp4e.debug/.gitignore deleted file mode 100644 index ae3c17260..000000000 --- a/org.eclipse.lsp4e.debug/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/org.eclipse.lsp4e.test/.gitignore b/org.eclipse.lsp4e.test/.gitignore deleted file mode 100644 index 0f630157f..000000000 --- a/org.eclipse.lsp4e.test/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target/ -/bin/ diff --git a/org.eclipse.lsp4e.tests.mock/.gitignore b/org.eclipse.lsp4e.tests.mock/.gitignore deleted file mode 100644 index ae3c17260..000000000 --- a/org.eclipse.lsp4e.tests.mock/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/org.eclipse.lsp4e.tests.mock/META-INF/MANIFEST.MF b/org.eclipse.lsp4e.tests.mock/META-INF/MANIFEST.MF index 1cc457540..ec938903f 100644 --- a/org.eclipse.lsp4e.tests.mock/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e.tests.mock/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Mock Language Server to test LSP4E Bundle-SymbolicName: org.eclipse.lsp4e.tests.mock -Bundle-Version: 0.16.9.qualifier +Bundle-Version: 0.16.10.qualifier Bundle-Vendor: Eclipse LSP4E Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Bundle: org.eclipse.lsp4j;bundle-version="[0.23.0,0.24.0)", diff --git a/org.eclipse.lsp4e/.classpath b/org.eclipse.lsp4e/.classpath index 8d8612144..7931ec26b 100644 --- a/org.eclipse.lsp4e/.classpath +++ b/org.eclipse.lsp4e/.classpath @@ -1,7 +1,21 @@ - + + + + + - + + + + + + + + + + + diff --git a/org.eclipse.lsp4e/.gitignore b/org.eclipse.lsp4e/.gitignore index b44d5e5a5..f8280b696 100644 --- a/org.eclipse.lsp4e/.gitignore +++ b/org.eclipse.lsp4e/.gitignore @@ -1,4 +1 @@ -/target/ -/bin/ /resources/highlight.min.js/ -/.settings/ diff --git a/org.eclipse.lsp4e/.project b/org.eclipse.lsp4e/.project index 31c95a6d2..c4e8a56b8 100644 --- a/org.eclipse.lsp4e/.project +++ b/org.eclipse.lsp4e/.project @@ -21,12 +21,12 @@ - org.eclipse.m2e.core.maven2Builder + org.eclipse.pde.ds.core.builder - org.eclipse.pde.ds.core.builder + org.eclipse.m2e.core.maven2Builder diff --git a/org.eclipse.lsp4e/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.lsp4e/.settings/org.eclipse.jdt.core.prefs index 0c5d1c584..bb5c97c19 100644 --- a/org.eclipse.lsp4e/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.lsp4e/.settings/org.eclipse.jdt.core.prefs @@ -1,13 +1,17 @@ eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.builder.annotationPath.allLocations=enabled +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=org.eclipse.lsp4j.jsonrpc.validation.NonNull org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.notowning=org.eclipse.jdt.annotation.NotOwning org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.annotation.owning=org.eclipse.jdt.annotation.Owning +org.eclipse.jdt.core.compiler.annotation.resourceanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 @@ -17,6 +21,7 @@ org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.autoboxing=ignore @@ -37,10 +42,12 @@ org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=error org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompatibleOwningContract=warning org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.insufficientResourceAnalysis=warning org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore @@ -59,7 +66,7 @@ org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=wa org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error org.eclipse.jdt.core.compiler.problem.nullReference=error org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=info org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning @@ -68,7 +75,7 @@ org.eclipse.jdt.core.compiler.problem.potentialNullReference=error org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore @@ -78,7 +85,8 @@ org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning diff --git a/org.eclipse.lsp4e/META-INF/MANIFEST.MF b/org.eclipse.lsp4e/META-INF/MANIFEST.MF index 1c1ad2820..a5cc761ee 100644 --- a/org.eclipse.lsp4e/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e/META-INF/MANIFEST.MF @@ -25,7 +25,6 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="3.12.0", org.eclipse.core.externaltools;bundle-version="1.0.400", org.eclipse.debug.ui;bundle-version="3.11.200", org.eclipse.swt;bundle-version="3.122.0", - org.eclipse.jdt.annotation;bundle-version="2.1.0";resolution:=optional, org.eclipse.tm4e.languageconfiguration, org.eclipse.tm4e.ui, org.eclipse.ui.editors, diff --git a/org.eclipse.lsp4e/build.properties b/org.eclipse.lsp4e/build.properties index 69bab11a2..834e201e3 100644 --- a/org.eclipse.lsp4e/build.properties +++ b/org.eclipse.lsp4e/build.properties @@ -10,3 +10,6 @@ bin.includes = META-INF/,\ schema/,\ OSGI-INF/ src.includes = schema/ + +# JDT Null Analysis for Eclipse +additional.bundles = org.eclipse.jdt.annotation 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 3be08fc42..974645e42 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.io.File; import java.net.URI; import java.util.Collections; @@ -33,7 +35,7 @@ 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; import org.eclipse.jface.text.DocumentEvent; @@ -73,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) { @@ -124,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); @@ -177,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()); @@ -217,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; @@ -235,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; } @@ -260,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); @@ -272,8 +271,8 @@ public void documentAboutToBeSaved() { } catch (ExecutionException e) { LanguageServerPlugin.logError(e); } catch (TimeoutException e) { - Integer timeoutCount = WILL_SAVE_WAIT_UNTIL_TIMEOUT_MAP.compute(identifier.getUri(), - (k, v) -> v == null ? 1 : Integer.valueOf(v + 1)); + Integer timeoutCount = castNonNull(WILL_SAVE_WAIT_UNTIL_TIMEOUT_MAP.compute(identifier.getUri(), + (k, v) -> v == null ? 1 : Integer.valueOf(v + 1))); String message = timeoutCount > WILL_SAVE_WAIT_UNTIL_COUNT_THRESHOLD ? Messages.DocumentContentSynchronizer_TimeoutThresholdMessage: Messages.DocumentContentSynchronizer_TimeoutMessage; @@ -287,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); @@ -296,53 +295,58 @@ 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); } } } - private synchronized IRegion[] getFormatRegions() { + private synchronized IRegion @Nullable [] getFormatRegions() { if (formatRegionsProvider != null) { return formatRegionsProvider.getFormattingRegions(document); } var serverId = "(serverDefinitionId=" + languageServerWrapper.serverDefinition.id + ")"; //$NON-NLS-1$ //$NON-NLS-2$ - var bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); - if (bundleContext != null) { - try { - ServiceReference reference = null; - var serviceReferences = bundleContext.getAllServiceReferences(IFormatRegionsProvider.class.getName(), serverId); - if (serviceReferences != null) { - reference = serviceReferences[0]; - } else { - //Use LSP4E default implementation: - reference = bundleContext.getServiceReference(IFormatRegionsProvider.class.getName()); - } - if (reference != null) { - formatRegionsProvider = (IFormatRegionsProvider) bundleContext.getService(reference); - if (formatRegionsProvider != null) { - return formatRegionsProvider.getFormattingRegions(document); + final var bundle = FrameworkUtil.getBundle(this.getClass()); + if (bundle != null) { + var bundleContext = bundle.getBundleContext(); + if (bundleContext != null) { + try { + ServiceReference reference = null; + var serviceReferences = bundleContext.getAllServiceReferences(IFormatRegionsProvider.class.getName(), serverId); + if (serviceReferences != null) { + reference = serviceReferences[0]; + } else { + //Use LSP4E default implementation: + reference = bundleContext.getServiceReference(IFormatRegionsProvider.class.getName()); } + if (reference != null) { + formatRegionsProvider = (IFormatRegionsProvider) bundleContext.getService(reference); + if (formatRegionsProvider != null) { + return formatRegionsProvider.getFormattingRegions(document); + } + } + } catch (InvalidSyntaxException e) { + LanguageServerPlugin.logError(e); } - } catch (InvalidSyntaxException e) { - LanguageServerPlugin.logError(e); } } 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 9a97054d9..11024f5d7 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; @@ -187,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()); @@ -254,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); @@ -269,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 @@ -336,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()); } @@ -346,36 +347,37 @@ 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); + public static 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); - } - 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; } @@ -392,18 +394,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; @@ -483,7 +485,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) { @@ -520,7 +522,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; } @@ -603,8 +605,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; } @@ -620,8 +621,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; } @@ -660,7 +660,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) { @@ -668,22 +668,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); @@ -713,7 +717,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; @@ -776,7 +780,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); @@ -791,15 +795,17 @@ protected static void openFileLocationInEditor(String uri, IWorkbenchPage page, if (targetDocument != null) { ISelectionProvider selectionProvider = part.getEditorSite().getSelectionProvider(); - ISelection selection = toSelection(optionalRange, targetDocument); - if (selection != null) { - selectionProvider.setSelection(selection); + if (selectionProvider != null) { + ISelection selection = toSelection(optionalRange, targetDocument); + if (selection != null) { + selectionProvider.setSelection(selection); + } } } } } - 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; } @@ -859,7 +865,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(); @@ -888,7 +894,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 @@ -914,7 +920,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); } @@ -925,7 +931,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; } @@ -951,9 +957,10 @@ public static void applyWorkspaceEdit(WorkspaceEdit wsEdit, String label) { // Open the resource in editor if there is the only one URI if (changedURIs.size() == 1) { - changedURIs.keySet().stream().findFirst().ifPresent(uri -> { + changedURIs.entrySet().stream().findFirst().ifPresent(e -> { // Select the only start position of the range or the document start - Range range = changedURIs.get(uri); + final var uri = e.getKey(); + final var range = e.getValue(); Position start = range.getStart() != null ? range.getStart() : new Position(0, 0); UI.runOnUIThread(() -> open(uri.toString(), new Range(start, start))); }); @@ -962,7 +969,6 @@ public static void applyWorkspaceEdit(WorkspaceEdit wsEdit, String label) { LanguageServerPlugin.logError(e); } } - } private static void runRefactorWizardOperation(Change change) { @@ -995,13 +1001,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 { @@ -1045,8 +1048,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 -> { @@ -1090,7 +1093,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) { @@ -1138,15 +1141,18 @@ private static CompositeChange toCompositeChange(WorkspaceEdit wsEdit, String na IFile newFile = getFileHandle(newURI); // If both files are within Eclipse workspace utilize Eclipse ltk MoveRenameResourceChange and RenameResourceChange - if (oldFile != null && oldFile.exists() && oldFile.getParent() != null - && newFile != null && newFile.getParent() != null && newFile.getParent().exists()) { - if (!newFile.exists() || rename.getOptions().getOverwrite()) { - if (oldFile.getParent().equals(newFile.getParent())) { - change.add(new RenameResourceChange(oldFile.getFullPath(), newFile.getName())); - } else { - change.add(new MoveRenameResourceChange(oldFile, newFile.getParent(), newFile.getName())); - } - return; + if (oldFile != null && oldFile.exists() && newFile != null) { + final var oldFileParent = oldFile.getParent(); + final var newFileParent = newFile.getParent(); + if (oldFileParent != null && newFileParent != null && newFileParent.exists()) { + if (!newFile.exists() || rename.getOptions().getOverwrite()) { + if (Objects.equals(oldFile.getParent(), newFile.getParent())) { + change.add(new RenameResourceChange(oldFile.getFullPath(), newFile.getName())); + } else { + change.add(new MoveRenameResourceChange(oldFile, newFile.getParent(), newFile.getName())); + } + return; + } } } @@ -1205,7 +1211,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; } @@ -1221,7 +1227,7 @@ private static void collectChangedURI(URI uri, List textEdits, Map 1 ? ResourcesPlugin.getWorkspace().getRoot().getFile(buffer.getLocation()) : null; if (res != null) { @@ -1299,17 +1305,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; @@ -1319,16 +1325,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$ @@ -1336,8 +1340,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()) { @@ -1370,8 +1373,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); @@ -1413,7 +1415,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(); @@ -1424,7 +1426,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); @@ -1451,7 +1453,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; @@ -1498,7 +1500,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(); } @@ -1518,7 +1520,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()); } @@ -1554,24 +1556,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 e9b7e61c1..34804cc26 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java @@ -17,6 +17,8 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -58,6 +60,7 @@ import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProduct; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; @@ -65,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; @@ -119,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 @@ -145,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; @@ -284,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) { @@ -299,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()); @@ -311,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() && languageServer != null) { + lspStreamProvider.handleMessage(message, 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(); }) @@ -339,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); @@ -408,14 +405,15 @@ 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) { - final String name = Platform.getProduct() != null ? Platform.getProduct().getName() : "Eclipse IDE"; //$NON-NLS-1$ + private CompletableFuture initServer(final @Nullable URI rootURI) { + final IProduct product = Platform.getProduct(); + final String name = product != null ? product.getName() : "Eclipse IDE"; //$NON-NLS-1$ final var workspaceClientCapabilities = SupportedFeatures.getWorkspaceClientCapabilities(); final var textDocumentClientCapabilities = SupportedFeatures.getTextDocumentClientCapabilities(); @@ -425,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 @@ -455,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 @@ -483,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() { @@ -583,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) { @@ -610,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) { @@ -636,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; @@ -680,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); @@ -697,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); } @@ -715,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) { @@ -732,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() @@ -777,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); } @@ -795,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 @@ -820,7 +817,7 @@ public void sendNotification(@NonNull Consumer fn) { * * @return Async result */ - public 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. @@ -834,7 +831,7 @@ public CompletableFuture execute(@NonNull Function CompletableFuture execute(@NonNull Function * @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); @@ -890,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. @@ -941,8 +938,8 @@ void registerCapability(RegistrationParams params) { break; case "workspace/executeCommand": //$NON-NLS-1$ final var gson = new Gson(); // TODO? retrieve the GSon used by LS - ExecuteCommandOptions executeCommandOptions = gson.fromJson((JsonObject) reg.getRegisterOptions(), - ExecuteCommandOptions.class); + ExecuteCommandOptions executeCommandOptions = castNonNull(gson.fromJson((JsonObject) reg.getRegisterOptions(), + ExecuteCommandOptions.class)); List newCommands = executeCommandOptions.getCommands(); if (!newCommands.isEmpty()) { addRegistration(reg, () -> unregisterCommands(newCommands)); @@ -1002,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$ @@ -1014,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) { @@ -1212,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..b8cef079e 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,8 @@ 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 + // some LS methods return null objects when they have nothing to report, and some return an empty List + if (t != null && !isEmptyCollection(t)) { result.complete(Optional.of(t)); } return t; @@ -185,43 +185,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 +254,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 +285,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 +329,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,8 +347,8 @@ public static class LanguageServerProjectExecutor extends LanguageServers boolean isEmpty(final T t) { - return t == null || (t instanceof Collection c && c.isEmpty()); + private static boolean isEmptyCollection(final Object obj) { + return (obj instanceof Collection c && c.isEmpty()); } protected Collection order(Collection wrappers) { @@ -355,18 +356,18 @@ protected Collection order(Collection 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 +382,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 +392,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 +411,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 +426,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 +444,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 +457,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 d6afd859e..4d344a551 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LaunchConfigurationStreamProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LaunchConfigurationStreamProvider.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -39,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; /** @@ -47,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; @@ -86,7 +89,7 @@ public int read() throws IOException { Thread.currentThread().interrupt(); } } - return queue.poll(); + return castNonNull(queue.poll()); } @Override @@ -96,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; @@ -108,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; } @@ -122,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; @@ -144,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) { @@ -155,21 +158,21 @@ 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$ systemProcessGetter.setAccessible(true); final var systemProcess = (Process) systemProcessGetter.invoke(process); - this.outputStream = systemProcess.getOutputStream(); + this.outputStream = castNonNull(systemProcess).getOutputStream(); } 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); @@ -208,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 7507f93b2..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; @@ -104,10 +104,10 @@ public LoggingStreamConnectionProviderProxy(StreamConnectionProvider provider, S logToFile = store.getBoolean(lsToFileLoggingId(serverId)); logToConsole = store.getBoolean(lsToConsoleLoggingId(serverId)); store.addPropertyChangeListener(event -> { - if (event.getProperty().equals(FILE_KEY)) { - logToFile = (boolean) event.getNewValue(); - } else if (event.getProperty().equals(STDERR_KEY)) { - logToConsole = (boolean) event.getNewValue(); + if (event.getProperty().equals(FILE_KEY) && event.getNewValue() instanceof Boolean newValue) { + logToFile = newValue; + } else if (event.getProperty().equals(STDERR_KEY) && event.getNewValue() instanceof Boolean newValue) { + logToConsole = newValue; } }); this.logFile = getLogFile(); @@ -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 6a3a0ceae..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; @@ -40,7 +41,7 @@ private static final class LSPNotification extends AbstractNotificationPopup { private final MessageParams messageParams; public LSPNotification(String label, MessageParams messageParams) { - super(Display.getCurrent()); + super(UI.getDisplay()); setParentShell(UI.getActiveShell()); this.label = label; this.messageParams = messageParams; @@ -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/CallHierarchyCommandHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyCommandHandler.java index 184ee7c04..b27c4e379 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyCommandHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyCommandHandler.java @@ -12,6 +12,7 @@ package org.eclipse.lsp4e.callhierarchy; import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.lsp4e.LSPEclipseUtils; @@ -46,7 +47,7 @@ protected void execute(ExecutionEvent event, ITextEditor editor) { } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(final @Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getCallHierarchyProvider, this::hasSelection); } } 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 94ba4774f..e55094172 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyContentProvider.java @@ -16,7 +16,7 @@ import java.util.Collections; import java.util.List; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.ITreeContentProvider; @@ -41,13 +41,13 @@ * Content provider for the call hierarchy tree view. */ public class CallHierarchyContentProvider implements ITreeContentProvider { - private TreeViewer treeViewer; - private LanguageServerWrapper languageServerWrapper; - private List rootItems; + private @Nullable TreeViewer treeViewer; + private @Nullable LanguageServerWrapper languageServerWrapper; + private @Nullable List rootItems; private String rootMessage = Messages.CH_finding_callers; @Override - public Object[] getElements(final Object inputElement) { + public Object[] getElements(final @Nullable Object inputElement) { if (rootItems != null) { return rootItems.toArray(); } @@ -64,7 +64,7 @@ public Object[] getChildren(final Object parentElement) { } @Override - public Object getParent(final Object element) { + public @Nullable Object getParent(final Object element) { if (element instanceof CallHierarchyViewTreeNode treeNode) { return treeNode.getParent(); } @@ -77,7 +77,7 @@ public boolean hasChildren(final Object element) { } @Override - public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { + public void inputChanged(final Viewer viewer, final @Nullable Object oldInput, final @Nullable Object newInput) { ITreeContentProvider.super.inputChanged(viewer, oldInput, newInput); treeViewer = (TreeViewer) viewer; @@ -86,22 +86,17 @@ public void inputChanged(final Viewer viewer, final Object oldInput, final Objec 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 @NonNull IDocument document, final int offset) throws BadLocationException { + private void initialise(final IDocument document, final int offset) throws BadLocationException { LanguageServerDocumentExecutor executor = LanguageServers.forDocument(document) .withCapability(ServerCapabilities::getCallHierarchyProvider); if (!executor.anyMatching()) { @@ -114,7 +109,7 @@ private void initialise(final @NonNull IDocument document, final int offset) thr languageServerWrapper = p.first(); List hierarchyItems = p.second(); if (!hierarchyItems.isEmpty()) { - rootItems = new ArrayList<>(hierarchyItems.size()); + final var rootItems = this.rootItems = new ArrayList<>(hierarchyItems.size()); for (CallHierarchyItem item : hierarchyItems) { rootItems.add(new CallHierarchyViewTreeNode(item)); } @@ -122,6 +117,7 @@ private void initialise(final @NonNull IDocument document, final int offset) thr rootMessage = Messages.CH_no_call_hierarchy; } PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { + final var treeViewer = this.treeViewer; if (treeViewer != null) { treeViewer.refresh(); treeViewer.expandToLevel(2); @@ -133,7 +129,6 @@ private void initialise(final @NonNull IDocument document, final int offset) thr } return result; }); - } private void handleRootError() { @@ -146,19 +141,28 @@ private void handleRootError() { } private Object[] findCallers(final CallHierarchyViewTreeNode callee) { - if (callee.getChildren() == null) { - treeViewer.getControl().setEnabled(false); + final var children = callee.getChildren(); + if (children == null) { + final var treeViewer = this.treeViewer; + if (treeViewer != null) { + treeViewer.getControl().setEnabled(false); + } updateCallers(callee); return new Object[] { Messages.CH_finding_callers }; } - return callee.getChildren(); + return children; } private void updateCallers(final CallHierarchyViewTreeNode callee) { - CallHierarchyIncomingCallsParams incomingCallParams = new CallHierarchyIncomingCallsParams( - callee.getCallContainer()); + final var languageServerWrapper = this.languageServerWrapper; + if(languageServerWrapper == null) + return; + + 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(); @@ -178,14 +182,15 @@ private void updateCallers(final CallHierarchyViewTreeNode callee) { }).handle((result, error) -> updateChildrenInView(callee, result, error)); } - private List updateChildrenInView(final CallHierarchyViewTreeNode callee, - final List children, final Throwable error) { + private @Nullable List updateChildrenInView(final CallHierarchyViewTreeNode callee, + final @Nullable List children, final @Nullable Throwable error) { if (error != null || children == null) { callee.setChildren(Collections.emptyList()); } else { callee.setChildren(children); } PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { + final var treeViewer = this.treeViewer; if (treeViewer != null) { treeViewer.refresh(); treeViewer.getControl().setEnabled(true); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java index 37f62088f..f2250f30b 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyLabelProvider.java @@ -12,6 +12,7 @@ package org.eclipse.lsp4e.callhierarchy; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; @@ -25,7 +26,7 @@ public class CallHierarchyLabelProvider extends LabelProvider implements IStyledLabelProvider { @Override - public Image getImage(final Object element) { + public @Nullable Image getImage(final @Nullable Object element) { if (element instanceof CallHierarchyViewTreeNode treeNode) { CallHierarchyItem callContainer = treeNode.getCallContainer(); Image res = LSPImages.imageFromSymbolKind(callContainer.getKind()); @@ -33,11 +34,11 @@ public Image getImage(final Object element) { return res; } } - return super.getImage(element); + return element == null ? null : super.getImage(element); } @Override - public StyledString getStyledText(final Object element) { + public @Nullable StyledString getStyledText(final @Nullable Object element) { if (element instanceof CallHierarchyViewTreeNode treeNode) { CallHierarchyItem callContainer = treeNode.getCallContainer(); StyledString styledString = new StyledString(); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyView.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyView.java index 6bbe5e750..aeec92760 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyView.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyView.java @@ -12,6 +12,8 @@ package org.eclipse.lsp4e.callhierarchy; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; @@ -30,7 +32,7 @@ public class CallHierarchyView extends ViewPart { public static final String ID = "org.eclipse.lsp4e.callHierarchy.callHierarchyView"; //$NON-NLS-1$ - protected TreeViewer treeViewer; + protected TreeViewer treeViewer = lateNonNull(); private final CallHierarchyContentProvider contentProvider = new CallHierarchyContentProvider(); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyViewTreeNode.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyViewTreeNode.java index 3d0a6dc06..10a928c7a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyViewTreeNode.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/CallHierarchyViewTreeNode.java @@ -14,7 +14,6 @@ import java.util.List; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4j.CallHierarchyItem; import org.eclipse.lsp4j.Range; @@ -28,7 +27,7 @@ public class CallHierarchyViewTreeNode { * the {@link CallHierarchyItem} of the callable containing the call site that * this node represents. */ - private final @NonNull CallHierarchyItem callContainer; + private final CallHierarchyItem callContainer; /** the location of the call site that this node represents. */ private final @Nullable Range callSite; @@ -44,7 +43,7 @@ public class CallHierarchyViewTreeNode { * @param callSite * the range in the callable of the call site. */ - public CallHierarchyViewTreeNode(final @NonNull CallHierarchyItem callContainer, final @Nullable Range callSite) { + public CallHierarchyViewTreeNode(final CallHierarchyItem callContainer, final @Nullable Range callSite) { this.callContainer = callContainer; this.callSite = callSite; } @@ -55,7 +54,7 @@ public CallHierarchyViewTreeNode(final @NonNull CallHierarchyItem callContainer, * @param callContainer * the {@link CallHierarchyItem} of the callable containing the call. */ - public CallHierarchyViewTreeNode(final @NonNull CallHierarchyItem callContainer) { + public CallHierarchyViewTreeNode(final CallHierarchyItem callContainer) { this.callContainer = callContainer; this.callSite = null; } @@ -94,7 +93,7 @@ public void setParent(final CallHierarchyViewTreeNode parent) { * @param children * the new children. */ - public void setChildren(final @NonNull List children) { + public void setChildren(final List children) { this.children = children.toArray(CallHierarchyViewTreeNode[]::new); } @@ -104,7 +103,7 @@ public void setChildren(final @NonNull List children) * * @return the containing {@link CallHierarchyItem}. */ - public @NonNull CallHierarchyItem getCallContainer() { + public CallHierarchyItem getCallContainer() { return callContainer; } @@ -122,7 +121,7 @@ public void setChildren(final @NonNull List children) * * @return the selection range of this node. */ - public @NonNull Range getSelectionRange() { + public Range getSelectionRange() { Range theCallSite = callSite; if (theCallSite != null) { return theCallSite; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/package-info.java new file mode 100644 index 000000000..57de39cd3 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/callhierarchy/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.callhierarchy; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/CommandExecutor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/CommandExecutor.java index 1c2551555..be6d9776b 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/CommandExecutor.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/CommandExecutor.java @@ -8,8 +8,7 @@ *******************************************************************************/ package org.eclipse.lsp4e.command; -import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_COMMAND_PARAMETER_ID; -import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_PATH_PARAMETER_ID; +import static org.eclipse.lsp4e.command.LSPCommandHandler.*; import java.net.URI; import java.util.ArrayList; @@ -30,7 +29,6 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LSPEclipseUtils; @@ -63,7 +61,7 @@ public class CommandExecutor { private static final String LSP_COMMAND_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.commandParameterType"; //$NON-NLS-1$ private static final String LSP_PATH_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.pathParameterType"; //$NON-NLS-1$ - public static CompletableFuture executeCommandClientSide(@NonNull Command command, @NonNull IDocument document) { + public static @Nullable CompletableFuture executeCommandClientSide(Command command, IDocument document) { IPath path = LSPEclipseUtils.toPath(document); if (path == null) { path = ResourcesPlugin.getWorkspace().getRoot().getLocation(); @@ -80,7 +78,7 @@ public static CompletableFuture executeCommandClientSide(@NonNull Comman return CompletableFuture.completedFuture(null); } - public static CompletableFuture executeCommandClientSide(@NonNull Command command, @NonNull IResource resource) { + public static @Nullable CompletableFuture executeCommandClientSide(Command command, IResource resource) { CompletableFuture r = executeCommandClientSide(command, resource.getFullPath()); if (r != null) { return r; @@ -93,20 +91,17 @@ public static CompletableFuture executeCommandClientSide(@NonNull Comman } @SuppressWarnings("unused") // ECJ compiler handlerService cannot be null because getService is declared as - // T getService(Class api), it infers the input is Class<@NonNull IHandlerService> and the output - // @NonNull IHandlerService, as it takes over the @NonNull annotation when inferring the return type, which + // T getService(Class api), it infers the input is Class and the output + // IHandlerService, as it takes over the annotation when inferring the return type, which // is a bug in its implementation - private static CompletableFuture executeCommandClientSide(@NonNull Command command, @Nullable IPath path) { + private static @Nullable CompletableFuture executeCommandClientSide(Command command, @Nullable IPath path) { IWorkbench workbench = PlatformUI.getWorkbench(); - if (workbench == null) { - return null; - } ParameterizedCommand parameterizedCommand = createEclipseCoreCommand(command, path, workbench); if (parameterizedCommand == null) { return null; } - @Nullable + IHandlerService handlerService = workbench.getService(IHandlerService.class); if (handlerService == null) { return null; @@ -124,7 +119,7 @@ private static CompletableFuture executeCommandClientSide(@NonNull Comma } // tentative fallback - private static CompletableFuture executeFallbackClientSide(@NonNull Command command, @NonNull URI initialUri) { + private static @Nullable CompletableFuture executeFallbackClientSide(Command command, URI initialUri) { if (command.getArguments() != null) { WorkspaceEdit edit = createWorkspaceEdit(command.getArguments(), initialUri); LSPEclipseUtils.applyWorkspaceEdit(edit, command.getTitle()); @@ -134,13 +129,13 @@ private static CompletableFuture executeFallbackClientSide(@NonNull Comm } @SuppressWarnings("unused") // ECJ compiler thinks commandService cannot be null (see above) - private static ParameterizedCommand createEclipseCoreCommand(@NonNull Command command, IPath context, - @NonNull IWorkbench workbench) { + private static @Nullable ParameterizedCommand createEclipseCoreCommand(Command command, @Nullable IPath context, + IWorkbench workbench) { // Usually commands are defined via extension point, but we synthesize one on // the fly for the command ID, since we do not want downstream users // having to define them. String commandId = command.getCommand(); - @Nullable + ICommandService commandService = workbench.getService(ICommandService.class); if (commandService == null) { return null; @@ -156,7 +151,7 @@ private static ParameterizedCommand createEclipseCoreCommand(@NonNull Command co coreCommand.define(commandId, null, category, parameters); } - final var parameters = new HashMap(); + final var parameters = new HashMap(); parameters.put(LSP_COMMAND_PARAMETER_ID, command); parameters.put(LSP_PATH_PARAMETER_ID, context); ParameterizedCommand parameterizedCommand = ParameterizedCommand.generateCommand(coreCommand, parameters); @@ -179,7 +174,7 @@ private static final class Pair { * Very empirical and unsafe heuristic to turn unknown command arguments into a * workspace edit... */ - private static WorkspaceEdit createWorkspaceEdit(List commandArguments, @NonNull URI initialUri) { + private static WorkspaceEdit createWorkspaceEdit(List commandArguments, URI initialUri) { final var workspaceEdit = new WorkspaceEdit(); final var changes = new HashMap>(); workspaceEdit.setChanges(changes); @@ -195,8 +190,11 @@ private static WorkspaceEdit createWorkspaceEdit(List commandArguments, changes.put(currentEntry.key.toString(), currentEntry.value); IResource res = LSPEclipseUtils.findResourceFor(argString); if (res != null) { - currentEntry.key = res.getLocationURI(); - currentEntry.value = new ArrayList<>(); + final var uri = res.getLocationURI(); + if (uri != null) { + currentEntry.key = uri; + currentEntry.value = new ArrayList<>(); + } } } else if (arg instanceof WorkspaceEdit wsEdit) { changes.putAll(wsEdit.getChanges()); @@ -213,8 +211,11 @@ private static WorkspaceEdit createWorkspaceEdit(List commandArguments, changes.put(currentEntry.key.toString(), currentEntry.value); IResource res = LSPEclipseUtils.findResourceFor(json.getAsString()); if (res != null) { - currentEntry.key = res.getLocationURI(); - currentEntry.value = new ArrayList<>(); + final var uri = res.getLocationURI(); + if (uri != null) { + currentEntry.key = uri; + currentEntry.value = new ArrayList<>(); + } } } } else if (arg instanceof JsonArray jsonArray) { @@ -228,13 +229,15 @@ private static WorkspaceEdit createWorkspaceEdit(List commandArguments, } else if (arg instanceof JsonObject jsonObject) { final var gson = new Gson(); // TODO? retrieve the GSon used by LS WorkspaceEdit wEdit = gson.fromJson(jsonObject, WorkspaceEdit.class); - Map> entries = wEdit.getChanges(); - if (wEdit != null && !entries.isEmpty()) { - changes.putAll(entries); - } else { - TextEdit edit = gson.fromJson(jsonObject, TextEdit.class); - if (edit != null && edit.getRange() != null) { - currentEntry.value.add(edit); + if (wEdit != null) { + Map> entries = wEdit.getChanges(); + if (!entries.isEmpty()) { + changes.putAll(entries); + } else { + TextEdit edit = gson.fromJson(jsonObject, TextEdit.class); + if (edit != null && edit.getRange() != null) { + currentEntry.value.add(edit); + } } } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java index 85a61b993..471850ef3 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java @@ -16,7 +16,7 @@ import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.IHandler; import org.eclipse.core.runtime.IPath; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4j.Command; import org.eclipse.ui.handlers.IHandlerService; @@ -50,7 +50,7 @@ public abstract class LSPCommandHandler extends AbstractHandler { public static final String LSP_PATH_PARAMETER_ID = "org.eclipse.lsp4e.path.param"; //$NON-NLS-1$ @Override - public final Object execute(ExecutionEvent event) throws ExecutionException { + public final @Nullable Object execute(ExecutionEvent event) throws ExecutionException { final var command = (Command) event.getObjectParameterForExecution(LSP_COMMAND_PARAMETER_ID); if (command == null) { return null; @@ -78,6 +78,6 @@ public final Object execute(ExecutionEvent event) throws ExecutionException { * @throws ExecutionException * if an exception occurred during execution. */ - public abstract Object execute(ExecutionEvent event, @NonNull Command command, IPath path) + public abstract @Nullable Object execute(ExecutionEvent event, Command command, IPath path) throws ExecutionException; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandConverter.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandConverter.java index a320380f8..689b97785 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandConverter.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandConverter.java @@ -28,12 +28,12 @@ public CommandConverter() { } @Override - public @Nullable Object convertToObject(String parameterValue) throws ParameterValueConversionException { + public @Nullable Object convertToObject(@Nullable String parameterValue) throws ParameterValueConversionException { return gson.fromJson(parameterValue, Command.class); } @Override - public String convertToString(Object parameterValue) throws ParameterValueConversionException { + public String convertToString(@Nullable Object parameterValue) throws ParameterValueConversionException { return gson.toJson(parameterValue); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/PathConverter.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/PathConverter.java index a0646dcb2..9405e6e1d 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/PathConverter.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/PathConverter.java @@ -14,6 +14,7 @@ import org.eclipse.core.commands.ParameterValueConversionException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.annotation.Nullable; /** * Serializes {@link IPath} instances and de-serializes them to {@link Path} instances. @@ -21,12 +22,12 @@ public class PathConverter extends AbstractParameterValueConverter { @Override - public Object convertToObject(String parameterValue) throws ParameterValueConversionException { - return new Path(parameterValue); + public @Nullable Object convertToObject(@Nullable String parameterValue) throws ParameterValueConversionException { + return parameterValue == null ? null : new Path(parameterValue); } @Override - public String convertToString(Object parameterValue) throws ParameterValueConversionException { + public String convertToString(@Nullable Object parameterValue) throws ParameterValueConversionException { return Objects.toString(parameterValue); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/package-info.java new file mode 100644 index 000000000..1af458e4d --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.command.internal; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/package-info.java new file mode 100644 index 000000000..8bd368e6d --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.command; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/EnablementTester.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/EnablementTester.java index d1ea2fd98..3d8a939f5 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/EnablementTester.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/EnablementTester.java @@ -24,6 +24,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; @@ -38,13 +39,13 @@ public final class EnablementTester { private final Expression expression; private final String description; - private final Supplier parent; + private final Supplier<@Nullable IEvaluationContext> parent; public EnablementTester(Expression expression, String description) { this(() -> null, expression, description); } - public EnablementTester(Supplier parent, Expression expression, String description) { + public EnablementTester(Supplier<@Nullable IEvaluationContext> parent, Expression expression, String description) { this.description = description; this.expression = expression; this.parent = parent; @@ -63,7 +64,7 @@ public String getDescription() { * * @return true if expression evaluates to true, false otherwise */ - public boolean evaluate(URI uri) { + public boolean evaluate(@Nullable URI uri) { boolean temporaryLoadDocument = false; IResource resource = null; try { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/package-info.java new file mode 100644 index 000000000..6a27560c2 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/enablement/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.enablement; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/DefaultFormatRegionsProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/DefaultFormatRegionsProvider.java index 5434c9cac..d3ee8048c 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/DefaultFormatRegionsProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/DefaultFormatRegionsProvider.java @@ -11,6 +11,7 @@ *******************************************************************************/ package org.eclipse.lsp4e.format; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.osgi.service.component.annotations.Component; @@ -23,7 +24,7 @@ public class DefaultFormatRegionsProvider implements IFormatRegionsProvider { @Override - public IRegion[] getFormattingRegions(IDocument document) { + public IRegion @Nullable [] getFormattingRegions(IDocument document) { // return null until user can disable the format-on-save feature in LSP4E. return null; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/IFormatRegionsProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/IFormatRegionsProvider.java index 3a63a641a..40dfa22d6 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/IFormatRegionsProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/IFormatRegionsProvider.java @@ -26,6 +26,7 @@ import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; @@ -67,7 +68,7 @@ public interface IFormatRegionsProvider { * @param document * @return region to be formatted or null if the document should not be formatted on save. */ - IRegion[] getFormattingRegions(IDocument document); + IRegion @Nullable [] getFormattingRegions(IDocument document); /** * Implementation for 'Format all lines' @@ -86,14 +87,12 @@ public static IRegion[] allLines(IDocument document) { * If successive lines have changed a region spans over the size of all successive lines. * The regions include line delimiters. * - * - * @param buffer the buffer to compare contents from * @param monitor to report progress to * @return the regions of the changed lines * */ - public static IRegion[] calculateEditedLineRegions(final IDocument document, final IProgressMonitor monitor) { - final IRegion[][] result = new IRegion[1][]; + public static IRegion @Nullable [] calculateEditedLineRegions(final IDocument document, final IProgressMonitor monitor) { + final IRegion[] @Nullable [] result = new IRegion[1][]; SafeRunner.run(new ISafeRunnable() { @Override diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/package-info.java new file mode 100644 index 000000000..4ba3a85ab --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/format/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.format; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/CancellationUtil.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/CancellationUtil.java index a644cbb89..6d10902e2 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/CancellationUtil.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/CancellationUtil.java @@ -15,6 +15,7 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; import org.eclipse.lsp4j.jsonrpc.messages.ResponseError; import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode; @@ -25,7 +26,7 @@ private CancellationUtil() { // this class shouldn't be instantiated } - public static boolean isRequestCancelledException(Throwable throwable) { + public static boolean isRequestCancelledException(@Nullable Throwable throwable) { if (throwable instanceof CompletionException || throwable instanceof ExecutionException) { throwable = throwable.getCause(); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/LSPDocumentAbstractHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/LSPDocumentAbstractHandler.java index b3062062b..b7d6c5fe1 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/LSPDocumentAbstractHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/LSPDocumentAbstractHandler.java @@ -23,7 +23,6 @@ import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.HandlerEvent; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; @@ -42,7 +41,7 @@ public abstract class LSPDocumentAbstractHandler extends AbstractHandler { private static class LanguageServerDocumentHandlerExecutor extends LanguageServerDocumentExecutor { - LanguageServerDocumentHandlerExecutor(@NonNull IDocument document) { + LanguageServerDocumentHandlerExecutor(IDocument document) { super(document); } @@ -57,15 +56,15 @@ private static class LanguageServerDocumentHandlerExecutor extends LanguageServe * capabilities. */ boolean anyMatching(Runnable runner) { - return getServers().stream().filter(wF -> matches(wF, runner)).findFirst().isPresent(); + return getServers().stream().anyMatch(wF -> matches(wF, runner)); } @Override - public @NonNull LanguageServerDocumentHandlerExecutor withFilter(final @NonNull Predicate filter) { + public LanguageServerDocumentHandlerExecutor withFilter(final Predicate filter) { return (LanguageServerDocumentHandlerExecutor) super.withFilter(filter); } - Boolean matches(@NonNull CompletableFuture<@Nullable LanguageServerWrapper> wrapperFuture, Runnable runner) { + private boolean matches(CompletableFuture<@Nullable LanguageServerWrapper> wrapperFuture, Runnable runner) { try { return wrapperFuture.thenApply(Objects::nonNull).get(50, TimeUnit.MILLISECONDS); } catch (java.util.concurrent.ExecutionException e) { @@ -79,14 +78,13 @@ Boolean matches(@NonNull CompletableFuture<@Nullable LanguageServerWrapper> wrap runner.run(); } }); - return Boolean.FALSE; } - return Boolean.FALSE; + return false; } } @Override - public Object execute(ExecutionEvent event) throws ExecutionException { + public @Nullable Object execute(ExecutionEvent event) throws ExecutionException { final var textEditor = UI.asTextEditor(HandlerUtil.getActiveEditor(event)); if (textEditor != null) { execute(event, textEditor); @@ -110,12 +108,12 @@ protected boolean hasSelection(ITextEditor textEditor) { return selection instanceof ITextSelection && !selection.isEmpty(); } - protected void setEnabled(final @NonNull Function> serverCapabilities, Predicate condition) { + protected void setEnabled(final Function> serverCapabilities, Predicate condition) { Predicate filter = f -> LSPEclipseUtils.hasCapability(serverCapabilities.apply(f)); setEnabled(filter, condition); } - protected void setEnabled(final @NonNull Predicate filter, Predicate condition) { + protected void setEnabled(final Predicate filter, Predicate condition) { ITextEditor textEditor = UI.getActiveTextEditor(); if (textEditor != null && condition.test(textEditor)) { IDocument document = LSPEclipseUtils.getDocument(textEditor); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/NullSafetyHelper.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/NullSafetyHelper.java new file mode 100644 index 000000000..618c5fea9 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/NullSafetyHelper.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2022 Sebastian Thomschke and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Sebastian Thomschke - initial implementation + */ +package org.eclipse.lsp4e.internal; + +import java.util.function.Supplier; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +public final class NullSafetyHelper { + + /** + * Casts a non-null value marked as {@link Nullable} to {@link NonNull}. + *

+ * Only use if you are sure the value is non-null but annotation-based null analysis was not able to determine it. + *

+ * This method is not meant for non-null input validation. + * + * @throws IllegalStateException if the given value is null + */ + public static @NonNull T castNonNull(final @Nullable T value) { + if (value == null) + throw new IllegalStateException("Unexpected null value present!"); //$NON-NLS-1$ + return value; + } + + /** + * Casts the elements of given array to {@link NonNull} without any validation. + *

+ * Only use if you are sure the value is non-null but annotation-based null analysis was not able to determine it. + */ + @SuppressWarnings("null") + public static @NonNull T castNonNullUnsafe(final T value) { + return value; + } + + /** + * Casts a non-null value as {@link Nullable}. + */ + public static @Nullable T castNullable(final T value) { + return value; + } + + public static T defaultIfNull(final @Nullable T object, final T defaultValue) { + if (object == null) { + return defaultValue; + } + return object; + } + + public static T defaultIfNull(final @Nullable T object, final Supplier defaultValue) { + if (object == null) { + return defaultValue.get(); + } + return object; + } + + /** + * Allows to initializes a @NonNull field with null that is + * initialized later. + */ + @SuppressWarnings("unchecked") + public static @NonNull T lateNonNull() { + return (T) castNonNullUnsafe(null); + } + + private NullSafetyHelper() { + } +} diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/StyleUtil.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/StyleUtil.java index 25a3237b6..a67043b8f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/StyleUtil.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/StyleUtil.java @@ -11,6 +11,7 @@ *******************************************************************************/ package org.eclipse.lsp4e.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jface.viewers.StyledString.Styler; import org.eclipse.swt.graphics.TextStyle; @@ -22,7 +23,7 @@ private StyleUtil() { public static final Styler DEPRECATE = new Styler() { @Override - public void applyStyles(TextStyle textStyle) { + public void applyStyles(@NonNullByDefault({}) TextStyle textStyle) { textStyle.strikeout = true; } }; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java index 2bfb80dc1..3a1f4f681 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java @@ -15,7 +15,6 @@ import java.util.Arrays; import java.util.List; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.lsp4j.CodeActionCapabilities; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.CodeActionKindCapabilities; @@ -63,7 +62,7 @@ public class SupportedFeatures { - public static @NonNull TextDocumentClientCapabilities getTextDocumentClientCapabilities() { + public static TextDocumentClientCapabilities getTextDocumentClientCapabilities() { final var textDocumentClientCapabilities = new TextDocumentClientCapabilities(); final var codeAction = new CodeActionCapabilities(new CodeActionLiteralSupportCapabilities( new CodeActionKindCapabilities(Arrays.asList(CodeActionKind.QuickFix, CodeActionKind.Refactor, @@ -127,7 +126,7 @@ public class SupportedFeatures { return textDocumentClientCapabilities; } - public static @NonNull WorkspaceClientCapabilities getWorkspaceClientCapabilities() { + public static WorkspaceClientCapabilities getWorkspaceClientCapabilities() { final var workspaceClientCapabilities = new WorkspaceClientCapabilities(); workspaceClientCapabilities.setApplyEdit(Boolean.TRUE); workspaceClientCapabilities.setConfiguration(Boolean.TRUE); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/package-info.java new file mode 100644 index 000000000..9ede697f8 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.internal; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionCompletionProposal.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionCompletionProposal.java index 7e5d7ba3d..e99d98790 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionCompletionProposal.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/CodeActionCompletionProposal.java @@ -11,6 +11,7 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.codeactions; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ContextInformation; import org.eclipse.jface.text.contentassist.ICompletionProposal; @@ -29,8 +30,8 @@ public class CodeActionCompletionProposal implements ICompletionProposal { - private CodeAction fcodeAction; - private Command fcommand; + private @Nullable CodeAction fcodeAction; + private @Nullable Command fcommand; private String fdisplayString; private final LanguageServerWrapper serverWrapper; @@ -39,13 +40,13 @@ public CodeActionCompletionProposal(Either command, Languag if (command.isLeft()) { fcommand = command.getLeft(); fdisplayString = fcommand.getTitle(); - } else if (command.isRight()) { + } else { fcodeAction = command.getRight(); fdisplayString = fcodeAction.getTitle(); } } - static boolean isCodeActionResolveSupported(ServerCapabilities capabilities) { + static boolean isCodeActionResolveSupported(@Nullable ServerCapabilities capabilities) { if (capabilities != null) { Either caProvider = capabilities.getCodeActionProvider(); if (caProvider.isLeft()) { @@ -62,6 +63,7 @@ static boolean isCodeActionResolveSupported(ServerCapabilities capabilities) { @Override public void apply(IDocument document) { + final var fcodeAction = this.fcodeAction; if (fcodeAction != null) { if (isCodeActionResolveSupported(serverWrapper.getServerCapabilities()) && fcodeAction.getEdit() == null) { // Unresolved code action "edit" property. Resolve it. @@ -76,7 +78,7 @@ public void apply(IDocument document) { } } - private void apply(CodeAction codeaction) { + private void apply(@Nullable CodeAction codeaction) { if (codeaction != null) { if (codeaction.getEdit() != null) { LSPEclipseUtils.applyWorkspaceEdit(codeaction.getEdit(), codeaction.getTitle()); @@ -88,13 +90,13 @@ private void apply(CodeAction codeaction) { } @Override - public Point getSelection(IDocument document) { + public @Nullable Point getSelection(IDocument document) { return null; } @Override - public String getAdditionalProposalInfo() { - return ""; //$NON-NLS-1$ + public @Nullable String getAdditionalProposalInfo() { + return null; } @Override @@ -103,12 +105,12 @@ public String getDisplayString() { } @Override - public Image getImage() { + public @Nullable Image getImage() { return null; } @Override - public IContextInformation getContextInformation() { + public @Nullable IContextInformation getContextInformation() { return new ContextInformation("some context display string", "some information display string"); //$NON-NLS-1$//$NON-NLS-2$ } 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 0e35472b5..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 @@ -20,6 +20,7 @@ import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.LanguageServerWrapper; @@ -34,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; @@ -53,7 +55,7 @@ public String getDescription() { } @Override - public Image getImage() { + public @Nullable Image getImage() { return null; } @@ -92,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() @@ -127,9 +130,6 @@ private ShowMessageRequestParams reportServerError(LanguageServerDefinition serv @Override public IMarker[] findOtherMarkers(IMarker[] markers) { - if (markers == null) { - return new IMarker[0]; - } return Arrays.stream(markers).filter(marker -> { try { return codeAction.getDiagnostics() 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 0bdddbd73..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 @@ -14,7 +14,7 @@ import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServerWrapper; import org.eclipse.lsp4e.LanguageServersRegistry; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; @@ -24,15 +24,16 @@ 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; public class CommandMarkerResolution extends WorkbenchMarkerResolution implements IMarkerResolution { - private final @NonNull Command command; + private final Command command; - public CommandMarkerResolution(@NonNull Command command) { + public CommandMarkerResolution(Command command) { this.command = command; } @@ -57,14 +58,13 @@ public void run(IMarker marker) { } LanguageServerWrapper wrapper = LanguageServiceAccessor.getLSWrapper(resource.getProject(), definition); - if (wrapper != null) { - ExecuteCommandOptions provider = wrapper.getServerCapabilities().getExecuteCommandProvider(); - if (provider != null && provider.getCommands().contains(command.getCommand())) { - wrapper.execute(ls -> ls.getWorkspaceService() - .executeCommand(new ExecuteCommandParams(command.getCommand(), command.getArguments()))); - } else { - CommandExecutor.executeCommandClientSide(command, resource); - } + 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()))); + } else { + CommandExecutor.executeCommandClientSide(command, resource); } } @@ -74,7 +74,7 @@ public String getDescription() { } @Override - public Image getImage() { + public @Nullable Image getImage() { return null; } 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 7d39a5272..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 @@ -11,7 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.codeactions; -import java.io.IOException; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URI; @@ -27,6 +28,7 @@ import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.AbstractInformationControlManager; import org.eclipse.jface.text.ITextHover; @@ -83,7 +85,7 @@ public String getLabel() { @Override public Image getImage() { // load class so image is loaded - return JFaceResources.getImage(ProgressInfoItem.class.getPackage().getName() + ".PROGRESS_DEFAULT"); //$NON-NLS-1$ + return JFaceResources.getImage(ProgressInfoItem.class.getPackageName() + ".PROGRESS_DEFAULT"); //$NON-NLS-1$ } @Override @@ -101,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 }; @@ -119,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}); @@ -132,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); @@ -174,7 +176,7 @@ private void reinvokeQuickfixProposalsIfNecessary(ITextViewer textViewer) { final var ca = (ContentAssistant) f.get(quickAssistant); Method m = ContentAssistant.class.getDeclaredMethod("isProposalPopupActive"); //$NON-NLS-1$ m.setAccessible(true); - boolean isProposalPopupActive = (Boolean) m.invoke(ca); + boolean isProposalPopupActive = (Boolean) castNonNull(m.invoke(ca)); if (isProposalPopupActive) { quickAssistant.showPossibleQuickAssists(); } @@ -198,7 +200,7 @@ private void reinvokeQuickfixProposalsIfNecessary(ITextViewer textViewer) { } } - static boolean providesCodeActions(final ServerCapabilities capabilities) { + static boolean providesCodeActions(final @Nullable ServerCapabilities capabilities) { return capabilities != null && LSPEclipseUtils.hasCapability(capabilities.getCodeActionProvider()); } @@ -217,7 +219,7 @@ public boolean hasResolutions(IMarker marker) { return false; } - static boolean canPerform(Either command) { + static boolean canPerform(@Nullable Either command) { if (command == null) { return false; } @@ -239,7 +241,7 @@ static boolean canPerform(Either command) { TextDocumentEdit textedit = change.getLeft(); VersionedTextDocumentIdentifier id = textedit.getTextDocument(); URI uri = URI.create(id.getUri()); - if (uri != null && LSPEclipseUtils.isReadOnly(uri)) { + if (LSPEclipseUtils.isReadOnly(uri)) { return false; } } @@ -249,7 +251,7 @@ static boolean canPerform(Either command) { if (changes != null) { for (java.util.Map.Entry> textEdit : changes.entrySet()) { URI uri = URI.create(textEdit.getKey()); - if (uri != null && LSPEclipseUtils.isReadOnly(uri)) { + if (LSPEclipseUtils.isReadOnly(uri)) { return false; } } 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 cdc507a3f..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; @@ -19,10 +21,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.contentassist.CompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; @@ -44,8 +46,8 @@ public class LSPCodeActionQuickAssistProcessor implements IQuickAssistProcessor { // Data necessary for caching proposals - private Object lock = new Object(); - private IQuickAssistInvocationContext cachedContext; + private final Object lock = new Object(); + private @Nullable IQuickAssistInvocationContext cachedContext; private List proposals = Collections.synchronizedList(new ArrayList<>()); private static final ICompletionProposal COMPUTING = new ICompletionProposal() { @@ -56,12 +58,12 @@ public void apply(IDocument document) { } @Override - public Point getSelection(IDocument document) { + public @Nullable Point getSelection(IDocument document) { return null; } @Override - public String getAdditionalProposalInfo() { + public @Nullable String getAdditionalProposalInfo() { return null; } @@ -71,19 +73,16 @@ public String getDisplayString() { } @Override - public Image getImage() { - return JFaceResources.getImage(ProgressInfoItem.class.getPackage().getName() + ".PROGRESS_DEFAULT"); //$NON-NLS-1$ + public @Nullable Image getImage() { + return JFaceResources.getImage(ProgressInfoItem.class.getPackageName() + ".PROGRESS_DEFAULT"); //$NON-NLS-1$ } @Override - public IContextInformation getContextInformation() { + public @Nullable IContextInformation getContextInformation() { return null; } - }; - CompletionProposal[] NO_PROPOSALS = {}; - @Override public String getErrorMessage() { return "CodeActions not implemented on this Language Server"; //$NON-NLS-1$ @@ -105,14 +104,14 @@ public boolean canAssist(IQuickAssistInvocationContext invocationContext) { } @Override - public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext invocationContext) { + public ICompletionProposal @Nullable [] computeQuickAssistProposals(IQuickAssistInvocationContext invocationContext) { IDocument document = invocationContext.getSourceViewer().getDocument(); if (document == null) { - return NO_PROPOSALS; + return null; } LanguageServerDocumentExecutor executor = LanguageServers.forDocument(document).withFilter(LSPCodeActionMarkerResolution::providesCodeActions); if (!executor.anyMatching()) { - return NO_PROPOSALS; + return null; } // If context has changed, i.e. new quick assist invocation rather than old @@ -120,14 +119,15 @@ public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationC // the UI boolean needNewQuery = true; synchronized (lock) { - needNewQuery = cachedContext == null || invocationContext == null || + final var cachedContext = this.cachedContext; + needNewQuery = cachedContext == null || cachedContext.getClass() != invocationContext.getClass() || cachedContext.getSourceViewer() != invocationContext.getSourceViewer() || cachedContext.getOffset() != invocationContext.getOffset() || cachedContext.getLength() != invocationContext.getLength(); // should also check whether (same) document content changed if (needNewQuery) { - cachedContext = invocationContext; + this.cachedContext = invocationContext; } } @@ -138,7 +138,7 @@ public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationC proposals.clear(); // Start all the servers computing actions - each server will append any code actions to the ongoing list of proposals // as a side effect of this request - List> futures = executor.computeAll((w, ls) -> ls.getTextDocumentService() + List> futures = executor.computeAll((w, ls) -> ls.getTextDocumentService() .codeAction(params) .thenAccept(actions -> LanguageServers.streamSafely(actions) .filter(LSPCodeActionMarkerResolution::canPerform) @@ -156,7 +156,7 @@ public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationC // Server calls didn't complete in time; those that did will have added their results to this.proposals and can be returned // as an intermediate result; as we're returning control to the UI, we need any stragglers to trigger a refresh when they arrive later on proposals.add(COMPUTING); - for (CompletableFuture future : futures) { + for (CompletableFuture<@Nullable Void> future : futures) { // Refresh will effectively re-enter this method with the same invocationContext and already computed proposals simply to show the proposals in the UI future.whenComplete((r, t) -> { if (futures.stream().allMatch(CompletableFuture::isDone)) { @@ -194,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 74609680a..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,7 +12,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.codeactions; -import java.net.URI; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.*; + import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -20,7 +21,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -52,8 +53,8 @@ public class LSPCodeActionsMenu extends ContributionItem implements IWorkbenchContribution { - private IDocument document; - private Range range; + private IDocument document = lateNonNull(); + private Range range = lateNonNull(); @Override public void initialize(IServiceLocator serviceLocator) { @@ -81,19 +82,18 @@ public void fill(final Menu menu, int index) { item.setEnabled(false); item.setText(Messages.computing); - final @NonNull IDocument document = this.document; - final URI fileUri = LSPEclipseUtils.toUri(document); + final IDocument document = this.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); - final @NonNull List<@NonNull CompletableFuture>>> actions - = LanguageServers.forDocument(document).withFilter(LSPCodeActionMarkerResolution::providesCodeActions) - .computeAll((w, ls) -> ls.getTextDocumentService().codeAction(params) - .whenComplete((codeActions, t) -> scheduleMenuUpdate(menu, item, index, document, w, t, codeActions))); + final List>>> actions = LanguageServers.forDocument(document) + .withFilter(LSPCodeActionMarkerResolution::providesCodeActions) + .computeAll((w, ls) -> ls.getTextDocumentService().codeAction(params).whenComplete( + (codeActions, t) -> scheduleMenuUpdate(menu, item, index, document, w, t, codeActions))); if (actions.isEmpty()) { item.setText(Messages.notImplemented); @@ -103,13 +103,15 @@ public void fill(final Menu menu, int index) { super.fill(menu, index); } - private void scheduleMenuUpdate(final Menu menu, final MenuItem placeHolder, final int index, final IDocument document, final LanguageServerWrapper wrapper, final Throwable u, final List> codeActions) { + private void scheduleMenuUpdate(final Menu menu, final MenuItem placeHolder, final int index, + final IDocument document, final LanguageServerWrapper wrapper, final @Nullable Throwable ex, + final @Nullable List<@Nullable Either> codeActions) { final var job = new UIJob(menu.getDisplay(), Messages.updateCodeActions_menu) { @Override - public IStatus runInUIThread(IProgressMonitor monitor) { - if (u != null) { + public IStatus runInUIThread(@Nullable IProgressMonitor monitor) { + if (ex != null) { final var item = new MenuItem(menu, SWT.NONE, index); - item.setText(u.getMessage()); + item.setText(String.valueOf(ex.getMessage())); item.setImage(LSPImages.getSharedImage(ISharedImages.IMG_DEC_FIELD_ERROR)); item.setEnabled(false); } else if (codeActions != null) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/package-info.java new file mode 100644 index 000000000..1ada6a32f --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.codeactions; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/CodeLensProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/CodeLensProvider.java index ad21a57c7..bf26be5b2 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/CodeLensProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/CodeLensProvider.java @@ -14,7 +14,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; @@ -30,24 +30,24 @@ public class CodeLensProvider extends AbstractCodeMiningProvider { - private CompletableFuture> provideCodeMinings(@NonNull IDocument document) { + private @Nullable CompletableFuture> provideCodeMinings(IDocument document) { URI docURI = LSPEclipseUtils.toUri(document); if (docURI != null) { final var param = new CodeLensParams(LSPEclipseUtils.toTextDocumentIdentifier(docURI)); LanguageServerDocumentExecutor executor = LanguageServers.forDocument(document) .withFilter(sc -> sc.getCodeLensProvider() != null); - return executor.collectAll((w, ls) -> ls.getTextDocumentService().codeLens(param) - .thenApply(codeLenses -> LanguageServers.streamSafely(codeLenses) - .map(codeLens -> toCodeMining(document, w, codeLens)) - .filter(Objects::nonNull))) - .thenApply(result -> result.stream().flatMap(s -> s).toList()); - } - else { + return executor + .collectAll((w, ls) -> ls.getTextDocumentService().codeLens(param) + .thenApply(codeLenses -> LanguageServers.streamSafely(codeLenses) + .map(codeLens -> toCodeMining(document, w, codeLens)).filter(Objects::nonNull))) + .thenApply(result -> result.stream().flatMap(s -> s).toList()); + } else { return null; } } - private LSPCodeMining toCodeMining(IDocument document, LanguageServerWrapper languageServerWrapper, CodeLens codeLens) { + private @Nullable LSPCodeMining toCodeMining(IDocument document, LanguageServerWrapper languageServerWrapper, + @Nullable CodeLens codeLens) { if (codeLens == null) { return null; } @@ -60,7 +60,7 @@ private LSPCodeMining toCodeMining(IDocument document, LanguageServerWrapper lan } @Override - public CompletableFuture> provideCodeMinings(ITextViewer viewer, + public @Nullable CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { IDocument document = viewer.getDocument(); return document != null ? provideCodeMinings(document) : null; 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 d916b6683..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 @@ -12,7 +12,6 @@ import java.util.function.Consumer; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -24,15 +23,16 @@ 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 { private CodeLens codeLens; private final LanguageServerWrapper languageServerWrapper; - private final @NonNull IDocument document; + private final IDocument document; - public LSPCodeMining(CodeLens codeLens, @NonNull IDocument document, LanguageServerWrapper languageServerWrapper, + public LSPCodeMining(CodeLens codeLens, IDocument document, LanguageServerWrapper languageServerWrapper, CodeLensProvider provider) throws BadLocationException { super(codeLens.getRange().getStart().getLine(), document, provider, null); this.codeLens = codeLens; @@ -41,7 +41,7 @@ public LSPCodeMining(CodeLens codeLens, @NonNull IDocument document, LanguageSer setLabel(getCodeLensString(codeLens)); } - protected static @Nullable String getCodeLensString(@NonNull CodeLens codeLens) { + protected static @Nullable String getCodeLensString(CodeLens codeLens) { Command command = codeLens.getCommand(); if (command == null || command.getTitle().isEmpty()) { return null; @@ -51,22 +51,26 @@ public LSPCodeMining(CodeLens codeLens, @NonNull IDocument document, LanguageSer @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)); } }); } @Override - public final Consumer getAction() { + public final @Nullable Consumer getAction() { final Command command = codeLens.getCommand(); if(command != null && command.getCommand() != null && !command.getCommand().isEmpty()) { return this::performAction; @@ -76,7 +80,8 @@ public final Consumer getAction() { } 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() @@ -86,5 +91,4 @@ private void performAction(MouseEvent mouseEvent) { } } - } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/package-info.java new file mode 100644 index 000000000..7832f693f --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codelens/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.codelens; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; 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 4972db60d..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 @@ -13,7 +13,6 @@ import java.util.function.Consumer; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; @@ -84,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, @@ -102,7 +101,7 @@ public void accept(MouseEvent event) { } } - public ColorInformationMining(ColorInformation colorInformation, @NonNull IDocument document, + public ColorInformationMining(ColorInformation colorInformation, IDocument document, TextDocumentIdentifier textDocumentIdentifier, LanguageServerWrapper languageServerWrapper, DocumentColorProvider colorProvider) throws BadLocationException { super(toPosition(colorInformation.getRange(), document), colorProvider, diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/DocumentColorProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/DocumentColorProvider.java index dbfe56029..5282491d0 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/DocumentColorProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/DocumentColorProvider.java @@ -20,7 +20,7 @@ import java.util.function.Function; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; @@ -51,7 +51,7 @@ public DocumentColorProvider() { colorTable = new HashMap<>(); } - private CompletableFuture> provideCodeMinings(@NonNull IDocument document) { + private @Nullable CompletableFuture> provideCodeMinings(IDocument document) { URI docURI = LSPEclipseUtils.toUri(document); if (docURI != null) { @@ -71,7 +71,7 @@ private CompletableFuture> provideCodeMinings(@NonNu } } - private ColorInformationMining toMining(ColorInformation color, @NonNull IDocument document, TextDocumentIdentifier textDocumentIdentifier, LanguageServerWrapper wrapper) { + private @Nullable ColorInformationMining toMining(ColorInformation color, IDocument document, TextDocumentIdentifier textDocumentIdentifier, LanguageServerWrapper wrapper) { try { return new ColorInformationMining(color, document, textDocumentIdentifier, wrapper, @@ -83,7 +83,7 @@ private ColorInformationMining toMining(ColorInformation color, @NonNull IDocume } @Override - public CompletableFuture> provideCodeMinings(ITextViewer viewer, + public @Nullable CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { IDocument document = viewer.getDocument(); return document != null ? provideCodeMinings(document) : null; @@ -102,7 +102,7 @@ public Color getColor(RGBA rgba, Display display) { return colorTable.computeIfAbsent(rgba, key -> new Color(display, rgba)); } - private static boolean isColorProvider(final ServerCapabilities capabilities) { + private static boolean isColorProvider(final @Nullable ServerCapabilities capabilities) { return capabilities != null && LSPEclipseUtils.hasCapability(capabilities.getColorProvider()); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/package-info.java new file mode 100644 index 000000000..a0d3536ae --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/color/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.color; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionProposalTools.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionProposalTools.java index cb55528f9..97aee48dd 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionProposalTools.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionProposalTools.java @@ -150,7 +150,7 @@ public static int getScoreOfFilterMatch(String documentFilter, String completion } private static int getScoreOfFilterMatchHelper(int prefixLength, String documentFilter, String completionFilter) { - if (documentFilter == null || documentFilter.isEmpty()) { + if (documentFilter.isEmpty()) { return 0; } char searchChar = documentFilter.charAt(0); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionSnippetParser.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionSnippetParser.java index 54f36dde8..a4be7779e 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionSnippetParser.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/CompletionSnippetParser.java @@ -24,7 +24,6 @@ import org.eclipse.jface.text.link.LinkedPosition; import org.eclipse.jface.text.link.ProposalPosition; - /** * A parser for the completion insert text in * snippet syntax 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 81e122dad..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 @@ -16,10 +16,13 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.completion; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -29,7 +32,7 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.internal.text.html.BrowserInformationControl; import org.eclipse.jface.text.AbstractReusableInformationControlCreator; import org.eclipse.jface.text.BadLocationException; @@ -118,25 +121,25 @@ public class LSCompletionProposal private final int initialOffset; protected int bestOffset = -1; protected int currentOffset = -1; - protected ITextViewer viewer; + protected @Nullable ITextViewer viewer; private final IDocument document; private final boolean isIncomplete; - private IRegion selection; - private LinkedPosition firstPosition; + private @Nullable IRegion selection; + private @Nullable LinkedPosition firstPosition; // private LSPDocumentInfo info; - private Integer rankCategory; - private Integer rankScore; - private String documentFilter; + private @Nullable Integer rankCategory; + private @Nullable Integer rankScore; + private @Nullable String documentFilter; private String documentFilterAddition = ""; //$NON-NLS-1$ private final LanguageServerWrapper languageServerWrapper; - public LSCompletionProposal(@NonNull IDocument document, int offset, @NonNull CompletionItem item, + public LSCompletionProposal(IDocument document, int offset, CompletionItem item, LanguageServerWrapper languageServerWrapper) { this(document, offset, item, null, languageServerWrapper, false); } - public LSCompletionProposal(@NonNull IDocument document, int offset, @NonNull CompletionItem item, - CompletionItemDefaults defaults, LanguageServerWrapper languageServerWrapper, boolean isIncomplete) { + public LSCompletionProposal(IDocument document, int offset, CompletionItem item, + @Nullable CompletionItemDefaults defaults, LanguageServerWrapper languageServerWrapper, boolean isIncomplete) { this.item = item; this.document = document; this.languageServerWrapper = languageServerWrapper; @@ -193,8 +196,8 @@ public String getDocumentFilter() throws BadLocationException { if (documentFilter != null) { return documentFilter + documentFilterAddition; } - documentFilter = CompletionProposalTools.getFilterFromDocument(document, currentOffset, - getFilterString(), bestOffset); + final var documentFilter = this.documentFilter = CompletionProposalTools.getFilterFromDocument(document, + currentOffset, getFilterString(), bestOffset); documentFilterAddition = ""; //$NON-NLS-1$ return documentFilter; } @@ -208,6 +211,7 @@ public String getDocumentFilter() throws BadLocationException { public int getRankScore() { if (rankScore != null) return rankScore; + int rankScore; try { rankScore = CompletionProposalTools.getScoreOfFilterMatch(getDocumentFilter(), getFilterString()); @@ -215,6 +219,7 @@ public int getRankScore() { LanguageServerPlugin.logError(e); rankScore = -1; } + this.rankScore = rankScore; return rankScore; } @@ -229,6 +234,7 @@ public int getRankCategory() { if (rankCategory != null) { return rankCategory; } + int rankCategory; try { rankCategory = CompletionProposalTools.getCategoryOfFilterMatch(getDocumentFilter(), getFilterString()); @@ -236,6 +242,7 @@ public int getRankCategory() { LanguageServerPlugin.logError(e); rankCategory = 5; } + this.rankCategory = rankCategory; return rankCategory; } @@ -312,7 +319,7 @@ public boolean isAutoInsertable() { } @Override - public IInformationControlCreator getInformationControlCreator() { + public @Nullable IInformationControlCreator getInformationControlCreator() { return new AbstractReusableInformationControlCreator() { @Override protected IInformationControl doCreateInformationControl(Shell parent) { @@ -348,7 +355,7 @@ public String getAdditionalProposalInfo(IProgressMonitor monitor) { return res.toString(); } - private boolean resolvesCompletionItem(final ServerCapabilities capabilities) { + private boolean resolvesCompletionItem(final @Nullable ServerCapabilities capabilities) { if (capabilities != null) { CompletionOptions completionProvider = capabilities.getCompletionProvider(); if (completionProvider != null) { @@ -373,7 +380,7 @@ private void resolveItem() { } } - private void updateCompletionItem(CompletionItem resolvedItem) { + private void updateCompletionItem(@Nullable CompletionItem resolvedItem) { if (resolvedItem == null) { return; } @@ -404,7 +411,7 @@ private void updateCompletionItem(CompletionItem resolvedItem) { } @Override - public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) { + public @Nullable CharSequence getPrefixCompletionText(IDocument document, int completionOffset) { return item.getInsertText().substring(completionOffset - bestOffset); } @@ -532,6 +539,7 @@ protected void apply(IDocument document, char trigger, int stateMask, int offset } boolean onlyPlaceCaret = regions.size() == 1 && regions.values().iterator().next().size() == 1 && regions.values().iterator().next().stream().noneMatch(ProposalPosition.class::isInstance); + final var viewer = this.viewer; if (viewer != null && !regions.isEmpty() && !onlyPlaceCaret) { final var model = new LinkedModeModel(); for (List positions: regions.values()) { @@ -558,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()))); @@ -583,7 +592,7 @@ private String adjustIndentation(IDocument document, String insertText, int inse return insertText.replace("\n", whitespacesBeforeInsertion); //$NON-NLS-1$ } - private int computeNewOffset(List additionalTextEdits, int insertionOffset, IDocument doc) { + private int computeNewOffset(@Nullable List additionalTextEdits, int insertionOffset, IDocument doc) { if (additionalTextEdits != null && !additionalTextEdits.isEmpty()) { int adjustment = 0; for (TextEdit edit : additionalTextEdits) { @@ -609,13 +618,23 @@ private int computeNewOffset(List additionalTextEdits, int insertionOf 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 -> { + IPath path = LSPEclipseUtils.toPath(document); + String fileName = path == null ? null : path.lastSegment(); yield fileName != null ? fileName : ""; //$NON-NLS-1$ } - case TM_FILENAME -> LSPEclipseUtils.toPath(document).lastSegment(); - 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 @@ -645,6 +664,7 @@ private String getVariableValue(String variableName) { } case TM_SELECTED_TEXT -> { try { + final var viewer = castNonNull(this.viewer); String selectedText = document.get(viewer.getSelectedRange().x, viewer.getSelectedRange().y); yield selectedText; } catch (BadLocationException e) { @@ -654,6 +674,7 @@ private String getVariableValue(String variableName) { } case TM_CURRENT_WORD -> { try { + final var viewer = castNonNull(this.viewer); String selectedText = document.get(viewer.getSelectedRange().x, viewer.getSelectedRange().y); int beforeSelection = viewer.getSelectedRange().x - 1; while (beforeSelection >= 0 && Character.isUnicodeIdentifierPart(document.getChar(beforeSelection))) { @@ -718,10 +739,12 @@ protected String getInsertText() { } @Override - public Point getSelection(IDocument document) { + public @Nullable Point getSelection(IDocument document) { + final var firstPosition = this.firstPosition; if (firstPosition != null) { return new Point(firstPosition.getOffset(), firstPosition.getLength()); } + final var selection = this.selection; if (selection == null) { return null; } @@ -729,28 +752,28 @@ public Point getSelection(IDocument document) { } @Override - public String getAdditionalProposalInfo() { + public @Nullable String getAdditionalProposalInfo() { return getAdditionalProposalInfo(new NullProgressMonitor()); } @Override - public Image getImage() { + public @Nullable Image getImage() { return LSPImages.imageFromCompletionItem(item); } @Override - public IContextInformation getContextInformation() { + public @Nullable IContextInformation getContextInformation() { return this; } @Override public String getContextDisplayString() { - return getAdditionalProposalInfo(); + return Objects.toString(getAdditionalProposalInfo()); } @Override public String getInformationDisplayString() { - return getAdditionalProposalInfo(); + return Objects.toString(getAdditionalProposalInfo()); } public String getSortText() { @@ -782,7 +805,7 @@ public void unselected(ITextViewer viewer) { } @Override - public boolean validate(IDocument document, int offset, DocumentEvent event) { + public boolean validate(IDocument document, int offset, @Nullable DocumentEvent event) { if (item.getLabel() == null || item.getLabel().isEmpty()) { return false; } @@ -805,7 +828,10 @@ public boolean validate(IDocument document, int offset, DocumentEvent event) { @Override public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) { this.viewer = viewer; - apply(viewer.getDocument(), trigger, stateMask, offset); + final var doc = viewer.getDocument(); + if (doc != null) { + apply(doc, trigger, stateMask, offset); + } } @Override @@ -819,7 +845,7 @@ public void apply(IDocument document) { } @Override - public char[] getTriggerCharacters() { + public char @Nullable [] getTriggerCharacters() { return null; } 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 764899165..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; @@ -30,7 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; @@ -68,18 +70,21 @@ public class LSContentAssistProcessor implements IContentAssistProcessor { private static final long TRIGGERS_TIMEOUT = 50; private static final long CONTEXT_INFORMATION_TIMEOUT = 1000; - private IDocument currentDocument; - private String errorMessage; + private @Nullable IDocument currentDocument; + private @Nullable String errorMessage; private final boolean errorAsCompletionItem; - private CompletableFuture<@NonNull List<@NonNull Void>> completionLanguageServersFuture; + private @Nullable CompletableFuture> completionLanguageServersFuture; private final Object completionTriggerCharsSemaphore = new Object(); private char[] completionTriggerChars = new char[0]; - private CompletableFuture<@NonNull List<@NonNull Void>> contextInformationLanguageServersFuture; + private @Nullable CompletableFuture> contextInformationLanguageServersFuture; private final Object contextTriggerCharsSemaphore = new Object(); 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() { @@ -99,8 +104,11 @@ public LSContentAssistProcessor(boolean errorAsCompletionItem, boolean incomplet private final Comparator proposalComparator = new LSCompletionProposalComparator(); @Override - public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { + public ICompletionProposal @Nullable [] computeCompletionProposals(ITextViewer viewer, int offset) { IDocument document = viewer.getDocument(); + if (document == null) { + return new LSCompletionProposal[0]; + } URI uri = LSPEclipseUtils.toUri(document); if (uri == null) { @@ -121,35 +129,42 @@ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int 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(); - 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() ? completion.getRight().isIncomplete() : false; - proposals.addAll(toProposals(document, offset, completion, w, cancellationSupport, isIncomplete)); + boolean isIncomplete = completion != null && completion.isRight() + ? completion.getRight().isIncomplete() + : false; + proposals.addAll(toProposals(document, offset, completion, w, cancellationSupport, + isIncomplete)); if (isIncomplete) { anyIncomplete.set(true); } }).exceptionally(t -> { if (!CancellationUtil.isRequestCancelledException(t)) { - LanguageServerPlugin.logError("'%s' LS failed to compute completion items.".formatted(w.serverDefinition.label), t); //$NON-NLS-1$ + LanguageServerPlugin.logError("'%s' LS failed to compute completion items." //$NON-NLS-1$ + .formatted(w.serverDefinition.label), t); } return null; })); cancellationSupport.execute(completionLanguageServersFuture); this.cancellationSupport = cancellationSupport; - // Wait for the result of all LSP requests 'textDocument/completions', this future will be canceled with the next completion - this.completionLanguageServersFuture.get(); + // Wait for the result of all LSP requests 'textDocument/completions', this + // future will be canceled with the next completion + completionLanguageServersFuture.get(); } catch (ExecutionException e) { - // Ideally exceptions from each LS are handled above and we shouldn't be getting into this block + // Ideally exceptions from each LS are handled above and we shouldn't be getting + // into this block this.errorMessage = createErrorMessage(offset, e); return createErrorProposal(offset, e); } catch (InterruptedException e) { @@ -172,8 +187,10 @@ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int Arrays.sort(completeProposals, proposalComparator); ICompletionProposal[] incompleteProposal = createIncompleProposal(offset, anyIncomplete.get()); if (incompleteProposal.length > 0) { - ICompletionProposal[] incompleteProposals = Arrays.copyOf(completeProposals, completeProposals.length + incompleteProposal.length, ICompletionProposal[].class); - System.arraycopy(incompleteProposal, 0, incompleteProposals, completeProposals.length, incompleteProposal.length); + ICompletionProposal[] incompleteProposals = Arrays.copyOf(completeProposals, + completeProposals.length + incompleteProposal.length, ICompletionProposal[].class); + System.arraycopy(incompleteProposal, 0, incompleteProposals, completeProposals.length, + incompleteProposal.length); return incompleteProposals; } return completeProposals; @@ -181,9 +198,9 @@ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int private ICompletionProposal[] createErrorProposal(int offset, Exception ex) { if (errorAsCompletionItem) { - return new ICompletionProposal[] {new CompletionProposal("", offset, 0, 0, null, Messages.completionError, null, ex.getMessage())}; //$NON-NLS-1$ - } - else { + return new ICompletionProposal[] { + new CompletionProposal("", offset, 0, 0, null, Messages.completionError, null, ex.getMessage()) }; //$NON-NLS-1$ + } else { return new ICompletionProposal[0]; } } @@ -194,12 +211,13 @@ private String createErrorMessage(int offset, Exception ex) { private ICompletionProposal[] createIncompleProposal(int offset, boolean incomplete) { if (incompleteAsCompletionItem && incomplete) { - return new ICompletionProposal[] {new CompletionProposal("", offset, 0, 0, null, Messages.completionIncomplete, null, Messages.continueIncomplete)}; //$NON-NLS-1$ + return new ICompletionProposal[] { new CompletionProposal("", offset, 0, 0, null, //$NON-NLS-1$ + Messages.completionIncomplete, null, Messages.continueIncomplete) }; } return new ICompletionProposal[0]; } - private void initiateLanguageServers(@NonNull IDocument document) { + private void initiateLanguageServers(IDocument document) { if (currentDocument != document) { this.currentDocument = document; if (this.completionLanguageServersFuture != null) { @@ -220,8 +238,9 @@ private void initiateLanguageServers(@NonNull 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()); @@ -229,8 +248,9 @@ private void initiateLanguageServers(@NonNull 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()); } @@ -242,38 +262,38 @@ private void initiateLanguageServers(@NonNull IDocument document) { private void initiateLanguageServers() { ITextEditor textEditor = UI.getActiveTextEditor(); - if(textEditor != null) { + if (textEditor != null) { IDocument document = LSPEclipseUtils.getDocument(textEditor); if (document != null) { initiateLanguageServers(document); } } } - private static List toProposals(IDocument document, - int offset, Either, CompletionList> completionList, LanguageServerWrapper languageServerWrapper, CancelChecker cancelChecker, boolean isIncomplete) { + + private static List toProposals(IDocument document, int offset, + @Nullable Either, CompletionList> completionList, + LanguageServerWrapper languageServerWrapper, CancelChecker cancelChecker, boolean isIncomplete) { if (completionList == null) { return Collections.emptyList(); } - //Stop the compute of ICompletionProposal if the completion has been cancelled + // Stop the compute of ICompletionProposal if the completion has been cancelled cancelChecker.checkCanceled(); CompletionItemDefaults defaults = completionList.map(o -> null, CompletionList::getItemDefaults); - List items = completionList.isLeft() ? completionList.getLeft() : completionList.getRight().getItems(); + List items = completionList.isLeft() ? completionList.getLeft() + : completionList.getRight().getItems(); return items.stream() // - .filter(Objects::nonNull) - .map(item -> new LSCompletionProposal(document, offset, item, defaults, + .filter(Objects::nonNull).map(item -> new LSCompletionProposal(document, offset, item, defaults, languageServerWrapper, isIncomplete)) .filter(proposal -> { - //Stop the compute of ICompletionProposal if the completion has been cancelled + // Stop the compute of ICompletionProposal if the completion has been cancelled cancelChecker.checkCanceled(); return true; - }) - .filter(proposal -> proposal.validate(document, offset, null)) - .map(ICompletionProposal.class::cast) + }).filter(proposal -> proposal.validate(document, offset, null)).map(ICompletionProposal.class::cast) .toList(); } @Override - public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { + public IContextInformation @Nullable [] computeContextInformation(ITextViewer viewer, int offset) { IDocument document = viewer.getDocument(); if (document == null) { return new IContextInformation[] { /* TODO? show error in context information */ }; @@ -289,17 +309,17 @@ public IContextInformation[] computeContextInformation(ITextViewer viewer, int o List contextInformations = Collections.synchronizedList(new ArrayList<>()); try { this.contextInformationLanguageServersFuture = LanguageServers.forDocument(document) - .withFilter(capabilities -> capabilities.getSignatureHelpProvider() != null).collectAll( - ls -> ls.getTextDocumentService().signatureHelp(param).thenAccept(signatureHelp -> { - if (signatureHelp != null) { - signatureHelp.getSignatures().stream() - .map(LSContentAssistProcessor::toContextInformation) - .forEach(contextInformations::add); - } - })); + .withFilter(capabilities -> capabilities.getSignatureHelpProvider() != null) + .collectAll(ls -> ls.getTextDocumentService().signatureHelp(param).thenAccept(signatureHelp -> { + if (signatureHelp != null) { + signatureHelp.getSignatures().stream().map(LSContentAssistProcessor::toContextInformation) + .forEach(contextInformations::add); + } + })); this.contextInformationLanguageServersFuture.get(CONTEXT_INFORMATION_TIMEOUT, TimeUnit.MILLISECONDS); } catch (ResponseErrorException | ExecutionException e) { - if (!CancellationUtil.isRequestCancelledException(e)) { // do not report error if the server has cancelled the request + if (!CancellationUtil.isRequestCancelledException(e)) { // do not report error if the server has cancelled + // the request LanguageServerPlugin.logError(e); } return new IContextInformation[] { /* TODO? show error in context information */ }; @@ -308,7 +328,8 @@ public IContextInformation[] computeContextInformation(ITextViewer viewer, int o Thread.currentThread().interrupt(); return new IContextInformation[] { /* TODO? show error in context information */ }; } catch (TimeoutException e) { - LanguageServerPlugin.logWarning("Could not compute context information due to timeout after " + CONTEXT_INFORMATION_TIMEOUT + " milliseconds", e); //$NON-NLS-1$//$NON-NLS-2$ + LanguageServerPlugin.logWarning("Could not compute context information due to timeout after " //$NON-NLS-1$ + + CONTEXT_INFORMATION_TIMEOUT + " milliseconds", e); //$NON-NLS-1$ return new IContextInformation[] { /* TODO? show error in context information */ }; } return contextInformations.toArray(IContextInformation[]::new); @@ -317,14 +338,14 @@ public IContextInformation[] computeContextInformation(ITextViewer viewer, int o private static IContextInformation toContextInformation(SignatureInformation information) { final var signature = new StringBuilder(information.getLabel()); String docString = LSPEclipseUtils.getDocString(information.getDocumentation()); - if (docString!=null && !docString.isEmpty()) { + if (docString != null && !docString.isEmpty()) { signature.append('\n').append(docString); } return new ContextInformation(information.getLabel(), signature.toString()); } - private void getFuture(CompletableFuture<@NonNull List<@NonNull Void>> future) { - if(future == null) { + private void getFuture(@Nullable CompletableFuture> future) { + if (future == null) { return; } @@ -334,15 +355,18 @@ private void getFuture(CompletableFuture<@NonNull List<@NonNull Void>> future) { LanguageServerPlugin.logError(e); Thread.currentThread().interrupt(); } catch (TimeoutException e) { - LanguageServerPlugin.logWarning("Could not get trigger characters due to timeout after " + TRIGGERS_TIMEOUT + " milliseconds", e); //$NON-NLS-1$//$NON-NLS-2$ + LanguageServerPlugin.logWarning( + "Could not get trigger characters due to timeout after " + TRIGGERS_TIMEOUT + " milliseconds", e); //$NON-NLS-1$//$NON-NLS-2$ } catch (OperationCanceledException | ResponseErrorException | ExecutionException | CancellationException e) { - if (!CancellationUtil.isRequestCancelledException(e)) { // do not report error if the server has cancelled the request + if (!CancellationUtil.isRequestCancelledException(e)) { // do not report error if the server has cancelled + // the request LanguageServerPlugin.logError(e); } } } - private static char[] mergeTriggers(char[] initialArray, Collection additionalTriggers) { + private static char[] mergeTriggers(char @Nullable [] initialArray, + @Nullable Collection additionalTriggers) { if (initialArray == null) { initialArray = new char[0]; } @@ -353,8 +377,8 @@ private static char[] mergeTriggers(char[] initialArray, Collection addi for (char c : initialArray) { triggers.add(c); } - additionalTriggers.stream().filter(s -> !Strings.isNullOrEmpty(s)) - .map(triggerChar -> triggerChar.charAt(0)).forEach(triggers::add); + additionalTriggers.stream().filter(s -> !Strings.isNullOrEmpty(s)).map(triggerChar -> triggerChar.charAt(0)) + .forEach(triggers::add); char[] res = new char[triggers.size()]; int i = 0; for (Character c : triggers) { @@ -365,26 +389,26 @@ private static char[] mergeTriggers(char[] initialArray, Collection addi } @Override - public char[] getCompletionProposalAutoActivationCharacters() { + public char @Nullable [] getCompletionProposalAutoActivationCharacters() { initiateLanguageServers(); getFuture(completionLanguageServersFuture); return completionTriggerChars; } @Override - public char[] getContextInformationAutoActivationCharacters() { + public char @Nullable [] getContextInformationAutoActivationCharacters() { initiateLanguageServers(); getFuture(contextInformationLanguageServersFuture); return contextTriggerChars; } @Override - public String getErrorMessage() { + public @Nullable String getErrorMessage() { return this.errorMessage; } @Override - public IContextInformationValidator getContextInformationValidator() { + public @Nullable IContextInformationValidator getContextInformationValidator() { return new ContextInformationValidator(this); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/package-info.java new file mode 100644 index 000000000..96e2a34b1 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/completion/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.completion; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/LSBasedHyperlink.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/LSBasedHyperlink.java index 90743a3de..e97a7586a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/LSBasedHyperlink.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/LSBasedHyperlink.java @@ -22,7 +22,6 @@ import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.lsp4e.LSPEclipseUtils; @@ -46,11 +45,11 @@ public LSBasedHyperlink(Either location, IRegion highlig this.locationType = locationType; } - public LSBasedHyperlink(@NonNull Location location, IRegion linkRegion, String locationType) { + public LSBasedHyperlink(Location location, IRegion linkRegion, String locationType) { this(Either.forLeft(location), linkRegion, locationType); } - public LSBasedHyperlink(@NonNull LocationLink locationLink, IRegion linkRegion, String locationType) { + public LSBasedHyperlink(LocationLink locationLink, IRegion linkRegion, String locationType) { this(Either.forRight(locationLink), linkRegion, locationType); } @@ -70,8 +69,6 @@ public String getHyperlinkText() { } /** - * - * @return * @noreference test only */ public Either getLocation() { @@ -88,17 +85,15 @@ public void open() { } private String getLabel() { - if (this.location != null) { - String uri = this.location.isLeft() ? this.location.getLeft().getUri() : this.location.getRight().getTargetUri(); - if (uri != null) { - if (uri.startsWith(LSPEclipseUtils.FILE_URI) && uri.length() > LSPEclipseUtils.FILE_URI.length()) { - return getFileBasedLabel(uri); - } - else if (uri.startsWith(LSPEclipseUtils.INTRO_URL)) { - return getIntroUrlBasedLabel(uri); - } - return getGenericUriBasedLabel(uri); + String uri = this.location.isLeft() ? this.location.getLeft().getUri() : this.location.getRight().getTargetUri(); + if (uri != null) { + if (uri.startsWith(LSPEclipseUtils.FILE_URI) && uri.length() > LSPEclipseUtils.FILE_URI.length()) { + return getFileBasedLabel(uri); } + else if (uri.startsWith(LSPEclipseUtils.INTRO_URL)) { + return getIntroUrlBasedLabel(uri); + } + return getGenericUriBasedLabel(uri); } return locationType; @@ -113,8 +108,7 @@ private String getIntroUrlBasedLabel(String uri) { return locationType + DASH_SEPARATOR + label; } } - } - catch (Exception e) { + } catch (Exception e) { LanguageServerPlugin.logError(e.getMessage(), e); } @@ -129,7 +123,7 @@ private String getFileBasedLabel(String uriStr) { URI uri = URI.create(uriStr); IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IFile[] files = workspaceRoot.findFilesForLocationURI(uri); - if (files != null && files.length == 1 && files[0].getProject() != null) { + if (files.length == 1 && files[0].getProject() != null) { IFile file = files[0]; IPath containerPath = file.getParent().getProjectRelativePath(); return locationType + DASH_SEPARATOR + file.getName() + DASH_SEPARATOR + file.getProject().getName() diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/OpenDeclarationHyperlinkDetector.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/OpenDeclarationHyperlinkDetector.java index 000631143..f20d1d66f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/OpenDeclarationHyperlinkDetector.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/OpenDeclarationHyperlinkDetector.java @@ -22,6 +22,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; @@ -44,7 +46,7 @@ public class OpenDeclarationHyperlinkDetector extends AbstractHyperlinkDetector { @Override - public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { + public IHyperlink @Nullable [] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { final IDocument document = textViewer.getDocument(); if (document == null) { return null; @@ -97,7 +99,7 @@ public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boo * the LSP locations */ private static Collection toHyperlinks(IDocument document, IRegion region, - String locationType, Either, List> locations) { + String locationType, @NonNullByDefault({}) Either, List> locations) { if (locations == null) { return Collections.emptyList(); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/package-info.java new file mode 100644 index 000000000..76eb1eb96 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/declaration/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.declaration; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/DiagnosticAnnotation.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/DiagnosticAnnotation.java index 687a8aebe..1f5fa337a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/DiagnosticAnnotation.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/DiagnosticAnnotation.java @@ -11,6 +11,7 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.diagnostics; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.source.Annotation; import org.eclipse.lsp4j.Diagnostic; @@ -33,7 +34,7 @@ public String getType() { } @Override - public void setType(String type) { + public void setType(@Nullable String type) { throw new UnsupportedOperationException(); } @@ -43,7 +44,7 @@ public String getText() { } @Override - public void setText(String text) { + public void setText(@Nullable String text) { throw new UnsupportedOperationException(); } 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 062d1aab9..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 @@ -34,7 +34,6 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -57,17 +56,17 @@ public class LSPDiagnosticsToMarkers implements Consumer markerAttributeComputer; - public LSPDiagnosticsToMarkers(@NonNull String serverId, @Nullable String markerType, @Nullable IMarkerAttributeComputer markerAttributeComputer) { + public LSPDiagnosticsToMarkers(String serverId, @Nullable String markerType, @Nullable IMarkerAttributeComputer markerAttributeComputer) { this.languageServerId = serverId; this.markerType = markerType != null ? markerType : LS_DIAGNOSTIC_MARKER_TYPE; this.markerAttributeComputer = Optional.ofNullable(markerAttributeComputer); } - public LSPDiagnosticsToMarkers(@NonNull String serverId) { + public LSPDiagnosticsToMarkers(String serverId) { this(serverId, null, null); } @@ -79,7 +78,7 @@ public LSPDiagnosticsToMarkers(@NonNull String serverId) { * @deprecated */ @Deprecated - public LSPDiagnosticsToMarkers(IProject project, @NonNull String serverId) { + public LSPDiagnosticsToMarkers(IProject project, String serverId) { this(serverId); } @@ -105,7 +104,7 @@ public void accept(PublishDiagnosticsParams diagnostics) { } } - private void updateEditorAnnotations(@NonNull ISourceViewer sourceViewer, PublishDiagnosticsParams diagnostics) { + private void updateEditorAnnotations(ISourceViewer sourceViewer, PublishDiagnosticsParams diagnostics) { IAnnotationModel annotationModel = sourceViewer.getAnnotationModel(); if (annotationModel == null) { return; @@ -120,9 +119,12 @@ private void updateEditorAnnotations(@NonNull ISourceViewer sourceViewer, Publis 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); } @@ -134,12 +136,12 @@ private void updateEditorAnnotations(@NonNull ISourceViewer sourceViewer, Publis private WorkspaceJob updateMarkers(PublishDiagnosticsParams diagnostics, IResource resource) { WorkspaceJob job = new WorkspaceJob("Update markers from diagnostics") { //$NON-NLS-1$ @Override - public boolean belongsTo(Object family) { + public boolean belongsTo(@Nullable Object family) { return LanguageServerPlugin.FAMILY_UPDATE_MARKERS == family; } @Override - public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + public IStatus runInWorkspace(@Nullable IProgressMonitor monitor) throws CoreException { if (!resource.exists()) { return Status.OK_STATUS; } @@ -203,7 +205,7 @@ public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { return job; } - protected void updateMarker(@NonNull Map targetAttributes, @NonNull IMarker marker) { + protected void updateMarker(Map targetAttributes, IMarker marker) { try { if (!targetAttributes.equals(marker.getAttributes())) { marker.setAttributes(targetAttributes); @@ -213,7 +215,7 @@ protected void updateMarker(@NonNull Map targetAttributes, @NonN } } - private IMarker getExistingMarkerFor(IDocument document, Diagnostic diagnostic, Set remainingMarkers) { + private @Nullable IMarker getExistingMarkerFor(@Nullable IDocument document, Diagnostic diagnostic, Set remainingMarkers) { if (document == null) { return null; } @@ -236,14 +238,11 @@ private IMarker getExistingMarkerFor(IDocument document, Diagnostic diagnostic, return null; } - private @NonNull Map computeMarkerAttributes(@Nullable IDocument document, - @NonNull Diagnostic diagnostic, @NonNull IResource resource) { + private Map computeMarkerAttributes(@Nullable IDocument document, + Diagnostic diagnostic, IResource resource) { Either code = diagnostic.getCode(); if (code != null && code.isLeft()) { - String left = code.getLeft(); - if (left != null) { - diagnostic.setCode(Either.forLeft(left.intern())); - } + diagnostic.setCode(Either.forLeft(code.getLeft().intern())); } String source = diagnostic.getSource(); if (source != null) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/package-info.java new file mode 100644 index 000000000..44c072a3c --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.diagnostics; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; 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 cdfc87aed..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 @@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; @@ -30,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; @@ -63,13 +63,13 @@ public String getHyperlinkText() { @Override public void open() { - LSPEclipseUtils.open(uri, UI.getActivePage(), null, true); + LSPEclipseUtils.open(uri, null, true); } } @Override - public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { + public IHyperlink @Nullable [] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { final IDocument document = textViewer.getDocument(); if (document == null) { return null; @@ -106,7 +106,7 @@ public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boo } } - private DocumentHyperlink toHyperlink(IRegion region, final IDocument document, DocumentLink link) { + private @Nullable DocumentHyperlink toHyperlink(IRegion region, final IDocument document, DocumentLink link) { DocumentHyperlink jfaceLink = null; try { int start = LSPEclipseUtils.toOffset(link.getRange().getStart(), document); 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 b9c449830..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 @@ -44,11 +44,11 @@ public class LSPDocumentLinkPresentationReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension, ITextViewerLifecycle { /** The target viewer. */ - private ITextViewer viewer; + private @Nullable ITextViewer viewer; - private CompletableFuture request; + private @Nullable CompletableFuture request; - private IDocument document; + private @Nullable IDocument document; @Override public void install(@Nullable ITextViewer viewer) { @@ -87,8 +87,10 @@ private void underline() { } } - private void underline(List links) { - if (document == null || links == null) { + private void underline(@Nullable List links) { + final var viewer = this.viewer; + final var document = this.document; + if (document == null || links == null || viewer == null) { return; } for (DocumentLink link : links) { @@ -147,12 +149,12 @@ private void cancel() { } @Override - public void setDocument(IDocument document) { + public void setDocument(@Nullable IDocument document) { this.document = document; } @Override - public void setProgressMonitor(IProgressMonitor monitor) { + public void setProgressMonitor(@Nullable IProgressMonitor monitor) { // Do nothing } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/package-info.java new file mode 100644 index 000000000..e6d7f7652 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/documentLink/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.documentLink; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/LSPFoldingReconcilingStrategy.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/LSPFoldingReconcilingStrategy.java index 76860452a..484071b61 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/LSPFoldingReconcilingStrategy.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/LSPFoldingReconcilingStrategy.java @@ -10,6 +10,8 @@ */ package org.eclipse.lsp4e.operations.folding; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.net.URI; import java.util.ArrayList; import java.util.Collections; @@ -21,7 +23,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -58,10 +60,10 @@ public class LSPFoldingReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension, IProjectionListener, ITextViewerLifecycle { - private IDocument document; - private ProjectionAnnotationModel projectionAnnotationModel; - private ProjectionViewer viewer; - private @NonNull List>> requests = List.of(); + private @Nullable IDocument document; + private @Nullable ProjectionAnnotationModel projectionAnnotationModel; + private @Nullable ProjectionViewer viewer; + private List>> requests = List.of(); private volatile long timestamp = 0; private final boolean collapseImports; @@ -127,13 +129,13 @@ public void markCollapsed() { } @Override - public void reconcile(IRegion subRegion) { - IDocument theDocument = document; - if (projectionAnnotationModel == null || theDocument == null) { + public void reconcile(@Nullable IRegion subRegion) { + final var document = this.document; + if (projectionAnnotationModel == null || document == null) { return; } - URI uri = LSPEclipseUtils.toUri(theDocument); + URI uri = LSPEclipseUtils.toUri(document); if (uri == null) { return; } @@ -141,12 +143,13 @@ public void reconcile(IRegion subRegion) { final var params = new FoldingRangeRequestParams(identifier); // cancel previous requests requests.forEach(request -> request.cancel(true)); - requests = LanguageServers.forDocument(theDocument).withCapability(ServerCapabilities::getFoldingRangeProvider) + requests = LanguageServers.forDocument(document) + .withCapability(ServerCapabilities::getFoldingRangeProvider) .computeAll(server -> server.getTextDocumentService().foldingRange(params)); requests.forEach(ranges -> ranges.thenAccept(this::applyFolding)); } - private void applyFolding(List ranges) { + private void applyFolding(@Nullable List ranges) { // these are what are passed off to the annotation model to // actually create and maintain the annotations final var deletions = new ArrayList(); @@ -188,8 +191,8 @@ public void install(ITextViewer viewer) { } if (viewer instanceof ProjectionViewer projViewer) { this.viewer = projViewer; - this.viewer.addProjectionListener(this); - this.projectionAnnotationModel = this.viewer.getProjectionAnnotationModel(); + projViewer.addProjectionListener(this); + this.projectionAnnotationModel = projViewer.getProjectionAnnotationModel(); } } @@ -204,7 +207,7 @@ public void uninstall() { } @Override - public void setDocument(IDocument document) { + public void setDocument(@Nullable IDocument document) { this.document = document; } @@ -237,9 +240,10 @@ public void projectionEnabled() { * the end line number * @throws BadLocationException */ - private void updateAnnotation(List deletions, - List existing, Map additions, int line, Integer endLineNumber, boolean collapsedByDefault) + private void updateAnnotation(List deletions, List existing, + Map additions, int line, Integer endLineNumber, boolean collapsedByDefault) throws BadLocationException { + final var document = castNonNull(this.document); int startOffset = document.getLineOffset(line); int endOffset = document.getLineOffset(endLineNumber) + document.getLineLength(endLineNumber); final var newPos = new Position(startOffset, endOffset - startOffset); @@ -263,7 +267,7 @@ private void updateAnnotation(List deletions, * @param deletions * the list of annotations to be deleted */ - protected void updateAnnotations(Annotation existingAnnotation, Position newPos, List deletions) { + protected void updateAnnotations(Annotation existingAnnotation, @Nullable Position newPos, List deletions) { if (existingAnnotation instanceof FoldingAnnotation foldingAnnotation) { // if a new position can be calculated then update the position of // the annotation, @@ -296,16 +300,18 @@ protected void updateAnnotations(Annotation existingAnnotation, Position newPos, */ protected void markInvalidAnnotationsForDeletion(List deletions, List existing) { + final var projectionAnnotationModel = this.projectionAnnotationModel; + if (projectionAnnotationModel == null) + return; Iterator iter = projectionAnnotationModel.getAnnotationIterator(); if (iter != null) { while (iter.hasNext()) { - Annotation anno = iter.next(); - if (anno instanceof FoldingAnnotation folding) { - Position pos = projectionAnnotationModel.getPosition(anno); + if (iter.next() instanceof FoldingAnnotation foldingAnno) { + Position pos = projectionAnnotationModel.getPosition(foldingAnno); if (pos.length == 0) { - deletions.add(folding); + deletions.add(foldingAnno); } else { - existing.add(folding); + existing.add(foldingAnno); } } } @@ -324,7 +330,7 @@ public void reconcile(DirtyRegion dirtyRegion, IRegion partition) { } @Override - public void setProgressMonitor(IProgressMonitor monitor) { + public void setProgressMonitor(@Nullable IProgressMonitor monitor) { // Do nothing } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/package-info.java new file mode 100644 index 000000000..47e753b2e --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/folding/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.folding; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatFilesHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatFilesHandler.java index 595ff38bc..b471023a6 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatFilesHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatFilesHandler.java @@ -30,10 +30,11 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.e4.core.commands.ExpressionContext; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.TextSelection; @@ -53,9 +54,12 @@ public class LSPFormatFilesHandler extends AbstractHandler { protected final LSPFormatter formatter = new LSPFormatter(); @Override - public Object execute(final ExecutionEvent event) throws ExecutionException { + public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException { if (event.getApplicationContext() instanceof final ExpressionContext ctx) { final var job = Job.create(Messages.LSPFormatFilesHandler_FormattingSelectedFiles, monitor -> { + if (monitor == null) + monitor = new NullProgressMonitor(); + final var selectedFiles = getSelectedFiles(ctx); final var subMonitor = SubMonitor.convert(monitor, selectedFiles.size()); for (final IFile file : selectedFiles) { @@ -73,7 +77,7 @@ public Object execute(final ExecutionEvent event) throws ExecutionException { return null; } - protected void formatFile(final @NonNull IFile file, final IProgressMonitor monitor) { + protected void formatFile(final IFile file, final IProgressMonitor monitor) { if (!file.exists() || !LanguageServersRegistry.getInstance().canUseLanguageServer(file)) return; @@ -122,12 +126,12 @@ protected void saveDocument(IDocumentProvider docProvider, IFile file, IProgress } } - protected Set<@NonNull IFile> getSelectedFiles(final ExpressionContext ctx) { + protected Set getSelectedFiles(final ExpressionContext ctx) { final var selection = getSelection(ctx); if (selection.isEmpty()) return Collections.emptySet(); - final var files = new HashSet<@NonNull IFile>(); + final var files = new HashSet(); for (final var item : selection) { try { if (item instanceof final IResource resource) { @@ -164,7 +168,7 @@ protected Collection getSelection(final ExpressionContext ctx) { } @Override - public void setEnabled(final Object evaluationContext) { + public void setEnabled(final @Nullable Object evaluationContext) { if (evaluationContext instanceof ExpressionContext ctx) { final var selection = getSelection(ctx); if (selection.isEmpty()) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatHandler.java index ef1f29b88..6833d6dd0 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/LSPFormatHandler.java @@ -16,6 +16,7 @@ import java.util.ConcurrentModificationException; import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; @@ -66,7 +67,7 @@ protected void execute(ExecutionEvent event, ITextEditor textEditor) { } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(LSPFormatter::supportsFormatting, this::hasSelection); } } 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 7e010d354..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 @@ -17,7 +17,6 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -38,7 +37,7 @@ import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; public class LSPFormatter { - public CompletableFuture> requestFormatting(@NonNull IDocument document, @NonNull ITextSelection textSelection) throws BadLocationException { + public CompletableFuture> requestFormatting(IDocument document, ITextSelection textSelection) throws BadLocationException { URI uri = LSPEclipseUtils.toUri(document); if (uri == null) { return CompletableFuture.completedFuture(Optional.empty()); @@ -58,7 +57,7 @@ public CompletableFuture> requestFormatting(@NonNull ID 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/format/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/package-info.java new file mode 100644 index 000000000..baf4b0b95 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/format/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.format; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/HighlightReconcilingStrategy.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/HighlightReconcilingStrategy.java index 3d99a6904..9245dc42b 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/HighlightReconcilingStrategy.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/HighlightReconcilingStrategy.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.highlight; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import java.net.URI; import java.util.HashMap; import java.util.Iterator; @@ -27,7 +29,6 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -73,19 +74,18 @@ public class HighlightReconcilingStrategy public static final String TEXT_ANNOTATION_TYPE = "org.eclipse.lsp4e.text"; //$NON-NLS-1$ private boolean enabled; - private ISourceViewer sourceViewer; - private IDocument document; - - private Job highlightJob; + private @Nullable ISourceViewer sourceViewer; + private @Nullable IDocument document; + private @Nullable Job highlightJob; /** * Holds the current occurrence annotations. */ - private Annotation[] fOccurrenceAnnotations = null; + private Annotation @Nullable [] fOccurrenceAnnotations = null; class EditorSelectionChangedListener implements ISelectionChangedListener { - public void install(ISelectionProvider selectionProvider) { + public void install(@Nullable ISelectionProvider selectionProvider) { if (selectionProvider == null) return; @@ -96,7 +96,7 @@ public void install(ISelectionProvider selectionProvider) { } } - public void uninstall(ISelectionProvider selectionProvider) { + public void uninstall(@Nullable ISelectionProvider selectionProvider) { if (selectionProvider == null) return; @@ -124,9 +124,9 @@ private void updateHighlights(ISelection selection) { } } - private EditorSelectionChangedListener editorSelectionChangedListener; + private EditorSelectionChangedListener editorSelectionChangedListener = lateNonNull(); - private @NonNull List<@NonNull CompletableFuture<@Nullable List>> requests = List.of(); + private List>> requests = List.of(); @Override public void install(ITextViewer viewer) { @@ -136,7 +136,7 @@ public void install(ITextViewer viewer) { this.enabled = preferences.getBoolean(TOGGLE_HIGHLIGHT_PREFERENCE, true); this.sourceViewer = thisSourceViewer; editorSelectionChangedListener = new EditorSelectionChangedListener(); - editorSelectionChangedListener.install(sourceViewer.getSelectionProvider()); + editorSelectionChangedListener.install(thisSourceViewer.getSelectionProvider()); } } @@ -152,16 +152,16 @@ public void uninstall() { } @Override - public void setProgressMonitor(IProgressMonitor monitor) { - + public void setProgressMonitor(@Nullable IProgressMonitor monitor) { } @Override public void initialReconcile() { + final var sourceViewer = this.sourceViewer; if (sourceViewer != null) { ISelectionProvider selectionProvider = sourceViewer.getSelectionProvider(); final StyledText textWidget = sourceViewer.getTextWidget(); - if (textWidget != null && selectionProvider != null) { + if (textWidget != null) { textWidget.getDisplay().asyncExec(() -> { if (!textWidget.isDisposed()) { updateHighlights(selectionProvider.getSelection()); @@ -172,7 +172,7 @@ public void initialReconcile() { } @Override - public void setDocument(IDocument document) { + public void setDocument(@Nullable IDocument document) { this.document = document; } @@ -183,8 +183,10 @@ public void setDocument(IDocument document) { * @param caretOffset * @param monitor */ - private void collectHighlights(int caretOffset, IProgressMonitor monitor) { - if (sourceViewer == null || !enabled || monitor.isCanceled()) { + private void collectHighlights(int caretOffset, @Nullable IProgressMonitor monitor) { + final var sourceViewer = this.sourceViewer; + final var document = this.document; + if (sourceViewer == null || document == null || !enabled || monitor != null && monitor.isCanceled()) { return; } cancel(); @@ -196,15 +198,16 @@ private void collectHighlights(int caretOffset, IProgressMonitor monitor) { return; } URI uri = LSPEclipseUtils.toUri(document); - if(uri == null) { + if (uri == null) { return; } final var identifier = LSPEclipseUtils.toTextDocumentIdentifier(uri); final var params = new DocumentHighlightParams(identifier, position); - requests = LanguageServers.forDocument(document).withCapability(ServerCapabilities::getDocumentHighlightProvider) + requests = LanguageServers.forDocument(document) + .withCapability(ServerCapabilities::getDocumentHighlightProvider) .computeAll(languageServer -> languageServer.getTextDocumentService().documentHighlight(params)); requests.forEach(request -> request.thenAcceptAsync(highlights -> { - if (!monitor.isCanceled()) { + if (monitor == null || !monitor.isCanceled()) { updateAnnotations(highlights, sourceViewer.getAnnotationModel()); } })); @@ -226,20 +229,19 @@ private void cancel() { * annotation model to update. */ private void updateAnnotations(@Nullable List highlights, IAnnotationModel annotationModel) { - if (highlights == null) + final var document = this.document; + if (highlights == null || document == null) return; final var annotationMap = new HashMap(highlights.size()); for (DocumentHighlight h : highlights) { - if (h != null) { - try { - int start = LSPEclipseUtils.toOffset(h.getRange().getStart(), document); - int end = LSPEclipseUtils.toOffset(h.getRange().getEnd(), document); - annotationMap.put(new Annotation(kindToAnnotationType(h.getKind()), false, null), - new org.eclipse.jface.text.Position(start, end - start)); - } catch (BadLocationException e) { - LanguageServerPlugin.logError(e); - } + try { + int start = LSPEclipseUtils.toOffset(h.getRange().getStart(), document); + int end = LSPEclipseUtils.toOffset(h.getRange().getEnd(), document); + annotationMap.put(new Annotation(kindToAnnotationType(h.getKind()), false, null), + new org.eclipse.jface.text.Position(start, end - start)); + } catch (Exception e) { + LanguageServerPlugin.logError(e); } } @@ -275,7 +277,12 @@ private Object getLockObject(IAnnotationModel annotationModel) { } void removeOccurrenceAnnotations() { + final var sourceViewer = this.sourceViewer; + if(sourceViewer == null) + return; + IAnnotationModel annotationModel = sourceViewer.getAnnotationModel(); + final var fOccurrenceAnnotations = this.fOccurrenceAnnotations; if (annotationModel == null || fOccurrenceAnnotations == null) return; @@ -286,7 +293,7 @@ void removeOccurrenceAnnotations() { for (Annotation fOccurrenceAnnotation : fOccurrenceAnnotations) annotationModel.removeAnnotation(fOccurrenceAnnotation); } - fOccurrenceAnnotations = null; + this.fOccurrenceAnnotations = null; } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/package-info.java new file mode 100644 index 000000000..faf1dfd2d --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/highlight/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.highlight; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/FocusableBrowserInformationControl.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/FocusableBrowserInformationControl.java index c79cc216e..875d080ba 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/FocusableBrowserInformationControl.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/FocusableBrowserInformationControl.java @@ -11,12 +11,10 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.hover; -import java.io.IOException; import java.net.URL; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.Platform; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.internal.text.html.BrowserInformationControl; import org.eclipse.jface.resource.ColorRegistry; @@ -71,7 +69,7 @@ public FocusableBrowserInformationControl(Shell parent) { super(parent, JFaceResources.DEFAULT_FONT, EditorsUI.getTooltipAffordanceString()); } - private double adjust(double height, Object margin) { + private double adjust(double height, @Nullable Object margin) { if (margin instanceof String marginString && marginString.endsWith("px")) { //$NON-NLS-1$ try { height += Integer.parseInt(marginString.substring(0, marginString.length() - 2)); @@ -125,7 +123,7 @@ protected void createContent(Composite parent) { b.setJavascriptEnabled(true); } - private static Object safeEvaluate(Browser browser, String expression) { + private static @Nullable Object safeEvaluate(Browser browser, String expression) { try { return browser.evaluate(expression); } catch (Exception ex) { @@ -144,7 +142,7 @@ private static boolean safeExecute(Browser browser, String expression) { } @Override - public void setInput(Object input) { + public void setInput(@Nullable Object input) { if (input instanceof String html) { input = styleHtml(html); } @@ -152,7 +150,7 @@ public void setInput(Object input) { } public String styleHtml(String html) { - if (html == null || html.isEmpty()) { + if (html.isEmpty()) { return html; } @@ -170,17 +168,20 @@ public String styleHtml(String html) { String hlStyle = null; try { - URL urlHJScript = FileLocator.toFileURL(LanguageServerPlugin.getDefault().getClass().getResource("/resources/highlight.min.js/highlight.min.js")); //$NON-NLS-1$ - URL urlHJCss = FileLocator.toFileURL(LanguageServerPlugin.getDefault().getClass().getResource(isDarkTheme() ? // + var pluginClass = LanguageServerPlugin.getDefault().getClass(); + URL urlHJScript = pluginClass.getResource("/resources/highlight.min.js/highlight.min.js"); //$NON-NLS-1$ + URL urlHJCss = pluginClass.getResource(isDarkTheme() ? // "/resources/highlight.min.js/styles/dark.min.css" : //$NON-NLS-1$ - "/resources/highlight.min.js/styles/default.min.css")); //$NON-NLS-1$ - if (urlHJScript != null && urlHJCss != null) { - hlStyle = "" + //$NON-NLS-1$ //$NON-NLS-2$ - "" + //$NON-NLS-1$ //$NON-NLS-2$ + "/resources/highlight.min.js/styles/default.min.css"); //$NON-NLS-1$ + URL fileUrlHJScript = urlHJScript == null ? null : FileLocator.toFileURL(urlHJScript); + URL fileUrlHJCss = urlHJCss == null ? null : FileLocator.toFileURL(urlHJCss); + if (fileUrlHJScript != null && fileUrlHJCss != null) { + hlStyle = "" + //$NON-NLS-1$ //$NON-NLS-2$ + "" + //$NON-NLS-1$ //$NON-NLS-2$ ""; //$NON-NLS-1$ } - } catch (IOException e) { - LanguageServerPlugin.logError(e); + } catch (Exception ex) { + LanguageServerPlugin.logError(ex); } int headIndex = html.indexOf(HEAD); @@ -197,7 +198,7 @@ private boolean isDarkTheme() { return (color.red * 0.299 + color.green * 0.587+ color.blue *0.114) < 128; //turn to grey and check the level } - private static @NonNull CharSequence toHTMLrgb(RGB rgb) { + private static CharSequence toHTMLrgb(RGB rgb) { final var builder = new StringBuilder(7); builder.append('#'); appendAsHexString(builder, rgb.red); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/LSPTextHover.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/LSPTextHover.java index 5869efd31..1933ffe3f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/LSPTextHover.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/LSPTextHover.java @@ -15,6 +15,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.hover; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -24,7 +26,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.internal.text.html.BrowserInformationControl; import org.eclipse.jface.text.AbstractReusableInformationControlCreator; @@ -61,39 +62,31 @@ public class LSPTextHover implements ITextHover, ITextHoverExtension { private static final MarkupParser MARKDOWN_PARSER = new MarkupParser(new MarkdownLanguage(true)); private static final int GET_TIMEOUT_MS = 1000; - private IRegion lastRegion; - private ITextViewer lastViewer; - private CompletableFuture<@NonNull List<@NonNull Hover>> request; + private @Nullable IRegion lastRegion; + private @Nullable ITextViewer lastViewer; + private @Nullable CompletableFuture> request; @Override - public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { - if (textViewer == null || hoverRegion == null) { - return null; - } - CompletableFuture hoverInfoFuture = getHoverInfoFuture(textViewer, hoverRegion); - if (hoverInfoFuture != null) { - try { - String result = hoverInfoFuture.get(GET_TIMEOUT_MS, TimeUnit.MILLISECONDS); - if (result != null) { - return result; - } - } catch (ExecutionException e) { - LanguageServerPlugin.logError(e); - } catch (InterruptedException e) { - LanguageServerPlugin.logError(e); - Thread.currentThread().interrupt(); - } catch (TimeoutException e) { - LanguageServerPlugin.logWarning("Could not get hover information due to timeout after " + GET_TIMEOUT_MS + " milliseconds", e); //$NON-NLS-1$ //$NON-NLS-2$ - } + public @Nullable String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { + CompletableFuture<@Nullable String> hoverInfoFuture = getHoverInfoFuture(textViewer, hoverRegion); + try { + return hoverInfoFuture.get(GET_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (ExecutionException e) { + LanguageServerPlugin.logError(e); + } catch (InterruptedException e) { + LanguageServerPlugin.logError(e); + Thread.currentThread().interrupt(); + } catch (TimeoutException e) { + LanguageServerPlugin.logWarning("Could not get hover information due to timeout after " + GET_TIMEOUT_MS + " milliseconds", e); //$NON-NLS-1$ //$NON-NLS-2$ } return null; } - public CompletableFuture getHoverInfoFuture(@NonNull ITextViewer textViewer, @NonNull IRegion hoverRegion) { + public CompletableFuture<@Nullable String> getHoverInfoFuture(ITextViewer textViewer, IRegion hoverRegion) { if (this.request == null || !textViewer.equals(this.lastViewer) || !hoverRegion.equals(this.lastRegion)) { initiateHoverRequest(textViewer, hoverRegion.getOffset()); } - return request.thenApply(hoversList -> { + return castNonNull(request).thenApply(hoversList -> { String result = hoversList.stream() .filter(Objects::nonNull) .map(LSPTextHover::getHoverString) @@ -108,11 +101,11 @@ public CompletableFuture getHoverInfoFuture(@NonNull ITextViewer textVie }); } - protected static @Nullable String getHoverString(@NonNull Hover hover) { + protected static @Nullable String getHoverString(Hover hover) { Either>, MarkupContent> hoverContent = hover.getContents(); if (hoverContent.isLeft()) { List> contents = hoverContent.getLeft(); - if (contents == null || contents.isEmpty()) { + if (contents.isEmpty()) { return null; } return contents.stream().map(content -> { @@ -139,20 +132,21 @@ public CompletableFuture getHoverInfoFuture(@NonNull ITextViewer textVie } @Override - public IRegion getHoverRegion(ITextViewer textViewer, int offset) { - if (textViewer == null) { - return null; - } - if (this.request == null || this.lastRegion == null || !textViewer.equals(this.lastViewer) - || offset < this.lastRegion.getOffset() || offset > lastRegion.getOffset() + lastRegion.getLength()) { + public @Nullable IRegion getHoverRegion(ITextViewer textViewer, int offset) { + final var lastRegion = this.lastRegion; + if (this.request == null || lastRegion == null || !textViewer.equals(this.lastViewer) + || offset < lastRegion.getOffset() || offset > lastRegion.getOffset() + lastRegion.getLength()) { initiateHoverRequest(textViewer, offset); } try { final IDocument document = textViewer.getDocument(); + if (document == null) { + return null; + } boolean[] oneHoverAtLeast = new boolean[] { false }; int[] regionStartOffset = new int[] { 0 }; int[] regionEndOffset = new int[] { document.getLength() }; - this.request.get(GET_TIMEOUT_MS, TimeUnit.MILLISECONDS).stream() + castNonNull(this.request).get(GET_TIMEOUT_MS, TimeUnit.MILLISECONDS).stream() .filter(Objects::nonNull) .map(Hover::getRange) .filter(Objects::nonNull) @@ -192,7 +186,7 @@ public IRegion getHoverRegion(ITextViewer textViewer, int offset) { * @param offset * the hovered offset. */ - private void initiateHoverRequest(@NonNull ITextViewer viewer, int offset) { + private void initiateHoverRequest(ITextViewer viewer, int offset) { final IDocument document = viewer.getDocument(); if (document == null) { return; @@ -210,7 +204,7 @@ private void initiateHoverRequest(@NonNull ITextViewer viewer, int offset) { } @Override - public IInformationControlCreator getHoverControlCreator() { + public @Nullable IInformationControlCreator getHoverControlCreator() { return new AbstractReusableInformationControlCreator() { @Override protected IInformationControl doCreateInformationControl(Shell parent) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/package-info.java new file mode 100644 index 000000000..46a8f2c04 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/hover/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.hover; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/InlayHintProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/InlayHintProvider.java index c1f76175c..188c9f9fe 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/InlayHintProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/inlayhint/InlayHintProvider.java @@ -16,7 +16,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; @@ -34,7 +34,7 @@ public class InlayHintProvider extends AbstractCodeMiningProvider { - private CompletableFuture> provideCodeMinings(@NonNull IDocument document) { + private @Nullable CompletableFuture> provideCodeMinings(IDocument document) { URI docURI = LSPEclipseUtils.toUri(document); if (docURI != null) { // Eclipse seems to request minings only when the document is loaded (or changed), rather than @@ -63,8 +63,8 @@ private CompletableFuture> provideCodeMinings(@NonNu } } - private LSPLineContentCodeMining toCodeMining(@NonNull IDocument document, @NonNull LanguageServerWrapper languageServerWrapper, - @NonNull InlayHint inlayHint) { + private @Nullable LSPLineContentCodeMining toCodeMining(IDocument document, LanguageServerWrapper languageServerWrapper, + InlayHint inlayHint) { try { return new LSPLineContentCodeMining(inlayHint, document, languageServerWrapper, InlayHintProvider.this); } catch (BadLocationException e) { @@ -74,7 +74,7 @@ private LSPLineContentCodeMining toCodeMining(@NonNull IDocument document, @NonN } @Override - public CompletableFuture> provideCodeMinings(ITextViewer viewer, + public @Nullable CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { IDocument document = viewer.getDocument(); return document != null ? provideCodeMinings(document) : null; 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 b7d2d5e9d..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 @@ -17,7 +17,6 @@ import java.util.stream.Collectors; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -49,14 +48,14 @@ public class LSPLineContentCodeMining extends LineContentCodeMining { private InlayHint inlayHint; - private final @NonNull LanguageServerWrapper wrapper; - private final @NonNull IDocument document; + private final LanguageServerWrapper wrapper; + private final IDocument document; - private Point location; - private FontData[] fontData; + private @Nullable Point location; + private FontData @Nullable [] fontData; - public LSPLineContentCodeMining(@NonNull InlayHint inlayHint, @NonNull IDocument document, @NonNull 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; @@ -65,15 +64,15 @@ public LSPLineContentCodeMining(@NonNull InlayHint inlayHint, @NonNull IDocument } @Override - public void setLabel(final String label) { + public void setLabel(final @Nullable String label) { if (label == null || label.isEmpty() || Character.isWhitespace(label.charAt(label.length() - 1))) super.setLabel(label); else super.setLabel(label + " "); //$NON-NLS-1$ } - protected static @Nullable String getInlayHintString(@NonNull InlayHint inlayHint) { - Either> label = inlayHint.getLabel(); + protected static @Nullable String getInlayHintString(InlayHint inlayHint) { + Either> label = inlayHint.getLabel(); return label.map(Function.identity(), (parts) -> { if (parts == null) { return null; @@ -83,12 +82,12 @@ public void setLabel(final 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)); } })); @@ -96,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(); @@ -122,30 +123,34 @@ private static org.eclipse.jface.text.Position toPosition(Position position, IDo } @Override - public final Consumer getAction() { - return inlayHint.getLabel().map(l -> null, r -> labelPartAction(r)); + public final @Nullable Consumer getAction() { + return inlayHint.getLabel().map(l -> null, this::labelPartAction); } - private Consumer labelPartAction(List labelParts) { + 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; } @@ -154,6 +159,7 @@ private Optional findLabelPart(MouseEvent me, List> request; + private @Nullable CompletableFuture> request; protected boolean fEnabled; protected void install() { @@ -49,7 +49,7 @@ protected void uninstall() { cancel(); } - protected CompletableFuture> collectLinkedEditingRanges(IDocument document, int offset) { + protected CompletableFuture> collectLinkedEditingRanges(@Nullable IDocument document, int offset) { cancel(); if (document == null) { @@ -68,7 +68,7 @@ protected CompletableFuture> collectLinkedEditingR } } - private boolean rangesContainOffset(@NonNull LinkedEditingRanges ranges, int offset, IDocument document) { + private boolean rangesContainOffset(LinkedEditingRanges ranges, int offset, IDocument document) { for (Range range : ranges.getRanges()) { if (LSPEclipseUtils.isOffsetInRange(offset, range, document)) { return true; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/LSPLinkedEditingReconcilingStrategy.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/LSPLinkedEditingReconcilingStrategy.java index 28a3656f6..14c3180ce 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/LSPLinkedEditingReconcilingStrategy.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/LSPLinkedEditingReconcilingStrategy.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.linkedediting; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -19,7 +21,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; @@ -53,14 +55,14 @@ public class LSPLinkedEditingReconcilingStrategy extends LSPLinkedEditingBase implements IReconcilingStrategy, IReconcilingStrategyExtension, ITextViewerLifecycle { - private ISourceViewer sourceViewer; - private IDocument fDocument; - private EditorSelectionChangedListener editorSelectionChangedListener; - private Job highlightJob; - private LinkedModeModel linkedModel; - - class EditorSelectionChangedListener implements ISelectionChangedListener { - public void install(ISelectionProvider selectionProvider) { + private @Nullable ISourceViewer sourceViewer; + private @Nullable IDocument document; + private @Nullable EditorSelectionChangedListener editorSelectionChangedListener; + private @Nullable Job highlightJob; + private @Nullable LinkedModeModel linkedModel; + + private final class EditorSelectionChangedListener implements ISelectionChangedListener { + public void install(@Nullable ISelectionProvider selectionProvider) { if (selectionProvider == null) return; @@ -71,7 +73,7 @@ public void install(ISelectionProvider selectionProvider) { } } - public void uninstall(ISelectionProvider selectionProvider) { + public void uninstall(@Nullable ISelectionProvider selectionProvider) { if (selectionProvider == null) return; @@ -94,13 +96,13 @@ public void install(ITextViewer viewer) { super.install(); this.sourceViewer = thisViewer; editorSelectionChangedListener = new EditorSelectionChangedListener(); - editorSelectionChangedListener.install(sourceViewer.getSelectionProvider()); + editorSelectionChangedListener.install(thisViewer.getSelectionProvider()); } } @Override public void uninstall() { - if (sourceViewer != null) { + if (sourceViewer != null && editorSelectionChangedListener != null) { editorSelectionChangedListener.uninstall(sourceViewer.getSelectionProvider()); } super.uninstall(); @@ -119,11 +121,12 @@ public void preferenceChange(PreferenceChangeEvent event) { } @Override - public void setProgressMonitor(IProgressMonitor monitor) { + public void setProgressMonitor(@Nullable IProgressMonitor monitor) { } @Override public void initialReconcile() { + final var sourceViewer = this.sourceViewer; if (sourceViewer != null) { ISelectionProvider selectionProvider = sourceViewer.getSelectionProvider(); final StyledText textWidget = sourceViewer.getTextWidget(); @@ -138,8 +141,8 @@ public void initialReconcile() { } @Override - public void setDocument(IDocument document) { - this.fDocument = document; + public void setDocument(@Nullable IDocument document) { + this.document = document; } @Override @@ -157,13 +160,14 @@ private void updateLinkedEditing(ISelection selection) { } private void updateLinkedEditing(int offset) { - if (sourceViewer != null && fDocument != null && fEnabled && linkedModel == null - || !linkedModel.anyPositionContains(offset)) { + final var linkedModel = this.linkedModel; + if (sourceViewer != null && document != null && fEnabled && (linkedModel == null + || !linkedModel.anyPositionContains(offset))) { if (linkedModel != null) { linkedModel.exit(ILinkedModeListener.EXIT_ALL); - linkedModel = null; + this.linkedModel = null; } - collectLinkedEditingRanges(fDocument, offset).thenAcceptAsync(optional -> { + collectLinkedEditingRanges(document, offset).thenAcceptAsync(optional -> { optional.ifPresent(this::applyLinkedEdit); }).exceptionally(e -> { if (!CancellationUtil.isRequestCancelledException(e)) { // do not report error if the server has cancelled the request @@ -174,7 +178,7 @@ private void updateLinkedEditing(int offset) { } } - private void applyLinkedEdit(LinkedEditingRanges ranges) { + private void applyLinkedEdit(@Nullable LinkedEditingRanges ranges) { if (highlightJob != null) { highlightJob.cancel(); } @@ -185,11 +189,12 @@ private void applyLinkedEdit(LinkedEditingRanges ranges) { highlightJob = new UIJob("LSP4E Linked Editing") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { - linkedModel = new LinkedModeModel(); + final var linkedModel = LSPLinkedEditingReconcilingStrategy.this.linkedModel = new LinkedModeModel(); try { linkedModel.addGroup(toJFaceGroup(ranges)); linkedModel.forceInstall(); - ITextSelection selectionBefore = (ITextSelection)sourceViewer.getSelectionProvider().getSelection(); + final var sourceViewer = castNonNull(LSPLinkedEditingReconcilingStrategy.this.sourceViewer); + ITextSelection selectionBefore = (ITextSelection) sourceViewer.getSelectionProvider().getSelection(); LinkedModeUI linkedMode = new EditorLinkedModeUI(linkedModel, sourceViewer); linkedMode.setExitPolicy((model, event, offset, length) -> { if (event.character == 0 || event.character == '\b') { @@ -220,12 +225,12 @@ public IStatus runInUIThread(IProgressMonitor monitor) { highlightJob.schedule(); } - String getValueInRange(IRegion selectedRegion, VerifyEvent event, int offset, int length) { + private @Nullable String getValueInRange(IRegion selectedRegion, VerifyEvent event, int offset, int length) { if (offset < selectedRegion.getOffset() || offset > selectedRegion.getOffset() + selectedRegion.getLength()) { return null; } try { - final var sb = new StringBuilder(fDocument.get(selectedRegion.getOffset(), selectedRegion.getLength())); // The range text before the insertion + final var sb = new StringBuilder(castNonNull(document).get(selectedRegion.getOffset(), selectedRegion.getLength())); // The range text before the insertion String newChars = event.character == 0 ? "" : Character.toString(event.character); //$NON-NLS-1$ sb.replace(offset - selectedRegion.getOffset(), offset - selectedRegion.getOffset() + selectedRegion.getLength(), newChars); return sb.toString(); @@ -235,12 +240,13 @@ String getValueInRange(IRegion selectedRegion, VerifyEvent event, int offset, in return null; } - private LinkedPositionGroup toJFaceGroup(@NonNull LinkedEditingRanges ranges) throws BadLocationException { + private LinkedPositionGroup toJFaceGroup(LinkedEditingRanges ranges) throws BadLocationException { + final var document = castNonNull(this.document); final var res = new LinkedPositionGroup(); for (Range range : ranges.getRanges()) { - int startOffset = LSPEclipseUtils.toOffset(range.getStart(), fDocument); - int length = LSPEclipseUtils.toOffset(range.getEnd(), fDocument) - startOffset; - res.addPosition(new LinkedPosition(fDocument, startOffset, length)); + int startOffset = LSPEclipseUtils.toOffset(range.getStart(), document); + int length = LSPEclipseUtils.toOffset(range.getEnd(), document) - startOffset; + res.addPosition(new LinkedPosition(document, startOffset, length)); } return res; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/package-info.java new file mode 100644 index 000000000..a476701b8 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/linkedediting/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.linkedediting; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchContentProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchContentProvider.java index addb1a48b..d999a4fd9 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchContentProvider.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.List; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.search.internal.ui.text.FileMatch; @@ -24,19 +25,22 @@ public class FileAndURIMatchContentProvider implements ITreeContentProvider { private final FileTreeContentProvider delegate; - private LSSearchResult searchResult; - private FileSearchResult filteredFileSearchResult; + private @Nullable LSSearchResult searchResult; + private @Nullable FileSearchResult filteredFileSearchResult; FileAndURIMatchContentProvider(FileTreeContentProvider delegate) { this.delegate = delegate; } @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + public void inputChanged(Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) { if (newInput instanceof FileSearchResult initial && initial.getQuery() instanceof FileSearchQuery query) { - this.filteredFileSearchResult = new FileSearchResult(query); - Arrays.stream(initial.getElements()).flatMap(element -> Arrays.stream(initial.getMatches(element))).filter(FileMatch.class::isInstance).forEach(this.filteredFileSearchResult::addMatch); - delegate.inputChanged(viewer, oldInput, this.filteredFileSearchResult); + final var filteredFileSearchResult = this.filteredFileSearchResult = new FileSearchResult(query); + Arrays.stream(initial.getElements()) // + .flatMap(element -> Arrays.stream(initial.getMatches(element))) // + .filter(FileMatch.class::isInstance) // + .forEach(filteredFileSearchResult::addMatch); + delegate.inputChanged(viewer, oldInput, filteredFileSearchResult); } if (newInput instanceof LSSearchResult searchResult) { this.searchResult = searchResult; @@ -44,7 +48,7 @@ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override - public Object[] getElements(Object inputElement) { + public Object[] getElements(@Nullable Object inputElement) { List res = new ArrayList<>(); res.addAll(Arrays.asList(delegate.getElements(inputElement == this.searchResult ? this.filteredFileSearchResult : inputElement))); if (inputElement instanceof AbstractTextSearchResult searchResult) { @@ -62,13 +66,13 @@ public Object[] getChildren(Object parentElement) { } @Override - public Object getParent(Object element) { + public @Nullable Object getParent(Object element) { return delegate.getParent(element); } @Override public boolean hasChildren(Object element) { - return delegate.hasChildren(element) || Arrays.asList(searchResult.getElements()).contains(element); + return delegate.hasChildren(element) || (searchResult != null && Arrays.asList(searchResult.getElements()).contains(element)); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchLabelProvider.java index 8f448a9d5..7e6ef72f2 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/FileAndURIMatchLabelProvider.java @@ -12,6 +12,7 @@ import java.net.URI; import java.net.URISyntaxException; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.StyledString; @@ -53,7 +54,7 @@ public void removeListener(ILabelProviderListener listener) { } @Override - public StyledString getStyledText(Object element) { + public @Nullable StyledString getStyledText(@Nullable Object element) { if (canDelegate(element)) { return resourceMatchDelegate.getStyledText(element); } @@ -84,7 +85,7 @@ public StyledString getStyledText(Object element) { } @Override - public Image getImage(Object element) { + public @Nullable Image getImage(@Nullable Object element) { if (canDelegate(element)) { return resourceMatchDelegate.getImage(element); } @@ -99,7 +100,7 @@ public Image getImage(Object element) { return null; } - private boolean canDelegate(Object element) { + private boolean canDelegate(@Nullable Object element) { return !(element instanceof URI || element instanceof URIMatch); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSFindReferences.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSFindReferences.java index bc49e668e..6f1d3d379 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSFindReferences.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSFindReferences.java @@ -15,6 +15,7 @@ import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.IHandler; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ISelection; @@ -49,7 +50,7 @@ protected void execute(ExecutionEvent event, ITextEditor textEditor) { } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getReferencesProvider, this::hasSelection); } 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 ba936aa7b..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,7 +14,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.references; -import java.net.URISyntaxException; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -29,7 +30,7 @@ import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; @@ -54,10 +55,10 @@ */ public class LSSearchQuery extends FileSearchQuery { - private final @NonNull IDocument document; + private final IDocument document; private final int offset; - private LSSearchResult result; + private @Nullable LSSearchResult result; /** * LSP search query to "Find references" from the given offset of the given @@ -66,28 +67,29 @@ public class LSSearchQuery extends FileSearchQuery { * @param offset * @param document */ - public LSSearchQuery(int offset, @NonNull IDocument document) { + public LSSearchQuery(int offset, IDocument document) { super("", false, false, true, true, null); //$NON-NLS-1$ this.document = document; this.offset = offset; } @Override - public IStatus run(IProgressMonitor monitor) throws OperationCanceledException { + public IStatus run(@Nullable IProgressMonitor monitor) throws OperationCanceledException { getSearchResult().removeAll(); try { // 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)); - @NonNull List<@NonNull CompletableFuture>> requests = LanguageServers.forDocument(document).withCapability(ServerCapabilities::getReferencesProvider) + List>> requests = LanguageServers.forDocument(document).withCapability(ServerCapabilities::getReferencesProvider) .computeAll(languageServer -> languageServer.getTextDocumentService().references(params)); CompletableFuture[] populateUIFutures = requests.stream().map(request -> request.thenAcceptAsync(locations -> { - if (locations != null) { + final var result = this.result; + if (locations != null && result != null) { // Convert each LSP Location to a Match search. locations.stream() // .filter(Objects::nonNull) // @@ -111,7 +113,7 @@ public IStatus run(IProgressMonitor monitor) throws OperationCanceledException { * the LSP location to convert. * @return the converted Eclipse search {@link Match}. */ - private static Match toMatch(@NonNull Location location) { + private static @Nullable Match toMatch(Location location) { IResource resource = LSPEclipseUtils.findResourceFor(location.getUri()); if (resource != null) { IDocument document = LSPEclipseUtils.getExistingDocument(resource); @@ -149,7 +151,7 @@ private static Match toMatch(@NonNull Location location) { } try { return URIMatch.create(location); - } catch (BadLocationException | URISyntaxException ex) { + } catch (Exception ex) { LanguageServerPlugin.logError(ex); return null; } @@ -157,8 +159,9 @@ private static Match toMatch(@NonNull Location location) { @Override public LSSearchResult getSearchResult() { + var result = this.result; if (result == null) { - result = new LSSearchResult(this); + result = this.result = new LSSearchResult(this); } return result; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResult.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResult.java index 6165b33a1..f5a1902d4 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResult.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResult.java @@ -18,6 +18,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.eclipse.core.resources.IFile; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.search.internal.ui.text.FileSearchResult; import org.eclipse.search.ui.ISearchResult; import org.eclipse.search.ui.text.AbstractTextSearchResult; @@ -42,7 +43,7 @@ public LSSearchResult(LSSearchQuery query) { private final Set nonFileElements = ConcurrentHashMap.newKeySet(); @Override - public IFile getFile(Object element) { + public @Nullable IFile getFile(@Nullable Object element) { return element instanceof IFile file ? file : null; } @@ -76,12 +77,12 @@ public LSSearchQuery getQuery() { } @Override - public IFileMatchAdapter getFileMatchAdapter() { + public @Nullable IFileMatchAdapter getFileMatchAdapter() { return this; } @Override - public IEditorMatchAdapter getEditorMatchAdapter() { + public @Nullable IEditorMatchAdapter getEditorMatchAdapter() { return this; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResultPage.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResultPage.java index 60fc292a9..e218d796e 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResultPage.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/LSSearchResultPage.java @@ -12,8 +12,8 @@ import java.util.Set; import org.eclipse.core.resources.IContainer; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; @@ -32,20 +32,18 @@ */ public class LSSearchResultPage extends FileSearchPage { - private ITreeContentProvider contentProvider; - @Override public void configureTreeViewer(TreeViewer viewer) { super.configureTreeViewer(viewer); FileTreeContentProvider fileMatchContentProvider = (FileTreeContentProvider)viewer.getContentProvider(); - this.contentProvider = new FileAndURIMatchContentProvider(fileMatchContentProvider); - viewer.setContentProvider(this.contentProvider); + final var contentProvider = new FileAndURIMatchContentProvider(fileMatchContentProvider); + viewer.setContentProvider(contentProvider); DecoratingFileSearchLabelProvider fileMatchDecoratingLabelProvider = (DecoratingFileSearchLabelProvider)viewer.getLabelProvider(); FileAndURIMatchBaseLabelProvider baseLabelProvider = new FileAndURIMatchBaseLabelProvider(fileMatchDecoratingLabelProvider.getStyledStringProvider()); viewer.setLabelProvider(new FileAndURIMatchLabelProvider(baseLabelProvider, fileMatchDecoratingLabelProvider)); viewer.setComparator(new ViewerComparator() { @Override - public int category(Object element) { + public int category(@Nullable Object element) { if (element instanceof IContainer) { return 1; } else if (element instanceof URI uri) { @@ -59,7 +57,7 @@ public int category(Object element) { } @Override - public int compare(Viewer viewer, Object e1, Object e2) { + public int compare(@Nullable Viewer viewer, @Nullable Object e1, @Nullable Object e2) { int cat1 = category(e1); int cat2 = category(e2); if (cat1 != cat2) { 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/references/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/package-info.java new file mode 100644 index 000000000..746ec2851 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/references/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.references; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameHandler.java index f8e17d52e..f7ce8942b 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameHandler.java @@ -16,6 +16,7 @@ package org.eclipse.lsp4e.operations.rename; import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ISelectionProvider; @@ -66,7 +67,7 @@ protected void execute(ExecutionEvent event, ITextEditor textEditor) { } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getRenameProvider, this::hasSelection); } } 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 1e9bef792..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; @@ -25,7 +27,6 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -56,29 +57,28 @@ /** * LTK {@link RefactoringProcessor} implementation to refactoring LSP symbols. - * */ public class LSPRenameProcessor extends RefactoringProcessor { private static final String ID = "org.eclipse.lsp4e.operations.rename"; //$NON-NLS-1$ - private final @NonNull IDocument document; + private final IDocument document; private final int offset; - private LanguageServerWrapper refactoringServer; + private @Nullable LanguageServerWrapper refactoringServer; - private String newName; + private @Nullable String newName; - private WorkspaceEdit rename; - private Either3 prepareRenameResult; + private @Nullable WorkspaceEdit rename; + private @Nullable Either3 prepareRenameResult; - public LSPRenameProcessor(@NonNull IDocument document, int offset) { + public LSPRenameProcessor(IDocument document, int offset) { this.document = document; this.offset = offset; } @Override - public Object[] getElements() { + public Object @Nullable [] getElements() { return null; } @@ -103,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") @@ -127,7 +126,9 @@ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) }); } } catch (TimeoutException e) { - LanguageServerPlugin.logWarning("Could not prepare rename due to timeout after 1 seconds in `textDocument/prepareRename`. 'newName' will be used", e); //$NON-NLS-1$ + LanguageServerPlugin.logWarning( + "Could not prepare rename due to timeout after 1 seconds in `textDocument/prepareRename`. 'newName' will be used", //$NON-NLS-1$ + e); } catch (Exception e) { status.addFatalError(getErrorMessage(e)); } @@ -135,7 +136,8 @@ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) } public String getPlaceholder() { - @Nullable String placeholder = null; + @Nullable + String placeholder = null; if (prepareRenameResult != null) { placeholder = prepareRenameResult.map(range -> { try { @@ -146,13 +148,12 @@ public String getPlaceholder() { LanguageServerPlugin.logError(e); return null; } - }, PrepareRenameResult::getPlaceholder, - options -> null); + }, PrepareRenameResult::getPlaceholder, options -> null); } - return placeholder != null && !placeholder.isBlank() ? placeholder :"newName"; //$NON-NLS-1$ + return placeholder != null && !placeholder.isBlank() ? placeholder : "newName"; //$NON-NLS-1$ } - public static boolean isPrepareRenameProvider(ServerCapabilities serverCapabilities) { + public static boolean isPrepareRenameProvider(@Nullable ServerCapabilities serverCapabilities) { if (serverCapabilities == null) { return false; } @@ -162,7 +163,7 @@ public static boolean isPrepareRenameProvider(ServerCapabilities serverCapabilit } if (renameProvider.isRight()) { - return renameProvider.getRight() != null && renameProvider.getRight().getPrepareProvider(); + return renameProvider.getRight().getPrepareProvider(); } return false; } @@ -171,26 +172,32 @@ public static boolean isPrepareRenameProvider(ServerCapabilities serverCapabilit public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { final var status = new RefactoringStatus(); + final var newName = this.newName; + if (newName == null) { + return status; + } 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); - if (params.getNewName() != null && refactoringServer != null) { - // TODO: how to manage ltk with CompletableFuture? Is 1000 ms is enough? - if (refactoringServer != null) { - rename = refactoringServer.execute(ls -> ls.getTextDocumentService().rename(params)).get(1000, TimeUnit.MILLISECONDS); - } else { - // Prepare timed out so we don't have a preferred server, so just try all the servers again - rename = LanguageServers.forDocument(document).withCapability(ServerCapabilities::getRenameProvider) - .computeFirst(ls -> ls.getTextDocumentService().rename(params)).get(1000, TimeUnit.MILLISECONDS).orElse(null); - } - if (!status.hasError() && (rename == null - || (rename.getChanges().isEmpty() && rename.getDocumentChanges().isEmpty()))) { - status.addWarning(Messages.rename_empty_message); - } + + // TODO: how to manage ltk with CompletableFuture? Is 1000 ms is enough? + final var refactoringServer = this.refactoringServer; + final WorkspaceEdit rename; + if (refactoringServer != null) { + rename = this.rename = refactoringServer.execute(ls -> ls.getTextDocumentService().rename(params)) + .get(1000, TimeUnit.MILLISECONDS); + } else { + // Prepare timed out so we don't have a preferred server, so just try all the servers again + rename = this.rename = LanguageServers.forDocument(document) + .withCapability(ServerCapabilities::getRenameProvider) + .computeFirst(ls -> ls.getTextDocumentService().rename(params)).get(1000, TimeUnit.MILLISECONDS) + .orElse(null); + } + if (!status.hasError() + && (rename == null || (rename.getChanges().isEmpty() && rename.getDocumentChanges().isEmpty()))) { + status.addWarning(Messages.rename_empty_message); } } catch (Exception e) { status.addFatalError(getErrorMessage(e)); @@ -201,15 +208,16 @@ public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditio private String getErrorMessage(Throwable e) { if (e.getCause() instanceof ResponseErrorException responseErrorException) { ResponseError responseError = responseErrorException.getResponseError(); - return responseError.getMessage() - + ((responseError.getData() instanceof String data) ? (": " + data) : ""); //$NON-NLS-1$ //$NON-NLS-2$ + return responseError.getMessage() + ((responseError.getData() instanceof String data) ? (": " + data) : ""); //$NON-NLS-1$ //$NON-NLS-2$ } else { - return e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName(); + final var msg = e.getMessage(); + return msg != null ? msg : e.getClass().getSimpleName(); } } @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)); @@ -218,8 +226,8 @@ public Change createChange(IProgressMonitor pm) throws CoreException, OperationC } @Override - public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) - throws CoreException { + public RefactoringParticipant @Nullable [] loadParticipants(RefactoringStatus status, + SharableParticipants sharedParticipants) throws CoreException { return null; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameRefactoringWizard.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameRefactoringWizard.java index c7646a505..4e001a095 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameRefactoringWizard.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/LSPRenameRefactoringWizard.java @@ -11,6 +11,8 @@ */ package org.eclipse.lsp4e.operations.rename; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.lsp4e.ui.Messages; import org.eclipse.ltk.core.refactoring.Refactoring; @@ -37,18 +39,19 @@ public LSPRenameRefactoringWizard(Refactoring refactoring) { @Override protected void addUserInputPages() { - @SuppressWarnings("null") LSPRenameProcessor processor = this.getRefactoring().getAdapter(LSPRenameProcessor.class); - this.addPage(new RenameInputWizardPage(processor)); + if(processor != null) { + this.addPage(new RenameInputWizardPage(processor)); + } } /** * Rename input wizard page. * */ - class RenameInputWizardPage extends UserInputWizardPage { + static final class RenameInputWizardPage extends UserInputWizardPage { - private Text nameText; + private Text nameText = lateNonNull(); private final LSPRenameProcessor processor; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/package-info.java new file mode 100644 index 000000000..59f70b1e0 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/rename/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.rename; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; 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 a30cf98d8..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 @@ -18,6 +18,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; @@ -58,12 +59,9 @@ public enum Direction { private static final String KEY = SelectionRangeHandler.class.getName(); - private SelectionRange root; - - private SelectionRange previous; - + private @Nullable SelectionRange root; + private @Nullable SelectionRange previous; private final StyledText styledText; - private boolean updating; public void setRoot(SelectionRange root) { @@ -90,10 +88,11 @@ public SelectionRangeHandler(StyledText styledText) { }); } - public SelectionRange getSelectionRange(Direction direction) { + public @Nullable SelectionRange getSelectionRange(Direction direction) { + var previous = this.previous; if (direction == Direction.UP) { if (previous != null) { - previous = previous.getParent(); + previous = this.previous = previous.getParent(); return previous; } } else { @@ -102,7 +101,7 @@ public SelectionRange getSelectionRange(Direction direction) { while (selectionRange != null) { SelectionRange parent = selectionRange.getParent(); if (previous.equals(parent)) { - previous = selectionRange; + previous = this.previous = selectionRange; return previous; } selectionRange = parent; @@ -130,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; + } + }); + } } } @@ -203,13 +204,16 @@ protected void execute(ExecutionEvent event, ITextEditor textEditor) { * @return the selection range hierarchy of the given document at the given * offset. */ - private CompletableFuture>> collectSelectionRanges(IDocument document, int offset) { + private CompletableFuture>> collectSelectionRanges(@Nullable IDocument document, int offset) { if (document == null) { return CompletableFuture.completedFuture(null); } 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) @@ -222,7 +226,7 @@ private CompletableFuture>> collectSelectionRanges } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getSelectionRangeProvider, x -> true); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/package-info.java new file mode 100644 index 000000000..62f881bc3 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/selectionRange/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.selectionRange; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; 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 628b034ac..5fe8e72b1 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 @@ -8,16 +8,15 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.semanticTokens; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + 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; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; @@ -46,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; @@ -84,15 +82,13 @@ public class SemanticHighlightReconcilerStrategy private final boolean disabled; - private ITextViewer viewer; - - private IDocument document; + private @Nullable ITextViewer viewer; - private StyleRangeHolder styleRangeHolder; + private @Nullable IDocument document; - private SemanticTokensDataStreamProcessor semanticTokensDataStreamProcessor; + private @Nullable StyleRangeHolder styleRangeHolder; - private boolean isInstalled; + private @Nullable SemanticTokensDataStreamProcessor semanticTokensDataStreamProcessor; /** * Written in {@link this.class#applyTextPresentation(TextPresentation)} @@ -107,12 +103,11 @@ public class SemanticHighlightReconcilerStrategy private volatile long timestamp = 0; - private CompletableFuture> semanticTokensFullFuture; + private @Nullable CompletableFuture> semanticTokensFullFuture; public SemanticHighlightReconcilerStrategy() { IPreferenceStore store = LanguageServerPlugin.getDefault().getPreferenceStore(); disabled = store.getBoolean("semanticHighlightReconciler.disabled"); //$NON-NLS-1$ - isInstalled = false; } /** @@ -125,19 +120,18 @@ public SemanticHighlightReconcilerStrategy() { */ @Override public void install(final ITextViewer textViewer) { - if (disabled || isInstalled) { + if (disabled || viewer != null) { return; } - viewer = textViewer; - styleRangeHolder = new StyleRangeHolder(); semanticTokensDataStreamProcessor = new SemanticTokensDataStreamProcessor(new TokenTypeMapper(textViewer), offsetMapper()); - if (viewer instanceof final TextViewer textViewerImpl) { + if (textViewer instanceof final TextViewer textViewerImpl) { textViewerImpl.addTextPresentationListener(this); } - viewer.addTextListener(styleRangeHolder); - isInstalled = true; + styleRangeHolder = new StyleRangeHolder(); + textViewer.addTextListener(styleRangeHolder); + viewer = textViewer; } /** @@ -146,24 +140,26 @@ public void install(final ITextViewer textViewer) { */ @Override public void uninstall() { - if (disabled || !isInstalled) { + final var viewer = this.viewer; + if (disabled || viewer == null) { return; } - isInstalled = false; // Indicate that we're not installed or in the phase of deinstalling + this.viewer = null; // Indicate that we're not installed or in the phase of deinstalling cancelSemanticTokensFull(); semanticTokensDataStreamProcessor = null; if (viewer instanceof final TextViewer textViewerImpl) { textViewerImpl.removeTextPresentationListener(this); } - viewer.removeTextListener(styleRangeHolder); - viewer = null; - styleRangeHolder = null; + if (styleRangeHolder != null) { + viewer.removeTextListener(styleRangeHolder); + styleRangeHolder = null; + } } - private @NonNull Function offsetMapper() { + private Function offsetMapper() { return p -> { try { - return LSPEclipseUtils.toOffset(p, document); + return LSPEclipseUtils.toOffset(p, castNonNull(document)); } catch (BadLocationException e) { throw new RuntimeException(e); } @@ -171,25 +167,24 @@ public void uninstall() { } private SemanticTokensParams getSemanticTokensParams() { - URI uri = LSPEclipseUtils.toUri(document); - if (uri != null) { - final var semanticTokensParams = new SemanticTokensParams(); - semanticTokensParams.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(uri)); - return semanticTokensParams; - } - return null; + URI uri = castNonNull(LSPEclipseUtils.toUri(document)); + final var semanticTokensParams = new SemanticTokensParams(); + semanticTokensParams.setTextDocument(LSPEclipseUtils.toTextDocumentIdentifier(uri)); + return semanticTokensParams; } - private void saveStyle(final Pair pair) { + private void saveStyle(final Pair<@Nullable SemanticTokens, @Nullable SemanticTokensLegend> pair) { final SemanticTokens semanticTokens = pair.first(); final SemanticTokensLegend semanticTokensLegend = pair.second(); // Skip any processing if not installed or at least one of the pair values is null - if (!isInstalled || semanticTokens == null || semanticTokensLegend == null) { + if (viewer == null || semanticTokens == null || semanticTokensLegend == null) { return; } List dataStream = semanticTokens.getData(); - if (!dataStream.isEmpty()) { + final var semanticTokensDataStreamProcessor = this.semanticTokensDataStreamProcessor; + final var styleRangeHolder = this.styleRangeHolder; + if (!dataStream.isEmpty() && semanticTokensDataStreamProcessor != null && styleRangeHolder != null) { List styleRanges = semanticTokensDataStreamProcessor.getStyleRanges(dataStream, semanticTokensLegend); styleRangeHolder.saveStyles(styleRanges); @@ -197,11 +192,11 @@ private void saveStyle(final Pair pair) { } @Override - public void setProgressMonitor(final IProgressMonitor monitor) { + public void setProgressMonitor(final @Nullable IProgressMonitor monitor) { } @Override - public void setDocument(final IDocument document) { + public void setDocument(final @Nullable IDocument document) { this.document = document; } @@ -235,16 +230,17 @@ private boolean outdatedTextPresentation(final long documentTimestamp) { } private void invalidateTextPresentation(final Long documentTimestamp) { - if (!isInstalled) { // Skip any processing + final var viewer = this.viewer; + if (viewer == null) { // Skip any processing return; } StyledText textWidget = viewer.getTextWidget(); textWidget.getDisplay().asyncExec(() -> { if (!textWidget.isDisposed() && outdatedTextPresentation(documentTimestamp)) { - ITextViewer theViewer = viewer; + ITextViewer theViewer = this.viewer; if (theViewer != null) { theViewer.invalidateTextPresentation(); - } + } } }); } @@ -256,29 +252,29 @@ private void cancelSemanticTokensFull() { } private void fullReconcile() { - if (disabled || !isInstalled) { // Skip any processing + final var viewer = this.viewer; + if (disabled || viewer == null) { // Skip any processing return; } - IDocument theDocument = document; + final var document = this.document; cancelSemanticTokensFull(); - if (theDocument != null) { - long modificationStamp = DocumentUtil.getDocumentModificationStamp(theDocument); - LanguageServerDocumentExecutor executor = LanguageServers.forDocument(theDocument) + if (document != null) { + long modificationStamp = DocumentUtil.getDocumentModificationStamp(document); + LanguageServerDocumentExecutor executor = LanguageServers.forDocument(document) .withFilter(this::hasSemanticTokensFull); try { - semanticTokensFullFuture = executor// - .computeFirst((w, ls) -> ls.getTextDocumentService().semanticTokensFull(getSemanticTokensParams())// + final var semanticTokensFullFuture = executor // + .computeFirst((w, ls) -> ls.getTextDocumentService().semanticTokensFull(getSemanticTokensParams()) // .thenApply(semanticTokens -> new VersionedSemanticTokens(modificationStamp, - Pair.of(semanticTokens, getSemanticTokensLegend(w)), theDocument))); - + Pair.of(semanticTokens, getSemanticTokensLegend(w)), document))); + this.semanticTokensFullFuture = semanticTokensFullFuture; semanticTokensFullFuture.get() // background thread with cancellation support, no timeout needed - .ifPresent(versionedSemanticTokens -> { - versionedSemanticTokens.apply(this::saveStyle, this::invalidateTextPresentation); - }); + .ifPresent(versionedSemanticTokens -> + versionedSemanticTokens.apply(this::saveStyle, this::invalidateTextPresentation)); } 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); } @@ -317,7 +313,7 @@ private void fullReconcileOnce() { public void applyTextPresentation(final TextPresentation textPresentation) { documentTimestampAtLastAppliedTextPresentation = DocumentUtil.getDocumentModificationStamp(document); IRegion extent = textPresentation.getExtent(); - if (extent != null) { + if (extent != null && styleRangeHolder != null) { textPresentation.replaceStyleRanges(styleRangeHolder.overlappingRanges(extent)); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticTokensDataStreamProcessor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticTokensDataStreamProcessor.java index 0647a1f77..13490eb94 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticTokensDataStreamProcessor.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/SemanticTokensDataStreamProcessor.java @@ -15,7 +15,6 @@ import java.util.List; import java.util.function.Function; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.TextAttribute; import org.eclipse.jface.text.rules.IToken; @@ -33,7 +32,7 @@ public class SemanticTokensDataStreamProcessor { private final Function offsetMapper; - private final Function tokenTypeMapper; + private final Function tokenTypeMapper; /** * Creates a new instance of {@link SemanticTokensDataStreamProcessor}. @@ -41,8 +40,8 @@ public class SemanticTokensDataStreamProcessor { * @param tokenTypeMapper * @param offsetMapper */ - public SemanticTokensDataStreamProcessor(@NonNull final Function tokenTypeMapper, - @NonNull final Function offsetMapper) { + public SemanticTokensDataStreamProcessor(final Function tokenTypeMapper, + final Function offsetMapper) { this.tokenTypeMapper = tokenTypeMapper; this.offsetMapper = offsetMapper; } @@ -52,10 +51,9 @@ public SemanticTokensDataStreamProcessor(@NonNull final Function * * @param dataStream * @param semanticTokensLegend - * @return */ - public @NonNull List getStyleRanges(@NonNull final List dataStream, - @NonNull final SemanticTokensLegend semanticTokensLegend) { + public List getStyleRanges(final List dataStream, + final SemanticTokensLegend semanticTokensLegend) { final var styleRanges = new ArrayList(dataStream.size() / 5); int idx = 0; @@ -104,7 +102,7 @@ public SemanticTokensDataStreamProcessor(@NonNull final Function return styleRanges; } - private String tokenType(final Integer data, final List legend) { + private @Nullable String tokenType(final Integer data, final List legend) { try { return legend.get(data); } catch (IndexOutOfBoundsException e) { @@ -129,7 +127,7 @@ private List tokenModifiers(final Integer data, final List legen return tokenModifiers; } - private TextAttribute textAttribute(final String tokenType) { + private @Nullable TextAttribute textAttribute(final @Nullable String tokenType) { if (tokenType != null) { IToken token = tokenTypeMapper.apply(tokenType); if (token != null) { @@ -152,7 +150,7 @@ private TextAttribute textAttribute(final String tokenType) { * @param attr * the attribute describing the style of the range to be styled */ - private @Nullable StyleRange getStyleRange(final int offset, final int length, final TextAttribute attr) { + private @Nullable StyleRange getStyleRange(final int offset, final int length, final @Nullable TextAttribute attr) { if (attr != null) { final int style = attr.getStyle(); final int fontStyle = style & (SWT.ITALIC | SWT.BOLD | SWT.NORMAL); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/StyleRangeHolder.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/StyleRangeHolder.java index ee8fe745f..9877d7795 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/StyleRangeHolder.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/StyleRangeHolder.java @@ -13,7 +13,6 @@ import java.util.Comparator; import java.util.List; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextListener; import org.eclipse.jface.text.Region; @@ -43,7 +42,7 @@ public StyleRangeHolder() { * * @param styleRanges */ - public void saveStyles(@NonNull final List styleRanges) { + public void saveStyles(final List styleRanges) { synchronized (previousRanges) { previousRanges.clear(); previousRanges.addAll(styleRanges); @@ -55,9 +54,8 @@ public void saveStyles(@NonNull final List styleRanges) { * return a copy of the saved styles that overlap the given region. * * @param region - * @return */ - public StyleRange[] overlappingRanges(@NonNull final IRegion region) { + public StyleRange[] overlappingRanges(final IRegion region) { synchronized (previousRanges) { // we need to create new styles because the text presentation might change a // style when applied to the presentation diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/TokenTypeMapper.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/TokenTypeMapper.java index 9ba6e714f..53baa3e58 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/TokenTypeMapper.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/TokenTypeMapper.java @@ -11,7 +11,7 @@ import java.util.function.Function; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.rules.IToken; import org.eclipse.tm4e.ui.text.TMPresentationReconciler; @@ -20,15 +20,15 @@ /** * A Class that maps TokenTypes to {@link IToken}. */ -public class TokenTypeMapper implements Function { - private @NonNull final ITextViewer viewer; +public class TokenTypeMapper implements Function { + private final ITextViewer viewer; - public TokenTypeMapper(@NonNull final ITextViewer viewer) { + public TokenTypeMapper(final ITextViewer viewer) { this.viewer = viewer; } @Override - public IToken apply(final String tokenType) { + public @Nullable IToken apply(final @Nullable String tokenType) { if (tokenType == null) { return null; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/VersionedSemanticTokens.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/VersionedSemanticTokens.java index 85508f930..18f192586 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/VersionedSemanticTokens.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/VersionedSemanticTokens.java @@ -14,6 +14,7 @@ import java.util.function.Consumer; import java.util.function.LongConsumer; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.Versioned; import org.eclipse.lsp4e.internal.DocumentUtil; @@ -23,11 +24,11 @@ /** * Specialization of Versioned for semanticTokens - * */ -public class VersionedSemanticTokens extends Versioned>{ +public class VersionedSemanticTokens extends Versioned>{ - public VersionedSemanticTokens(long version, Pair data, IDocument document) { + public VersionedSemanticTokens(long version, Pair<@Nullable SemanticTokens, @Nullable SemanticTokensLegend> data, + IDocument document) { super(document, version, data); } @@ -36,7 +37,7 @@ public VersionedSemanticTokens(long version, Pair> first, LongConsumer second) { + public void apply(Consumer> first, LongConsumer second) { if (sourceDocumentVersion == DocumentUtil.getDocumentModificationStamp(document)) { first.accept(data); second.accept(sourceDocumentVersion); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/package-info.java new file mode 100644 index 000000000..ad6803b93 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/semanticTokens/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.semanticTokens; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileDialog.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileDialog.java index 89c49f221..34818c2e2 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileDialog.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileDialog.java @@ -12,7 +12,7 @@ package org.eclipse.lsp4e.operations.symbols; import org.eclipse.core.resources.IFile; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.dialogs.PopupDialog; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -55,10 +55,10 @@ public class LSPSymbolInFileDialog extends PopupDialog { private final OutlineViewerInput outlineViewerInput; private final ITextEditor textEditor; - private TreeViewer viewer; + private @Nullable TreeViewer viewer; - public LSPSymbolInFileDialog(@NonNull Shell parentShell, @NonNull ITextEditor textEditor, - @NonNull IDocument document, @NonNull LanguageServerWrapper wrapper) { + public LSPSymbolInFileDialog(Shell parentShell, ITextEditor textEditor, + IDocument document, LanguageServerWrapper wrapper) { super(parentShell, PopupDialog.INFOPOPUPRESIZE_SHELLSTYLE, true, true, true, false, false, null, null); outlineViewerInput = new OutlineViewerInput(document, wrapper, textEditor); this.textEditor = textEditor; @@ -71,7 +71,7 @@ protected Control createDialogArea(Composite parent) { getShell().setText(NLS.bind(Messages.symbolsInFile, documentFile == null ? null : documentFile.getName())); final var filteredTree = new FilteredTree(parent, SWT.BORDER, new PatternFilter(), true, false); - viewer = filteredTree.getViewer(); + final TreeViewer viewer = this.viewer = filteredTree.getViewer(); viewer.setData(LSSymbolsContentProvider.VIEWER_PROPERTY_IS_QUICK_OUTLINE, Boolean.TRUE); final var contentService = new NavigatorContentService(CNFOutlinePage.ID, viewer); @@ -130,14 +130,14 @@ public void mouseUp(MouseEvent e) { } }); - this.getShell().addDisposeListener(event -> viewer = null); + getShell().addDisposeListener(event -> this.viewer = null); viewer.setInput(outlineViewerInput); return filteredTree; } private void gotoSelectedElement() { - Object selectedElement= getSelectedElement(); + Object selectedElement = getSelectedElement(); if (selectedElement != null) { close(); @@ -163,7 +163,7 @@ private void gotoSelectedElement() { } } - private Object getSelectedElement() { + private @Nullable Object getSelectedElement() { TreeViewer treeViewer = this.viewer; if (treeViewer == null) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileHandler.java index ef0bc39e4..0da276aee 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInFileHandler.java @@ -15,6 +15,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServers; @@ -51,7 +52,7 @@ protected void execute(ExecutionEvent event, ITextEditor textEditor) { } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getDocumentSymbolProvider, x -> true); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceDialog.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceDialog.java index 48260aea5..8e7c8edd8 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceDialog.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceDialog.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -28,7 +29,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -57,7 +58,7 @@ public class LSPSymbolInWorkspaceDialog extends FilteredItemsSelectionDialog { private static final class InternalSymbolsLabelProvider extends SymbolsLabelProvider { - private String pattern; + private @Nullable String pattern; private final BoldStylerProvider stylerProvider; public InternalSymbolsLabelProvider(BoldStylerProvider stylerProvider) { @@ -67,17 +68,21 @@ public InternalSymbolsLabelProvider(BoldStylerProvider stylerProvider) { } @Override - public StyledString getStyledText(Object element) { + public StyledString getStyledText(@Nullable Object element) { StyledString styledString = super.getStyledText(element); - int index = styledString.getString().toLowerCase().indexOf(pattern); - if (index != -1) { - styledString.setStyle(index, pattern.length(), stylerProvider.getBoldStyler()); + final var pattern = this.pattern; + if (pattern != null) { + int index = styledString.getString().toLowerCase().indexOf(pattern); + if (index != -1) { + styledString.setStyle(index, pattern.length(), stylerProvider.getBoldStyler()); + } } return styledString; } @Override - protected int getMaxSeverity(@NonNull IResource resource, @NonNull IDocument doc, @NonNull Range range) throws CoreException, BadLocationException { + protected int getMaxSeverity(IResource resource, IDocument doc, Range range) + throws CoreException, BadLocationException { int maxSeverity = -1; for (IMarker marker : resource.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_ZERO)) { int offset = marker.getAttribute(IMarker.CHAR_START, -1); @@ -88,7 +93,7 @@ protected int getMaxSeverity(@NonNull IResource resource, @NonNull IDocument doc return maxSeverity; } - public void setPattern(String pattern) { + public void setPattern(@Nullable String pattern) { this.pattern = pattern; } } @@ -96,17 +101,17 @@ public void setPattern(String pattern) { private final class InternalItemsFilter extends ItemsFilter { @Override - public boolean matchItem(Object item) { + public boolean matchItem(@Nullable Object item) { return true; } @Override - public boolean isConsistentItem(Object item) { + public boolean isConsistentItem(@Nullable Object item) { return true; } @Override - public boolean isSubFilter(ItemsFilter filter) { + public boolean isSubFilter(@Nullable ItemsFilter filter) { return false; } } @@ -115,7 +120,7 @@ public boolean isSubFilter(ItemsFilter filter) { private final IProject project; - private List, List>>> request; + private @Nullable List, List>>> request; public LSPSymbolInWorkspaceDialog(Shell shell, IProject project, BoldStylerProvider stylerProvider) { super(shell); @@ -143,36 +148,36 @@ protected void fillContentProvider(AbstractContentProvider contentProvider, Item return; } final var params = new WorkspaceSymbolParams(itemsFilter.getPattern()); - request = LanguageServers.forProject(project).withCapability(ServerCapabilities::getWorkspaceSymbolProvider) - .computeAll((w, ls) -> ls.getWorkspaceService().symbol(params)); - request.stream().map(s -> s.thenApply(LSPSymbolInWorkspaceDialog::eitherToWorkspaceSymbols)) - .forEach(cf -> { - if (monitor.isCanceled()) { - return; - } - try { - final List items = cf.get(1, TimeUnit.SECONDS); - if(items != null) { - for (Object item : items) { - if (item != null) { - contentProvider.add(item, itemsFilter); - } + request = LanguageServers.forProject(project) // + .withCapability(ServerCapabilities::getWorkspaceSymbolProvider) // + .computeAll((w, ls) -> ls.getWorkspaceService().symbol(params)); + request.stream().map(( + CompletableFuture<@Nullable Either, List<@Nullable ? extends WorkspaceSymbol>>> f) -> f + .thenApply(LSPSymbolInWorkspaceDialog::eitherToWorkspaceSymbols)) + .forEach(cf -> { + if (monitor.isCanceled()) { + return; + } + try { + for (Object item : cf.get(1, TimeUnit.SECONDS)) { + contentProvider.add(item, itemsFilter); } + } catch (ExecutionException e) { + LanguageServerPlugin.logError(e); + } catch (InterruptedException e) { + LanguageServerPlugin.logError(e); + Thread.currentThread().interrupt(); + } catch (TimeoutException e) { + LanguageServerPlugin.logWarning( + "Could not get workspace symbols due to timeout after 1 seconds in `workspace/symbol`", //$NON-NLS-1$ + e); } - } catch (ExecutionException e) { - LanguageServerPlugin.logError(e); - } catch (InterruptedException e) { - LanguageServerPlugin.logError(e); - Thread.currentThread().interrupt(); - } catch (TimeoutException e) { - LanguageServerPlugin.logWarning("Could not get workspace symbols due to timeout after 1 seconds in `workspace/symbol`", e); //$NON-NLS-1$ - } - }); + }); } @Override public String getElementName(Object item) { - return ((WorkspaceSymbol)item).getName(); + return ((WorkspaceSymbol) item).getName(); } @Override @@ -195,17 +200,20 @@ protected IDialogSettings getDialogSettings() { } @Override - protected Control createExtendedContentArea(Composite parent) { + protected @Nullable Control createExtendedContentArea(Composite parent) { return null; } - private static List toWorkspaceSymbols(List source) { - return source == null ? - List.of() : - source.stream().map(LSPSymbolInWorkspaceDialog::toWorkspaceSymbol).toList(); + private static List toWorkspaceSymbols(@Nullable List source) { + return source == null // + ? List.of() + : source.stream() // + .map(LSPSymbolInWorkspaceDialog::toWorkspaceSymbol) // + .filter(Objects::nonNull) // + .toList(); } - private static WorkspaceSymbol toWorkspaceSymbol(SymbolInformation symbolinformation) { + private static @Nullable WorkspaceSymbol toWorkspaceSymbol(@Nullable SymbolInformation symbolinformation) { if (symbolinformation == null) { return null; } @@ -214,7 +222,8 @@ private static WorkspaceSymbol toWorkspaceSymbol(SymbolInformation symbolinforma res.setLocation(Either.forLeft(symbolinformation.getLocation())); res.setKind(symbolinformation.getKind()); res.setContainerName(symbolinformation.getContainerName()); - List tags = symbolinformation.getTags() != null ? new ArrayList<>(symbolinformation.getTags()) : new ArrayList<>(1); + List tags = symbolinformation.getTags() != null ? new ArrayList<>(symbolinformation.getTags()) + : new ArrayList<>(1); if (symbolinformation.getDeprecated() != null && symbolinformation.getDeprecated().booleanValue()) { tags.add(SymbolTag.Deprecated); } @@ -222,7 +231,10 @@ private static WorkspaceSymbol toWorkspaceSymbol(SymbolInformation symbolinforma return res; } - static List eitherToWorkspaceSymbols(Either, List> source) { - return source.map(LSPSymbolInWorkspaceDialog::toWorkspaceSymbols, Function.identity()); + static List eitherToWorkspaceSymbols( + final @Nullable Either, List<@Nullable ? extends WorkspaceSymbol>> source) { + return source == null // + ? List.of() + : source.map(LSPSymbolInWorkspaceDialog::toWorkspaceSymbols, Function.identity()); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceHandler.java index a36dc9ebb..0c7e66165 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/LSPSymbolInWorkspaceHandler.java @@ -16,6 +16,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.text.contentassist.BoldStylerProvider; import org.eclipse.jface.viewers.IStructuredSelection; @@ -33,7 +34,7 @@ public class LSPSymbolInWorkspaceHandler extends LSPDocumentAbstractHandler { @Override - public Object execute(ExecutionEvent event) throws ExecutionException { + public @Nullable Object execute(ExecutionEvent event) throws ExecutionException { IEditorPart part = HandlerUtil.getActiveEditor(event); IResource resource = null; if (part != null && part.getEditorInput() != null) { @@ -81,7 +82,7 @@ protected void execute(ExecutionEvent event, ITextEditor textEditor) { } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getWorkspaceSymbolProvider, x -> true); } 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/symbols/WorkspaceSymbolsQuickAccessProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/WorkspaceSymbolsQuickAccessProvider.java index 1015af567..88941f932 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/WorkspaceSymbolsQuickAccessProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/WorkspaceSymbolsQuickAccessProvider.java @@ -20,20 +20,22 @@ import java.util.concurrent.TimeoutException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.LanguageServerWrapper; import org.eclipse.lsp4e.LanguageServiceAccessor; +import org.eclipse.lsp4j.SymbolInformation; +import org.eclipse.lsp4j.WorkspaceSymbol; import org.eclipse.lsp4j.WorkspaceSymbolParams; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.ui.quickaccess.IQuickAccessComputer; import org.eclipse.ui.quickaccess.IQuickAccessComputerExtension; import org.eclipse.ui.quickaccess.QuickAccessElement; public class WorkspaceSymbolsQuickAccessProvider implements IQuickAccessComputer, IQuickAccessComputerExtension { - - private List<@NonNull LanguageServerWrapper> usedLanguageServerWrappers; + private @Nullable List usedLanguageServerWrappers; @Override public QuickAccessElement[] computeElements() { @@ -46,13 +48,15 @@ public void resetState() { @Override public boolean needsRefresh() { - return this.usedLanguageServerWrappers == null - || !this.usedLanguageServerWrappers.equals(LanguageServiceAccessor.getStartedWrappers(capabilities -> LSPEclipseUtils.hasCapability(capabilities.getWorkspaceSymbolProvider()), true)); + final var usedLanguageServerWrappers = this.usedLanguageServerWrappers; + return usedLanguageServerWrappers == null + || !usedLanguageServerWrappers.equals(LanguageServiceAccessor.getStartedWrappers(capabilities -> LSPEclipseUtils.hasCapability(capabilities.getWorkspaceSymbolProvider()), true)); } @Override public QuickAccessElement[] computeElements(String query, IProgressMonitor monitor) { - this.usedLanguageServerWrappers = LanguageServiceAccessor.getStartedWrappers(capabilities -> LSPEclipseUtils.hasCapability(capabilities.getWorkspaceSymbolProvider()), true); + final var usedLanguageServerWrappers = this.usedLanguageServerWrappers = LanguageServiceAccessor + .getStartedWrappers(capabilities -> LSPEclipseUtils.hasCapability(capabilities.getWorkspaceSymbolProvider()), true); if (usedLanguageServerWrappers.isEmpty()) { return new QuickAccessElement[0]; } @@ -61,7 +65,7 @@ public QuickAccessElement[] computeElements(String query, IProgressMonitor monit try { CompletableFuture.allOf(usedLanguageServerWrappers.stream() - .map(w -> w.execute(ls -> ls.getWorkspaceService().symbol(params).thenAcceptAsync(symbols -> { + .map(w -> w.execute(ls -> ls.getWorkspaceService().symbol(params).thenAcceptAsync((@Nullable Either, List<@Nullable ? extends WorkspaceSymbol>> symbols) -> { if (symbols != null) { res.addAll(LSPSymbolInWorkspaceDialog.eitherToWorkspaceSymbols(symbols).stream().map(WorkspaceSymbolQuickAccessElement::new) .toList()); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/package-info.java new file mode 100644 index 000000000..3af9863ef --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/symbols/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.symbols; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; 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 8ad0305c9..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 @@ -8,10 +8,13 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.typeHierarchy; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import java.util.AbstractMap.SimpleEntry; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ITreeContentProvider; @@ -32,7 +35,7 @@ public class TypeHierarchyContentProvider implements ITreeContentProvider { private final LanguageServerDefinition lsDefinition; private final IDocument document; private boolean showSuperTypes; - private LanguageServerWrapper wrapper; + private LanguageServerWrapper wrapper = lateNonNull(); public TypeHierarchyContentProvider(LanguageServerDefinition lsDefinition, IDocument document, boolean showSuperTypes) { this.lsDefinition = lsDefinition; @@ -41,11 +44,15 @@ public TypeHierarchyContentProvider(LanguageServerDefinition lsDefinition, IDocu } @Override - public Object[] getElements(Object inputElement) { + 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 -> { @@ -80,7 +87,7 @@ public Object[] getChildren(Object parentElement) { } @Override - public Object getParent(Object element) { + public @Nullable Object getParent(Object element) { return null; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyDialog.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyDialog.java index 4d8a2bd9d..bcfb51f27 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyDialog.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyDialog.java @@ -9,7 +9,6 @@ package org.eclipse.lsp4e.operations.typeHierarchy; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.dialogs.PopupDialog; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; @@ -42,10 +41,10 @@ public class TypeHierarchyDialog extends PopupDialog { private static boolean showSuperTypes = true; private final LanguageServerDefinition lsDefinition; - private final @NonNull IDocument document; + private final IDocument document; private final ITextSelection textSelection; - public TypeHierarchyDialog(@NonNull Shell parentShell, ITextSelection textSelection, @NonNull IDocument document, @NonNull LanguageServerDefinition ls) { + public TypeHierarchyDialog(Shell parentShell, ITextSelection textSelection, IDocument document, LanguageServerDefinition ls) { super(parentShell, PopupDialog.INFOPOPUPRESIZE_SHELLSTYLE, true, true, true, false, false, null, null); this.lsDefinition = ls; this.document = document; @@ -99,8 +98,9 @@ private void updateHierarchyModeItem(ToolItem hierchyModeItem, boolean showSuper viewer.setLabelProvider(new TypeHierarchyItemLabelProvider()); viewer.setAutoExpandLevel(2); viewer.addDoubleClickListener(event -> { - TypeHierarchyItem item = (TypeHierarchyItem)((IStructuredSelection)event.getSelection()).getFirstElement(); - LSPEclipseUtils.open(item.getUri(), item.getSelectionRange()); + if(((IStructuredSelection)event.getSelection()).getFirstElement() instanceof final TypeHierarchyItem item) { + LSPEclipseUtils.open(item.getUri(), item.getSelectionRange()); + } }); final var sorter = new CommonViewerSorter(); @@ -123,5 +123,3 @@ protected void configureShell(Shell shell) { shell.setSize(280, 300); } } - - 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 c8dcb02b6..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 @@ -11,6 +11,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.lsp4e.LSPEclipseUtils; @@ -24,14 +25,16 @@ 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 - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getTypeDefinitionProvider, editor -> true); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java index 77f6cb50e..1cea86359 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyItemLabelProvider.java @@ -8,6 +8,7 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.typeHierarchy; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; @@ -26,15 +27,15 @@ public String getText(Object element) { } @Override - public Image getImage(Object element) { + public @Nullable Image getImage(@Nullable Object element) { if (element instanceof TypeHierarchyItem item) { return LSPImages.imageFromSymbolKind(item.getKind()); } - return super.getImage(element); + return element == null ? null : super.getImage(element); } @Override - public StyledString getStyledText(Object element) { + public StyledString getStyledText(@Nullable Object element) { if (element instanceof TypeHierarchyItem item) { return new StyledString(item.getName()); } else if (element instanceof String s) { 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 b4995fb28..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 @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.operations.typeHierarchy; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; @@ -31,6 +33,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider; @@ -85,18 +88,18 @@ public class TypeHierarchyView extends ViewPart { - class SymbolsContainer { + private static final class SymbolsContainer { private final SymbolsModel symbolsModel; private volatile boolean isDirty = true; private boolean temporaryLoadedDocument = false; - private volatile URI uri; + private volatile URI uri = lateNonNull(); SymbolsContainer(URI uri) { this.symbolsModel = new SymbolsModel(); setUri(uri); } - public IDocument getDocument() { + public @Nullable IDocument getDocument() { IDocument document = null; var file = LSPEclipseUtils.getFileHandle(uri); if (file == null) { @@ -144,18 +147,20 @@ public void dispose() { } public static final String ID = "org.eclipse.lsp4e.operations.typeHierarchy.TypeHierarchyView"; //$NON-NLS-1$ - TypeHierarchyViewContentProvider contentProvider = new TypeHierarchyViewContentProvider(); - DecoratingStyledCellLabelProvider symbolsLabelProvider = new DecoratingStyledCellLabelProvider(new SymbolsLabelProvider(), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(), DecorationContext.DEFAULT_CONTEXT); + + private TypeHierarchyViewContentProvider contentProvider = new TypeHierarchyViewContentProvider(); + private DecoratingStyledCellLabelProvider symbolsLabelProvider = new DecoratingStyledCellLabelProvider(new SymbolsLabelProvider(), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(), DecorationContext.DEFAULT_CONTEXT); + // widgets - private PageBook pagebook; - private SashForm splitter; - private ViewForm memberViewForm; - private CLabel memberLabel; - private Label infoText; + private PageBook pagebook = lateNonNull(); + private SashForm splitter = lateNonNull(); + private ViewForm memberViewForm = lateNonNull(); + private CLabel memberLabel = lateNonNull(); + private Label infoText = lateNonNull(); // viewers - private TableViewer memberViewer; - protected TreeViewer treeViewer; + private TableViewer memberViewer = lateNonNull(); + private TreeViewer treeViewer = lateNonNull(); private HashMap cachedSymbols = new HashMap<>(); @@ -172,7 +177,7 @@ public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) { } @Override - public void underlyingFileMoved(IFileBuffer buffer, IPath path) { + public void underlyingFileMoved(@Nullable IFileBuffer buffer, IPath path) { var symbolsContainer = getSymbolsContainer(buffer); // check if this file has been cached: if (symbolsContainer != null && buffer != null) { @@ -186,7 +191,7 @@ public void underlyingFileMoved(IFileBuffer buffer, IPath path) { } } - private SymbolsContainer getSymbolsContainer(IFileBuffer buffer) { + private @Nullable SymbolsContainer getSymbolsContainer(@Nullable IFileBuffer buffer) { if (buffer != null) { return cachedSymbols.get(LSPEclipseUtils.toUri(buffer)); } @@ -273,7 +278,7 @@ private void onHierarchySelectionChanged(SelectionChangedEvent event) { } } - private void refreshMemberViewer(SymbolsContainer symbolsContainer, String typeName, boolean documentModified) { + private void refreshMemberViewer(@Nullable SymbolsContainer symbolsContainer, String typeName, boolean documentModified) { memberViewer.setInput(new PendingUpdateAdapter()); memberLabel.setImage(JFaceResources.getImage(ProgressManager.WAITING_JOB_KEY)); memberLabel.setText(Messages.outline_computingSymbols); @@ -306,10 +311,11 @@ private Control createMemberControl(ViewForm parent) { memberViewer.setContentProvider(new TypeMemberContentProvider()); memberViewer.setLabelProvider(symbolsLabelProvider); memberViewer.addOpenListener(event -> { - DocumentSymbolWithURI container = (DocumentSymbolWithURI)((IStructuredSelection) event.getSelection()).getFirstElement(); - var symbolsContainer = cachedSymbols.get(container.uri); - if (symbolsContainer != null) { - LSPEclipseUtils.open(symbolsContainer.uri.toASCIIString(), container.symbol.getRange()); + if (((IStructuredSelection) event.getSelection()).getFirstElement() instanceof DocumentSymbolWithURI container) { + var symbolsContainer = cachedSymbols.get(container.uri); + if (symbolsContainer != null) { + LSPEclipseUtils.open(symbolsContainer.uri.toASCIIString(), container.symbol.getRange()); + } } }); return memberViewer.getControl(); @@ -368,45 +374,49 @@ private SymbolsContainer getSymbolsContainer(URI uri) { return cachedSymbols.computeIfAbsent(uri, entry -> new SymbolsContainer(uri)); } - private synchronized void refreshSymbols(SymbolsContainer symbolsContainer, boolean documentModified) { + private synchronized void refreshSymbols(@Nullable SymbolsContainer symbolsContainer, boolean documentModified) { if (symbolsContainer == null || (!symbolsContainer.isDirty && !documentModified)) { return; } 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); } } - private DocumentSymbolWithURI getDocumentSymbol(SymbolsContainer symbolsContainer, String typeName) { + private @Nullable DocumentSymbolWithURI getDocumentSymbol(@Nullable SymbolsContainer symbolsContainer, String typeName) { if (symbolsContainer != null) { var elements = symbolsContainer.symbolsModel.getElements(); for (var element : elements) { @@ -428,7 +438,7 @@ private boolean isClass(SymbolKind kind) { return SymbolKind.Class.equals(kind) || SymbolKind.Struct.equals(kind) ; } - private DocumentSymbol searchInChildren(List children, String typeName) { + private @Nullable DocumentSymbol searchInChildren(@Nullable List children, String typeName) { if (children == null) { return null; } 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 ffecaf251..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 @@ -15,7 +15,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.ITreeContentProvider; @@ -41,14 +41,14 @@ import org.eclipse.ui.PlatformUI; public class TypeHierarchyViewContentProvider implements ITreeContentProvider { - private TreeViewer treeViewer; - private LanguageServerWrapper languageServerWrapper; + private @Nullable TreeViewer treeViewer; + private @Nullable LanguageServerWrapper languageServerWrapper; private List hierarchyItems = Collections.emptyList(); public boolean showSuperTypes = true; - public IDocument document; + public @Nullable IDocument document; @Override - public Object[] getElements(Object inputElement) { + public Object[] getElements(@Nullable Object inputElement) { if (hierarchyItems.isEmpty()) { return new Object[] { Messages.TH_no_type_hierarchy }; } @@ -57,7 +57,7 @@ public Object[] getElements(Object inputElement) { @Override public Object[] getChildren(Object parentElement) { - if (parentElement instanceof TypeHierarchyItem parentItem) { + if (parentElement instanceof TypeHierarchyItem parentItem && languageServerWrapper != null) { try { return languageServerWrapper.execute(ls -> { TextDocumentService textDocumentService = ls.getTextDocumentService(); @@ -76,7 +76,7 @@ public Object[] getChildren(Object parentElement) { } @Override - public Object getParent(Object element) { + public @Nullable Object getParent(Object element) { return null; } @@ -86,7 +86,7 @@ public boolean hasChildren(Object element) { } @Override - public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { + public void inputChanged(final Viewer viewer, final @Nullable Object oldInput, final @Nullable Object newInput) { ITreeContentProvider.super.inputChanged(viewer, oldInput, newInput); if (newInput instanceof HierarchyViewInput viewInput) { @@ -108,7 +108,7 @@ public void inputChanged(final Viewer viewer, final Object oldInput, final Objec } - private void initialise(final @NonNull IDocument document, final int offset, TreeViewer viewer) throws BadLocationException { + private void initialise(final IDocument document, final int offset, TreeViewer viewer) throws BadLocationException { LanguageServerDocumentExecutor executor = LanguageServers.forDocument(document) .withCapability(ServerCapabilities::getTypeHierarchyProvider); if (!executor.anyMatching()) { @@ -116,6 +116,9 @@ private void initialise(final @NonNull IDocument document, final int offset, Tre 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(); @@ -123,6 +126,7 @@ private void initialise(final @NonNull IDocument document, final int offset, Tre hierarchyItems = p.second(); treeViewer = viewer; PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { + final var treeViewer = this.treeViewer; if (treeViewer != null) { treeViewer.refresh(); treeViewer.expandToLevel(2); @@ -139,12 +143,13 @@ private void initialise(final @NonNull IDocument document, final int offset, Tre } return result; }); - } - private static TypeHierarchyPrepareParams toTypeHierarchyPrepareParams(int offset, final @NonNull 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/operations/typeHierarchy/TypeHierarchyViewHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyViewHandler.java index 64093e213..417ac7847 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyViewHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeHierarchyViewHandler.java @@ -12,6 +12,7 @@ package org.eclipse.lsp4e.operations.typeHierarchy; import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.lsp4e.LSPEclipseUtils; @@ -45,7 +46,7 @@ protected void execute(ExecutionEvent event, ITextEditor editor) { } @Override - public void setEnabled(Object evaluationContext) { + public void setEnabled(@Nullable Object evaluationContext) { setEnabled(ServerCapabilities::getTypeHierarchyProvider, this::hasSelection); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeMemberContentProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeMemberContentProvider.java index df7ef47dc..2bdd1f9dc 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeMemberContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/TypeMemberContentProvider.java @@ -14,7 +14,7 @@ import java.net.URI; import java.util.List; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.lsp4e.outline.SymbolsModel.DocumentSymbolWithURI; import org.eclipse.lsp4j.DocumentSymbol; @@ -23,14 +23,14 @@ public class TypeMemberContentProvider implements IStructuredContentProvider { private static final Object[] NO_CHILDREN = new Object[0]; @Override - public Object[] getElements(Object inputElement) { + public Object[] getElements(@Nullable Object inputElement) { if (inputElement instanceof DocumentSymbolWithURI symbolContainer) { return toContainer(symbolContainer.symbol.getChildren(), symbolContainer.uri); } return NO_CHILDREN; } - private Object[] toContainer(List symbols, @NonNull URI uri) { + private Object[] toContainer(@Nullable List symbols, URI uri) { if (symbols != null) { var container = new DocumentSymbolWithURI[symbols.size()]; for (int i = 0; i < symbols.size(); i++) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/package-info.java new file mode 100644 index 000000000..e559e6efd --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/typeHierarchy/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.operations.typeHierarchy; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java index 240f27893..067aa5cd4 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/CNFOutlinePage.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.outline; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.*; + import java.util.ArrayList; import org.eclipse.core.runtime.Adapters; @@ -20,7 +22,7 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -62,18 +64,17 @@ public class CNFOutlinePage implements IContentOutlinePage, ILabelProviderListen public static final String SHOW_KIND_PREFERENCE = ID + ".showKind"; //$NON-NLS-1$ public static final String SORT_OUTLINE_PREFERENCE = ID + ".sortOutline"; //$NON-NLS-1$ - private CommonViewer outlineViewer; + private CommonViewer outlineViewer = lateNonNull(); private final IEclipsePreferences preferences; - private final ITextEditor textEditor; - private final ITextViewer textEditorViewer; + private final @Nullable ITextEditor textEditor; + private final @Nullable ITextViewer textEditorViewer; - private final IDocument document; + private final @Nullable IDocument document; - @NonNull private final LanguageServerWrapper wrapper; - public CNFOutlinePage(@NonNull LanguageServerWrapper wrapper, @Nullable ITextEditor textEditor) { + public CNFOutlinePage(LanguageServerWrapper wrapper, @Nullable ITextEditor textEditor) { preferences = InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID); preferences.addPreferenceChangeListener(this); this.textEditor = textEditor; @@ -83,23 +84,24 @@ public CNFOutlinePage(@NonNull LanguageServerWrapper wrapper, @Nullable ITextEdi } @Override - public void createControl(Composite parent) { + public void createControl(@NonNullByDefault({}) Composite parent) { outlineViewer = new CommonViewer(ID, parent, SWT.NONE); if (document != null) { outlineViewer.setInput(new OutlineViewerInput(document, wrapper, textEditor)); } outlineViewer.setSorter(new CommonViewerSorter()); outlineViewer.getLabelProvider().addListener(this); + final var textEditor = this.textEditor; if (textEditor != null) { outlineViewer.addOpenListener(event -> { if (preferences.getBoolean(LINK_WITH_EDITOR_PREFERENCE, true)) textEditor.setFocus(); }); outlineViewer.addSelectionChangedListener(event -> { + final var document = this.document; if (document != null && preferences.getBoolean(LINK_WITH_EDITOR_PREFERENCE, true) - && outlineViewer.getTree().isFocusControl() && outlineViewer.getSelection() != null) { - Object selection = ((TreeSelection) outlineViewer.getSelection()).getFirstElement(); - Range range = getRangeSelection(selection); + && outlineViewer.getTree().isFocusControl() && outlineViewer.getSelection() instanceof TreeSelection sel) { + Range range = getRangeSelection(sel.getFirstElement()); if (range != null) { try { int startOffset = document.getLineOffset(range.getStart().getLine()) @@ -113,6 +115,7 @@ public void createControl(Composite parent) { } } }); + final var textEditorViewer = this.textEditorViewer; if (textEditorViewer != null) { editorSelectionChangedListener = new EditorSelectionChangedListener(); editorSelectionChangedListener.install(textEditorViewer.getSelectionProvider()); @@ -127,7 +130,7 @@ public void createControl(Composite parent) { * the selected symbol. * @return the range of the given selection and null otherwise. */ - private Range getRangeSelection(Object selection) { + private @Nullable Range getRangeSelection(@Nullable Object selection) { if (selection == null) { return null; } @@ -143,9 +146,6 @@ private Range getRangeSelection(Object selection) { class EditorSelectionChangedListener implements ISelectionChangedListener { public void install(ISelectionProvider selectionProvider) { - if (selectionProvider == null) - return; - if (selectionProvider instanceof IPostSelectionProvider provider) { provider.addPostSelectionChangedListener(this); } else { @@ -154,9 +154,6 @@ public void install(ISelectionProvider selectionProvider) { } public void uninstall(ISelectionProvider selectionProvider) { - if (selectionProvider == null) - return; - if (selectionProvider instanceof IPostSelectionProvider provider) { provider.removePostSelectionChangedListener(this); } else { @@ -166,6 +163,10 @@ public void uninstall(ISelectionProvider selectionProvider) { @Override public void selectionChanged(SelectionChangedEvent event) { + final var document = CNFOutlinePage.this.document; + if(document == null) { + return; + } ISelection selection = event.getSelection(); if (selection instanceof ITextSelection textSelection) { if (!preferences.getBoolean(LINK_WITH_EDITOR_PREFERENCE, true)) { @@ -179,7 +180,7 @@ public void selectionChanged(SelectionChangedEvent event) { } } - private EditorSelectionChangedListener editorSelectionChangedListener; + private @Nullable EditorSelectionChangedListener editorSelectionChangedListener; public static void refreshTreeSelection(TreeViewer viewer, int offset, IDocument document) { final var contentProvider = (ITreeContentProvider) viewer.getContentProvider(); @@ -187,7 +188,7 @@ public static void refreshTreeSelection(TreeViewer viewer, int offset, IDocument return; } - Object[] objects = contentProvider.getElements(null); + Object @Nullable [] objects = contentProvider.getElements(null); final var path = new ArrayList(); while (objects != null && objects.length > 0) { boolean found = false; @@ -219,7 +220,7 @@ public static void refreshTreeSelection(TreeViewer viewer, int offset, IDocument } @SuppressWarnings("unused") - private static Range toRange(Object object) { + private static @Nullable Range toRange(Object object) { Range range = null; @Nullable SymbolInformation symbol = object instanceof SymbolInformation symbolInformation ? symbolInformation @@ -261,12 +262,12 @@ public void dispose() { } @Override - public Control getControl() { + public @Nullable Control getControl() { return this.outlineViewer.getControl(); } @Override - public void setActionBars(IActionBars actionBars) { + public void setActionBars(@NonNullByDefault({}) IActionBars actionBars) { // nothing to do yet, comment requested by sonar } @@ -302,7 +303,7 @@ public void labelProviderChanged(LabelProviderChangedEvent event) { @Override public void preferenceChange(final PreferenceChangeEvent event) { - if (SORT_OUTLINE_PREFERENCE.equals(event.getKey()) && outlineViewer != null) { + if (SORT_OUTLINE_PREFERENCE.equals(event.getKey()) && castNullable(outlineViewer) != null) { outlineViewer.refresh(); } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/EditorToOutlineAdapterFactory.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/EditorToOutlineAdapterFactory.java index 4467fca5b..e4558e2b3 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/EditorToOutlineAdapterFactory.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/EditorToOutlineAdapterFactory.java @@ -23,7 +23,7 @@ import java.util.concurrent.TimeoutException; import org.eclipse.core.runtime.IAdapterFactory; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; @@ -45,7 +45,7 @@ public class EditorToOutlineAdapterFactory implements IAdapterFactory { private static final Map LANG_SERVER_CACHE = Collections.synchronizedMap(new HashMap<>()); @Override - public T getAdapter(Object adaptableObject, Class adapterType) { + public @Nullable T getAdapter(@Nullable Object adaptableObject, @Nullable Class adapterType) { if (adapterType == IContentOutlinePage.class && adaptableObject instanceof IEditorPart editorPart) { final IEditorInput editorInput = editorPart.getEditorInput(); @@ -86,7 +86,7 @@ public Class[] getAdapterList() { return new Class[] { IContentOutlinePage.class }; } - private static CNFOutlinePage createOutlinePage(IEditorPart editorPart, @NonNull LanguageServerWrapper wrapper) { + private static CNFOutlinePage createOutlinePage(IEditorPart editorPart, LanguageServerWrapper wrapper) { return new CNFOutlinePage(wrapper, UI.asTextEditor(editorPart)); } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/HasCNFOutlinePage.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/HasCNFOutlinePage.java index abc2db850..b4528fb65 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/HasCNFOutlinePage.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/HasCNFOutlinePage.java @@ -12,12 +12,13 @@ package org.eclipse.lsp4e.outline; import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.ui.views.contentoutline.ContentOutline; public class HasCNFOutlinePage 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 ContentOutline outline) { return outline.getCurrentPage() instanceof CNFOutlinePage; } 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 069e263ab..68a1ec119 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/LSSymbolsContentProvider.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.outline; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; @@ -68,20 +70,13 @@ public class LSSymbolsContentProvider implements ICommonContentProvider, ITreeCo public static final String VIEWER_PROPERTY_IS_QUICK_OUTLINE = "isQuickOutline"; //$NON-NLS-1$ - @NonNullByDefault public static final class OutlineViewerInput { public final IDocument document; public final LanguageServerWrapper wrapper; - - @Nullable - public final ITextEditor textEditor; - - @Nullable - public final IFile documentFile; - - @Nullable - private final URI documentURI; + public final @Nullable ITextEditor textEditor; + public final @Nullable IFile documentFile; + private final @Nullable URI documentURI; public OutlineViewerInput(IDocument document, LanguageServerWrapper wrapper, @Nullable ITextEditor textEditor) { this.document = document; @@ -167,17 +162,17 @@ protected void initialProcess() { } @Override - protected void process(DirtyRegion dirtyRegion) { + protected void process(@NonNullByDefault({}) DirtyRegion dirtyRegion) { refreshTreeContentFromLS(); } @Override - protected void reconcilerDocumentChanged(IDocument newDocument) { + protected void reconcilerDocumentChanged(@NonNullByDefault({}) IDocument newDocument) { // Do nothing } @Override - public IReconcilingStrategy getReconcilingStrategy(String contentType) { + public @Nullable IReconcilingStrategy getReconcilingStrategy(@NonNullByDefault({}) String contentType) { return null; } } @@ -221,15 +216,15 @@ public void resourceChanged(IResourceChangeEvent event) { } } - private TreeViewer viewer; - private volatile Throwable lastError; - private OutlineViewerInput outlineViewerInput; + private TreeViewer viewer = lateNonNull(); + private volatile @Nullable Throwable lastError; + private OutlineViewerInput outlineViewerInput = lateNonNull(); private final SymbolsModel symbolsModel = new SymbolsModel(); - private volatile CompletableFuture>> symbols; + private volatile @Nullable CompletableFuture<@Nullable List>> symbols; private final boolean refreshOnResourceChanged; private boolean isQuickOutline; - private IOutlineUpdater outlineUpdater; + private @Nullable IOutlineUpdater outlineUpdater; public LSSymbolsContentProvider() { this(false); @@ -240,11 +235,11 @@ public LSSymbolsContentProvider(boolean refreshOnResourceChanged) { } @Override - public void init(ICommonContentExtensionSite aConfig) { + public void init(@NonNullByDefault({}) ICommonContentExtensionSite aConfig) { } @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + public void inputChanged(Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) { if (outlineUpdater != null) { outlineUpdater.uninstall(); } @@ -276,7 +271,7 @@ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } private IOutlineUpdater createOutlineUpdater() { - if (refreshOnResourceChanged) { + if (refreshOnResourceChanged && outlineViewerInput.documentFile != null) { return new ResourceChangeOutlineUpdater(outlineViewerInput.documentFile); } final ITextViewer textViewer = LSPEclipseUtils.getTextViewer(outlineViewerInput.textEditor); @@ -285,7 +280,7 @@ private IOutlineUpdater createOutlineUpdater() { } @Override - public Object[] getElements(Object inputElement) { + public Object[] getElements(@Nullable Object inputElement) { if (this.symbols != null && !this.symbols.isDone()) { return new Object[] { new PendingUpdateAdapter() }; } @@ -301,7 +296,7 @@ public Object[] getChildren(Object parentElement) { } @Override - public Object getParent(Object element) { + public @Nullable Object getParent(Object element) { return symbolsModel.getParent(element); } @@ -325,7 +320,7 @@ protected void refreshTreeContentFromLS() { } final var params = new DocumentSymbolParams(LSPEclipseUtils.toTextDocumentIdentifier(documentURI)); - symbols = outlineViewerInput.wrapper.execute(ls -> ls.getTextDocumentService().documentSymbol(params)); + final var symbols = this.symbols = outlineViewerInput.wrapper.execute(ls -> ls.getTextDocumentService().documentSymbol(params)); symbols.thenAcceptAsync(response -> { symbolsModel.update(response); lastError = null; @@ -378,10 +373,10 @@ public void dispose() { } @Override - public void restoreState(IMemento aMemento) { + public void restoreState(@NonNullByDefault({}) IMemento aMemento) { } @Override - public void saveState(IMemento aMemento) { + public void saveState(@NonNullByDefault({}) IMemento aMemento) { } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineSorter.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineSorter.java index d9cfd50e8..f8229203b 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineSorter.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/OutlineSorter.java @@ -13,6 +13,7 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.lsp4e.LanguageServerPlugin; @@ -26,7 +27,7 @@ public class OutlineSorter extends ViewerComparator { protected final IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID); @Override - public int compare(final Viewer viewer, final Object o1, final Object o2) { + public int compare(final @Nullable Viewer viewer, final @Nullable Object o1, final @Nullable Object o2) { if (!isSortingEnabled()) return 0; @@ -42,7 +43,10 @@ public int compare(final Viewer viewer, final Object o1, final Object o2) { return name1.compareTo(name2); } - private String getName(Object element) { + private @Nullable String getName(@Nullable Object element) { + if (element == null) + return null; + if (element instanceof Either either) { element = either.get(); } @@ -59,7 +63,7 @@ private String getName(Object element) { } @Override - public boolean isSorterProperty(final Object element, final String property) { + public boolean isSorterProperty(final @Nullable Object element, final @Nullable String property) { return "name".equals(property); //$NON-NLS-1$ } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ShowKindHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ShowKindHandler.java index 85570d263..227260ec3 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ShowKindHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ShowKindHandler.java @@ -17,13 +17,14 @@ import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.ui.handlers.HandlerUtil; public class ShowKindHandler extends AbstractHandler { @Override - public Object execute(ExecutionEvent event) throws ExecutionException { + public @Nullable Object execute(ExecutionEvent event) throws ExecutionException { Command command = event.getCommand(); boolean oldValue = HandlerUtil.toggleCommandState(command); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolInformationPropertyTester.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolInformationPropertyTester.java index a344b6dc6..bea8371e7 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolInformationPropertyTester.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolInformationPropertyTester.java @@ -15,6 +15,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4j.SymbolInformation; /** @@ -59,7 +60,7 @@ public class SymbolInformationPropertyTester extends PropertyTester { public static final String CONTENT_TYPE_ID = "contentTypeId"; //$NON-NLS-1$ @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 SymbolInformation info) { if (info.getLocation() == null || info.getLocation().getUri() == null) { return false; @@ -70,7 +71,7 @@ public boolean test(Object receiver, String property, Object[] args, Object expe case CONTENT_TYPE_ID -> { IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); IContentType contentType = contentTypeManager.findContentTypeFor(uri); - yield contentType != null && contentType.getId().equals(expectedValue.toString()); + yield contentType != null && expectedValue != null && contentType.getId().equals(expectedValue.toString()); } default -> false; }; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java index 982b36621..e6d74ff1b 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsLabelProvider.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.outline; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNullable; + import java.net.URI; import java.util.Arrays; import java.util.Comparator; @@ -28,7 +30,8 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -116,7 +119,7 @@ public void dispose() { } @Override - public Image getImage(Object element) { + public @Nullable Image getImage(@Nullable Object element) { if (element == null){ return null; } @@ -150,7 +153,7 @@ public Image getImage(Object element) { /* * Implementation node: for problem decoration,m aybe consider using a ILabelDecorator/IDelayedLabelDecorator? */ - if (file != null) { + if (file != null && res != null) { Range range = null; if (element instanceof SymbolInformation symbol) { range = symbol.getLocation().getRange(); @@ -182,7 +185,7 @@ public Image getImage(Object element) { return res; } - protected int getMaxSeverity(@NonNull IResource resource, @NonNull IDocument doc, @NonNull Range range) + protected int getMaxSeverity(IResource resource, IDocument doc, Range range) throws CoreException, BadLocationException { if (!severities.containsKey(resource)) { refreshMarkersByLine(resource); @@ -230,8 +233,8 @@ private Image getOverlay(Image res, int maxSeverity) { if (maxSeverity != 1 && maxSeverity != 2) { throw new IllegalArgumentException("Severity " + maxSeverity + " not supported."); //$NON-NLS-1$ //$NON-NLS-2$ } - Image[] currentOverlays = this.overlays.computeIfAbsent(res, key -> new Image[2]); - if (currentOverlays[maxSeverity - 1] == null) { + Image[] currentOverlays = this.overlays.computeIfAbsent(res, key -> new Image [2]); + if (castNullable(currentOverlays[maxSeverity - 1]) == null) { String overlayId = null; if (maxSeverity == IMarker.SEVERITY_ERROR) { overlayId = ISharedImages.IMG_DEC_FIELD_ERROR; @@ -250,7 +253,7 @@ public String getText(Object element) { } @Override - public StyledString getStyledText(Object element) { + public StyledString getStyledText(@Nullable Object element) { if (element instanceof PendingUpdateAdapter) { return new StyledString(Messages.outline_computingSymbols); @@ -330,7 +333,7 @@ public StyledString getStyledText(Object element) { return res; } - private boolean isDeprecated(List tags) { + private boolean isDeprecated(@Nullable List tags) { if(tags != null){ return tags.contains(SymbolTag.Deprecated); } @@ -338,20 +341,20 @@ private boolean isDeprecated(List tags) { } @Override - public void restoreState(IMemento aMemento) { + public void restoreState(@NonNullByDefault({}) IMemento aMemento) { } @Override - public void saveState(IMemento aMemento) { + public void saveState(@NonNullByDefault({}) IMemento aMemento) { } @Override - public String getDescription(Object anElement) { + public @Nullable String getDescription(@NonNullByDefault({}) Object anElement) { return null; } @Override - public void init(ICommonContentExtensionSite aConfig) { + public void init(@NonNullByDefault({}) ICommonContentExtensionSite aConfig) { } @Override diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java index 64a149f36..9df05c2fa 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.outline; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.*; + import java.net.URI; import java.util.ArrayDeque; import java.util.ArrayList; @@ -23,7 +25,7 @@ import java.util.Objects; import java.util.function.Function; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.TreePath; import org.eclipse.lsp4j.DocumentSymbol; import org.eclipse.lsp4j.Location; @@ -39,7 +41,7 @@ public class SymbolsModel { private volatile Map> childrenMap = Collections.emptyMap(); private volatile List rootSymbols = Collections.emptyList(); - private URI uri; + private @Nullable URI uri; /** * @deprecated use {@link DocumentSymbolWithURI} @@ -47,15 +49,15 @@ public class SymbolsModel { @Deprecated(since = "0.17.0", forRemoval = true) public static class DocumentSymbolWithFile { public final DocumentSymbol symbol; - public final @NonNull URI uri; + public final URI uri; - public DocumentSymbolWithFile(DocumentSymbol symbol, @NonNull URI uri) { + public DocumentSymbolWithFile(DocumentSymbol symbol, URI uri) { this.symbol = symbol; this.uri = uri; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof DocumentSymbolWithFile other && // Objects.equals(this.symbol, other.symbol) && // Objects.equals(this.uri, other.uri); @@ -69,16 +71,16 @@ public int hashCode() { public static class DocumentSymbolWithURI extends DocumentSymbolWithFile { public final DocumentSymbol symbol; - public final @NonNull URI uri; + public final URI uri; - public DocumentSymbolWithURI(DocumentSymbol symbol, @NonNull URI uri) { + public DocumentSymbolWithURI(DocumentSymbol symbol, URI uri) { super(symbol, uri); this.symbol = symbol; this.uri = uri; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof DocumentSymbolWithURI other && // Objects.equals(this.symbol, other.symbol) && // Objects.equals(this.uri, other.uri); @@ -90,7 +92,7 @@ public int hashCode() { } } - public synchronized boolean update(List> response) { + public synchronized boolean update(@Nullable List> response) { // TODO update model only on real change if (response == null || response.isEmpty()) { childrenMap = Collections.emptyMap(); @@ -114,15 +116,15 @@ public synchronized boolean update(List children = childrenMap.get(parentElement); @@ -191,7 +193,7 @@ public Object[] getChildren(Object parentElement) { return EMPTY; } - public boolean hasChildren(Object parentElement) { + public boolean hasChildren(@Nullable Object parentElement) { if (parentElement != null) { if (parentElement instanceof SymbolInformation) { List children = childrenMap.get(parentElement); @@ -208,7 +210,7 @@ public boolean hasChildren(Object parentElement) { return false; } - public Object getParent(Object element) { + public @Nullable Object getParent(@Nullable Object element) { if (element instanceof SymbolInformation) { for (Map.Entry> entry : childrenMap.entrySet()) { if (entry.getValue().contains(element)) { @@ -219,18 +221,18 @@ public Object getParent(Object element) { return null; } - public void setUri(URI uri) { + public void setUri(@Nullable URI uri) { this.uri = uri; } - public TreePath toUpdatedSymbol(TreePath initialSymbol) { + public @Nullable TreePath toUpdatedSymbol(TreePath initialSymbol) { final var res = new ArrayList(initialSymbol.getSegmentCount()); Object currentSymbol = null; for (int i = 0; i < initialSymbol.getSegmentCount(); i++) { String name = getName(initialSymbol.getSegment(i)); Object[] currentChildren = (currentSymbol == null ? getElements() : getChildren(currentSymbol)); - currentSymbol = Arrays.stream(currentChildren).filter(child -> Objects.equals(getName(child), name)) - .findAny().orElse(null); + currentSymbol = castNullable(Arrays.stream(currentChildren).filter(child -> Objects.equals(getName(child), name)) + .findAny().orElse(null)); if (currentSymbol == null) { return null; } @@ -239,7 +241,7 @@ public TreePath toUpdatedSymbol(TreePath initialSymbol) { return new TreePath(res.toArray()); } - private String getName(Object segment) { + private @Nullable String getName(Object segment) { if (segment instanceof DocumentSymbolWithURI symbolWithURI) { segment = symbolWithURI.symbol; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleLinkingHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleLinkingHandler.java index f9a4aed5d..e38f73b86 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleLinkingHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleLinkingHandler.java @@ -17,13 +17,14 @@ import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.ui.handlers.HandlerUtil; public class ToggleLinkingHandler extends AbstractHandler { @Override - public Object execute(ExecutionEvent event) throws ExecutionException { + public @Nullable Object execute(ExecutionEvent event) throws ExecutionException { Command command = event.getCommand(); boolean oldValue = HandlerUtil.toggleCommandState(command); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleSortOutlineHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleSortOutlineHandler.java index 1ec074dfb..e5178016a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleSortOutlineHandler.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/ToggleSortOutlineHandler.java @@ -16,13 +16,14 @@ import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.ui.handlers.HandlerUtil; public class ToggleSortOutlineHandler extends AbstractHandler { @Override - public Object execute(final ExecutionEvent event) throws ExecutionException { + public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException { final boolean oldValue = HandlerUtil.toggleCommandState(event.getCommand()); final IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(LanguageServerPlugin.PLUGIN_ID); prefs.putBoolean(CNFOutlinePage.SORT_OUTLINE_PREFERENCE, !oldValue); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/package-info.java new file mode 100644 index 000000000..e0cea35f3 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.outline; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; 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; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/LSPProgressManager.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/LSPProgressManager.java index d32e977b3..6af20537f 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/LSPProgressManager.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/LSPProgressManager.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.progress; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNullable; + import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingQueue; @@ -23,11 +25,12 @@ import org.eclipse.core.runtime.ICoreRunnable; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; import org.eclipse.lsp4e.ui.Messages; @@ -44,8 +47,8 @@ public class LSPProgressManager { private final Map> progressMap; private final Map currentPercentageMap; - private LanguageServer languageServer; - private LanguageServerDefinition languageServerDefinition; + private @Nullable LanguageServer languageServer; + private @Nullable LanguageServerDefinition languageServerDefinition; private final Set done; private final Set jobs; @@ -67,7 +70,7 @@ public void connect(final LanguageServer languageServer, LanguageServerDefinitio * the {@link WorkDoneProgressCreateParams} to be used to create the progress * @return the completable future */ - public @NonNull CompletableFuture createProgress(final @NonNull WorkDoneProgressCreateParams params) { + public CompletableFuture createProgress(final WorkDoneProgressCreateParams params) { final var queue = new LinkedBlockingDeque(); String jobIdentifier = params.getToken().map(Function.identity(), Object::toString); @@ -88,7 +91,8 @@ private void createJob(final LinkedBlockingDeque queue, final St || languageServerDefinition.label == null || languageServerDefinition.label.isBlank() // ? Messages.LSPProgressManager_BackgroundJobName : languageServerDefinition.label; - Job job = Job.create(jobName, (ICoreRunnable) monitor -> { + Job job = Job.create(jobName, (ICoreRunnable) mon -> { + final var monitor = mon == null ? new NullProgressMonitor() : mon; try { while (true) { if (monitor.isCanceled()) { @@ -101,7 +105,7 @@ private void createJob(final LinkedBlockingDeque queue, final St } throw new OperationCanceledException(); } - ProgressParams nextProgressNotification = queue.pollFirst(1, TimeUnit.SECONDS); + ProgressParams nextProgressNotification = castNullable(queue.pollFirst(1, TimeUnit.SECONDS)); if (nextProgressNotification != null ) { WorkDoneProgressNotification progressNotification = nextProgressNotification.getValue().getLeft(); if (progressNotification != null) { @@ -182,7 +186,7 @@ private void report(final WorkDoneProgressReport report, final IProgressMonitor * @param params * the {@link ProgressParams} used for the progress notification */ - public void notifyProgress(final @NonNull ProgressParams params) { + public void notifyProgress(final ProgressParams params) { String jobIdentifier = params.getToken().map(Function.identity(), Object::toString); BlockingQueue progress = progressMap.get(jobIdentifier); if (progress != null) { // may happen if the server does not wait on the return value of the future of createProgress diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/package-info.java new file mode 100644 index 000000000..903b32aed --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/progress/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.progress; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/CreateFileChange.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/CreateFileChange.java index 801c9b04f..9d43cfe87 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/CreateFileChange.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/CreateFileChange.java @@ -37,6 +37,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LSPEclipseUtils; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.lsp4e.ui.Messages; @@ -51,15 +52,15 @@ public class CreateFileChange extends ResourceChange { private final URI uri; private final String fSource; - private String fEncoding; + private @Nullable String fEncoding; private boolean fExplicitEncoding; private final long fStampToRestore; - public CreateFileChange(URI uri, String source, String encoding) { + public CreateFileChange(URI uri, String source, @Nullable String encoding) { this(uri, source, encoding, IResource.NULL_STAMP); } - public CreateFileChange(URI uri, String source, String encoding, long stampToRestore) { + public CreateFileChange(URI uri, String source, @Nullable String encoding, long stampToRestore) { Assert.isNotNull(uri, "uri"); //$NON-NLS-1$ Assert.isNotNull(source, "source"); //$NON-NLS-1$ this.uri = uri; @@ -81,7 +82,7 @@ public String getName() { } @Override - protected IFile getModifiedResource() { + protected @Nullable IFile getModifiedResource() { return LSPEclipseUtils.getFileHandle(this.uri); } @@ -98,10 +99,10 @@ public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException { } @Override - public Change perform(IProgressMonitor pm) throws CoreException { + public @Nullable Change perform(IProgressMonitor pm) throws CoreException { pm.beginTask(NLS.bind(Messages.edit_CreateFile, uri), 3); - initializeEncoding(); + final var fEncoding = initializeEncoding(); try (InputStream is= new ByteArrayInputStream(fSource.getBytes(fEncoding))) { @@ -141,7 +142,10 @@ public Change perform(IProgressMonitor pm) throws CoreException { } } else { final var file = new File(this.uri); - Files.createDirectories(file.getParentFile().toPath()); + final var parentFile = file.getParentFile(); + if (parentFile != null) { + Files.createDirectories(parentFile.toPath()); + } if (!file.createNewFile()) { throw new IOException(String.format("Failed to create file '%s'",file)); //$NON-NLS-1$ } @@ -155,7 +159,7 @@ public Change perform(IProgressMonitor pm) throws CoreException { return null; } - private void initializeEncoding() { + private String initializeEncoding() { if (fEncoding == null) { fExplicitEncoding= false; IFile ifile = getModifiedResource(); @@ -185,5 +189,6 @@ private void initializeEncoding() { } } Assert.isNotNull(fEncoding); + return fEncoding; } } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/DeleteExternalFile.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/DeleteExternalFile.java index f8938b36a..a37d38c9d 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/DeleteExternalFile.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/DeleteExternalFile.java @@ -19,16 +19,16 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServerPlugin; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.RefactoringStatus; public class DeleteExternalFile extends Change { - private final @NonNull File file; + private final File file; - public DeleteExternalFile(@NonNull File file) { + public DeleteExternalFile(File file) { this.file = file; } @@ -48,7 +48,7 @@ public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException { } @Override - public Change perform(IProgressMonitor pm) throws CoreException { + public @Nullable Change perform(IProgressMonitor pm) throws CoreException { try { Files.delete(this.file.toPath()); } catch (IOException e) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/LSPTextChange.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/LSPTextChange.java index 5f1deeb6e..6e334494e 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/LSPTextChange.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/LSPTextChange.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.refactoring; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.*; + import java.net.URI; import org.eclipse.core.filebuffers.FileBuffers; @@ -26,7 +28,7 @@ import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4e.LSPEclipseUtils; @@ -47,22 +49,22 @@ @SuppressWarnings("restriction") public class LSPTextChange extends TextChange { - private final @NonNull URI fileUri; + private final URI fileUri; - private Either file; + private Either file = lateNonNull(); private int fAcquireCount; - private ITextFileBuffer fBuffer; - private @NonNull String newText; - private Range range; + private @Nullable ITextFileBuffer fBuffer; + private String newText; + private @Nullable Range range; - public LSPTextChange(@NonNull String name, @NonNull URI fileUri, @NonNull TextEdit textEdit) { + public LSPTextChange(String name, URI fileUri, TextEdit textEdit) { super(name); this.fileUri = fileUri; this.newText = textEdit.getNewText(); this.range = textEdit.getRange(); } - public LSPTextChange(@NonNull String name, @NonNull URI fileUri, @NonNull String newText) { + public LSPTextChange(String name, URI fileUri, String newText) { super(name); this.fileUri = fileUri; this.newText = newText; @@ -73,7 +75,7 @@ public LSPTextChange(@NonNull String name, @NonNull URI fileUri, @NonNull String protected IDocument acquireDocument(IProgressMonitor pm) throws CoreException { fAcquireCount++; if (fAcquireCount > 1) { - return fBuffer.getDocument(); + return castNonNull(this.fBuffer).getDocument(); } IFile iFile = LSPEclipseUtils.getFileHandle(this.fileUri); @@ -106,9 +108,10 @@ protected IDocument acquireDocument(IProgressMonitor pm) throws CoreException { // because that's used by the preview logic to compute the changed document. We do it here rather than in the constructor // since we need the document to translate line offsets into character offset. Strictly this would not work then // if the platform called getEdit() prior to this method being traversed, but it seems to be OK in practice. - final IDocument document = fBuffer.getDocument(); + final IDocument document = castNonNull(this.fBuffer).getDocument(); int offset = 0; int length = document.getLength(); + final var range = this.range; if (range != null && getEdit() == null) { try { offset = LSPEclipseUtils.toOffset(range.getStart(), document); @@ -124,7 +127,7 @@ protected IDocument acquireDocument(IProgressMonitor pm) throws CoreException { @Override protected void commit(IDocument document, IProgressMonitor pm) throws CoreException { - this.fBuffer.commit(pm, true); + castNonNull(this.fBuffer).commit(pm, true); } @Override @@ -132,7 +135,7 @@ protected void releaseDocument(IDocument document, IProgressMonitor pm) throws C Assert.isTrue(fAcquireCount > 0); if (fAcquireCount == 1) { ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager(); - this.fBuffer.commit(pm, true); + castNonNull(this.fBuffer).commit(pm, true); if (this.file.isLeft()) { manager.disconnect(this.file.getLeft().getFullPath(), LocationKind.IFILE, pm); } else { @@ -143,7 +146,7 @@ protected void releaseDocument(IDocument document, IProgressMonitor pm) throws C } @Override - protected Change createUndoChange(UndoEdit edit) { + protected @Nullable Change createUndoChange(UndoEdit edit) { throw new UnsupportedOperationException("Should not be called!"); //$NON-NLS-1$ } @@ -158,7 +161,7 @@ public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException { } @Override - public Object getModifiedElement() { + public @Nullable Object getModifiedElement() { IFile file = LSPEclipseUtils.getFileHandle(this.fileUri); if (file != null) { return file; @@ -170,7 +173,7 @@ public Object getModifiedElement() { } @Override - public Change perform(IProgressMonitor pm) throws CoreException { + public @Nullable Change perform(IProgressMonitor pm) throws CoreException { pm.beginTask("", 3); //$NON-NLS-1$ IDocument document = null; @@ -179,6 +182,7 @@ public Change perform(IProgressMonitor pm) throws CoreException { int offset = 0; int length = document.getLength(); + final var range = this.range; if (range != null) { offset = LSPEclipseUtils.toOffset(range.getStart(), document); length = LSPEclipseUtils.toOffset(range.getEnd(), document) - offset; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/package-info.java new file mode 100644 index 000000000..54bab061f --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/refactoring/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.refactoring; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessOverSocketStreamConnectionProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessOverSocketStreamConnectionProvider.java index 4322674df..0da494f76 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessOverSocketStreamConnectionProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessOverSocketStreamConnectionProvider.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Objects; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.lsp4e.LanguageServerPlugin; /** @@ -30,9 +31,9 @@ public abstract class ProcessOverSocketStreamConnectionProvider extends ProcessStreamConnectionProvider { private final int port; - private Socket socket; - private InputStream inputStream; - private OutputStream outputStream; + private @Nullable Socket socket; + private @Nullable InputStream inputStream; + private @Nullable OutputStream outputStream; protected ProcessOverSocketStreamConnectionProvider(List commands, int port) { super(commands); @@ -49,7 +50,7 @@ public void start() throws IOException { final var serverSocket = new ServerSocket(port); final var socketThread = new Thread(() -> { try { - socket = serverSocket.accept(); + this.socket = serverSocket.accept(); } catch (IOException e) { LanguageServerPlugin.logError(e); } finally { @@ -70,6 +71,7 @@ public void start() throws IOException { Thread.currentThread().interrupt(); } + final var socket = this.socket; if (socket == null) { throw new IOException("Unable to make socket connection: " + this); //$NON-NLS-1$ } @@ -79,12 +81,12 @@ public void start() throws IOException { } @Override - public InputStream getInputStream() { + public @Nullable InputStream getInputStream() { return inputStream; } @Override - public OutputStream getOutputStream() { + public @Nullable OutputStream getOutputStream() { return outputStream; } @@ -107,7 +109,7 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessStreamConnectionProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessStreamConnectionProvider.java index 3a583e570..986387887 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessStreamConnectionProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/ProcessStreamConnectionProvider.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.server; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -30,7 +32,7 @@ public abstract class ProcessStreamConnectionProvider implements StreamConnectionProvider, IAdaptable { private @Nullable Process process; - private List commands; + private @Nullable List commands; private @Nullable String workingDir; protected ProcessStreamConnectionProvider() { @@ -47,7 +49,8 @@ protected ProcessStreamConnectionProvider(List commands, String workingD @Override public void start() throws IOException { - if (this.commands == null || this.commands.isEmpty() || this.commands.stream().anyMatch(Objects::isNull)) { + final var commands = this.commands; + if (commands == null || commands.isEmpty() || commands.stream().anyMatch(Objects::isNull)) { throw new IOException("Unable to start language server: " + this); //$NON-NLS-1$ } @@ -60,9 +63,10 @@ public void start() throws IOException { } protected ProcessBuilder createProcessBuilder() { - final var builder = new ProcessBuilder(getCommands()); - if (getWorkingDirectory() != null) { - builder.directory(new File(getWorkingDirectory())); + final var builder = new ProcessBuilder(castNonNull(getCommands())); + final var workDir = getWorkingDirectory(); + if (workDir != null) { + builder.directory(new File(workDir)); } builder.redirectError(ProcessBuilder.Redirect.INHERIT); return builder; @@ -96,7 +100,7 @@ public void stop() { @Override @SuppressWarnings("unchecked") - public T getAdapter(Class adapter) { + public @Nullable T getAdapter(@Nullable Class adapter) { final var process = this.process; if(adapter == ProcessHandle.class) { try { @@ -108,7 +112,7 @@ public T getAdapter(Class adapter) { return null; } - protected List getCommands() { + protected @Nullable List getCommands() { return commands; } @@ -125,7 +129,7 @@ public void setWorkingDirectory(String workingDir) { } @Override - public boolean equals(Object obj) { + public boolean equals(final @Nullable Object obj) { if (obj == null) { return false; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/StreamConnectionProvider.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/StreamConnectionProvider.java index dba12780b..c928c2511 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/StreamConnectionProvider.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/StreamConnectionProvider.java @@ -40,9 +40,9 @@ public interface StreamConnectionProvider { public void start() throws IOException; - public InputStream getInputStream(); + public @Nullable InputStream getInputStream(); - public OutputStream getOutputStream(); + public @Nullable OutputStream getOutputStream(); /** * Returns the {@link InputStream} connected to the error output of the process @@ -65,7 +65,7 @@ public interface StreamConnectionProvider { * @return a newly created {@link InputStream} that copies all data to the * provided {@link OutputStream} */ - public default InputStream forwardCopyTo(InputStream input, OutputStream output) { + public default @Nullable InputStream forwardCopyTo(@Nullable InputStream input, @Nullable OutputStream output) { if (input == null) return null; if (output == null) @@ -104,7 +104,7 @@ public int read(byte[] b) throws IOException { /** * User provided initialization options. */ - public default Object getInitializationOptions(@Nullable URI rootUri){ + public default @Nullable Object getInitializationOptions(@Nullable URI rootUri){ return null; } @@ -117,7 +117,7 @@ public default Object getInitializationOptions(@Nullable URI rootUri){ * @return an object whose fields represent the different experimental features * supported by the client. */ - public default Object getExperimentalFeaturesPOJO() { + public default @Nullable Object getExperimentalFeaturesPOJO() { return null; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/package-info.java new file mode 100644 index 000000000..a7e061400 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/server/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.server; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/EnableDisableLSJob.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/EnableDisableLSJob.java index c6dbb3518..52b4433dd 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/EnableDisableLSJob.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/EnableDisableLSJob.java @@ -17,6 +17,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.Nullable; import org.eclipse.lsp4e.ContentTypeToLanguageServerDefinition; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; import org.eclipse.lsp4e.LanguageServiceAccessor; @@ -25,17 +26,17 @@ public class EnableDisableLSJob extends Job { private final List serverDefinitions; - private final IEditorReference[] editors; + private final IEditorReference @Nullable [] editors; public EnableDisableLSJob(List serverDefinitions, - IEditorReference[] editors) { + IEditorReference @Nullable [] editors) { super(Messages.enableDisableLSJob); this.serverDefinitions = serverDefinitions; this.editors = editors; } @Override - protected IStatus run(IProgressMonitor monitor) { + protected IStatus run(@Nullable IProgressMonitor monitor) { for (ContentTypeToLanguageServerDefinition changedDefinition : serverDefinitions) { LanguageServerDefinition serverDefinition = changedDefinition.getValue(); if (serverDefinition != null) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java index cd2bc504d..8f6f4359d 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LSPImages.java @@ -18,6 +18,7 @@ import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.lsp4e.LSPEclipseUtils; @@ -38,7 +39,7 @@ private LSPImages() { // this class shouldn't be instantiated } - private static ImageRegistry imageRegistry; + private static @Nullable ImageRegistry imageRegistry; private static final Map colorToImageCache = new HashMap<>(); private static final String ICONS_PATH = "$nl$/icons/full/"; //$NON-NLS-1$ private static final String OBJECT = ICONS_PATH + "obj16/"; // basic colors - size 16x16 //$NON-NLS-1$ @@ -123,7 +124,7 @@ private static final void declareRegistryImage(String key, String path) { desc = ImageDescriptor.createFromURL(url); } } - imageRegistry.put(key, desc); + getImageRegistry().put(key, desc); } /** @@ -141,8 +142,9 @@ public static ImageDescriptor getImageDescriptor(String key) { } public static ImageRegistry getImageRegistry() { + ImageRegistry imageRegistry = LSPImages.imageRegistry; if (imageRegistry == null) { - imageRegistry = LanguageServerPlugin.getDefault().getImageRegistry(); + imageRegistry = LSPImages.imageRegistry = LanguageServerPlugin.getDefault().getImageRegistry(); } return imageRegistry; } @@ -151,7 +153,7 @@ public static ImageRegistry getImageRegistry() { * @param imageId See static IMG_* fields of {@link ISharedImages} * @return the workbench's shared image for the , or null if not found */ - public static Image getSharedImage(String imageId) { + public static @Nullable Image getSharedImage(@Nullable String imageId) { if(imageId == null) { return null; } @@ -162,14 +164,14 @@ public static Image getSharedImage(String imageId) { * @param imageId See static IMG_* fields of {@link ISharedImages} * @return the workbench's shared image descriptor for the workbench, or null if not found */ - public static ImageDescriptor getSharedImageDescriptor(String imageId) { + public static @Nullable ImageDescriptor getSharedImageDescriptor(@Nullable String imageId) { if(imageId == null) { return null; } return PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(imageId); } - public static Image imageFromSymbolKind(SymbolKind kind) { + public static @Nullable Image imageFromSymbolKind(@Nullable SymbolKind kind) { if (kind == null) { return EMPTY_IMAGE; } @@ -199,7 +201,7 @@ public static Image imageFromSymbolKind(SymbolKind kind) { }; } - public static Image imageFromCompletionItem(CompletionItem completionItem) { + public static @Nullable Image imageFromCompletionItem(CompletionItem completionItem) { return switch (completionItem.getKind()) { case Text -> getImage(IMG_TEXT); case Method -> getImage(IMG_METHOD); @@ -226,7 +228,7 @@ public static Image imageFromCompletionItem(CompletionItem completionItem) { }; } - private static Image getImageForColor(CompletionItem completionItem) { + private static @Nullable Image getImageForColor(CompletionItem completionItem) { String hexValue = null; // TODO most probably can be extended for more cases diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServerPreferencePage.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServerPreferencePage.java index 972203242..a51f34169 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServerPreferencePage.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServerPreferencePage.java @@ -11,9 +11,12 @@ *******************************************************************************/ package org.eclipse.lsp4e.ui; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.GridDataFactory; @@ -47,16 +50,15 @@ public class LanguageServerPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { - private LanguageServersRegistry registry; - private List workingCopy; - private Button removeButton; - private CheckboxTableViewer checkboxViewer; - private TableViewer viewer; + private final LanguageServersRegistry registry = LanguageServersRegistry.getInstance(); + private List workingCopy = lateNonNull(); + private Button removeButton = lateNonNull(); + private CheckboxTableViewer checkboxViewer = lateNonNull(); + private TableViewer viewer = lateNonNull(); private final SelectionAdapter contentTypeLinkListener; - private List changedDefinitions; + private final List changedDefinitions = new ArrayList<>(); public LanguageServerPreferencePage() { - contentTypeLinkListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { @@ -69,8 +71,6 @@ public void widgetSelected(SelectionEvent e) { @Override public void init(IWorkbench workbench) { - this.changedDefinitions = new ArrayList<>(); - this.registry = LanguageServersRegistry.getInstance(); } @Override @@ -188,7 +188,7 @@ private void createStaticServersTable(Composite res) { enablementColumn.getColumn().setWidth(70); enablementColumn.setLabelProvider(new ColumnLabelProvider() { @Override - public String getText(Object element) { + public @Nullable String getText(Object element) { return null; } }); @@ -239,7 +239,7 @@ public String getText(Object element) { } @Override - public Color getBackground(Object element) { + public @Nullable Color getBackground(Object element) { EnablementTester tester = ((ContentTypeToLanguageServerDefinition) element) .getEnablementCondition(); if (tester == null) { @@ -280,7 +280,7 @@ public boolean performOk() { return super.performOk(); } - private IEditorReference[] getEditors() { + private IEditorReference @Nullable [] getEditors() { var page = UI.getActivePage(); if (page != null) { return page.getEditorReferences(); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServersView.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServersView.java index 81f0a08ae..1826049fa 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServersView.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LanguageServersView.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.ui; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -22,6 +24,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.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; @@ -50,8 +53,8 @@ public class LanguageServersView extends ViewPart { private static final String EMPTY = ""; //$NON-NLS-1$ private static final String NOT_AVAILABLE = "n/a"; //$NON-NLS-1$ - private TableViewer viewer; - private Job viewerRefreshJob; + private TableViewer viewer = lateNonNull(); + private @Nullable Job viewerRefreshJob; private final Map actionButtons = new HashMap<>(); private final List columnLabelProviders = new ArrayList<>(); @@ -62,12 +65,12 @@ public class LanguageServersView extends ViewPart { private int compare(int columnIndex, @Nullable final Object e1, @Nullable final Object e2) { var labelProvider = columnLabelProviders.get(columnIndex); return getComparator().compare( // - Objects.toString(labelProvider.getText(e1), EMPTY), - Objects.toString(labelProvider.getText(e2), EMPTY)); + Objects.toString(e1 == null ? null : labelProvider.getText(e1), EMPTY), + Objects.toString(e2 == null ? null : labelProvider.getText(e2), EMPTY)); } @Override - public int compare(final Viewer viewer, @Nullable final Object e1, @Nullable final Object e2) { + public int compare(final @Nullable Viewer viewer, @Nullable final Object e1, @Nullable final Object e2) { int sortResult = compare(tableSortColumn, e1, e2); // use the "Initial Project" column as secondary sort column @@ -115,7 +118,7 @@ public void createPartControl(Composite parent) { createColumn(EMPTY, 26, new ColumnLabelProvider() { @Override - public void update(ViewerCell cell) { + public void update(@NonNullByDefault({}) ViewerCell cell) { final var lsWrapper = (LanguageServerWrapper) cell.getElement(); final var item = (TableItem) cell.getItem(); final var buttons = actionButtons.computeIfAbsent(lsWrapper, unused -> { @@ -222,7 +225,7 @@ public void dispose() { } private void scheduleRefreshJob() { - viewerRefreshJob = new Job("Refresh Language Server Processes view") { //$NON-NLS-1$ + final var viewerRefreshJob = this.viewerRefreshJob = new Job("Refresh Language Server Processes view") { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { if (getSite().getPage().isPartVisible(LanguageServersView.this)) { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LoggingPreferencePage.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LoggingPreferencePage.java index 62a27f647..b1e5ab4a0 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LoggingPreferencePage.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/LoggingPreferencePage.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.eclipse.lsp4e.ui; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.lateNonNull; import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; import java.util.ArrayList; @@ -19,6 +20,7 @@ import java.util.HashSet; import java.util.Map; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; @@ -62,26 +64,29 @@ private BooleanMapEditingSupport(TableViewer viewer, Map map) } @Override - protected void setValue(Object element, Object value) { - final var server = (ContentTypeToLanguageServerDefinition) element; - map.put(server.getValue().id, (Boolean)value); - hasLoggingBeenChanged = true; - getViewer().refresh(element); + protected void setValue(@Nullable Object element, @Nullable Object value) { + if(element instanceof ContentTypeToLanguageServerDefinition server + && value instanceof Boolean booleanValue) { + map.put(server.getValue().id, booleanValue); + hasLoggingBeenChanged = true; + getViewer().refresh(element); + } } @Override - protected Object getValue(Object element) { - final var server = (ContentTypeToLanguageServerDefinition) element; - return map.get(server.getValue().id); + protected @Nullable Object getValue(@Nullable Object element) { + if(element instanceof ContentTypeToLanguageServerDefinition server) + return map.get(server.getValue().id); + return null; } @Override - protected CellEditor getCellEditor(Object element) { + protected CellEditor getCellEditor(@Nullable Object element) { return new CheckboxCellEditor(); } @Override - protected boolean canEdit(Object element) { + protected boolean canEdit(@Nullable Object element) { return true; } } @@ -102,8 +107,8 @@ public String getText(Object element) { } } - private TableViewer languageServerViewer; - private TableViewer launchConfigurationViewer; + private TableViewer languageServerViewer = lateNonNull(); + private TableViewer launchConfigurationViewer = lateNonNull(); private final Map serverEnableLoggingToFile = new HashMap<>(); private final Map serverEnableLoggingToConsole = new HashMap<>(); private final IPreferenceStore store = LanguageServerPlugin.getDefault().getPreferenceStore(); diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/Messages.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/Messages.java index 4ad92d8a3..4e50010b8 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/Messages.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/Messages.java @@ -13,8 +13,10 @@ *******************************************************************************/ package org.eclipse.lsp4e.ui; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.osgi.util.NLS; +@NonNullByDefault({}) public final class Messages extends NLS { public static String definitionHyperlinkLabel; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/NewContentTypeLSPLaunchDialog.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/NewContentTypeLSPLaunchDialog.java index 6239b933c..81cbb8edf 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/NewContentTypeLSPLaunchDialog.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/NewContentTypeLSPLaunchDialog.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.eclipse.lsp4e.ui; +import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull; + import java.util.ArrayList; import java.util.Collections; import java.util.Objects; @@ -27,12 +29,11 @@ import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationTreeContentProvider; import org.eclipse.debug.ui.DebugUITools; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.DecoratingLabelProvider; -import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; @@ -67,10 +68,13 @@ public String getText(Object element) { private static final class ContentTypesContentProvider implements ITreeContentProvider { - private IContentTypeManager manager; + private @Nullable IContentTypeManager manager; @Override - public Object[] getChildren(Object parentElement) { + public Object[] getChildren(@Nullable Object parentElement) { + final var manager = this.manager; + if(manager == null) + return new Object[0]; final var elements = new ArrayList(); final var baseType = (IContentType) parentElement; IContentType[] contentTypes = manager.getAllContentTypes(); @@ -84,7 +88,7 @@ public Object[] getChildren(Object parentElement) { } @Override - public Object getParent(Object element) { + public @Nullable Object getParent(Object element) { final var contentType = (IContentType) element; return contentType.getBaseType(); } @@ -95,22 +99,19 @@ public boolean hasChildren(Object element) { } @Override - public Object[] getElements(Object inputElement) { + public Object[] getElements(@Nullable Object inputElement) { return getChildren(null); } @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + public void inputChanged(Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) { manager = (IContentTypeManager) newInput; } } - protected IContentType contentType; - protected ILaunchConfiguration launchConfig; - protected Set launchMode; - - // - //// + protected @Nullable IContentType contentType; + protected @Nullable ILaunchConfiguration launchConfig; + protected Set launchMode = Collections.emptySet(); protected NewContentTypeLSPLaunchDialog(Shell parentShell) { super(parentShell); @@ -131,13 +132,9 @@ protected Control createDialogArea(Composite parent) { contentTypesViewer.setComparator(new ViewerComparator()); contentTypesViewer.setInput(Platform.getContentTypeManager()); contentTypesViewer.addSelectionChangedListener(event -> { - IContentType newContentType = null; - if (event.getSelection() instanceof IStructuredSelection sel) { - if (sel.size() == 1 && sel.getFirstElement() instanceof IContentType contentType) { - newContentType = contentType; - } + if (event.getSelection() instanceof IStructuredSelection sel && sel.getFirstElement() instanceof IContentType newContentType) { + contentType = newContentType; } - contentType = newContentType; updateButtons(); }); // copied from LaunchConfigurationDialog : todo use LaunchConfigurationFilteredTree @@ -178,11 +175,10 @@ public String getText(Object o) { updateButtons(); }); launchModeViewer.addSelectionChangedListener(event -> { - ISelection sel = event.getSelection(); - if (sel.isEmpty()) { - launchMode = null; - } else if (sel instanceof IStructuredSelection structuredSel) { - launchMode = (Set) structuredSel.getFirstElement(); + if (event.getSelection() instanceof IStructuredSelection sel && sel.getFirstElement() instanceof Set mode) { + launchMode = (Set) mode; + } else { + launchMode = Collections.emptySet(); } updateButtons(); }); @@ -197,18 +193,19 @@ protected Control createContents(Composite parent) { } public IContentType getContentType() { - return this.contentType; + return castNonNull(this.contentType); } public ILaunchConfiguration getLaunchConfiguration() { - return this.launchConfig; + return castNonNull(this.launchConfig); } private void updateButtons() { - getButton(OK).setEnabled(contentType != null && launchConfig != null && launchMode != null); + getButton(OK).setEnabled(contentType != null && launchConfig != null && !launchMode.isEmpty()); } private void updateLaunchModes(ComboViewer launchModeViewer) { + final var launchConfig = this.launchConfig; if (launchConfig == null) { launchModeViewer.setInput(Collections.emptyList()); } else { @@ -232,7 +229,7 @@ private void updateLaunchModes(ComboViewer launchModeViewer) { } if (currentMode == null) { for (Set mode : modes) { - if (mode.size() == 1 && mode.iterator().next().equals(ILaunchManager.RUN_MODE)) { + if (mode.size() == 1 && ILaunchManager.RUN_MODE.equals(mode.iterator().next())) { currentMode = mode; launchModeViewer.setSelection(new StructuredSelection(currentMode)); } @@ -245,7 +242,7 @@ private void updateLaunchModes(ComboViewer launchModeViewer) { updateButtons(); } - public @NonNull Set getLaunchMode() { + public Set getLaunchMode() { return this.launchMode; } diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/package-info.java new file mode 100644 index 000000000..400b8a120 --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.ui; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/views/package-info.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/views/package-info.java new file mode 100644 index 000000000..ef8076a1a --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/ui/views/package-info.java @@ -0,0 +1,6 @@ +@NonNullByDefault({ ARRAY_CONTENTS, PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT }) +package org.eclipse.lsp4e.ui.views; + +import static org.eclipse.jdt.annotation.DefaultLocation.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/pom.xml b/pom.xml index 122c387b3..05af911e6 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,16 @@ repository + + + + com.vegardit.no-npe + no-npe-eea-all + 1.0.3 + provided + + + @@ -152,6 +162,21 @@ + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho-version} + + true + + -annotationpath + CLASSPATH + + + + true + + org.eclipse.tycho tycho-packaging-plugin @@ -276,11 +301,11 @@ false - dash-licenses-snapshots - https://repo.eclipse.org/content/repositories/dash-licenses-snapshots/ + dash-licenses-snapshots + https://repo.eclipse.org/content/repositories/dash-licenses-snapshots/ true false - +