diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF
index aa360a3b0..acfdd5921 100644
--- a/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF
+++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/META-INF/MANIFEST.MF
@@ -36,7 +36,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0",
com.google.guava,
org.eclipse.core.expressions,
org.springsource.ide.eclipse.commons.core;bundle-version="4.17.0",
- org.springframework.tooling.jdt.ls.commons;bundle-version="4.17.0"
+ org.springframework.tooling.jdt.ls.commons;bundle-version="4.17.0",
+ org.eclipse.jface.notifications
Import-Package: com.google.common.base,
com.google.common.collect,
com.google.gson;version="2.7.0",
diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java
index f1a3bf608..993050512 100644
--- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java
+++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017, 2022 Pivotal, Inc.
+ * Copyright (c) 2017, 2023 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -14,6 +14,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
+import java.net.URL;
import java.nio.file.FileSystems;
import java.util.Arrays;
import java.util.Collection;
@@ -24,7 +25,8 @@ import java.util.Map;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
-import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.notifications.AbstractNotificationPopup;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.lsp4e.server.StreamConnectionProvider;
@@ -34,7 +36,18 @@ import org.eclipse.lsp4j.jsonrpc.messages.Message;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.springframework.tooling.boot.ls.prefs.CategoryProblemsSeverityPrefsPage;
import org.springframework.tooling.boot.ls.prefs.FileListEditor;
@@ -164,19 +177,19 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
if (preferenceStore.getBoolean(Constants.PREF_REWRITE_RECONCILE_PROMPT) && !preferenceStore.getBoolean(Constants.PREF_REWRITE_RECONCILE)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
- int result = MessageDialog.open(MessageDialog.INFORMATION, Display.getCurrent().getActiveShell(),
- "Spring Boot Java Reconciling",
- "Do you wish to enable additional Java sources reconciling to get Spring specific validations and suggestions?",
- SWT.NONE, "Yes", "No", "Do not show again");
- switch (result) {
- case 0:
- preferenceStore.setValue(Constants.PREF_REWRITE_RECONCILE, true);
- break;
- case 2:
- preferenceStore.setValue(Constants.PREF_REWRITE_RECONCILE_PROMPT, false);
- break;
- default:
- break;
+ try {
+ NotificationQuestionWithLink question = new NotificationQuestionWithLink(
+ "Spring Java Reconcile",
+ "Do you wish to enable additional Java sources reconciling to get Spring specific validations and suggestions?\n\n" +
+ "See Validations And Quick Fixes for more details.",
+ new URL("https://github.com/spring-projects/sts4/wiki/Validations-And-Quick-Fixes"),
+ () -> preferenceStore.setValue(Constants.PREF_REWRITE_RECONCILE, true),
+ () -> {},
+ () -> preferenceStore.setValue(Constants.PREF_REWRITE_RECONCILE_PROMPT, false));
+ question.setDelayClose(-1);
+ question.open();
+ } catch (Exception e) {
+ BootLanguageServerPlugin.getDefault().getLog().error("Failed to Spring Java Reconcile popup", e);
}
});
}
@@ -294,4 +307,115 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
return RemoteBootAppsDataHolder.getDefault().getRemoteApps().getValues();
}
+ private static class NotificationQuestionWithLink extends AbstractNotificationPopup {
+
+ private final String title;
+ private final String textWithLinkTag;
+ private final URL linkUrl;
+
+ /*
+ * Notification open does not block on open and ignores setBlockOnOpen(true)
+ * Therefore handlers are passed directly in until this changes
+ */
+ private Runnable yesHandler;
+ private Runnable noHandler;
+ private Runnable stopAskingHandler;
+
+
+ public NotificationQuestionWithLink(String title, String textWithLinkTag, URL linkUrl,
+ Runnable yesHandler, Runnable noHandler, Runnable stopAskingHandler) {
+ super(Display.getCurrent());
+
+ this.title = title;
+ this.textWithLinkTag = textWithLinkTag;
+ this.linkUrl = linkUrl;
+ this.yesHandler = yesHandler;
+ this.noHandler = noHandler;
+ this.stopAskingHandler = stopAskingHandler;
+
+ setParentShell(PlatformUI.getWorkbench().getDisplay().getActiveShell());
+ setBlockOnOpen(true);
+ }
+
+ @Override
+ public String getPopupShellTitle() {
+ return title;
+ }
+
+ @Override
+ protected void createContentArea(Composite parent) {
+ Link link = new Link(parent, SWT.WRAP);
+ link.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ link.setText(textWithLinkTag);
+ if (linkUrl != null) {
+ link.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ try {
+ PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(linkUrl);
+ } catch (PartInitException ex) {
+ BootLanguageServerPlugin.getDefault().getLog().error("Failed to open browser", ex);
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+ }
+
+ Composite buttonsComposite = new Composite(parent, SWT.None);
+ buttonsComposite.setLayoutData(GridDataFactory.swtDefaults().grab(true, false).align(SWT.RIGHT, SWT.CENTER).create());
+ GridLayout buttonsLayout = new GridLayout(3, false);
+ buttonsLayout.horizontalSpacing = 0;
+ buttonsComposite.setLayout(buttonsLayout);
+
+ Button yesButton = new Button(buttonsComposite, SWT.None);
+ yesButton.setText("Yes");
+ yesButton.setLayoutData(GridDataFactory.swtDefaults().create());
+ yesButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ setReturnCode(SWT.YES);
+ close();
+ yesHandler.run();
+ }
+ });
+
+ Button noButton = new Button(buttonsComposite, SWT.None);
+ noButton.setText("No");
+ noButton.setLayoutData(GridDataFactory.swtDefaults().create());
+ noButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ setReturnCode(SWT.NO);
+ close();
+ noHandler.run();
+ }
+ });
+
+ Button stopAskingButton = new Button(buttonsComposite, SWT.None);
+ stopAskingButton.setText("Stop Asking");
+ stopAskingButton.setLayoutData(GridDataFactory.swtDefaults().create());
+ stopAskingButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ setReturnCode(SWT.SAVE);
+ close();
+ stopAskingHandler.run();
+ }
+ });
+ }
+
+ @Override
+ public Image getPopupShellImage(int maximumHeight) {
+ return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_INFO_TSK);
+ }
+
+ }
+
}
diff --git a/vscode-extensions/vscode-spring-boot/lib/Main.ts b/vscode-extensions/vscode-spring-boot/lib/Main.ts
index 74a3c6862..93786cee0 100644
--- a/vscode-extensions/vscode-spring-boot/lib/Main.ts
+++ b/vscode-extensions/vscode-spring-boot/lib/Main.ts
@@ -22,7 +22,7 @@ const FACTORIES_LANGUAGE_ID = "spring-factories";
const YES = 'Yes';
const NO = 'No';
-const NEVER_SHOW_AGAIN = "Do not show again";
+const STOP_ASKING = "Stop Asking";
const RECONCILING_PREF_KEY = 'boot-java.rewrite.reconcile';
const RECONCILING_PROMPT_PREF_KEY = 'vscode-spring-boot.rewrite.reconcile-prompt';
@@ -48,8 +48,8 @@ export function activate(context: VSCode.ExtensionContext): Thenable {
- if (selection === NEVER_SHOW_AGAIN) {
+ STOP_ASKING).then(selection => {
+ if (selection === STOP_ASKING) {
options.workspaceOptions.update('checkJVM', false);
}
}
@@ -143,12 +143,13 @@ export function activate(context: VSCode.ExtensionContext): Thenable {
+ VSCode.window.showInformationMessage('Do you wish to enable additional Java sources reconciling to get Spring specific validations and suggestions?\n\n' +
+ 'For more details see [Validations And Quick Fixes](https://github.com/spring-projects/sts4/wiki/Validations-And-Quick-Fixes)', YES, NO, STOP_ASKING).then(answer => {
switch (answer) {
case YES:
VSCode.workspace.getConfiguration().update(RECONCILING_PREF_KEY, true, true);
break;
- case NEVER_SHOW_AGAIN:
+ case STOP_ASKING:
VSCode.workspace.getConfiguration().update(RECONCILING_PROMPT_PREF_KEY, false, true);
break;
default: