From dd2f60ad914db564be066d1d8f16b8e8b847dc18 Mon Sep 17 00:00:00 2001 From: aboyko Date: Sun, 20 Nov 2022 21:51:48 -0500 Subject: [PATCH] Send rewrite commands only to Boot LS. Start Boot LS on menu invocation --- .../boot/ls/BootLanguageServerPlugin.java | 4 +- .../commands/RewriteRefactoringsHandler.java | 114 +++++++++--------- .../java/rewrite/RewriteRecipeRepository.java | 13 +- 3 files changed, 69 insertions(+), 62 deletions(-) diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java index ba530bdff..b6b383afe 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPlugin.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2020 Pivotal, Inc. + * Copyright (c) 2017, 2022 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 @@ -31,6 +31,8 @@ import org.springframework.tooling.boot.ls.prefs.CategoryProblemsSeverityPrefsPa */ public class BootLanguageServerPlugin extends AbstractUIPlugin { + public static String BOOT_LS_DEFINITION_ID = "org.eclipse.languageserver.languages.springboot"; + public static String PLUGIN_ID = "org.springframework.tooling.boot.ls"; private static final Object LSP4E_COMMAND_SYMBOL_IN_WORKSPACE = "org.eclipse.lsp4e.symbolinworkspace"; diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/RewriteRefactoringsHandler.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/RewriteRefactoringsHandler.java index 1716238f8..0d6555a5e 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/RewriteRefactoringsHandler.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/RewriteRefactoringsHandler.java @@ -16,23 +16,24 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.CompletableFuture; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; +import org.eclipse.lsp4e.LanguageServerWrapper; +import org.eclipse.lsp4e.LanguageServersRegistry; +import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; import org.eclipse.lsp4e.LanguageServiceAccessor; import org.eclipse.lsp4j.ExecuteCommandParams; -import org.eclipse.lsp4j.services.LanguageServer; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.handlers.HandlerUtil; @@ -88,70 +89,63 @@ public class RewriteRefactoringsHandler extends AbstractHandler { } try { if (project != null && CoreUtil.promptForProjectSave(project)) { - List<@NonNull LanguageServer> usedLanguageServers = LanguageServiceAccessor - .getActiveLanguageServers(serverCapabilities -> true); + LanguageServerDefinition def = LanguageServersRegistry.getInstance().getDefinition(BootLanguageServerPlugin.BOOT_LS_DEFINITION_ID); + Assert.isLegal(def != null, "No definition found for Boot Language Server"); + LanguageServerWrapper wrapper = LanguageServiceAccessor.getLSWrapper(project, def); - if (!usedLanguageServers.isEmpty()) { - final String uri = project.getLocationURI().toString(); - ExecuteCommandParams commandParams = new ExecuteCommandParams(); - commandParams.setCommand(REWRITE_REFACTORINGS_LIST); - commandParams.setArguments(List.of(uri)); + final String uri = project.getLocationURI().toString(); + ExecuteCommandParams commandParams = new ExecuteCommandParams(); + commandParams.setCommand(REWRITE_REFACTORINGS_LIST); + commandParams.setArguments(List.of(uri)); - try { - List allRewriteRecipesJson = new ArrayList<>(); - List syncRecipesJson = Collections.synchronizedList(allRewriteRecipesJson); - CompletableFuture.allOf(usedLanguageServers.stream() - .map(ls -> ls.getWorkspaceService().executeCommand(commandParams).thenAccept(or -> { - if (or != null) { - syncRecipesJson.add(or); - } - }).exceptionally(t -> null)) - .toArray(CompletableFuture[]::new)).thenRun(() -> { - allRewriteRecipesJson.stream().filter(List.class::isInstance).map(List.class::cast).findFirst().ifPresent(obj -> { - RecipeDescriptor[] descriptors = serializationGson.fromJson(serializationGson.toJson(obj), RecipeDescriptor[].class); - - PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { - RecipeTreeModel recipesModel = new RecipeTreeModel(descriptors); - int returnCode = new SelectRecipesDialog(Display.getCurrent().getActiveShell(), recipesModel).open(); - if (returnCode == Window.OK) { + try { + List allRewriteRecipesJson = new ArrayList<>(); + List syncRecipesJson = Collections.synchronizedList(allRewriteRecipesJson); + wrapper.getInitializedServer().thenComposeAsync(ls -> ls.getWorkspaceService().executeCommand(commandParams).thenAccept(or -> { + if (or != null) { + syncRecipesJson.add(or); + } + })) + .thenRun(() -> { + allRewriteRecipesJson.stream().filter(List.class::isInstance).map(List.class::cast).findFirst().ifPresent(obj -> { + RecipeDescriptor[] descriptors = serializationGson.fromJson(serializationGson.toJson(obj), RecipeDescriptor[].class); + + PlatformUI.getWorkbench().getDisplay().asyncExec(() -> { + RecipeTreeModel recipesModel = new RecipeTreeModel(descriptors); + int returnCode = new SelectRecipesDialog(Display.getCurrent().getActiveShell(), recipesModel).open(); + if (returnCode == Window.OK) { + try { + RecipeDescriptor recipeToApply = recipesModel.getSelectedRecipeDescriptors(); + PlatformUI.getWorkbench().getProgressService().run(true, false, monitor -> { try { - RecipeDescriptor recipeToApply = recipesModel.getSelectedRecipeDescriptors(); - PlatformUI.getWorkbench().getProgressService().run(true, false, monitor -> { - try { - if (!usedLanguageServers.isEmpty()) { - monitor.beginTask("Applying recipe '" + recipeToApply.displayName + "'", IProgressMonitor.UNKNOWN); - ExecuteCommandParams cmdParams = new ExecuteCommandParams(); - cmdParams.setCommand(REWRITE_REFACTORINGS_EXEC); - cmdParams.setArguments(List.of( - uri, - serializationGson.toJsonTree(recipeToApply) - )); - - CompletableFuture.allOf(usedLanguageServers.stream() - .map(ls -> ls.getWorkspaceService().executeCommand(cmdParams)) - .toArray(CompletableFuture[]::new)).get(); - } - } catch (Exception e) { - throw new InvocationTargetException(e); - } finally { - monitor.done(); - } - }); + monitor.beginTask("Applying recipe '" + recipeToApply.displayName + "'", IProgressMonitor.UNKNOWN); + ExecuteCommandParams cmdParams = new ExecuteCommandParams(); + cmdParams.setCommand(REWRITE_REFACTORINGS_EXEC); + cmdParams.setArguments(List.of( + uri, + serializationGson.toJsonTree(recipeToApply) + )); - } catch (CoreException | InvocationTargetException | InterruptedException e) { - BootLanguageServerPlugin.getDefault().getLog().error(e.getMessage(), e); - MessageDialog.openError(Display.getCurrent().getActiveShell(), "Error", "Failed to apply Rewrite recipe(s). See error log for more details."); + wrapper.getInitializedServer() + .thenComposeAsync(ls -> ls.getWorkspaceService().executeCommand(cmdParams)).get(); + } catch (Exception e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); } - } - }); - }); - + }); + + } catch (CoreException | InvocationTargetException | InterruptedException e) { + BootLanguageServerPlugin.getDefault().getLog().error(e.getMessage(), e); + MessageDialog.openError(Display.getCurrent().getActiveShell(), "Error", "Failed to apply Rewrite recipe(s). See error log for more details."); + } + } }); - } catch (Exception e) { - throw new ExecutionException("Failed to apply Rewrite recipe(s)", e); - } + }); + }); + } catch (Exception e) { + throw new ExecutionException("Failed to apply Rewrite recipe(s)", e); } - } } catch (Exception e) { throw new ExecutionException("Failed to save project resource(s)", e); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java index 954b1c5d1..1bf4a8178 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java @@ -118,9 +118,11 @@ public class RewriteRecipeRepository implements ApplicationContextAware { this.scanDirs = UNINITIALIZED_SET; this.scanFiles = UNINITIALIZED_SET; this.recipeFilters = UNINITIALIZED_SET; + CompletableFuture firstConfigLoaded = new CompletableFuture<>(); config.addListener(l -> { Set recipeFilterFromConfig = config.getRecipesFilters(); + boolean firstTimeConfig = recipeFilters == UNINITIALIZED_SET && scanDirs == UNINITIALIZED_SET && scanFiles == UNINITIALIZED_SET; if (recipeFilters == UNINITIALIZED_SET || recipeFilters.equals(recipeFilterFromConfig)) { recipeFilters = recipeFilterFromConfig; } @@ -130,10 +132,19 @@ public class RewriteRecipeRepository implements ApplicationContextAware { // Therefore it is best to store the scanDirs here right after it is received, not during scan process or anything else done async scanDirs = config.getRecipeDirectories(); scanFiles = config.getRecipeFiles(); - load(); + if (!firstTimeConfig) { + load(); + } + } + // First time config loaded. Received when server fully initialized. Start load after the firstConfigLoaded then and assign it to loaded field such that it is never null + if (firstTimeConfig) { + firstConfigLoaded.complete(null); } }); + // Initial configuration is followed by load() as a special case to have 'loaded' future value to never be null + this.loaded = firstConfigLoaded.thenCompose(v -> load()); + registerCommands(); }