From 8bfaf9d16d21c093ab74735a2206d95a930b5730 Mon Sep 17 00:00:00 2001 From: aboyko Date: Tue, 11 Mar 2025 11:51:09 -0400 Subject: [PATCH] Add field declaration and import --- .../META-INF/MANIFEST.MF | 5 +- .../ls/eclipse/commons/InjectBean.java | 159 ++++++++++++++++++ .../commons/STS4LanguageClientImpl.java | 9 + .../commons/protocol/STS4LanguageClient.java | 6 + .../protocol/java/InjectBeanParams.java | 5 + .../testharness/LanguageServerHarness.java | 6 + .../BootJavaCompletionEngineConfigurer.java | 5 +- .../java/beans/BeanCompletionProposal.java | 126 ++++++++++---- .../java/beans/BeanCompletionProvider.java | 8 +- 9 files changed, 287 insertions(+), 42 deletions(-) create mode 100644 eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java create mode 100644 headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF index b4d411483..90b241a84 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF @@ -23,7 +23,10 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.8.0", org.eclipse.ui.editors, org.springsource.ide.eclipse.commons.core, org.eclipse.e4.ui.css.swt.theme, - org.eclipse.swt + org.eclipse.swt, + org.eclipse.ui.workbench, + org.eclipse.jdt.core.manipulation, + org.eclipse.ltk.core.refactoring Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy Export-Package: org.springframework.tooling.ls.eclipse.commons, diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java new file mode 100644 index 000000000..25662823c --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/InjectBean.java @@ -0,0 +1,159 @@ +package org.springframework.tooling.ls.eclipse.commons; + +import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; +import org.eclipse.jdt.core.dom.rewrite.ListRewrite; +import org.eclipse.jdt.core.refactoring.CompilationUnitChange; +import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; +import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; +import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; +import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; +import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; +import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.ResourceOperation; +import org.eclipse.lsp4j.TextDocumentEdit; +import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.text.edits.TextEditGroup; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.PartInitException; +import org.springframework.tooling.jdt.ls.commons.Logger; +import org.springframework.tooling.jdt.ls.commons.resources.ResourceUtils; + +public final class InjectBean { + + private final Logger logger; + + public InjectBean(Logger logger) { + this.logger = logger; + } + + public TextDocumentEdit computeEdits(String docUri, String fieldType, String fieldName) { + try { + URI resourceUri = URI.create(docUri); + IJavaProject project = ResourceUtils.getJavaProject(resourceUri); + + Optional optEditorInput = LSPEclipseUtils.findOpenEditorsFor(resourceUri).stream().map(e -> { + try { + return e.getEditorInput(); + } catch (PartInitException e1) { + return null; + } + }).findFirst(); + + if (project != null && optEditorInput.isPresent()) { + ICompilationUnit cu = JavaPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(optEditorInput.get()); + RefactoringASTParser parser= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL); + CompilationUnit domCu = parser.parse(cu, true); + + + CompilationUnitRewrite cuRewrite = new CompilationUnitRewrite(null, cu, domCu, Map.of()); + + AST ast= cuRewrite.getAST(); + VariableDeclarationFragment variableDeclarationFragment= ast.newVariableDeclarationFragment(); + variableDeclarationFragment.setName(ast.newSimpleName(fieldName)); + + FieldDeclaration fieldDeclaration= ast.newFieldDeclaration(variableDeclarationFragment); + + fieldDeclaration.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD)); + fieldDeclaration.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.FINAL_KEYWORD)); + + IJavaElement el = project.findElement("L" + fieldType.replace(".", "/") + ";", null); + IType itype = project.findType(fieldType); + String packageName = itype.getPackageFragment().getElementName(); + String fqn = itype.getFullyQualifiedName(); +// org.eclipse.jdt.core.dom.ITypeBinding typeBinding= Bindings.normalizeForDeclarationUse(tb, ast); + ImportRewrite importRewrite= cuRewrite.getImportRewrite(); + ImportRewriteContext context= new ContextSensitiveImportRewriteContext(cuRewrite.getRoot(), 0, importRewrite); +// Type t = importRewrite.addImport(typeBinding, ast, context, TypeLocation.FIELD); + String typeName = importRewrite.addImport(fqn); + fieldDeclaration.setType(ast.newSimpleType(ast.newName(typeName))); + + AbstractTypeDeclaration parent = (AbstractTypeDeclaration) domCu.types().get(0); + Assert.isNotNull(parent); + ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(parent, parent.getBodyDeclarationsProperty()); + TextEditGroup msg= cuRewrite.createGroupDescription(RefactoringCoreMessages.ExtractConstantRefactoring_declare_constant); + listRewrite.insertFirst(fieldDeclaration, msg); + +// TextEdit edit = cuRewrite.getASTRewrite().rewriteAST(); + + CompilationUnitChange c = cuRewrite.createChange(false); + List textEdits = convertTextEdit(domCu, c.getEdit()); + + logger.log("Here"); + return textEdits.isEmpty() ? null : new TextDocumentEdit(new VersionedTextDocumentIdentifier(docUri, 0), textEdits); + } + + } catch (Exception e) { + logger.log(e); + } + return null; + } + + private List convertTextEdit(CompilationUnit domCu, TextEdit te) { + LinkedList edits = new LinkedList<>(); + org.eclipse.lsp4j.TextEdit edit = convertSingleEdit(domCu, te); + if (edit != null) { + edits.add(edit); + } + for (TextEdit c : te.getChildren()) { + edits.addAll(convertTextEdit(domCu, c)); + } + return edits; + } + + private org.eclipse.lsp4j.TextEdit convertSingleEdit(CompilationUnit domCu, TextEdit te) { + if (te instanceof DeleteEdit de) { + org.eclipse.lsp4j.TextEdit edit = new org.eclipse.lsp4j.TextEdit(); + int startLine = domCu.getLineNumber(de.getOffset()); + int startColumn = domCu.getColumnNumber(de.getOffset()) - 1; + int endLine = domCu.getLineNumber(de.getOffset() + de.getLength()); + int endColumn = domCu.getColumnNumber(de.getOffset() + de.getLength()) - 1; + edit.setNewText(""); + edit.setRange(new Range(new Position(startLine, startColumn), new Position(endLine, endColumn))); + return edit; + } else if (te instanceof ReplaceEdit re) { + org.eclipse.lsp4j.TextEdit edit = new org.eclipse.lsp4j.TextEdit(); + int startLine = domCu.getLineNumber(re.getOffset()); + int startColumn = domCu.getColumnNumber(re.getOffset()) - 1; + int endLine = domCu.getLineNumber(re.getOffset() + re.getLength()); + int endColumn = domCu.getColumnNumber(re.getOffset() + re.getLength()) - 1; + edit.setNewText(re.getText()); + edit.setRange(new Range(new Position(startLine, startColumn), new Position(endLine, endColumn))); + return edit; + } else if (te instanceof InsertEdit ie) { + org.eclipse.lsp4j.TextEdit edit = new org.eclipse.lsp4j.TextEdit(); + int startLine = domCu.getLineNumber(ie.getOffset()); + int startColumn = domCu.getColumnNumber(ie.getOffset()) - 1; + edit.setNewText(ie.getText()); + edit.setRange(new Range(new Position(startLine, startColumn), new Position(startLine, startColumn))); + return edit; + } + return null; + } + +} diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java index 5cd242bec..6673ddfde 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java @@ -59,6 +59,7 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.MarkupContent; import org.eclipse.lsp4j.MarkupKind; import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; @@ -77,6 +78,7 @@ import org.springframework.ide.vscode.commons.protocol.LiveProcessSummary; import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient; import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams; import org.springframework.ide.vscode.commons.protocol.java.Gav; +import org.springframework.ide.vscode.commons.protocol.java.InjectBeanParams; import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData; import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams; import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams; @@ -646,4 +648,11 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La return BuildInfo.projectGAV(params, executor, Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance)); } + @Override + public CompletableFuture injectBean(InjectBeanParams params) { + return CompletableFuture + .supplyAsync(() -> new InjectBean(Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance)) + .computeEdits(params.docUri(), params.type(), params.name())); + } + } diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/STS4LanguageClient.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/STS4LanguageClient.java index 055355d4a..debb70df2 100644 --- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/STS4LanguageClient.java +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/STS4LanguageClient.java @@ -15,6 +15,8 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.MarkupContent; +import org.eclipse.lsp4j.ResourceOperation; +import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.jsonrpc.json.ResponseJsonAdapter; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; @@ -22,6 +24,7 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.services.LanguageClient; import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams; import org.springframework.ide.vscode.commons.protocol.java.Gav; +import org.springframework.ide.vscode.commons.protocol.java.InjectBeanParams; import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData; import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams; import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams; @@ -101,4 +104,7 @@ public interface STS4LanguageClient extends LanguageClient, SpringIndexLanguageC @JsonRequest("sts/project/gav") CompletableFuture> projectGAV(ProjectGavParams params); + @JsonRequest("sts/java/injectBean") + CompletableFuture injectBean(InjectBeanParams params); + } diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java new file mode 100644 index 000000000..9b3242162 --- /dev/null +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/InjectBeanParams.java @@ -0,0 +1,5 @@ +package org.springframework.ide.vscode.commons.protocol.java; + +public record InjectBeanParams(String docUri, String type, String name) { + +} diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index add53dda8..15085fdbe 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -129,6 +129,7 @@ import org.springframework.ide.vscode.commons.protocol.LiveProcessSummary; import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient; import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams; import org.springframework.ide.vscode.commons.protocol.java.Gav; +import org.springframework.ide.vscode.commons.protocol.java.InjectBeanParams; import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData; import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams; import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams; @@ -486,6 +487,11 @@ public class LanguageServerHarness { // TODO: perhaps at some point the client would need to ask the server for new inlay-hints for opened documents. return CompletableFuture.completedFuture(null); } + + @Override + public CompletableFuture injectBean(InjectBeanParams params) { + return CompletableFuture.completedFuture(null); + } }); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java index a980de8a2..2ce228b55 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java @@ -117,7 +117,8 @@ public class BootJavaCompletionEngineConfigurer { CompilationUnitCache cuCache, SpringMetamodelIndex springIndex, RewriteRefactorings rewriteRefactorings, - BootJavaConfig config) { + BootJavaConfig config, + SimpleLanguageServer server) { SpringPropertyIndexProvider indexProvider = params.indexProvider; JavaProjectFinder javaProjectFinder = params.projectFinder; @@ -172,7 +173,7 @@ public class BootJavaCompletionEngineConfigurer { providers.put(Annotations.SCHEDULED, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of( "cron", new CronExpressionCompletionProvider()))); - providers.put(Annotations.BEAN, new BeanCompletionProvider(javaProjectFinder, springIndex, rewriteRefactorings, config, cuCache)); + providers.put(Annotations.BEAN, new BeanCompletionProvider(javaProjectFinder, springIndex, rewriteRefactorings, config, cuCache, server)); return new BootJavaCompletionEngine(cuCache, providers, snippetManager); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java index 33041d70f..41be083dc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProposal.java @@ -10,8 +10,13 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.beans; +import java.util.List; +import java.net.URI; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; import org.apache.commons.text.similarity.JaroWinklerSimilarity; import org.eclipse.core.runtime.Assert; @@ -21,7 +26,10 @@ import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.Assignment; +import org.eclipse.jdt.core.dom.Block; +import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; @@ -37,6 +45,9 @@ import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewr import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.lsp4j.CompletionItemKind; import org.eclipse.lsp4j.CompletionItemLabelDetails; +import org.eclipse.lsp4j.ResourceOperation; +import org.eclipse.lsp4j.TextDocumentEdit; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; import org.openrewrite.java.tree.JavaType; @@ -44,15 +55,20 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine; import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings; +import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposalWithScore; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.protocol.java.InjectBeanParams; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.util.text.IDocument; +import com.google.common.base.Suppliers; + /** * @author Udayani V * @author Alex Boyko @@ -80,8 +96,11 @@ public class BeanCompletionProposal implements ICompletionProposalWithScore { private CompilationUnitCache cuCache; - public BeanCompletionProposal(IJavaProject project, ASTNode node, int offset, IDocument doc, String beanId, String beanType, + private SimpleLanguageServer server; + + public BeanCompletionProposal(SimpleLanguageServer server, IJavaProject project, ASTNode node, int offset, IDocument doc, String beanId, String beanType, String fieldName, String className, RewriteRefactorings rewriteRefactorings, CompilationUnitCache cuCache) { + this.server = server; this.project = project; this.node = node; this.offset = offset; @@ -177,26 +196,26 @@ public class BeanCompletionProposal implements ICompletionProposalWithScore { private DocumentEdits computeEdit() { // try { - CompilationUnit cu = ASTNodes.getParent(node, org.eclipse.jdt.core.dom.CompilationUnit.class); - RefactoringASTParser parser= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL); - - try { -// CompilationUnit cuRefactorNode = cuCache.parseCuWithReusableEnv(project, URI.create(doc.getUri())); - return createFieldDeclaration(cu); - } catch (JavaModelException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); -// } catch (ExecutionException e) { +// CompilationUnit cu = ASTNodes.getParent(node, org.eclipse.jdt.core.dom.CompilationUnit.class); +// RefactoringASTParser parser= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL); +// +// try { +//// CompilationUnit cuRefactorNode = cuCache.parseCuWithReusableEnv(project, URI.create(doc.getUri())); +// return createFieldDeclaration(cu); +// } catch (JavaModelException e) { // // TODO Auto-generated catch block // e.printStackTrace(); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return new DocumentEdits(doc, false); +// } catch (IllegalArgumentException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +//// } catch (ExecutionException e) { +//// // TODO Auto-generated catch block +//// e.printStackTrace(); +// } catch (Exception e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// return new DocumentEdits(doc, false); // ASTRewrite astRewrite= ASTRewrite.create(cuRefactorNode.getAST()); @@ -222,24 +241,25 @@ public class BeanCompletionProposal implements ICompletionProposalWithScore { // throw new IllegalStateException(e); // } -// if (isInsideConstructor(node)) { -// if (node instanceof Block) { -// edits.insert(offset, "this.%s = %s;".formatted(fieldName, fieldName)); -// } else { -// if (node.getParent() instanceof Assignment || node.getParent() instanceof FieldAccess) { -// edits.replace(offset - prefix.length(), offset, "%s = %s;".formatted(fieldName, fieldName)); -// } else { -// edits.replace(offset - prefix.length(), offset, "this.%s = %s;".formatted(fieldName, fieldName)); -// } -// } -// } else { -// if (node instanceof Block) { -// edits.insert(offset, fieldName); -// } else { -// edits.replace(offset - prefix.length(), offset, fieldName); -// } -// } -// return edits; + DocumentEdits edits = new DocumentEdits(doc, false); + if (isInsideConstructor(node)) { + if (node instanceof Block) { + edits.insert(offset, "this.%s = %s;".formatted(fieldName, fieldName)); + } else { + if (node.getParent() instanceof Assignment || node.getParent() instanceof FieldAccess) { + edits.replace(offset - prefix.length(), offset, "%s = %s;".formatted(fieldName, fieldName)); + } else { + edits.replace(offset - prefix.length(), offset, "this.%s = %s;".formatted(fieldName, fieldName)); + } + } + } else { + if (node instanceof Block) { + edits.insert(offset, fieldName); + } else { + edits.replace(offset - prefix.length(), offset, fieldName); + } + } + return edits; } @Override @@ -289,6 +309,38 @@ public class BeanCompletionProposal implements ICompletionProposalWithScore { return false; } + @Override + public Optional> getAdditionalEdit() { + return Optional.of(Suppliers.memoize(() -> { + long start = System.currentTimeMillis(); + DocumentEdits additionalEdit = new DocumentEdits(doc, false); + try { + TextDocumentEdit beanInjectEdits = server.getClient().injectBean(new InjectBeanParams(doc.getUri(), beanType, fieldName)).get(); + if (beanInjectEdits != null) { + for (org.eclipse.lsp4j.TextEdit e : beanInjectEdits.getEdits()) { + try { + int startLineOffset = doc.getLineOffset(e.getRange().getStart().getLine() - 1); + if (!e.getRange().getEnd().equals(e.getRange().getStart())) { + int endLineOffset = doc.getLineOffset(e.getRange().getEnd().getLine() - 1); + additionalEdit.delete(startLineOffset + e.getRange().getStart().getCharacter() + 1, endLineOffset + e.getRange().getEnd().getCharacter() + 1); + } + if (!e.getNewText().isEmpty()) { + additionalEdit.insert(startLineOffset + e.getRange().getStart().getCharacter() + 1 + , e.getNewText()); + } + } catch (BadLocationException ex) { + log.error("Failed to compute edit", ex); + } + } + } + } catch (Exception e) { + log.error("Failed to fetch edits for Java LS", e); + } + log.info("ADDITIONAL EDIT %d".formatted(System.currentTimeMillis() - start)); + return additionalEdit; + })); + } + @Override public int hashCode() { return Objects.hash(beanId, beanType); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProvider.java index c77df7b45..6906c790d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/BeanCompletionProvider.java @@ -40,6 +40,7 @@ import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.protocol.spring.Bean; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -58,13 +59,16 @@ public class BeanCompletionProvider implements CompletionProvider { private CompilationUnitCache cuCache; + private SimpleLanguageServer server; + public BeanCompletionProvider(JavaProjectFinder javaProjectFinder, SpringMetamodelIndex springIndex, - RewriteRefactorings rewriteRefactorings, BootJavaConfig config, CompilationUnitCache cuCache) { + RewriteRefactorings rewriteRefactorings, BootJavaConfig config, CompilationUnitCache cuCache, SimpleLanguageServer server) { this.javaProjectFinder = javaProjectFinder; this.springIndex = springIndex; this.rewriteRefactorings = rewriteRefactorings; this.config = config; this.cuCache = cuCache; + this.server = server; } @Override @@ -138,7 +142,7 @@ public class BeanCompletionProvider implements CompletionProvider { for (int i = 0; i < Integer.MAX_VALUE && fieldNames.contains(fieldName); i++, fieldName = "%s_%d".formatted(bean.getName(), i)) { // nothing } - BeanCompletionProposal proposal = new BeanCompletionProposal(project, node, offset, doc, bean.getName(), + BeanCompletionProposal proposal = new BeanCompletionProposal(server, project, node, offset, doc, bean.getName(), bean.getType(), fieldName, className, rewriteRefactorings, cuCache); if (proposal.getScore() > 0) {