Skip to content

Commit

Permalink
Support of 'classpath:' prefix in spring.config.imports property
Browse files Browse the repository at this point in the history
See: #536
  • Loading branch information
kdvolder committed Jan 28, 2022
1 parent f911a44 commit 0562afa
Show file tree
Hide file tree
Showing 14 changed files with 782 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.app;

import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.getValueType;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
import org.springframework.ide.vscode.boot.java.value.ValuePropertyKeyProposal;
import org.springframework.ide.vscode.boot.metadata.CachingValueProvider;
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
import org.springframework.ide.vscode.boot.metadata.hints.ValueHintHoverInfo;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
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.ICompletionEngine;
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.LanguageSpecific;
import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
import org.springframework.ide.vscode.commons.util.FuzzyMap.Match;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.stereotype.Component;

import com.google.common.collect.ImmutableList;

import reactor.core.publisher.Flux;

@Component
public class ClasspathResourceCompletionProvider implements ICompletionEngine, LanguageSpecific {

private static String[] CLASSPATH_PREFIXES = {
"classpath:",
"classpath*:"
};

private static PrefixFinder PREFIX_FINDER = new PrefixFinder() {
@Override
protected boolean isPrefixChar(char c) {
return Character.isJavaIdentifierPart(c) || c=='-' || c=='.' || c=='/' || c==':' || c=='*';
}
};
private static final Collection<LanguageId> LANGUAGES = ImmutableList.of(
LanguageId.BOOT_PROPERTIES,
LanguageId.BOOT_PROPERTIES_YAML
);

@Autowired JavaProjectFinder projectFinder;

public ClasspathResourceCompletionProvider(BootLanguageServerParams params) {
}

private static class ClasspathHints extends CachingValueProvider {
@Override
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
return Flux.fromStream(
IClasspathUtil.getClasspathResources(javaProject.getClasspath()).stream()
.distinct().map(r -> r.replaceAll("\\\\", "/"))
.map(StsValueHint::create)
);
}
}

private ClasspathHints classpathHints = new ClasspathHints();
private PropertyCompletionFactory completionFactory = new PropertyCompletionFactory();

@Override
public Collection<ICompletionProposal> getCompletions(TextDocument doc, int offset) {
ImmutableList.Builder<ICompletionProposal> proposals = ImmutableList.builder();
IJavaProject jp = projectFinder.find(doc.getId()).orElse(null);
if (jp!=null) {
String prefix = PREFIX_FINDER.getPrefix(doc, offset);
for (String CLASSPATH : CLASSPATH_PREFIXES) {
if (prefix.startsWith(CLASSPATH)) {
String query = prefix.substring(CLASSPATH.length());
Flux<StsValueHint> valueHints = classpathHints.getValues(jp, query);
valueHints.toStream().forEach(hint -> {
String valueCandidate = hint.getValue();
int startOfValue = offset - query.length();
double score = FuzzyMatcher.matchScore(query, valueCandidate);
if (score != 0) {
DocumentEdits edits = new DocumentEdits(doc, false);
edits.delete(startOfValue, offset);
edits.insert(offset, valueCandidate);
proposals.add(completionFactory.valueProposal(valueCandidate, query, "String",
score, edits, ValueHintHoverInfo.create(hint))
);
}
});
}
}
}
return proposals.build();
}

