diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 54b27a9ae..15c3cfc0d 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -59,6 +59,7 @@ import org.eclipse.lsp4j.Registration; import org.eclipse.lsp4j.RegistrationParams; import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.SetTraceParams; +import org.eclipse.lsp4j.ShowDocumentParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextDocumentSyncKind; import org.eclipse.lsp4j.WorkDoneProgressBegin; @@ -104,6 +105,7 @@ import org.springframework.ide.vscode.commons.util.CollectionUtil; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -350,6 +352,17 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC if (hasExecuteCommandSupport) { getWorkspaceService().onExecuteCommand(this::executeCommand); } + + onCommand("sts/show/document", p -> { + ShowDocumentParams showDocParams = new Gson().fromJson((JsonElement)p.getArguments().get(0), ShowDocumentParams.class); + return getClient().showDocument(showDocParams).thenApply(r -> { + if (!r.isSuccess()) { + MessageParams messageParams = new MessageParams(MessageType.Error, "Failed to open: " + showDocParams.getUri()); + getClient().showMessage(messageParams); + } + return null; + }); + }); ServerCapabilities cap = getServerCapabilities(); if (appContext!=null) { Map extraCaps = appContext.getBeansOfType(ServerCapabilityInitializer.class); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java index 773c19bf2..407f7893a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java @@ -31,7 +31,7 @@ abstract public class AbstractDiagnosticValidator implements VersionValidator { this.diagnosticSeverityProvider = diagnosticSeverityProvider; } - protected Diagnostic createDiagnostic(CodeAction action, VersionValidationProblemType problemType, String diagnosticMessage) { + protected Diagnostic createDiagnostic(List actions, VersionValidationProblemType problemType, String diagnosticMessage) { DiagnosticSeverity severity = diagnosticSeverityProvider.getDiagnosticSeverity(problemType); // No severity means that this validator is set to "IGNORE" in the preferences @@ -55,11 +55,13 @@ abstract public class AbstractDiagnosticValidator implements VersionValidator { diagnostic.setRange(range); diagnostic.setSeverity(severity); - if (action != null) { - Diagnostic refDiagnostic = new Diagnostic(diagnostic.getRange(), diagnostic.getMessage(), - diagnostic.getSeverity(), diagnostic.getSource()); - action.setDiagnostics(List.of(refDiagnostic)); - diagnostic.setData(List.of(action)); + if (actions != null) { + for (CodeAction action : actions) { + Diagnostic refDiagnostic = new Diagnostic(diagnostic.getRange(), diagnostic.getMessage(), + diagnostic.getSeverity(), diagnostic.getSource()); + action.setDiagnostics(List.of(refDiagnostic)); + } + diagnostic.setData(actions); } return diagnostic; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java index aefb81cc6..0393bd060 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.validation.generations; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -18,6 +19,8 @@ import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.ShowDocumentParams; import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -27,6 +30,8 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Diagnosti import com.google.common.collect.ImmutableList; public class UpdateBootVersion extends AbstractDiagnosticValidator { + + private static final String RELEASE_NOTES_URL_PREFIX = "https://github.com/spring-projects/spring-boot/releases/tag/v"; private Optional bootUpgradeOpt; @@ -54,8 +59,10 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { StringBuffer message = new StringBuffer(); message.append("Newer major version of Spring Boot available: "); message.append(latest.toString()); + + List actions = new ArrayList<>(2); - CodeAction ca = bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> { + bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> { CodeAction c = new CodeAction(); c.setKind(CodeActionKind.QuickFix); c.setTitle("Upgrade to Spring Boot " + targetVersion + " (executes the full project conversion recipe from OpenRewrite)"); @@ -63,8 +70,11 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { c.setCommand(new Command("Upgrade to Version " + targetVersion, commandId, ImmutableList.of(javaProject.getLocationUri().toASCIIString(), targetVersion))); return c; - }).orElse(null); - return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString())); + }).ifPresent(actions::add); + + actions.add(openReleaseNotesCodeAction(latest)); + + return Optional.ofNullable(createDiagnostic(actions, problemType, message.toString())); } return Optional.empty(); } @@ -78,8 +88,10 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { StringBuffer message = new StringBuffer(); message.append("Newer minor version of Spring Boot available: "); message.append(latest.toString()); + + List actions = new ArrayList<>(2); - CodeAction ca = bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> { + bootUpgradeOpt.flatMap(bu -> bu.getNearestAvailableMinorVersion(latest)).map(targetVersion -> { CodeAction c = new CodeAction(); c.setKind(CodeActionKind.QuickFix); c.setTitle("Upgrade to Spring Boot " + targetVersion + " (executes the full project conversion recipe from OpenRewrite)"); @@ -87,9 +99,11 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { c.setCommand(new Command("Upgrade to Version " + targetVersion, commandId, ImmutableList.of(javaProject.getLocationUri().toASCIIString(), targetVersion))); return c; - }).orElse(null); + }).ifPresent(actions::add); + + actions.add(openReleaseNotesCodeAction(latest)); - return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString())); + return Optional.ofNullable(createDiagnostic(actions, problemType, message.toString())); } return Optional.empty(); } @@ -104,7 +118,9 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { message.append("Newer patch version of Spring Boot available: "); message.append(latest.toString()); - CodeAction ca = bootUpgradeOpt.map(bu -> { + List actions = new ArrayList<>(2); + + bootUpgradeOpt.map(bu -> { CodeAction c = new CodeAction(); c.setKind(CodeActionKind.QuickFix); c.setTitle("Upgrade to Spring Boot " + latest.toString() + " (Maven dependency version changes only)"); @@ -112,10 +128,25 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { c.setCommand(new Command("Upgrade to Version " + latest.toString(), commandId, ImmutableList.of(javaProject.getLocationUri().toASCIIString(), latest.toString()))); return c; - }).orElse(null); + }).ifPresent(actions::add); - return Optional.ofNullable(createDiagnostic(ca, problemType, message.toString())); + actions.add(openReleaseNotesCodeAction(latest)); + + return Optional.ofNullable(createDiagnostic(actions, problemType, message.toString())); } return Optional.empty(); } + + private static CodeAction openReleaseNotesCodeAction(Version version) { + CodeAction releaseNoteLink = new CodeAction(); + releaseNoteLink.setKind(CodeActionKind.QuickFix); + releaseNoteLink.setTitle("Open Release Notes for Spring Boot " + version.toString()); + ShowDocumentParams showDocumentParams = new ShowDocumentParams(RELEASE_NOTES_URL_PREFIX + version.toString()); + showDocumentParams.setExternal(true); + showDocumentParams.setTakeFocus(true); + showDocumentParams.setSelection(new Range()); + releaseNoteLink.setCommand(new Command("Release Notes for Spring Boot " + version.toString(), "sts/show/document", + ImmutableList.of(showDocumentParams))); + return releaseNoteLink; + } } \ No newline at end of file