Skip to content

Commit 0562afa

Browse files
committed
Support of 'classpath:' prefix in spring.config.imports property
See: #536
1 parent f911a44 commit 0562afa

File tree

14 files changed

+782
-43
lines changed

14 files changed

+782
-43
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2020 Pivotal, 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+
* Pivotal, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.app;
12+
13+
import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.getValueType;
14+
15+
import java.util.Collection;
16+
import java.util.List;
17+
import java.util.Optional;
18+
import java.util.stream.Stream;
19+
20+
import org.springframework.beans.factory.annotation.Autowired;
21+
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
22+
import org.springframework.ide.vscode.boot.java.value.ValuePropertyKeyProposal;
23+
import org.springframework.ide.vscode.boot.metadata.CachingValueProvider;
24+
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
25+
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
26+
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
27+
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
28+
import org.springframework.ide.vscode.boot.metadata.hints.ValueHintHoverInfo;
29+
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
30+
import org.springframework.ide.vscode.commons.java.IJavaProject;
31+
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
32+
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
33+
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
34+
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
35+
import org.springframework.ide.vscode.commons.languageserver.util.LanguageSpecific;
36+
import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder;
37+
import org.springframework.ide.vscode.commons.util.BadLocationException;
38+
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
39+
import org.springframework.ide.vscode.commons.util.FuzzyMap.Match;
40+
import org.springframework.ide.vscode.commons.util.text.LanguageId;
41+
import org.springframework.ide.vscode.commons.util.text.TextDocument;
42+
import org.springframework.stereotype.Component;
43+
44+
import com.google.common.collect.ImmutableList;
45+
46+
import reactor.core.publisher.Flux;
47+
48+
@Component
49+
public class ClasspathResourceCompletionProvider implements ICompletionEngine, LanguageSpecific {
50+
51+
private static String[] CLASSPATH_PREFIXES = {
52+
"classpath:",
53+
"classpath*:"
54+
};
55+
56+
private static PrefixFinder PREFIX_FINDER = new PrefixFinder() {
57+
@Override
58+
protected boolean isPrefixChar(char c) {
59+
return Character.isJavaIdentifierPart(c) || c=='-' || c=='.' || c=='/' || c==':' || c=='*';
60+
}
61+
};
62+
private static final Collection<LanguageId> LANGUAGES = ImmutableList.of(
63+
LanguageId.BOOT_PROPERTIES,
64+
LanguageId.BOOT_PROPERTIES_YAML
65+
);
66+
67+
@Autowired JavaProjectFinder projectFinder;
68+
69+
public ClasspathResourceCompletionProvider(BootLanguageServerParams params) {
70+
}
71+
72+
private static class ClasspathHints extends CachingValueProvider {
73+
@Override
74+
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
75+
return Flux.fromStream(
76+
IClasspathUtil.getClasspathResources(javaProject.getClasspath()).stream()
77+
.distinct().map(r -> r.replaceAll("\\\\", "/"))
78+
.map(StsValueHint::create)
79+
);
80+
}
81+
}
82+
83+
private ClasspathHints classpathHints = new ClasspathHints();
84+
private PropertyCompletionFactory completionFactory = new PropertyCompletionFactory();
85+
86+
@Override
87+
public Collection<ICompletionProposal> getCompletions(TextDocument doc, int offset) {
88+
ImmutableList.Builder<ICompletionProposal> proposals = ImmutableList.builder();
89+
IJavaProject jp = projectFinder.find(doc.getId()).orElse(null);
90+
if (jp!=null) {
91+
String prefix = PREFIX_FINDER.getPrefix(doc, offset);
92+
for (String CLASSPATH : CLASSPATH_PREFIXES) {
93+
if (prefix.startsWith(CLASSPATH)) {
94+
String query = prefix.substring(CLASSPATH.length());
95+
Flux<StsValueHint> valueHints = classpathHints.getValues(jp, query);
96+
valueHints.toStream().forEach(hint -> {
97+
String valueCandidate = hint.getValue();
98+
int startOfValue = offset - query.length();
99+
double score = FuzzyMatcher.matchScore(query, valueCandidate);
100+
if (score != 0) {
101+
DocumentEdits edits = new DocumentEdits(doc, false);
102+
edits.delete(startOfValue, offset);
103+
edits.insert(offset, valueCandidate);
104+
proposals.add(completionFactory.valueProposal(valueCandidate, query, "String",
105+
score, edits, ValueHintHoverInfo.create(hint))
106+
);
107+
}
108+
});
109+
}
110+
}
111+
}
112+
return proposals.build();
113+
}
114+
115+
@Override
116+
public Collection<LanguageId> supportedLanguages() {
117+
return LANGUAGES;
118+
}
119+
120+
}

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

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@ public class ResourceHintProvider implements ValueProviderStrategy {
4343

4444
@Override
4545
public Flux<StsValueHint> getValues(IJavaProject javaProject, String query) {
46-
for (String prefix : CLASSPATH_PREFIXES) {
47-
if (query.startsWith(prefix)) {
48-
return classpathHints
49-
.getValues(javaProject, query.substring(prefix.length()))
50-
.map((hint) -> hint.prefixWith(prefix));
51-
}
52-
}
5346
return Flux.fromIterable(urlPrefixHints);
5447
}
5548

@@ -59,18 +52,4 @@ public Flux<StsValueHint> getValues(IJavaProject javaProject, String query) {
5952
.collect(Collectors.toList())
6053
);
6154

62-
private ClasspathHints classpathHints = new ClasspathHints();
63-
64-
private static class ClasspathHints extends CachingValueProvider {
65-
@Override
66-
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
67-
return Flux.fromStream(
68-
IClasspathUtil.getClasspathResources(javaProject.getClasspath()).stream()
69-
.distinct().map(r -> r.replaceAll("\\\\", "/"))
70-
.map(StsValueHint::create)
71-
);
72-
}
73-
}
74-
75-
7655
}

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

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
7979
projectContents.createFile("src/main/resources/application.yml", "");
8080
};
8181

