From 084d3a9ad738de19b0e9e4985f7c8308f6cade04 Mon Sep 17 00:00:00 2001 From: aboyko Date: Thu, 3 Oct 2024 16:50:45 -0400 Subject: [PATCH] Apply edit via LSP. Fix maven changes --- .../vscode/commons/rewrite/ORDocUtils.java | 24 +++++----- .../copilot/CopilotAgentCommandHandler.java | 45 ++++++++++++++----- .../copilot/InjectMavenActionHandler.java | 44 +++++++++--------- .../copilot/ProjectArtifactEditGenerator.java | 5 ++- .../lib/copilot/guideApply.ts | 45 +++---------------- 5 files changed, 75 insertions(+), 88 deletions(-) diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/ORDocUtils.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/ORDocUtils.java index 7949f1a2b..aeba4c493 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/ORDocUtils.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/ORDocUtils.java @@ -161,17 +161,10 @@ public class ORDocUtils { WorkspaceEdit we = new WorkspaceEdit(); we.setDocumentChanges(new ArrayList<>()); for (Result result : results) { - if (result.getBefore() == null) { - String docUri = result.getAfter().getSourcePath().toUri().toASCIIString(); - createNewFileEdit(docUri, result.getAfter().printAll(), changeAnnotationId, we); - } else if (result.getAfter() == null) { - String docUri = result.getBefore().getSourcePath().toUri().toASCIIString(); - createDeleteFileEdit(docUri, we); - } else { - String docUri = result.getBefore().getSourcePath().toUri().toASCIIString(); - createUpdateFileEdit(documents, docUri, result.getBefore().printAll(), result.getAfter().printAll(), changeAnnotationId, we); - } - + String docUri = result.getBefore() == null ? result.getAfter().getSourcePath().toUri().toASCIIString() : result.getBefore().getSourcePath().toUri().toASCIIString(); + String oldContent = result.getBefore() == null ? null : result.getBefore().printAll(); + String newContent = result.getAfter() == null ? null : result.getAfter().printAll(); + createWorkspaceEdit(documents, docUri, oldContent, newContent, changeAnnotationId, we); } return Optional.of(we); } @@ -180,7 +173,7 @@ public class ORDocUtils { if(oldContent == null) { createNewFileEdit(docUri, newContent, changeAnnotationId, we); } else if (newContent == null) { - createDeleteFileEdit(docUri, we); + createDeleteFileEdit(docUri, changeAnnotationId, we); } else { createUpdateFileEdit(documents, docUri, oldContent, newContent, changeAnnotationId, we); } @@ -190,6 +183,7 @@ public class ORDocUtils { WorkspaceEdit we) { CreateFile ro = new CreateFile(); ro.setUri(docUri); + ro.setAnnotationId(changeAnnotationId); we.getDocumentChanges().add(Either.forRight(ro)); TextDocumentEdit te = new TextDocumentEdit(); @@ -199,8 +193,10 @@ public class ORDocUtils { we.getDocumentChanges().add(Either.forLeft(te)); } - private static void createDeleteFileEdit(String docUri, WorkspaceEdit we) { - we.getDocumentChanges().add(Either.forRight(new DeleteFile(docUri))); + private static void createDeleteFileEdit(String docUri, String changeAnnotationId, WorkspaceEdit we) { + DeleteFile ro = new DeleteFile(docUri); + ro.setAnnotationId(changeAnnotationId); + we.getDocumentChanges().add(Either.forRight(ro)); } private static void createUpdateFileEdit(SimpleTextDocumentService documents, String docUri, String oldContent, diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/CopilotAgentCommandHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/CopilotAgentCommandHandler.java index ea761f94f..68d738ff9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/CopilotAgentCommandHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/CopilotAgentCommandHandler.java @@ -1,10 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom, 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + *******************************************************************************/ package org.springframework.ide.vscode.boot.java.copilot; import java.io.IOException; import java.nio.file.Paths; import java.util.List; +import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import org.eclipse.lsp4j.ApplyWorkspaceEditParams; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceEdit; @@ -12,6 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.copilot.util.ResponseModifier; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.IndefiniteProgressTask; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; @@ -41,15 +55,27 @@ public class CopilotAgentCommandHandler { server.onCommand(CMD_COPILOT_AGENT_ENHANCERESPONSE, (params) -> { return enhanceResponseHandler(params); }); - log.info("Registered command handler: {}", CMD_COPILOT_AGENT_ENHANCERESPONSE); server.onCommand(CMD_COPILOT_AGENT_LSPEDITS, params -> { - try { - return createLspEdits(params); - } catch (IOException e) { - log.error(e.getMessage()); - } - return null; + final IndefiniteProgressTask progressTask = server.getProgressService().createIndefiniteProgressTask(UUID.randomUUID().toString(), "Applying GenAI Response", "Computing changes..."); + return CompletableFuture.supplyAsync(() -> { + try { + return createLspEdits(params); + } catch (IOException e) { + throw new CompletionException(e); + } + }).thenCompose(we -> { + if (progressTask != null) { + progressTask.progressEvent("Applying document changes..."); + } + return server.getClient().applyEdit(new ApplyWorkspaceEditParams(we, "Apply GenAI Response")).thenCompose(res -> { + if (res.isApplied()) { + return CompletableFuture.completedFuture("success"); + } else { + return CompletableFuture.completedFuture(null); + } + }); + }).whenComplete((o,t) -> progressTask.done()); }); } @@ -60,7 +86,7 @@ public class CopilotAgentCommandHandler { return CompletableFuture.completedFuture(modifiedResp); } - private CompletableFuture createLspEdits(ExecuteCommandParams params) throws IOException { + private WorkspaceEdit createLspEdits(ExecuteCommandParams params) throws IOException { log.info("Command Handler for lsp edits: "); String docURI = ((JsonElement) params.getArguments().get(0)).getAsString(); String content = ((JsonElement) params.getArguments().get(1)).getAsString(); @@ -69,8 +95,7 @@ public class CopilotAgentCommandHandler { List projectArtifacts = computeProjectArtifacts(content); ProjectArtifactEditGenerator editGenerator = new ProjectArtifactEditGenerator(server.getTextDocumentService(), projectArtifacts, Paths.get(project.getLocationUri()), docURI); - WorkspaceEdit we = editGenerator.process().getResult(); - return CompletableFuture.completedFuture(we); + return editGenerator.process().getResult(); } List computeProjectArtifacts(String response) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/InjectMavenActionHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/InjectMavenActionHandler.java index f8cdcc3db..186d266d3 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/InjectMavenActionHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/InjectMavenActionHandler.java @@ -77,8 +77,7 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler { for (InjectMavenBuildPlugin p : buildPlugins) { List xmlDocuments = parseToXml(p.getText()); for (Xml.Document xmlDocument : xmlDocuments) { - MavenPluginMetadata pm = findMavenPluginTags(xmlDocument); - if (pm != null) { + for (MavenPluginMetadata pm : findMavenPluginTags(xmlDocument)) { AddPlugin addPlugin = new AddPlugin(pm.groupId(), pm.artifactId(), pm.version(), pm.configuration(), pm.dependencies(), pm.executions(), pm.filePattern()); aggregateRecipe.getRecipeList().add(addPlugin); @@ -88,8 +87,7 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler { for (InjectMavenRepository r : repositories) { List xmlDocuments = parseToXml(r.getText()); for (Xml.Document xmlDocument : xmlDocuments) { - MavenRepositoryMetadata rm = findRepositoryTags(xmlDocument); - if (rm != null) { + for (MavenRepositoryMetadata rm : findRepositoryTags(xmlDocument)) { AddRepository addRepository = new AddRepository(rm.id(), rm.url(), rm.repoName(), null, rm.snapshotsEnabled(), null, null, rm.releasesEnabled(), null, null, null); aggregateRecipe.getRecipeList().add(addRepository); @@ -99,8 +97,7 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler { for (InjectMavenDependencyManagement dm : dependencyManagements) { List xmlDocuments = parseToXml(dm.getText()); for (Xml.Document xmlDocument : xmlDocuments) { - MavenDependencyMetadata mdm = findMavenDependencyTags(xmlDocument); - if (mdm != null) { + for (MavenDependencyMetadata mdm : findMavenDependencyTags(xmlDocument)) { AddManagedDependency addManagedDependency = new AddManagedDependency(mdm.groupId(), mdm.artifactId(), mdm.version(), mdm.scope(), null, mdm.classifier(), null, null, null, null); aggregateRecipe.getRecipeList().add(addManagedDependency); @@ -118,8 +115,9 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler { return xmlDocuments; } - public MavenDependencyMetadata findMavenDependencyTags(Xml.Document xmlDocument) { + public List findMavenDependencyTags(Xml.Document xmlDocument) { Set dependencyTags = FindTags.find(xmlDocument, "//dependency"); + List deps = new ArrayList<>(dependencyTags.size()); for (Tag dependencyTag : dependencyTags) { String groupId = dependencyTag.getChildValue("groupId").orElse(null); String artifactId = dependencyTag.getChildValue("artifactId").orElse(null); @@ -127,16 +125,16 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler { String scope = dependencyTag.getChildValue("scope").orElse(null); String type = dependencyTag.getChildValue("type").orElse(null); String classifier = dependencyTag.getChildValue("classifier").orElse(null); - if (groupId != null && artifactId != null && version != null) - return new MavenDependencyMetadata(groupId, artifactId, version, scope, type, classifier); - + if (groupId != null && artifactId != null && version != null) { + deps.add(new MavenDependencyMetadata(groupId, artifactId, version, scope, type, classifier)); + } } - return null; + return deps; } - public MavenPluginMetadata findMavenPluginTags(Xml.Document xmlDocument) { + public List findMavenPluginTags(Xml.Document xmlDocument) { Set pluginTags = FindTags.find(xmlDocument, "//plugin"); - + List plugins = new ArrayList<>(pluginTags.size()); for (Tag pluginTag : pluginTags) { String groupId = pluginTag.getChildValue("groupId").orElse(null); String artifactId = pluginTag.getChildValue("artifactId").orElse(null); @@ -146,16 +144,17 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler { String executions = pluginTag.getChildValue("executions").orElse(null); String filePattern = pluginTag.getChildValue("filePattern").orElse(null); - if (groupId != null && artifactId != null && version != null) - return new MavenPluginMetadata(groupId, artifactId, version, configuration, dependencies, executions, - filePattern); + if (groupId != null && artifactId != null && version != null) { + plugins.add(new MavenPluginMetadata(groupId, artifactId, version, configuration, dependencies, executions, + filePattern)); + } } - return null; + return plugins; } - private MavenRepositoryMetadata findRepositoryTags(Document xmlDocument) { + private List findRepositoryTags(Document xmlDocument) { Set repoTags = FindTags.find(xmlDocument, "//plugin"); - + List repos = new ArrayList<>(repoTags.size()); for (Tag repoTag : repoTags) { String id = repoTag.getChildValue("id").orElse(null); String url = repoTag.getChildValue("url").orElse(null); @@ -163,10 +162,11 @@ public class InjectMavenActionHandler extends AbstractInjectMavenActionHandler { boolean snapshotsEnabled = Boolean.parseBoolean(repoTag.getChildValue("snapshotsEnabled").orElse(null)); boolean releasesEnabled = Boolean.parseBoolean(repoTag.getChildValue("releasesEnabled").orElse(null)); - if (id != null && url != null) - return new MavenRepositoryMetadata(id, url, repoName, snapshotsEnabled, releasesEnabled); + if (id != null && url != null) { + repos.add(new MavenRepositoryMetadata(id, url, repoName, snapshotsEnabled, releasesEnabled)); + } } - return null; + return repos; } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/ProjectArtifactEditGenerator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/ProjectArtifactEditGenerator.java index 50466e2ac..cdc52f245 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/ProjectArtifactEditGenerator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/copilot/ProjectArtifactEditGenerator.java @@ -135,8 +135,9 @@ public class ProjectArtifactEditGenerator { // Move the parsing to injectMavenActionHandler List xmlDocuments = injectMavenActionHandler.parseToXml(projectArtifact.getText()); for (Xml.Document xmlDocument : xmlDocuments) { - MavenDependencyMetadata dep = injectMavenActionHandler.findMavenDependencyTags(xmlDocument); - injectMavenActionHandler.injectDependency(dep); + for (MavenDependencyMetadata dep : injectMavenActionHandler.findMavenDependencyTags(xmlDocument)) { + injectMavenActionHandler.injectDependency(dep); + } } List res = injectMavenActionHandler.run().getChangeset().getAllResults(); diff --git a/vscode-extensions/vscode-spring-boot/lib/copilot/guideApply.ts b/vscode-extensions/vscode-spring-boot/lib/copilot/guideApply.ts index 628dcedb3..5295d30d2 100644 --- a/vscode-extensions/vscode-spring-boot/lib/copilot/guideApply.ts +++ b/vscode-extensions/vscode-spring-boot/lib/copilot/guideApply.ts @@ -1,45 +1,10 @@ -import { Uri, workspace, window, commands, ProgressLocation } from "vscode"; +import { Uri, commands } from "vscode"; import { getTargetGuideMardown, readResponseFromFile } from "./util"; -import { createConverter } from "vscode-languageclient/lib/common/protocolConverter"; -import fs from "fs"; -import path from "path"; - - -const CONVERTER = createConverter(undefined, true, true); -const CANCELLED = "Cancelled"; export async function applyLspEdit(uri: Uri) { - try { - if (!uri) { - uri = await getTargetGuideMardown(); - } - - window.withProgress({ - location: ProgressLocation.Window, - title: "Copilot agent", - cancellable: true - }, async (progress, cancellation) => { - progress.report({ message: "applying edits..." }); - const fileContent = (await readResponseFromFile(uri)).toString(); - const lspEdit = await commands.executeCommand("sts/copilot/agent/lspEdits", uri.toString(), fileContent); - const workspaceEdit = await CONVERTER.asWorkspaceEdit(lspEdit); - - - await Promise.all(workspaceEdit.entries().map(async ([uri, edits]) => { - console.log(edits); - if (fs.existsSync(uri.fsPath)) { - const doc = await workspace.openTextDocument(uri.fsPath); - await window.showTextDocument(doc); - } - })); - - return await workspace.applyEdit(workspaceEdit, { - isRefactoring: true - }); - }); - } catch (error) { - if (error !== CANCELLED) { - window.showErrorMessage(error); - } + if (!uri) { + uri = await getTargetGuideMardown(); } + const fileContent = (await readResponseFromFile(uri)).toString(); + await commands.executeCommand("sts/copilot/agent/lspEdits", uri.toString(), fileContent); } \ No newline at end of file