@Override
public Collection<LanguageId> supportedLanguages() {
return LANGUAGES;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@ public class ResourceHintProvider implements ValueProviderStrategy {

@Override
public Flux<StsValueHint> getValues(IJavaProject javaProject, String query) {
for (String prefix : CLASSPATH_PREFIXES) {
if (query.startsWith(prefix)) {
return classpathHints
.getValues(javaProject, query.substring(prefix.length()))
.map((hint) -> hint.prefixWith(prefix));
}
}
return Flux.fromIterable(urlPrefixHints);
}

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

private ClasspathHints classpathHints = new ClasspathHints();

private static class ClasspathHints extends CachingValueProvider {
@Override
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
return Flux.fromStream(
IClasspathUtil.getClasspathResources(javaProject.getClasspath()).stream()
.distinct().map(r -> r.replaceAll("\\\\", "/"))
.map(StsValueHint::create)
);
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,23 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
projectContents.createFile("src/main/resources/application.yml", "");
};

@Test public void configImportClasspathCompletions() throws Exception {
MavenJavaProject p = createPredefinedMavenProject("demo-conf-import");
useProject(p);

// assertCompletions("spring.config.import=<*>",
// // ==>
// "spring.config.import=classpath:<*>",
// "spring.config.import=configtree:<*>",
// "spring.config.import=file:<*>"
// );

assertCompletions("spring.config.import=classpath:ex<*>",
// ==>
"spring.config.import=classpath:extra.properties<*>"
);
}

@Test public void reconcilesWithMultiDocuments() throws Exception {
//See: https://github.com/spring-projects/sts4/issues/533

Expand Down Expand Up @@ -1601,8 +1618,8 @@ public void testReconcileCatchesParseError() throws Exception {
assertCompletionsDisplayString(
"my.nice.resource=classpath:app<*>\n"
,// =>
"classpath:application.properties",
"classpath:application.yml"
"application.properties",
"application.yml"
);

//Test 'list item' context:
Expand All @@ -1620,30 +1637,30 @@ public void testReconcileCatchesParseError() throws Exception {
assertCompletionsDisplayString(
"my.nice.list[0]=classpath:app<*>\n"
,// =>
"classpath:application.properties",
"classpath:application.yml"
"application.properties",
"application.yml"
);

assertCompletionWithLabel(
"my.nice.list[0]=classpath:app<*>\n"
,// ==========
"classpath:application.yml"
"application.yml"
, // =>
"my.nice.list[0]=classpath:application.yml<*>\n"
);

assertCompletionWithLabel(
"my.nice.list[0]= classpath:app<*>\n"
,// ==========
"classpath:application.yml"
"application.yml"
, // =>
"my.nice.list[0]= classpath:application.yml<*>\n"
);

assertCompletionWithLabel(
"my.nice.list[0]=classpath:<*>\n"
,// ==========
"classpath:application.yml"
"application.yml"
, // =>
"my.nice.list[0]=classpath:application.yml<*>\n"
);
Expand All @@ -1654,7 +1671,7 @@ public void testReconcileCatchesParseError() throws Exception {
assertCompletionWithLabel(
"my.nice.resource=classpath:word<*>\n"
,//===============
"classpath:stuff/wordlist.txt"
"stuff/wordlist.txt"
,// =>
"my.nice.resource=classpath:stuff/wordlist.txt<*>\n"
);
Expand All @@ -1671,7 +1688,7 @@ public void testReconcileCatchesParseError() throws Exception {
assertCompletionWithLabel(
"my.nice."+kind+"=classpath:<*>"
,//===========
"classpath:stuff/wordlist.txt"
"stuff/wordlist.txt"
,//=>
"my.nice."+kind+"=classpath:stuff/wordlist.txt<*>"
);
Expand All @@ -1689,7 +1706,7 @@ public void testReconcileCatchesParseError() throws Exception {
assertCompletionWithLabel(
"my.nice."+kind+"=classpath:stuff/wordlist.txt,classpath:app<*>"
,//===========
"classpath:application.yml"
"application.yml"
,//=>
"my.nice."+kind+"=classpath:stuff/wordlist.txt,classpath:application.yml<*>"
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4064,7 +4064,7 @@ private String getMissingPropertyName(CodeAction fix) {
" list:\n"+
" - classpath:<*>\n"
,// ==========
"classpath:application.yml"
"application.yml"
, // =>
"my:\n" +
" nice:\n" +
Expand All @@ -4089,8 +4089,8 @@ private String getMissingPropertyName(CodeAction fix) {
" nice:\n" +
" resource: classpath:app<*>\n"
,// =>
"classpath:application.properties",
"classpath:application.yml"
"application.properties",
"application.yml"
);

//Test 'list item' context:
Expand All @@ -4101,8 +4101,8 @@ private String getMissingPropertyName(CodeAction fix) {
" list:\n"+
" - classpath:app<*>\n"
,// =>
"classpath:application.properties",
"classpath:application.yml"
"application.properties",
"application.yml"
);

assertCompletionWithLabel(
Expand All @@ -4111,7 +4111,7 @@ private String getMissingPropertyName(CodeAction fix) {
" list:\n"+
" - classpath:app<*>\n"
,// ==========
"classpath:application.yml"
"application.yml"
, // =>
"my:\n" +
" nice:\n" +
Expand All @@ -4125,7 +4125,7 @@ private String getMissingPropertyName(CodeAction fix) {
" list:\n"+
" - classpath:<*>\n"
,// ==========
"classpath:application.yml"
"application.yml"
, // =>
"my:\n" +
" nice:\n" +
Expand All @@ -4141,8 +4141,8 @@ private String getMissingPropertyName(CodeAction fix) {
" resource:\n"+
" classpath:app<*>\n"
,// =>
"classpath:application.properties",
"classpath:application.yml"
"application.properties",
"application.yml"
);

assertCompletionWithLabel(
Expand All @@ -4151,7 +4151,7 @@ private String getMissingPropertyName(CodeAction fix) {
" resource:\n"+
" classpath:app<*>\n"
,//===============
"classpath:application.properties"
"application.properties"
,// =>
"my:\n" +
" nice:\n" +
Expand All @@ -4165,7 +4165,7 @@ private String getMissingPropertyName(CodeAction fix) {
" resource:\n"+
" classpath:<*>\n"
,//===============
"classpath:application.properties"
"application.properties"
,// =>
"my:\n" +
" nice:\n" +
Expand All @@ -4180,7 +4180,7 @@ private String getMissingPropertyName(CodeAction fix) {
" resource:\n"+
" classpath:word<*>\n"
,//===============
"classpath:stuff/wordlist.txt"
"stuff/wordlist.txt"
,// =>
"my:\n" +
" nice:\n" +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
Loading

0 comments on commit 0562afa

Please sign in to comment.