Skip to content

Commit 6e1f814

Browse files
committed
GH-1305: add content-assist for property keys and prefixes for conditional on property annotation
Signed-off-by: Martin Lippert <[email protected]>
1 parent 9c367fd commit 6e1f814

File tree

8 files changed

+466
-6
lines changed

8 files changed

+466
-6
lines changed

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.ide.vscode.boot.java.beans.ProfileCompletionProvider;
3333
import org.springframework.ide.vscode.boot.java.beans.QualifierCompletionProvider;
3434
import org.springframework.ide.vscode.boot.java.beans.ResourceCompletionProvider;
35+
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalOnPropertyCompletionProcessor;
3536
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalOnResourceCompletionProcessor;
3637
import org.springframework.ide.vscode.boot.java.contextconfiguration.ContextConfigurationProcessor;
3738
import org.springframework.ide.vscode.boot.java.cron.CronExpressionCompletionProvider;
@@ -127,6 +128,11 @@ BootJavaCompletionEngine javaCompletionEngine(
127128
providers.put(Annotations.CONDITIONAL_ON_RESOURCE, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of(
128129
"resources", new ConditionalOnResourceCompletionProcessor())));
129130

131+
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of(
132+
"value", new ConditionalOnPropertyCompletionProcessor(indexProvider, adHocProperties, ConditionalOnPropertyCompletionProcessor.Mode.PROPERTY),
133+
"name", new ConditionalOnPropertyCompletionProcessor(indexProvider, adHocProperties, ConditionalOnPropertyCompletionProcessor.Mode.PROPERTY),
134+
"prefix", new ConditionalOnPropertyCompletionProcessor(indexProvider, adHocProperties, ConditionalOnPropertyCompletionProcessor.Mode.PREFIX))));
135+
130136
providers.put(Annotations.SCOPE, new AnnotationAttributeCompletionProcessor(javaProjectFinder, Map.of(
131137
"value", new ScopeCompletionProcessor())));
132138

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.conditionals;
12+
13+
import java.util.ArrayList;
14+
import java.util.Collections;
15+
import java.util.List;
16+
import java.util.Set;
17+
import java.util.TreeSet;
18+
19+
import org.eclipse.jdt.core.dom.ASTNode;
20+
import org.eclipse.jdt.core.dom.MemberValuePair;
21+
import org.eclipse.jdt.core.dom.NormalAnnotation;
22+
import org.eclipse.jdt.core.dom.StringLiteral;
23+
import org.springframework.ide.vscode.boot.java.annotations.AnnotationAttributeCompletionProvider;
24+
import org.springframework.ide.vscode.boot.java.annotations.AnnotationAttributeProposal;
25+
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
26+
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
27+
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
28+
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
29+
import org.springframework.ide.vscode.commons.java.IJavaProject;
30+
import org.springframework.ide.vscode.commons.util.FuzzyMap;
31+
32+
/**
33+
* @author Martin Lippert
34+
*/
35+
public class ConditionalOnPropertyCompletionProcessor implements AnnotationAttributeCompletionProvider {
36+
37+
public enum Mode {
38+
PREFIX, PROPERTY
39+
}
40+
41+
private final SpringPropertyIndexProvider indexProvider;
42+
private final ProjectBasedPropertyIndexProvider adHocIndexProvider;
43+
private final Mode mode;
44+
45+
public ConditionalOnPropertyCompletionProcessor(SpringPropertyIndexProvider indexProvider,
46+
ProjectBasedPropertyIndexProvider adHocIndexProvider,
47+
Mode mode) {
48+
this.indexProvider = indexProvider;
49+
this.adHocIndexProvider = adHocIndexProvider;
50+
this.mode = mode;
51+
}
52+
53+
@Override
54+
public List<AnnotationAttributeProposal> getCompletionCandidates(IJavaProject project, ASTNode node) {
55+
if (Mode.PROPERTY == this.mode) {
56+
String prefix = getPrefixAttributeValue(node);
57+
return findProperties(project, prefix);
58+
}
59+
else if (Mode.PREFIX == this.mode) {
60+
return findPrefixes(project);
61+
}
62+
else {
63+
return Collections.emptyList();
64+
}
65+
}
66+
67+
private List<AnnotationAttributeProposal> findProperties(IJavaProject project, String prefix) {
68+
List<AnnotationAttributeProposal> result = new ArrayList<>();
69+
70+
// First the 'real' properties, Then also add 'ad-hoc' properties
71+
addPropertyProposals(indexProvider.getIndex(project).getProperties(), prefix, result);
72+
addPropertyProposals(adHocIndexProvider.getIndex(project), prefix, result);
73+
74+
result.sort((p1, p2) -> p1.getLabel().compareTo(p2.getLabel()));
75+
76+
return result;
77+
}
78+
79+
private List<AnnotationAttributeProposal> findPrefixes(IJavaProject project) {
80+
Set<AnnotationAttributeProposal> prefixes = new TreeSet<>((p1, p2) -> p1.getLabel().compareTo(p2.getLabel()));
81+
82+
// First the 'real' properties, then also add 'ad-hoc' properties
83+
addPrefixProposals(indexProvider.getIndex(project).getProperties(), prefixes);
84+
addPrefixProposals(adHocIndexProvider.getIndex(project), prefixes);
85+
86+
return new ArrayList<>(prefixes);
87+
}
88+
89+
private void addPropertyProposals(FuzzyMap<PropertyInfo> properties, String prefix, List<AnnotationAttributeProposal> result) {
90+
properties.forEach(propertyInfo -> {
91+
String propID = propertyInfo.getId();
92+
93+
if (prefix != null) {
94+
if (prefix.length() > 0
95+
&& prefix.length() < propID.length()
96+
&& propID.startsWith(prefix)) {
97+
98+
String remainingValue = propID.substring(prefix.length() + 1);
99+
result.add(new AnnotationAttributeProposal(propID, propID, remainingValue));
100+
}
101+
}
102+
else {
103+
result.add(new AnnotationAttributeProposal(propID));
104+
}
105+
});
106+
}
107+
108+
private void addPrefixProposals(FuzzyMap<PropertyInfo> properties, Set<AnnotationAttributeProposal> prefixes) {
109+
properties.forEach(propertyInfo -> {
110+
String prefix = getPrefix(propertyInfo.getId());
111+
while (prefix != null) {
112+
prefixes.add(new AnnotationAttributeProposal(prefix));
113+
prefix = getPrefix(prefix);
114+
}
115+
});
116+
}
117+
118+
private String getPrefix(String key) {
119+
int index = key.lastIndexOf('.');
120+
if (index >= 0) {
121+
return key.substring(0, index);
122+
}
123+
else {
124+
return null;
125+
}
126+
}
127+
128+
private String getPrefixAttributeValue(ASTNode node) {
129+
ASTNode annotationNode = ASTUtils.getNearestAnnotationParent(node);
130+
if (annotationNode != null && annotationNode instanceof NormalAnnotation) {
131+
NormalAnnotation annotation = (NormalAnnotation) annotationNode;
132+
133+
List<?> values = annotation.values();
134+
for (Object value : values) {
135+
if (value instanceof MemberValuePair) {
136+
MemberValuePair valuePair = (MemberValuePair) value;
137+
String valuePairName = valuePair.getName() != null ? valuePair.getName().toString() : null;
138+
139+
if (valuePairName != null && "prefix".equals(valuePairName)
140+
&& valuePair.getValue() != null && valuePair.getValue() instanceof StringLiteral) {
141+
StringLiteral prefixLiteral = (StringLiteral) valuePair.getValue();
142+
String valuePairValue = prefixLiteral.getLiteralValue();
143+
return valuePairValue;
144+
}
145+
}
146+
147+
}
148+
}
149+
150+
return null;
151+
}
152+
153+
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2016, 2023 Pivotal, Inc.
2+
* Copyright (c) 2016, 2024 Pivotal, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -55,6 +55,11 @@ public SpringPropertyIndex getIndex(IDocument doc) {
5555
return SpringPropertyIndex.EMPTY_INDEX;
5656
}
5757

58+
@Override
59+
public SpringPropertyIndex getIndex(IJavaProject project) {
60+
return indexManager.get(project, progressService);
61+
}
62+
5863
public void setProgressService(ProgressService progressService) {
5964
this.progressService = progressService;
6065
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2015, 2019 Pivotal, Inc.
2+
* Copyright (c) 2015, 2024 Pivotal, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -10,9 +10,12 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.metadata;
1212

13+
import org.springframework.ide.vscode.commons.java.IJavaProject;
1314
import org.springframework.ide.vscode.commons.util.text.IDocument;
1415

1516
public interface SpringPropertyIndexProvider {
1617
SpringPropertyIndex getIndex(IDocument doc);
18+
SpringPropertyIndex getIndex(IJavaProject project);
19+
1720
void onChange(Runnable runnable);
1821
}

headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public SpringPropertyIndex getIndex(IDocument doc) {
5757
}
5858
}
5959

60+
@Override
61+
public SpringPropertyIndex getIndex(IJavaProject project) {
62+
return getIndex((IDocument) null);
63+
}
64+
6065
@Override
6166
public void onChange(Runnable runnable) {
6267

0 commit comments

Comments
 (0)