82+
@Test public void configImportClasspathCompletions() throws Exception {
83+
MavenJavaProject p = createPredefinedMavenProject("demo-conf-import");
84+
useProject(p);
85+
86+
// assertCompletions("spring.config.import=<*>",
87+
// // ==>
88+
// "spring.config.import=classpath:<*>",
89+
// "spring.config.import=configtree:<*>",
90+
// "spring.config.import=file:<*>"
91+
// );
92+
93+
assertCompletions("spring.config.import=classpath:ex<*>",
94+
// ==>
95+
"spring.config.import=classpath:extra.properties<*>"
96+
);
97+
}
98+
8299
@Test public void reconcilesWithMultiDocuments() throws Exception {
83100
//See: https://github.com/spring-projects/sts4/issues/533
84101

@@ -1601,8 +1618,8 @@ public void testReconcileCatchesParseError() throws Exception {
16011618
assertCompletionsDisplayString(
16021619
"my.nice.resource=classpath:app<*>\n"
16031620
,// =>
1604-
"classpath:application.properties",
1605-
"classpath:application.yml"
1621+
"application.properties",
1622+
"application.yml"
16061623
);
16071624

16081625
//Test 'list item' context:
@@ -1620,30 +1637,30 @@ public void testReconcileCatchesParseError() throws Exception {
16201637
assertCompletionsDisplayString(
16211638
"my.nice.list[0]=classpath:app<*>\n"
16221639
,// =>
1623-
"classpath:application.properties",
1624-
"classpath:application.yml"
1640+
"application.properties",
1641+
"application.yml"
16251642
);
16261643

16271644
assertCompletionWithLabel(
16281645
"my.nice.list[0]=classpath:app<*>\n"
16291646
,// ==========
1630-
"classpath:application.yml"
1647+
"application.yml"
16311648
, // =>
16321649
"my.nice.list[0]=classpath:application.yml<*>\n"
16331650
);
16341651

16351652
assertCompletionWithLabel(
16361653
"my.nice.list[0]= classpath:app<*>\n"
16371654
,// ==========
1638-
"classpath:application.yml"
1655+
"application.yml"
16391656
, // =>
16401657
"my.nice.list[0]= classpath:application.yml<*>\n"
16411658
);
16421659

16431660
assertCompletionWithLabel(
16441661
"my.nice.list[0]=classpath:<*>\n"
16451662
,// ==========
1646-
"classpath:application.yml"
1663+
"application.yml"
16471664
, // =>
16481665
"my.nice.list[0]=classpath:application.yml<*>\n"
16491666
);
@@ -1654,7 +1671,7 @@ public void testReconcileCatchesParseError() throws Exception {
16541671
assertCompletionWithLabel(
16551672
"my.nice.resource=classpath:word<*>\n"
16561673
,//===============
1657-
"classpath:stuff/wordlist.txt"
1674+
"stuff/wordlist.txt"
16581675
,// =>
16591676
"my.nice.resource=classpath:stuff/wordlist.txt<*>\n"
16601677
);
@@ -1671,7 +1688,7 @@ public void testReconcileCatchesParseError() throws Exception {
16711688
assertCompletionWithLabel(
16721689
"my.nice."+kind+"=classpath:<*>"
16731690
,//===========
1674-
"classpath:stuff/wordlist.txt"
1691+
"stuff/wordlist.txt"
16751692
,//=>
16761693
"my.nice."+kind+"=classpath:stuff/wordlist.txt<*>"
16771694
);
@@ -1689,7 +1706,7 @@ public void testReconcileCatchesParseError() throws Exception {
16891706
assertCompletionWithLabel(
16901707
"my.nice."+kind+"=classpath:stuff/wordlist.txt,classpath:app<*>"
16911708
,//===========
1692-
"classpath:application.yml"
1709+
"application.yml"
16931710
,//=>
16941711
"my.nice."+kind+"=classpath:stuff/wordlist.txt,classpath:application.yml<*>"
16951712
);

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4064,7 +4064,7 @@ private String getMissingPropertyName(CodeAction fix) {
40644064
" list:\n"+
40654065
" - classpath:<*>\n"
40664066
,// ==========
4067-
"classpath:application.yml"
4067+
"application.yml"
40684068
, // =>
40694069
"my:\n" +
40704070
" nice:\n" +
@@ -4089,8 +4089,8 @@ private String getMissingPropertyName(CodeAction fix) {
40894089
" nice:\n" +
40904090
" resource: classpath:app<*>\n"
40914091
,// =>
4092-
"classpath:application.properties",
4093-
"classpath:application.yml"
4092+
"application.properties",
4093+
"application.yml"
40944094
);
40954095

40964096
//Test 'list item' context:
@@ -4101,8 +4101,8 @@ private String getMissingPropertyName(CodeAction fix) {
41014101
" list:\n"+
41024102
" - classpath:app<*>\n"
41034103
,// =>
4104-
"classpath:application.properties",
4105-
"classpath:application.yml"
4104+
"application.properties",
4105+
"application.yml"
41064106
);
41074107

41084108
assertCompletionWithLabel(
@@ -4111,7 +4111,7 @@ private String getMissingPropertyName(CodeAction fix) {
41114111
" list:\n"+
41124112
" - classpath:app<*>\n"
41134113
,// ==========
4114-
"classpath:application.yml"
4114+
"application.yml"
41154115
, // =>
41164116
"my:\n" +
41174117
" nice:\n" +
@@ -4125,7 +4125,7 @@ private String getMissingPropertyName(CodeAction fix) {
41254125
" list:\n"+
41264126
" - classpath:<*>\n"
41274127
,// ==========
4128-
"classpath:application.yml"
4128+
"application.yml"
41294129
, // =>
41304130
"my:\n" +
41314131
" nice:\n" +
@@ -4141,8 +4141,8 @@ private String getMissingPropertyName(CodeAction fix) {
41414141
" resource:\n"+
41424142
" classpath:app<*>\n"
41434143
,// =>
4144-
"classpath:application.properties",
4145-
"classpath:application.yml"
4144+
"application.properties",
4145+
"application.yml"
41464146
);
41474147

41484148
assertCompletionWithLabel(
@@ -4151,7 +4151,7 @@ private String getMissingPropertyName(CodeAction fix) {
41514151
" resource:\n"+
41524152
" classpath:app<*>\n"
41534153
,//===============
4154-
"classpath:application.properties"
4154+
"application.properties"
41554155
,// =>
41564156
"my:\n" +
41574157
" nice:\n" +
@@ -4165,7 +4165,7 @@ private String getMissingPropertyName(CodeAction fix) {
41654165
" resource:\n"+
41664166
" classpath:<*>\n"
41674167
,//===============
4168-
"classpath:application.properties"
4168+
"application.properties"
41694169
,// =>
41704170
"my:\n" +
41714171
" nice:\n" +
@@ -4180,7 +4180,7 @@ private String getMissingPropertyName(CodeAction fix) {
41804180
" resource:\n"+
41814181
" classpath:word<*>\n"
41824182
,//===============
4183-
"classpath:stuff/wordlist.txt"
4183+
"stuff/wordlist.txt"
41844184
,// =>
41854185
"my:\n" +
41864186
" nice:\n" +
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
HELP.md
2+
target/
3+
!.mvn/wrapper/maven-wrapper.jar
4+
!**/src/main/**/target/
5+
!**/src/test/**/target/
6+
7+
### STS ###
8+
.apt_generated
9+
.classpath
10+
.factorypath
11+
.project
12+
.settings
13+
.springBeans
14+
.sts4-cache
15+
16+
### IntelliJ IDEA ###
17+
.idea
18+
*.iws
19+
*.iml
20+
*.ipr
21+
22+
### NetBeans ###
23+
/nbproject/private/
24+
/nbbuild/
25+
/dist/
26+
/nbdist/
27+
/.nb-gradle/
28+
build/
29+
!**/src/main/**/build/
30+
!**/src/test/**/build/
31+
32+
### VS Code ###
33+
.vscode/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
2+
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar

0 commit comments

Comments
 (0)