From 99d703aff2b2509fd3b2ea98d038cd68b7e09534 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Fri, 27 Sep 2024 13:55:03 +0200 Subject: [PATCH] added non-working test cases for future reference and some code polish --- .../java/value/ValueCompletionProcessor.java | 68 ++++++++++--------- .../java/value/ValuePropertyKeyProposal.java | 1 + .../java/value/test/ValueCompletionTest.java | 39 +++++++++++ 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java index 7f39dbe8c9..a696ff416e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java @@ -24,6 +24,7 @@ import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; @@ -75,6 +76,11 @@ public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding IJavaProject project = optionalProject.get(); + // in case the node is embedded in an qualified name, e.g. "file.txt", use the fully qualified node instead just a part + if (node instanceof Name && node.getParent() instanceof QualifiedName) { + node = node.getParent(); + } + // case: @Value(<*>) if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) { List> matches = findMatches("", doc); @@ -95,20 +101,16 @@ public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding addClasspathResourceProposals(project, doc, offset, offset, "", true, completions); } // case: @Value(prefix<*>) - else if (node instanceof SimpleName && node.getParent() instanceof Annotation) { + else if (node instanceof Name && node.getParent() instanceof Annotation) { computeProposalsForSimpleName(project, node, completions, offset, doc); } - // case: @Value(file.ext<*>) - the "." causes a QualifierNode to be generated - else if (node instanceof SimpleName && node.getParent() instanceof QualifiedName && node.getParent().getParent() instanceof Annotation) { - computeProposalsForSimpleName(project, node.getParent(), completions, offset, doc); - } // case: @Value(value=<*>) - else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair + else if (node instanceof Name && node.getParent() instanceof MemberValuePair && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { computeProposalsForSimpleName(project, node, completions, offset, doc); } // case: @Value(value=<*>) - else if (node instanceof SimpleName && node.getParent() instanceof QualifiedName && node.getParent().getParent() instanceof MemberValuePair + else if (node instanceof Name && node.getParent() instanceof QualifiedName && node.getParent().getParent() instanceof MemberValuePair && "value".equals(((MemberValuePair)node.getParent().getParent()).getName().toString())) { computeProposalsForSimpleName(project, node.getParent(), completions, offset, doc); } @@ -131,29 +133,6 @@ else if (node instanceof StringLiteral && node.getParent() instanceof MemberValu } } - private void addClasspathResourceProposals(IJavaProject project, TextDocument doc, int startOffset, int endOffset, String prefix, boolean includeQuotes, Collection completions) { - String[] resources = findResources(project, prefix); - - double score = resources.length + 1000; - for (String resource : resources) { - - DocumentEdits edits = new DocumentEdits(doc, false); - - if (includeQuotes) { - edits.replace(startOffset, endOffset, "\"classpath:" + resource + "\""); - } - else { - edits.replace(startOffset, endOffset, "classpath:" + resource); - } - - String label = "classpath:" + resource; - - ICompletionProposal proposal = new AnnotationAttributeCompletionProposal(edits, label, label, null, score--); - completions.add(proposal); - } - - } - private void computeProposalsForSimpleName(IJavaProject project, ASTNode node, Collection completions, int offset, TextDocument doc) { String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition()); @@ -253,15 +232,15 @@ public String identifyPropertyPrefix(String nodeContent, int offset) { private List> findMatches(String prefix, IDocument doc) { FuzzyMap index = indexProvider.getIndex(doc).getProperties(); - List> matches =index.find(camelCaseToHyphens(prefix)); + List> matches = index.find(camelCaseToHyphens(prefix)); - //First the 'real' properties. + // First the 'real' properties. Set suggestedKeys = new HashSet<>(); for (Match m : matches) { suggestedKeys.add(m.data.getId()); } - //Then also add 'ad-hoc' properties (see https://www.pivotaltracker.com/story/show/153107266). + // Then also add 'ad-hoc' properties Optional p = projectFinder.find(new TextDocumentIdentifier(doc.getUri())); if (p.isPresent()) { index = adHocIndexProvider.getIndex(p.get()); @@ -274,6 +253,29 @@ private List> findMatches(String prefix, IDocument doc) { return matches; } + private void addClasspathResourceProposals(IJavaProject project, TextDocument doc, int startOffset, int endOffset, String prefix, boolean includeQuotes, Collection completions) { + String[] resources = findResources(project, prefix); + + double score = resources.length + 1000; + for (String resource : resources) { + + DocumentEdits edits = new DocumentEdits(doc, false); + + if (includeQuotes) { + edits.replace(startOffset, endOffset, "\"classpath:" + resource + "\""); + } + else { + edits.replace(startOffset, endOffset, "classpath:" + resource); + } + + String label = "classpath:" + resource; + + ICompletionProposal proposal = new AnnotationAttributeCompletionProposal(edits, label, label, null, score--); + completions.add(proposal); + } + + } + private String[] findResources(IJavaProject project, String prefix) { String[] resources = IClasspathUtil.getClasspathResources(project.getClasspath()).stream() .distinct() diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyKeyProposal.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyKeyProposal.java index 2d90a87537..bcb742cb91 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyKeyProposal.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyKeyProposal.java @@ -24,6 +24,7 @@ public class ValuePropertyKeyProposal extends ScoreableProposal { private static final String EMPTY_DETAIL = ""; + private DocumentEdits edits; private String label; private String detail; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java index 79916d86ce..bd06f3107a 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java @@ -294,6 +294,20 @@ void testPlainPrefixCompletion() throws Exception { assertClasspathCompletions(); } +// The parser removes the (spring.) piece from the AST in this case, so there is no way +// to clearly identify this case +// +// @Test +// void testComplexPrefixCompletionParamNameCursorRightAfterDot() throws Exception { +// prepareCase("@Value(\"onField\")", "@Value(spring.<*>)"); +// prepareDefaultIndexData(); +// +// assertPropertyCompletions( +// "@Value(\"${spring.prop1}\"<*>)"); +// +// assertClasspathCompletions(); +// } + @Test void testComplexPrefixCompletion() throws Exception { prepareCase("@Value(\"onField\")", "@Value(spring.pr<*>)"); @@ -316,6 +330,20 @@ void testPrefixCompletionWithParamName() throws Exception { assertClasspathCompletions(); } +// The parser removes the (spring.) piece from the AST in this case, so there is no way +// to clearly identify this case +// +// @Test +// void testPrefixCompletionWithParamNameCursorRightAfterDot() throws Exception { +// prepareCase("@Value(\"onField\")", "@Value(value=spring.<*>)"); +// prepareDefaultIndexData(); +// +// assertPropertyCompletions( +// "@Value(value=\"${spring.prop1}\"<*>)"); +// +// assertClasspathCompletions(); +// } + @Test void testComplexPrefixCompletionWithParamName() throws Exception { prepareCase("@Value(\"onField\")", "@Value(value=spring.pr<*>)"); @@ -419,6 +447,17 @@ void testQoutedPrefixCompletion() throws Exception { assertClasspathCompletions(); } + @Test + void testComplexPrefixCompletionWithQuotesAndDotRightAfterPrefix() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"spring.<*>\")"); + prepareDefaultIndexData(); + + assertPropertyCompletions( + "@Value(\"${spring.prop1}<*>\")"); + + assertClasspathCompletions(); + } + @Test void testComplexPrefixCompletionWithQuotes() throws Exception { prepareCase("@Value(\"onField\")", "@Value(\"spring.pr<*>\")");