Merge branch 'master' of github.com:spring-projects/sts4
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
*.class
|
||||
**/.classpath
|
||||
**/classpath.txt
|
||||
**/.idea
|
||||
**/*.iml
|
||||
|
||||
@@ -7,20 +7,66 @@
|
||||
<content-type
|
||||
base-type="org.eclipse.jdt.core.javaProperties"
|
||||
default-charset="ISO-8859-1"
|
||||
id="org.springframework.boot.ide.properties.applicationProperties"
|
||||
id="org.springframework.boot.ide.properties.application.properties"
|
||||
name="Spring Properties File"
|
||||
priority="high">
|
||||
</content-type>
|
||||
<file-association
|
||||
content-type="org.springframework.boot.ide.properties.applicationProperties"
|
||||
content-type="org.springframework.boot.ide.properties.application.properties"
|
||||
file-names="application.properties,application-dev.properties">
|
||||
</file-association>
|
||||
</extension>
|
||||
|
||||
<extension
|
||||
point="org.eclipse.core.contenttype.contentTypes">
|
||||
<content-type
|
||||
base-type="org.eclipse.core.runtime.text"
|
||||
default-charset="UTF-8"
|
||||
id="org.springframework.boot.ide.properties.application.yml"
|
||||
name="Spring Yaml Properties File"
|
||||
priority="high">
|
||||
</content-type>
|
||||
<file-association
|
||||
content-type="org.springframework.boot.ide.properties.application.yml"
|
||||
file-names="application.yml,bootstrap.yml,application-dev.yml">
|
||||
</file-association>
|
||||
</extension>
|
||||
|
||||
<extension
|
||||
point="org.eclipse.core.contenttype.contentTypes">
|
||||
<content-type
|
||||
base-type="org.eclipse.core.runtime.text"
|
||||
default-charset="UTF-8"
|
||||
id="org.springframework.boot.ide.manifest.yml"
|
||||
name="Cloudfoundry Manifest"
|
||||
priority="high">
|
||||
</content-type>
|
||||
<file-association
|
||||
content-type="org.springframework.boot.ide.manifest.yml"
|
||||
file-names="manifest.yml">
|
||||
</file-association>
|
||||
</extension>
|
||||
|
||||
<extension
|
||||
point="org.eclipse.ui.editors">
|
||||
<editorContentTypeBinding
|
||||
contentTypeId="org.springframework.boot.ide.properties.applicationProperties"
|
||||
contentTypeId="org.springframework.boot.ide.properties.application.properties"
|
||||
editorId="org.eclipse.ui.genericeditor.GenericEditor">
|
||||
</editorContentTypeBinding>
|
||||
</extension>
|
||||
|
||||
<extension
|
||||
point="org.eclipse.ui.editors">
|
||||
<editorContentTypeBinding
|
||||
contentTypeId="org.springframework.boot.ide.properties.application.yml"
|
||||
editorId="org.eclipse.ui.genericeditor.GenericEditor">
|
||||
</editorContentTypeBinding>
|
||||
</extension>
|
||||
|
||||
<extension
|
||||
point="org.eclipse.ui.editors">
|
||||
<editorContentTypeBinding
|
||||
contentTypeId="org.springframework.boot.ide.manifest.yml"
|
||||
editorId="org.eclipse.ui.genericeditor.GenericEditor">
|
||||
</editorContentTypeBinding>
|
||||
</extension>
|
||||
|
||||
@@ -11,8 +11,22 @@
|
||||
package org.springframework.ide.vscode.application.properties.metadata;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* A abstract {@link ValueProviderStrategy} that is mean to help speedup successive invocations of
|
||||
@@ -53,74 +67,79 @@ public abstract class CachingValueProvider implements ValueProviderStrategy {
|
||||
*/
|
||||
private int MAX_RESULTS = 500;
|
||||
|
||||
// private Cache<Tuple2<String,String>, CacheEntry> cache = createCache();
|
||||
//
|
||||
// private class CacheEntry {
|
||||
// boolean isComplete = false;
|
||||
// int count = 0;
|
||||
// Flux<StsValueHint> values;
|
||||
//
|
||||
// public CacheEntry(String query, Flux<StsValueHint> producer) {
|
||||
// values = producer
|
||||
// .take(MAX_RESULTS)
|
||||
// .cache(MAX_RESULTS);
|
||||
// values.subscribe(); // create infinite demand so that we actually force cache entries to be fetched upto the max.
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String toString() {
|
||||
// return "CacheEntry [isComplete=" + isComplete + ", count=" + count + "]";
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public final Flux<StsValueHint> getValues(IJavaProject javaProject, String query) {
|
||||
//// debug("CA query: "+query);
|
||||
// Tuple2<String, String> key = key(javaProject, query);
|
||||
// CacheEntry cached = cache.get(key);
|
||||
// if (cached==null) {
|
||||
// cache.put(key, cached = new CacheEntry(query, getValuesIncremental(javaProject, query)));
|
||||
// }
|
||||
// return cached.values;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Tries to use an already cached, complete result for a query that is a prefix of the current query to speed things up.
|
||||
// * <p>
|
||||
// * Falls back on doing a full-blown search if there's no usable 'prefix-query' in the cache.
|
||||
// */
|
||||
// private Flux<StsValueHint> getValuesIncremental(IJavaProject javaProject, String query) {
|
||||
//// debug("trying to solve "+query+" incrementally");
|
||||
// String subquery = query;
|
||||
// while (subquery.length()>=1) {
|
||||
// subquery = subquery.substring(0, subquery.length()-1);
|
||||
// CacheEntry cached = cache.get(key(javaProject, subquery));
|
||||
// if (cached!=null) {
|
||||
// System.out.println("cached "+subquery+": "+cached);
|
||||
// if (cached.isComplete) {
|
||||
//// debug("filtering "+subquery+" -> "+query);
|
||||
// return cached.values
|
||||
//// .doOnNext((hint) -> debug("filter["+query+"]: "+hint.getValue()))
|
||||
// .filter((hint) -> 0!=FuzzyMatcher.matchScore(query, hint.getValue().toString()));
|
||||
// } else {
|
||||
//// debug("subquery "+subquery+" cached but is incomplete");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//// debug("full search for: "+query);
|
||||
// return getValuesAsycn(javaProject, query);
|
||||
// }
|
||||
//
|
||||
// protected abstract Flux<StsValueHint> getValuesAsycn(IJavaProject javaProject, String query);
|
||||
//
|
||||
// private Tuple2<String,String> key(IJavaProject javaProject, String query) {
|
||||
// return Tuples.of(javaProject==null?null:javaProject.getElementName(), query);
|
||||
// }
|
||||
//
|
||||
// protected <K,V> Cache<K,V> createCache() {
|
||||
// return new LimitedTimeCache<>(Duration.ofMinutes(1));
|
||||
// }
|
||||
private Cache<Tuple2<String,String>, CacheEntry> cache = createCache();
|
||||
|
||||
private class CacheEntry {
|
||||
boolean isComplete = false;
|
||||
int count = 0;
|
||||
Flux<StsValueHint> values;
|
||||
|
||||
public CacheEntry(String query, Flux<StsValueHint> producer) {
|
||||
values = producer
|
||||
.take(MAX_RESULTS)
|
||||
.cache(MAX_RESULTS);
|
||||
values.subscribe(); // create infinite demand so that we actually force cache entries to be fetched upto the max.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CacheEntry [isComplete=" + isComplete + ", count=" + count + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Flux<StsValueHint> getValues(IJavaProject javaProject, String query) {
|
||||
Tuple2<String, String> key = key(javaProject, query);
|
||||
CacheEntry cached = null;
|
||||
try {
|
||||
cached = cache.get(key, () -> new CacheEntry(query, getValuesIncremental(javaProject, query)));
|
||||
} catch (ExecutionException e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return cached.values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to use an already cached, complete result for a query that is a prefix of the current query to speed things up.
|
||||
* <p>
|
||||
* Falls back on doing a full-blown search if there's no usable 'prefix-query' in the cache.
|
||||
*/
|
||||
private Flux<StsValueHint> getValuesIncremental(IJavaProject javaProject, String query) {
|
||||
// debug("trying to solve "+query+" incrementally");
|
||||
String subquery = query;
|
||||
while (subquery.length()>=1) {
|
||||
subquery = subquery.substring(0, subquery.length()-1);
|
||||
CacheEntry cached = null;
|
||||
try {
|
||||
cached = cache.get(key(javaProject, subquery), () -> null);
|
||||
} catch (ExecutionException | InvalidCacheLoadException e) {
|
||||
// Log.log(e);
|
||||
}
|
||||
if (cached!=null) {
|
||||
System.out.println("cached "+subquery+": "+cached);
|
||||
if (cached.isComplete) {
|
||||
return cached.values
|
||||
// .doOnNext((hint) -> debug("filter["+query+"]: "+hint.getValue()))
|
||||
.filter((hint) -> 0!=FuzzyMatcher.matchScore(query, hint.getValue().toString()));
|
||||
} else {
|
||||
// debug("subquery "+subquery+" cached but is incomplete");
|
||||
}
|
||||
}
|
||||
}
|
||||
// debug("full search for: "+query);
|
||||
return getValuesAsycn(javaProject, query);
|
||||
}
|
||||
|
||||
protected abstract Flux<StsValueHint> getValuesAsycn(IJavaProject javaProject, String query);
|
||||
|
||||
private Tuple2<String,String> key(IJavaProject javaProject, String query) {
|
||||
return Tuples.of(javaProject==null?null:javaProject.getElementName(), query);
|
||||
}
|
||||
|
||||
protected <K,V> Cache<K,V> createCache() {
|
||||
return CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||
}
|
||||
|
||||
public static void restoreDefaults() {
|
||||
TIMEOUT = DEFAULT_TIMEOUT;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.springframework.ide.vscode.application.properties.metadata;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ResourceHintProvider implements ValueProviderStrategy {
|
||||
|
||||
private static String[] CLASSPATH_PREFIXES = {
|
||||
"classpath:",
|
||||
"classpath*:"
|
||||
};
|
||||
|
||||
private static final String[] URL_PREFIXES = new String[] {
|
||||
"classpath:",
|
||||
"classpath*:",
|
||||
"file:",
|
||||
"http://",
|
||||
"https://"
|
||||
};
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
final private ImmutableList<StsValueHint> urlPrefixHints = ImmutableList.copyOf(
|
||||
Arrays.stream(URL_PREFIXES)
|
||||
.map(StsValueHint::create)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
private ClasspathHints classpathHints = new ClasspathHints();
|
||||
|
||||
private static class ClasspathHints extends CachingValueProvider {
|
||||
@Override
|
||||
protected Flux<StsValueHint> getValuesAsycn(IJavaProject javaProject, String query) {
|
||||
return Flux.fromStream(javaProject.getClasspath().getClasspathResources().distinct().map(StsValueHint::create));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -97,6 +97,11 @@ public class PropertyCompletionFactory {
|
||||
return typeUtil.niceTypeName((Type) type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return getBaseDisplayString() + " : " + typeUtil.niceTypeName(getType());
|
||||
}
|
||||
|
||||
};
|
||||
if (property.isDeprecated()) {
|
||||
proposal.deprecate();
|
||||
@@ -157,6 +162,12 @@ public class PropertyCompletionFactory {
|
||||
protected String niceTypeName(YType type) {
|
||||
return typeUtil.niceTypeName(((Type)type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return getBaseDisplayString() + " : " + typeUtil.niceTypeName(getType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.stream.Stream;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.Deprecation;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.ResourceHintProvider;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.util.DeprecationUtil;
|
||||
@@ -577,7 +578,7 @@ public class TypeUtil {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
// valueHints("org.springframework.core.io.Resource", new ResourceHintProvider());
|
||||
valueHints("org.springframework.core.io.Resource", new ResourceHintProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,6 +90,12 @@ public class PropertiesMetadataTestData {
|
||||
public Stream<Path> getClasspathEntries() throws Exception {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getClasspathResources() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
});
|
||||
for (ConfigurationMetadataProperty propertyInfo : datas.values()) {
|
||||
index.add(propertyInfo);
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.jboss.jandex.MethodInfo;
|
||||
import org.jboss.jandex.PrimitiveType;
|
||||
import org.jboss.jandex.Type;
|
||||
import org.jboss.jandex.Type.Kind;
|
||||
import org.springframework.ide.vscode.commons.java.Flags;
|
||||
import org.springframework.ide.vscode.commons.java.IAnnotation;
|
||||
import org.springframework.ide.vscode.commons.java.IField;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaType;
|
||||
@@ -28,8 +29,6 @@ import org.springframework.ide.vscode.commons.util.HtmlSnippet;
|
||||
|
||||
public class Wrappers {
|
||||
|
||||
private static final int AccEnum = 0x4000;
|
||||
|
||||
public static IType wrap(IndexView index, ClassInfo info) {
|
||||
if (info == null) {
|
||||
return null;
|
||||
@@ -75,7 +74,7 @@ public class Wrappers {
|
||||
|
||||
@Override
|
||||
public boolean isEnum() {
|
||||
return (info.flags() & AccEnum) != 0;
|
||||
return Flags.isEnum(info.flags());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,7 +160,7 @@ public class Wrappers {
|
||||
|
||||
@Override
|
||||
public boolean isEnumConstant() {
|
||||
return (field.flags() & AccEnum) != 0;
|
||||
return Flags.isEnum(field.flags());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,4 +30,10 @@ public interface IClasspath {
|
||||
*/
|
||||
Stream<Path> getClasspathEntries() throws Exception;
|
||||
|
||||
/**
|
||||
* Classpath resources paths relative to the source folder path
|
||||
* @return classpath resource relative paths
|
||||
*/
|
||||
Stream<String> getClasspathResources();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package org.springframework.ide.vscode.application.yaml.completions;
|
||||
package org.springframework.ide.vscode.commons.languageserver.completion;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
/**
|
||||
* Temprary placeholder which sort of replaces the LazyProposalApplier from old STS.
|
||||
* It really does nothing right now. Somehow this should be tied into LS protocol so
|
||||
@@ -13,7 +10,7 @@ import org.springframework.ide.vscode.commons.util.Log;
|
||||
* Right now this is not lazy at all and the completion edits are just computed immediatly.
|
||||
*/
|
||||
public class LazyProposalApplier {
|
||||
|
||||
|
||||
public static DocumentEdits from(Callable<DocumentEdits> createEdits) throws Exception {
|
||||
return createEdits.call();
|
||||
}
|
||||
@@ -27,6 +27,8 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
|
||||
|
||||
private final static int MAX_COMPLETIONS = 20;
|
||||
|
||||
private int maxCompletions = MAX_COMPLETIONS;
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger(VscodeCompletionEngineAdapter.class);
|
||||
|
||||
public static final String VS_CODE_CURSOR_MARKER = "{{}}";
|
||||
@@ -39,6 +41,10 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
public void setMaxCompletionsNumber(int maxCompletions) {
|
||||
this.maxCompletions = maxCompletions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CompletionList> getCompletions(TextDocumentPositionParams params) {
|
||||
//TODO: This returns a CompletableFuture which suggests we should try to do expensive work asyncly.
|
||||
@@ -58,7 +64,7 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
|
||||
int count = 0;
|
||||
for (ICompletionProposal c : completions) {
|
||||
count++;
|
||||
if (count>MAX_COMPLETIONS) {
|
||||
if (maxCompletions > 0 && count>maxCompletions) {
|
||||
list.setIsIncomplete(true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.springframework.ide.vscode.commons.util.Log;
|
||||
public class DefaultJavaProjectFinder implements JavaProjectFinder {
|
||||
|
||||
private final IJavaProjectFinderStrategy[] STRATEGIES = new IJavaProjectFinderStrategy[] {
|
||||
new JavaProjectWithClasspathFileFinderStrategy(),
|
||||
new MavenProjectFinderStrategy()
|
||||
new MavenProjectFinderStrategy(),
|
||||
new JavaProjectWithClasspathFileFinderStrategy()
|
||||
};
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,12 +35,14 @@ public class JavaProjectWithClasspathFileFinderStrategy implements IJavaProjectF
|
||||
URI uri = new URI(uriStr);
|
||||
// TODO: This only work with File uri. Should it work with others
|
||||
// too?
|
||||
File file = toFile(uri);
|
||||
File cpFile = FileUtils.findFile(file, MavenCore.CLASSPATH_TXT);
|
||||
if (cpFile != null) {
|
||||
return cache.get(cpFile, () -> {
|
||||
return new JavaProjectWithClasspathFile(cpFile);
|
||||
});
|
||||
if (uri.getScheme().equalsIgnoreCase("file")) {
|
||||
File file = toFile(uri);
|
||||
File cpFile = FileUtils.findFile(file, MavenCore.CLASSPATH_TXT);
|
||||
if (cpFile != null) {
|
||||
return cache.get(cpFile, () -> {
|
||||
return new JavaProjectWithClasspathFile(cpFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -41,12 +41,14 @@ public class MavenProjectFinderStrategy implements IJavaProjectFinderStrategy {
|
||||
URI uri = new URI(uriStr);
|
||||
// TODO: This only work with File uri. Should it work with others
|
||||
// too?
|
||||
File file = new File(uri).getAbsoluteFile();
|
||||
File pomFile = FileUtils.findFile(file, MavenCore.POM_XML);
|
||||
if (pomFile != null) {
|
||||
return cache.get(pomFile, () -> {
|
||||
return new MavenJavaProject(pomFile);
|
||||
});
|
||||
if (uri.getScheme().equalsIgnoreCase("file")) {
|
||||
File file = new File(uri).getAbsoluteFile();
|
||||
File pomFile = FileUtils.findFile(file, MavenCore.POM_XML);
|
||||
if (pomFile != null) {
|
||||
return cache.get(pomFile, () -> {
|
||||
return new MavenJavaProject(pomFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -182,80 +182,66 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Hover> hover(TextDocumentPositionParams position) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SignatureHelp> signatureHelp(TextDocumentPositionParams position) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends Location>> definition(TextDocumentPositionParams position) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends SymbolInformation>> documentSymbol(DocumentSymbolParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends Command>> codeAction(CodeActionParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends CodeLens>> codeLens(CodeLensParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CodeLens> resolveCodeLens(CodeLens unresolved) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends TextEdit>> rangeFormatting(DocumentRangeFormattingParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends TextEdit>> onTypeFormatting(DocumentOnTypeFormattingParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<WorkspaceEdit> rename(RenameParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didSave(DidSaveTextDocumentParams params) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void publishDiagnostics(TextDocument doc, List<Diagnostic> diagnostics) {
|
||||
@@ -274,8 +260,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(TextDocumentPositionParams position) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Futures.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@ package org.springframework.ide.vscode.commons.maven.java;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.codehaus.plexus.util.DirectoryScanner;
|
||||
import org.springframework.ide.vscode.commons.jandex.JandexIndex;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
@@ -70,5 +72,22 @@ public class MavenProjectClasspath implements IClasspath {
|
||||
private File findIndexFile(File jarFile) {
|
||||
return new File(maven.getIndexFolder().toString(), jarFile.getName() + "-" + jarFile.lastModified() + ".jdx");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Stream<String> getClasspathResources() {
|
||||
return project.getBuild().getResources().stream().flatMap(resource -> {
|
||||
DirectoryScanner scanner = new DirectoryScanner();
|
||||
scanner.setBasedir(resource.getDirectory());
|
||||
if (resource.getIncludes() != null && !resource.getIncludes().isEmpty()) {
|
||||
scanner.setIncludes(resource.getIncludes().toArray(new String[resource.getIncludes().size()]));
|
||||
}
|
||||
if (resource.getExcludes() != null && !resource.getExcludes().isEmpty()) {
|
||||
scanner.setExcludes(resource.getExcludes().toArray(new String[resource.getExcludes().size()]));
|
||||
}
|
||||
scanner.setCaseSensitive(false);
|
||||
scanner.scan();
|
||||
return Arrays.stream(scanner.getIncludedFiles());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,4 +37,9 @@ public class FileClasspath implements IClasspath {
|
||||
classpathFilePath.getParent().resolve("target/test-classes")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> getClasspathResources() {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ keyChar
|
||||
;
|
||||
|
||||
separatorAndValue
|
||||
: (Space | Colon | Equals) valueChar+
|
||||
: (Space | Colon | Equals) valueChar*
|
||||
;
|
||||
|
||||
valueChar
|
||||
|
||||
@@ -12,10 +12,12 @@ package org.springframework.ide.vscode.java.properties.antlr.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRErrorListener;
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.ConsoleErrorListener;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
@@ -23,6 +25,7 @@ import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.atn.ATNConfigSet;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.JavaPropertiesParser.CommentLineContext;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.JavaPropertiesParser.EmptyLineContext;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.JavaPropertiesParser.KeyContext;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.JavaPropertiesParser.PropertyLineContext;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.JavaPropertiesParser.SeparatorAndValueContext;
|
||||
@@ -53,6 +56,9 @@ public class AntlrParser implements Parser {
|
||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||
JavaPropertiesParser parser = new JavaPropertiesParser(tokens);
|
||||
|
||||
// To avoid printing parse errors in the console
|
||||
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
|
||||
|
||||
// Add listener to collect various parser errors
|
||||
parser.addErrorListener(new ANTLRErrorListener() {
|
||||
|
||||
@@ -90,7 +96,9 @@ public class AntlrParser implements Parser {
|
||||
|
||||
@Override
|
||||
public void exitPropertyLine(PropertyLineContext ctx) {
|
||||
astNodes.add(new KeyValuePair(ctx, key, value));
|
||||
KeyValuePair pair = new KeyValuePair(ctx, key, value);
|
||||
key.parent = value.parent = pair;
|
||||
astNodes.add(pair);
|
||||
key = null;
|
||||
value = null;
|
||||
}
|
||||
@@ -110,8 +118,13 @@ public class AntlrParser implements Parser {
|
||||
value = new Value(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitEmptyLine(EmptyLineContext ctx) {
|
||||
astNodes.add(new EmptyLine(ctx));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
parser.parse();
|
||||
|
||||
// Collect and return parse results
|
||||
@@ -156,6 +169,9 @@ public class AntlrParser implements Parser {
|
||||
|
||||
private static abstract class Node implements PropertiesAst.Node {
|
||||
|
||||
Node parent;
|
||||
List<Node> children;
|
||||
|
||||
abstract protected ParserRuleContext getContext();
|
||||
|
||||
@Override
|
||||
@@ -167,14 +183,40 @@ public class AntlrParser implements Parser {
|
||||
public int getLength() {
|
||||
return getContext().getStop().getStartIndex() - getOffset() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class Comment implements PropertiesAst.Comment {
|
||||
private static class EmptyLine extends Node implements PropertiesAst.EmptyLine {
|
||||
|
||||
private EmptyLineContext context;
|
||||
|
||||
public EmptyLine(EmptyLineContext context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EmptyLineContext getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Comment extends Node implements PropertiesAst.Comment {
|
||||
|
||||
private CommentLineContext context;
|
||||
|
||||
public Comment(CommentLineContext context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@@ -190,6 +232,11 @@ public class AntlrParser implements Parser {
|
||||
public int getLength() {
|
||||
return context.getStop().getStartIndex() - getOffset() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CommentLineContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -200,9 +247,11 @@ public class AntlrParser implements Parser {
|
||||
private Value value;
|
||||
|
||||
public KeyValuePair(PropertyLineContext context, Key key, Value value) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.children = ImmutableList.of(key, value);
|
||||
}
|
||||
|
||||
protected PropertyLineContext getContext() {
|
||||
@@ -218,6 +267,18 @@ public class AntlrParser implements Parser {
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
// Exclude the line break at the end
|
||||
int length = super.getLength();
|
||||
String text = getContext().getText();
|
||||
if (text.charAt(getContext().getStop().getStartIndex() - getOffset()) == '\n') {
|
||||
length--;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class Key extends Node implements PropertiesAst.Key {
|
||||
@@ -241,6 +302,11 @@ public class AntlrParser implements Parser {
|
||||
return context.getText().replace("\\:", ":").replace("\\=", "=");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyValuePair getParent() {
|
||||
return (KeyValuePair) super.getParent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -257,9 +323,9 @@ public class AntlrParser implements Parser {
|
||||
|
||||
private void init() {
|
||||
// Remove the separator, if it exists
|
||||
value = context.getText().replaceAll("^\\s*[:=]?\\s*", "");
|
||||
value = context.getText().replaceAll("^\\s*[:=]?", "");
|
||||
// Remove all escaped line breaks with trailing spaces
|
||||
decoded = value.replaceAll("\\\\(\r?\n|\r)[ \t\f]*", "");
|
||||
decoded = value.replaceAll("^\\s*", "").replaceAll("\\\\(\r?\n|\r)[ \t\f]*", "");
|
||||
try {
|
||||
decoded = PropertiesFileEscapes.unescape(decoded);
|
||||
} catch (Exception e) {
|
||||
@@ -283,6 +349,10 @@ public class AntlrParser implements Parser {
|
||||
return context.getStart().getStartIndex() + (context.getText().length() - value.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyValuePair getParent() {
|
||||
return (KeyValuePair) super.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -623,20 +623,20 @@ public class JavaPropertiesParser extends Parser {
|
||||
} else {
|
||||
consume();
|
||||
}
|
||||
setState(80);
|
||||
setState(82);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
do {
|
||||
while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << Backslash) | (1L << Colon) | (1L << Equals) | (1L << Exclamation) | (1L << Number) | (1L << Space) | (1L << IdentifierChar))) != 0)) {
|
||||
{
|
||||
{
|
||||
setState(79);
|
||||
valueChar();
|
||||
}
|
||||
}
|
||||
setState(82);
|
||||
setState(84);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
} while ( (((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << Backslash) | (1L << Colon) | (1L << Equals) | (1L << Exclamation) | (1L << Number) | (1L << Space) | (1L << IdentifierChar))) != 0) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RecognitionException re) {
|
||||
@@ -677,56 +677,56 @@ public class JavaPropertiesParser extends Parser {
|
||||
ValueCharContext _localctx = new ValueCharContext(_ctx, getState());
|
||||
enterRule(_localctx, 18, RULE_valueChar);
|
||||
try {
|
||||
setState(92);
|
||||
setState(93);
|
||||
switch (_input.LA(1)) {
|
||||
case IdentifierChar:
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(84);
|
||||
setState(85);
|
||||
match(IdentifierChar);
|
||||
}
|
||||
break;
|
||||
case Exclamation:
|
||||
enterOuterAlt(_localctx, 2);
|
||||
{
|
||||
setState(85);
|
||||
setState(86);
|
||||
match(Exclamation);
|
||||
}
|
||||
break;
|
||||
case Number:
|
||||
enterOuterAlt(_localctx, 3);
|
||||
{
|
||||
setState(86);
|
||||
setState(87);
|
||||
match(Number);
|
||||
}
|
||||
break;
|
||||
case Space:
|
||||
enterOuterAlt(_localctx, 4);
|
||||
{
|
||||
setState(87);
|
||||
setState(88);
|
||||
match(Space);
|
||||
}
|
||||
break;
|
||||
case Backslash:
|
||||
enterOuterAlt(_localctx, 5);
|
||||
{
|
||||
setState(88);
|
||||
match(Backslash);
|
||||
setState(89);
|
||||
match(Backslash);
|
||||
setState(90);
|
||||
match(LineBreak);
|
||||
}
|
||||
break;
|
||||
case Equals:
|
||||
enterOuterAlt(_localctx, 6);
|
||||
{
|
||||
setState(90);
|
||||
setState(91);
|
||||
match(Equals);
|
||||
}
|
||||
break;
|
||||
case Colon:
|
||||
enterOuterAlt(_localctx, 7);
|
||||
{
|
||||
setState(91);
|
||||
setState(92);
|
||||
match(Colon);
|
||||
}
|
||||
break;
|
||||
@@ -746,31 +746,31 @@ public class JavaPropertiesParser extends Parser {
|
||||
}
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\na\4\2\t\2\4\3\t"+
|
||||
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\nb\4\2\t\2\4\3\t"+
|
||||
"\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\3"+
|
||||
"\2\7\2\30\n\2\f\2\16\2\33\13\2\3\2\3\2\3\3\3\3\3\3\5\3\"\n\3\3\4\7\4%"+
|
||||
"\n\4\f\4\16\4(\13\4\3\4\3\4\3\5\7\5-\n\5\f\5\16\5\60\13\5\3\5\3\5\7\5"+
|
||||
"\64\n\5\f\5\16\5\67\13\5\3\5\3\5\3\6\7\6<\n\6\f\6\16\6?\13\6\3\6\3\6\3"+
|
||||
"\7\3\7\3\7\3\7\3\b\6\bH\n\b\r\b\16\bI\3\t\3\t\3\t\5\tO\n\t\3\n\3\n\6\n"+
|
||||
"S\n\n\r\n\16\nT\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\13\5\13_\n\13\3\13"+
|
||||
"\2\2\f\2\4\6\b\n\f\16\20\22\24\2\7\3\2\6\7\3\2\b\b\3\3\b\b\3\2\4\5\4\2"+
|
||||
"\4\5\t\tf\2\31\3\2\2\2\4!\3\2\2\2\6&\3\2\2\2\b.\3\2\2\2\n=\3\2\2\2\fB"+
|
||||
"\3\2\2\2\16G\3\2\2\2\20N\3\2\2\2\22P\3\2\2\2\24^\3\2\2\2\26\30\5\4\3\2"+
|
||||
"\27\26\3\2\2\2\30\33\3\2\2\2\31\27\3\2\2\2\31\32\3\2\2\2\32\34\3\2\2\2"+
|
||||
"\33\31\3\2\2\2\34\35\7\2\2\3\35\3\3\2\2\2\36\"\5\6\4\2\37\"\5\b\5\2 \""+
|
||||
"\5\n\6\2!\36\3\2\2\2!\37\3\2\2\2! \3\2\2\2\"\5\3\2\2\2#%\7\t\2\2$#\3\2"+
|
||||
"\2\2%(\3\2\2\2&$\3\2\2\2&\'\3\2\2\2\')\3\2\2\2(&\3\2\2\2)*\5\f\7\2*\7"+
|
||||
"\3\2\2\2+-\7\t\2\2,+\3\2\2\2-\60\3\2\2\2.,\3\2\2\2./\3\2\2\2/\61\3\2\2"+
|
||||
"\2\60.\3\2\2\2\61\65\t\2\2\2\62\64\n\3\2\2\63\62\3\2\2\2\64\67\3\2\2\2"+
|
||||
"\65\63\3\2\2\2\65\66\3\2\2\2\668\3\2\2\2\67\65\3\2\2\289\t\4\2\29\t\3"+
|
||||
"\2\2\2:<\7\t\2\2;:\3\2\2\2<?\3\2\2\2=;\3\2\2\2=>\3\2\2\2>@\3\2\2\2?=\3"+
|
||||
"\2\2\2@A\7\b\2\2A\13\3\2\2\2BC\5\16\b\2CD\5\22\n\2DE\t\4\2\2E\r\3\2\2"+
|
||||
"\2FH\5\20\t\2GF\3\2\2\2HI\3\2\2\2IG\3\2\2\2IJ\3\2\2\2J\17\3\2\2\2KO\7"+
|
||||
"\n\2\2LM\7\3\2\2MO\t\5\2\2NK\3\2\2\2NL\3\2\2\2O\21\3\2\2\2PR\t\6\2\2Q"+
|
||||
"S\5\24\13\2RQ\3\2\2\2ST\3\2\2\2TR\3\2\2\2TU\3\2\2\2U\23\3\2\2\2V_\7\n"+
|
||||
"\2\2W_\7\6\2\2X_\7\7\2\2Y_\7\t\2\2Z[\7\3\2\2[_\7\b\2\2\\_\7\5\2\2]_\7"+
|
||||
"\4\2\2^V\3\2\2\2^W\3\2\2\2^X\3\2\2\2^Y\3\2\2\2^Z\3\2\2\2^\\\3\2\2\2^]"+
|
||||
"\3\2\2\2_\25\3\2\2\2\f\31!&.\65=INT^";
|
||||
"\7\3\7\3\7\3\7\3\b\6\bH\n\b\r\b\16\bI\3\t\3\t\3\t\5\tO\n\t\3\n\3\n\7\n"+
|
||||
"S\n\n\f\n\16\nV\13\n\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\13\5\13`\n\13"+
|
||||
"\3\13\2\2\f\2\4\6\b\n\f\16\20\22\24\2\7\3\2\6\7\3\2\b\b\3\3\b\b\3\2\4"+
|
||||
"\5\4\2\4\5\t\tg\2\31\3\2\2\2\4!\3\2\2\2\6&\3\2\2\2\b.\3\2\2\2\n=\3\2\2"+
|
||||
"\2\fB\3\2\2\2\16G\3\2\2\2\20N\3\2\2\2\22P\3\2\2\2\24_\3\2\2\2\26\30\5"+
|
||||
"\4\3\2\27\26\3\2\2\2\30\33\3\2\2\2\31\27\3\2\2\2\31\32\3\2\2\2\32\34\3"+
|
||||
"\2\2\2\33\31\3\2\2\2\34\35\7\2\2\3\35\3\3\2\2\2\36\"\5\6\4\2\37\"\5\b"+
|
||||
"\5\2 \"\5\n\6\2!\36\3\2\2\2!\37\3\2\2\2! \3\2\2\2\"\5\3\2\2\2#%\7\t\2"+
|
||||
"\2$#\3\2\2\2%(\3\2\2\2&$\3\2\2\2&\'\3\2\2\2\')\3\2\2\2(&\3\2\2\2)*\5\f"+
|
||||
"\7\2*\7\3\2\2\2+-\7\t\2\2,+\3\2\2\2-\60\3\2\2\2.,\3\2\2\2./\3\2\2\2/\61"+
|
||||
"\3\2\2\2\60.\3\2\2\2\61\65\t\2\2\2\62\64\n\3\2\2\63\62\3\2\2\2\64\67\3"+
|
||||
"\2\2\2\65\63\3\2\2\2\65\66\3\2\2\2\668\3\2\2\2\67\65\3\2\2\289\t\4\2\2"+
|
||||
"9\t\3\2\2\2:<\7\t\2\2;:\3\2\2\2<?\3\2\2\2=;\3\2\2\2=>\3\2\2\2>@\3\2\2"+
|
||||
"\2?=\3\2\2\2@A\7\b\2\2A\13\3\2\2\2BC\5\16\b\2CD\5\22\n\2DE\t\4\2\2E\r"+
|
||||
"\3\2\2\2FH\5\20\t\2GF\3\2\2\2HI\3\2\2\2IG\3\2\2\2IJ\3\2\2\2J\17\3\2\2"+
|
||||
"\2KO\7\n\2\2LM\7\3\2\2MO\t\5\2\2NK\3\2\2\2NL\3\2\2\2O\21\3\2\2\2PT\t\6"+
|
||||
"\2\2QS\5\24\13\2RQ\3\2\2\2SV\3\2\2\2TR\3\2\2\2TU\3\2\2\2U\23\3\2\2\2V"+
|
||||
"T\3\2\2\2W`\7\n\2\2X`\7\6\2\2Y`\7\7\2\2Z`\7\t\2\2[\\\7\3\2\2\\`\7\b\2"+
|
||||
"\2]`\7\5\2\2^`\7\4\2\2_W\3\2\2\2_X\3\2\2\2_Y\3\2\2\2_Z\3\2\2\2_[\3\2\2"+
|
||||
"\2_]\3\2\2\2_^\3\2\2\2`\25\3\2\2\2\f\31!&.\65=INT_";
|
||||
public static final ATN _ATN =
|
||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
||||
static {
|
||||
|
||||
@@ -31,7 +31,7 @@ public final class PropertiesAst {
|
||||
* Retrieves all AST nodes
|
||||
* @return List of AST nodes sorted by line number
|
||||
*/
|
||||
public List<? extends Node> getAllNodes() {
|
||||
public List<Node> getAllNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,52 @@ public final class PropertiesAst {
|
||||
return (List<T>) l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find node in AST corresponding to offset position
|
||||
* @param offset Position in the text
|
||||
* @return AST node corresponding to the offset position
|
||||
*/
|
||||
public Node findNode(int offset) {
|
||||
return findNode(nodes, offset, 0, nodes.size() - 1);
|
||||
}
|
||||
|
||||
private Node findNode(List<? extends Node> nodes, int offset, int start, int end) {
|
||||
if (nodes == null) {
|
||||
return null;
|
||||
}
|
||||
if (start == end) {
|
||||
Node node = nodes.get(start);
|
||||
if (node.getOffset() <= offset && offset <= node.getOffset() + node.getLength()) {
|
||||
Node found = findChildNode(node, offset);
|
||||
return found == null ? node : found;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (start < end ) {
|
||||
int pivotIndex = (start + end) / 2;
|
||||
Node node = nodes.get(pivotIndex);
|
||||
if (node.getOffset() > offset) {
|
||||
return findNode(nodes, offset, start, pivotIndex - 1);
|
||||
} else if (offset > node.getOffset() + node.getLength()) {
|
||||
return findNode(nodes, offset, pivotIndex + 1, end);
|
||||
} else {
|
||||
Node found = findChildNode(node, offset);
|
||||
return found == null ? node : found;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Node findChildNode(Node node, int offset) {
|
||||
if (node.getChildren() == null) {
|
||||
return null;
|
||||
} else {
|
||||
return findNode(node.getChildren(), offset, 0, node.getChildren().size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Java Properties AST node
|
||||
*/
|
||||
@@ -65,6 +111,18 @@ public final class PropertiesAst {
|
||||
*/
|
||||
int getLength();
|
||||
|
||||
/**
|
||||
* Node's parent
|
||||
* @return parent node
|
||||
*/
|
||||
Node getParent();
|
||||
|
||||
/**
|
||||
* Node's children
|
||||
* @return children nodes
|
||||
*/
|
||||
List<? extends Node> getChildren();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,6 +130,13 @@ public final class PropertiesAst {
|
||||
*/
|
||||
public interface Comment extends Node {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AST node for empty line
|
||||
*/
|
||||
public interface EmptyLine extends Node {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,6 +168,8 @@ public final class PropertiesAst {
|
||||
* @return Decoded property name
|
||||
*/
|
||||
String decode();
|
||||
|
||||
KeyValuePair getParent();
|
||||
|
||||
}
|
||||
|
||||
@@ -117,6 +184,7 @@ public final class PropertiesAst {
|
||||
*/
|
||||
String decode();
|
||||
|
||||
KeyValuePair getParent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -83,34 +83,6 @@ public class PropertiesAntlrParserTest {
|
||||
testCommentLine(" # This is comment = ", "# This is comment = ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLines1() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n\n \t \t \n\t\t\n");
|
||||
assertTrue(results.syntaxErrors.isEmpty());
|
||||
assertTrue(results.problems.isEmpty());
|
||||
assertEquals(1, results.ast.getAllNodes().size());
|
||||
assertEquals(1, results.ast.getNodes(Comment.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLines2() throws Exception {
|
||||
ParseResults results = parser.parse("\n\n \t \t \n# Comment\n\t\t\n");
|
||||
assertTrue(results.syntaxErrors.isEmpty());
|
||||
assertTrue(results.problems.isEmpty());
|
||||
assertEquals(1, results.ast.getAllNodes().size());
|
||||
assertEquals(1, results.ast.getNodes(Comment.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLines3() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n\nkey = value 1 \n \t \t \n\t\t\n");
|
||||
assertTrue(results.syntaxErrors.isEmpty());
|
||||
assertTrue(results.problems.isEmpty());
|
||||
assertEquals(2, results.ast.getAllNodes().size());
|
||||
assertEquals(1, results.ast.getNodes(Comment.class).size());
|
||||
assertEquals(1, results.ast.getNodes(KeyValuePair.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyWithEqualsSeparator() throws Exception {
|
||||
testPropertyLine("key=value", "key", "key", "value", "value");
|
||||
@@ -118,7 +90,7 @@ public class PropertiesAntlrParserTest {
|
||||
|
||||
@Test
|
||||
public void testPropertyWithEqualsSeparatorAndSpaces() throws Exception {
|
||||
testPropertyLine("key \t = \t \tvalue", "key", "key", "value", "value");
|
||||
testPropertyLine("key \t = \t \tvalue", "key", "key", "value", " \t \tvalue");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -128,7 +100,7 @@ public class PropertiesAntlrParserTest {
|
||||
|
||||
@Test
|
||||
public void testPropertyWithColonSeparatorAndSpaces() throws Exception {
|
||||
testPropertyLine("key \t : \t \tvalue", "key", "key", "value", "value");
|
||||
testPropertyLine("key \t : \t \tvalue", "key", "key", "value", " \t \tvalue");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -168,17 +140,17 @@ public class PropertiesAntlrParserTest {
|
||||
|
||||
@Test
|
||||
public void testEqualsValueSeparatedWithEqualsAndSpace() throws Exception {
|
||||
testPropertyLine("key7 = =", "key7", "key7", "=", "=");
|
||||
testPropertyLine("key7 = =", "key7", "key7", "=", " =");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValueWithTrailingSpaces() throws Exception {
|
||||
testPropertyLine("key = value 1 ", "key", "key", "value 1 ", "value 1 ");
|
||||
testPropertyLine("key = value 1 ", "key", "key", "value 1 ", " value 1 ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnodeCharKeyAndValue() throws Exception {
|
||||
testPropertyLine("k\u2b22ey\u2b28 = val\u2b24ue 1\u2b24 ", "k\u2b22ey\u2b28", "k\u2b22ey\u2b28", "val\u2b24ue 1\u2b24 ", "val\u2b24ue 1\u2b24 ");
|
||||
testPropertyLine("k\u2b22ey\u2b28 = val\u2b24ue 1\u2b24 ", "k\u2b22ey\u2b28", "k\u2b22ey\u2b28", "val\u2b24ue 1\u2b24 ", " val\u2b24ue 1\u2b24 ");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package org.springframework.ide.vscode.java.properties.parser.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.AntlrParser;
|
||||
import org.springframework.ide.vscode.java.properties.parser.ParseResults;
|
||||
import org.springframework.ide.vscode.java.properties.parser.Parser;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.Comment;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.EmptyLine;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.Key;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.KeyValuePair;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.Node;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.Value;
|
||||
|
||||
public class PropertiesAstTest {
|
||||
|
||||
Parser parser = new AntlrParser();
|
||||
|
||||
@Test
|
||||
public void testLines1() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n\n \t \t \n\t\t\n");
|
||||
assertTrue(results.syntaxErrors.isEmpty());
|
||||
assertTrue(results.problems.isEmpty());
|
||||
assertEquals(4, results.ast.getAllNodes().size());
|
||||
assertEquals(1, results.ast.getNodes(Comment.class).size());
|
||||
assertEquals(3, results.ast.getNodes(EmptyLine.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLines2() throws Exception {
|
||||
ParseResults results = parser.parse("\n\n \t \t \n# Comment\n\t\t\n");
|
||||
assertTrue(results.syntaxErrors.isEmpty());
|
||||
assertTrue(results.problems.isEmpty());
|
||||
assertEquals(5, results.ast.getAllNodes().size());
|
||||
assertEquals(1, results.ast.getNodes(Comment.class).size());
|
||||
assertEquals(4, results.ast.getNodes(EmptyLine.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLines3() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n\nkey = value 1 \n \t \t \n\t\t\n");
|
||||
assertTrue(results.syntaxErrors.isEmpty());
|
||||
assertTrue(results.problems.isEmpty());
|
||||
assertEquals(5, results.ast.getAllNodes().size());
|
||||
assertEquals(1, results.ast.getNodes(Comment.class).size());
|
||||
assertEquals(1, results.ast.getNodes(KeyValuePair.class).size());
|
||||
assertEquals(3, results.ast.getNodes(EmptyLine.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLines4() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment-1\n\nkey = value 1 \n# Comment-2");
|
||||
assertEquals(4, results.ast.getAllNodes().size());
|
||||
assertEquals(2, results.ast.getNodes(Comment.class).size());
|
||||
assertEquals(1, results.ast.getNodes(KeyValuePair.class).size());
|
||||
assertEquals(1, results.ast.getNodes(EmptyLine.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLines5() throws Exception {
|
||||
ParseResults results = parser.parse("#comment\nliquibase.enabled=\n#comment");
|
||||
assertEquals(3, results.ast.getAllNodes().size());
|
||||
assertEquals(2, results.ast.getNodes(Comment.class).size());
|
||||
assertEquals(1, results.ast.getNodes(KeyValuePair.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionComment() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n" + "key = value\n");
|
||||
|
||||
Node node = results.ast.findNode(7);
|
||||
assertTrue(node instanceof Comment);
|
||||
assertTrue(node.getOffset() <= 7 && 7 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(9);
|
||||
assertTrue(node instanceof Comment);
|
||||
assertTrue(node.getOffset() <= 9 && 9 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(0);
|
||||
assertTrue(node instanceof Comment);
|
||||
assertTrue(node.getOffset() <= 0 && 0 <= node.getOffset() + node.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionEmptyLine() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n" + "key = value\n" + "\t\n");
|
||||
Node node = results.ast.findNode(23);
|
||||
assertTrue(node instanceof EmptyLine);
|
||||
assertTrue(node.getOffset() <= 23 && 23 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(24);
|
||||
assertTrue(node instanceof EmptyLine);
|
||||
assertTrue(node.getOffset() <= 24 && 24 <= node.getOffset() + node.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionKey() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n" + "key = value\n" + "\t\n");
|
||||
Node node = results.ast.findNode(10);
|
||||
assertTrue(node instanceof Key);
|
||||
assertTrue(node.getOffset() <= 10 && 10 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(12);
|
||||
assertTrue(node instanceof Key);
|
||||
assertTrue(node.getOffset() <= 12 && 12 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(13);
|
||||
assertTrue(node instanceof Key);
|
||||
assertTrue(node.getOffset() <= 13 && 13 <= node.getOffset() + node.getLength());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionPair() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n" + "key = value\n" + "\t\n");
|
||||
Node node = results.ast.findNode(15);
|
||||
assertTrue(node instanceof KeyValuePair);
|
||||
assertTrue(node.getOffset() <= 15 && 15 <= node.getOffset() + node.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionValue() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n" + "key = value\n");
|
||||
Node node = results.ast.findNode(17);
|
||||
assertTrue(node instanceof Value);
|
||||
assertTrue(node.getOffset() <= 17 && 17 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(22);
|
||||
assertTrue(node instanceof Value);
|
||||
assertTrue(node.getOffset() <= 22 && 22 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(16);
|
||||
assertTrue(node instanceof Value);
|
||||
assertTrue(node.getOffset() <= 16 && 16 <= node.getOffset() + node.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionValueEofAtEnd() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n" + "key = value");
|
||||
Node node = results.ast.findNode(22);
|
||||
assertTrue(node instanceof Value);
|
||||
assertTrue(node.getOffset() <= 22 && 22 <= node.getOffset() + node.getLength());
|
||||
|
||||
node = results.ast.findNode(16);
|
||||
assertTrue(node instanceof Value);
|
||||
assertTrue(node.getOffset() <= 16 && 16 <= node.getOffset() + node.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionEmptyValue() throws Exception {
|
||||
ParseResults results = parser.parse("# Comment\n" + "key =");
|
||||
Node node = results.ast.findNode(16);
|
||||
assertTrue(node instanceof Value);
|
||||
assertTrue(node.getOffset() <= 16 && 16 <= node.getOffset() + node.getLength());
|
||||
}
|
||||
|
||||
}
|
||||
31
vscode-extensions/commons/project-test-harness/.classpath
Normal file
31
vscode-extensions/commons/project-test-harness/.classpath
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="test-projects">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
@@ -1,5 +1,5 @@
|
||||
/.classpath
|
||||
/.project
|
||||
**/.classpath
|
||||
**/classpath.txt
|
||||
**/.project
|
||||
**/.settings/*.*
|
||||
|
||||
@@ -37,4 +37,11 @@
|
||||
<version>${junit-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>test-projects</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
@@ -10,10 +10,21 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.project.harness;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
@@ -33,7 +44,7 @@ public class ProjectsHarness {
|
||||
|
||||
public static final ProjectsHarness INSTANCE = new ProjectsHarness();;
|
||||
|
||||
public Cache<String, IJavaProject> cache = CacheBuilder.newBuilder().build();
|
||||
public Cache<String, IJavaProject> cache = CacheBuilder.newBuilder().concurrencyLevel(1).build();
|
||||
|
||||
private enum ProjectType {
|
||||
MAVEN,
|
||||
@@ -59,10 +70,94 @@ public class ProjectsHarness {
|
||||
});
|
||||
}
|
||||
|
||||
protected Path getProjectPath(String name) throws URISyntaxException {
|
||||
URL sourceLocation = ProjectsHarness.class.getProtectionDomain().getCodeSource().getLocation();
|
||||
protected Path getProjectPath(String name) throws URISyntaxException, IOException {
|
||||
URI sourceLocation = ProjectsHarness.class.getProtectionDomain().getCodeSource().getLocation().toURI();
|
||||
// file:/Users/aboyko/git/sts4/vscode-extensions/commons/project-test-harness/target/project-test-harness-0.0.1-SNAPSHOT.jar
|
||||
return Paths.get(sourceLocation.toURI()).getParent().getParent().resolve("test-projects").resolve(name);
|
||||
Path testProjectsPath = Paths.get(sourceLocation).getParent().getParent().resolve("test-projects").resolve(name);
|
||||
if (Files.exists(testProjectsPath)) {
|
||||
return testProjectsPath;
|
||||
} else {
|
||||
/*
|
||||
* If "test-projects" folder is not found then extract test project
|
||||
* from the jar's "test-projects" folder and copy it in the temp
|
||||
* folder
|
||||
*/
|
||||
return getProjectPathFromClasspath(name);
|
||||
}
|
||||
}
|
||||
|
||||
private Path getProjectPathFromClasspath(String name) throws URISyntaxException, IOException {
|
||||
URI resource = ProjectsHarness.class.getResource("/" + name).toURI();
|
||||
if (resource.getScheme().equalsIgnoreCase("jar")) {
|
||||
return getProjectPathFromJar(resource);
|
||||
} else {
|
||||
return Paths.get(resource);
|
||||
}
|
||||
}
|
||||
|
||||
private Path getProjectPathFromJar(URI jar) throws IOException {
|
||||
final String[] array = jar.toString().split("!");
|
||||
URI firstHalf = URI.create(array[0]);
|
||||
Path tempFolderPath = Paths.get(new File(System.getProperty(MavenCore.JAVA_IO_TMPDIR)).toURI());
|
||||
FileSystem fs = FileSystems.newFileSystem(firstHalf, Collections.emptyMap());
|
||||
try {
|
||||
Path path = fs.getPath(array[1]);
|
||||
Path projectCopyPath = tempFolderPath.resolve(path.getFileName().toString());
|
||||
if (Files.exists(projectCopyPath)) {
|
||||
recursiveDelete(projectCopyPath);
|
||||
}
|
||||
recursiveCopy(path, tempFolderPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
System.out.println("Copied test project to: " + projectCopyPath);
|
||||
return projectCopyPath;
|
||||
} finally {
|
||||
fs.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void recursiveCopy(Path source, Path target, CopyOption... options) throws IOException {
|
||||
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
|
||||
|
||||
Path destination = target;
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
destination = destination.resolve(dir.getFileName().toString());
|
||||
Files.copy(dir, destination, options);
|
||||
return super.preVisitDirectory(dir, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Path newFile = destination.resolve(file.getFileName().toString());
|
||||
Files.copy(file, newFile, options);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
destination = destination.getParent();
|
||||
return super.postVisitDirectory(dir, exc);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static void recursiveDelete(Path path) throws IOException {
|
||||
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
Files.delete(dir);
|
||||
return super.postVisitDirectory(dir, exc);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public MavenJavaProject mavenProject(String name) throws Exception {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
@@ -49,7 +49,8 @@ export function activate(context: VSCode.ExtensionContext) {
|
||||
// TODO: Remove <any> cast ones https://github.com/Microsoft/vscode-languageserver-node/issues/9 is resolved
|
||||
documentSelector: [
|
||||
<any> {language: 'ini', pattern: '**/application*.properties'},
|
||||
<any> {language: 'java-properties', pattern: '**/application*.properties'}
|
||||
<any> {language: 'java-properties', pattern: '**/application*.properties'},
|
||||
<any> {language: 'properties', pattern: '**/application*.properties'}
|
||||
],
|
||||
synchronize: {
|
||||
// Synchronize the setting section to the server:
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:ini",
|
||||
"onLanguage:java-properties"
|
||||
"onLanguage:java-properties",
|
||||
"onLanguage:properties"
|
||||
],
|
||||
"main": "./out/lib/Main",
|
||||
"files": [
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.application.properties;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionOptions;
|
||||
import org.eclipse.lsp4j.ServerCapabilities;
|
||||
import org.eclipse.lsp4j.TextDocumentSyncKind;
|
||||
import org.springframework.ide.vscode.application.properties.completions.SpringPropertiesCompletionEngine;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider;
|
||||
import org.springframework.ide.vscode.application.properties.reconcile.SpringPropertiesReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.TextDocument;
|
||||
@@ -30,18 +33,39 @@ public class ApplicationPropertiesLanguageServer extends SimpleLanguageServer {
|
||||
|
||||
private SpringPropertyIndexProvider indexProvider;
|
||||
private TypeUtilProvider typeUtilProvider;
|
||||
private VscodeCompletionEngineAdapter completionEngine;
|
||||
private SpringPropertiesReconcileEngine reconcileEngine;
|
||||
|
||||
|
||||
public ApplicationPropertiesLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider) {
|
||||
public ApplicationPropertiesLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder javaProjectFinder) {
|
||||
this.indexProvider = indexProvider;
|
||||
this.typeUtilProvider = typeUtilProvider;
|
||||
SimpleTextDocumentService documents = getTextDocumentService();
|
||||
|
||||
IReconcileEngine reconcileEngine = getReconcileEngine();
|
||||
reconcileEngine = getReconcileEngine();
|
||||
documents.onDidChangeContent(params -> {
|
||||
TextDocument doc = params.getDocument();
|
||||
validateWith(doc, reconcileEngine);
|
||||
});
|
||||
|
||||
SpringPropertiesCompletionEngine propertiesCompletionEngine = new SpringPropertiesCompletionEngine(
|
||||
indexProvider,
|
||||
typeUtilProvider,
|
||||
javaProjectFinder
|
||||
);
|
||||
completionEngine = new VscodeCompletionEngineAdapter(this, propertiesCompletionEngine);
|
||||
completionEngine.setMaxCompletionsNumber(-1);
|
||||
documents.onCompletion(completionEngine::getCompletions);
|
||||
documents.onCompletionResolve(completionEngine::resolveCompletion);
|
||||
|
||||
}
|
||||
|
||||
public void setMaxCompletionsNumber(int number) {
|
||||
completionEngine.setMaxCompletionsNumber(number);
|
||||
}
|
||||
|
||||
public void setRecordSyntaxErrors(boolean record) {
|
||||
reconcileEngine.setRecordSyntaxErrors(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,10 +74,14 @@ public class ApplicationPropertiesLanguageServer extends SimpleLanguageServer {
|
||||
|
||||
c.setTextDocumentSync(TextDocumentSyncKind.Full);
|
||||
|
||||
CompletionOptions completionProvider = new CompletionOptions();
|
||||
completionProvider.setResolveProvider(false);
|
||||
c.setCompletionProvider(completionProvider);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
protected IReconcileEngine getReconcileEngine() {
|
||||
protected SpringPropertiesReconcileEngine getReconcileEngine() {
|
||||
return new SpringPropertiesReconcileEngine(indexProvider, typeUtilProvider);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
package org.springframework.ide.vscode.application.properties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.DefaultSpringPropertyIndexProvider;
|
||||
@@ -36,7 +35,7 @@ public class Main {
|
||||
JavaProjectFinder javaProjectFinder = JavaProjectFinder.DEFAULT;
|
||||
SpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder);
|
||||
TypeUtilProvider typeUtilProvider = (IDocument doc) -> new TypeUtil(javaProjectFinder.find(doc));
|
||||
LanguageServer server = new ApplicationPropertiesLanguageServer(indexProvider, typeUtilProvider);
|
||||
LanguageServer server = new ApplicationPropertiesLanguageServer(indexProvider, typeUtilProvider, javaProjectFinder);
|
||||
return server;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,582 @@
|
||||
package org.springframework.ide.vscode.application.properties.completions;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.util.StringUtil.camelCaseToHyphens;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.ide.vscode.application.properties.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.completions.PropertyCompletionFactory;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.hints.HintProvider;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.hints.HintProviders;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.hints.ValueHintHoverInfo;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.Type;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.TypeParser;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil.BeanPropertyNameMode;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil.EnumCaseMode;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.TypedProperty;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap.Match;
|
||||
import org.springframework.ide.vscode.application.properties.reconcile.PropertyNavigator;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.LazyProposalApplier;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.IDocument;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.AntlrParser;
|
||||
import org.springframework.ide.vscode.java.properties.parser.ParseResults;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.EmptyLine;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.Key;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.Node;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.Value;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
class PropertiesCompletionProposalsCalculator {
|
||||
|
||||
private static final Pattern SPACES = Pattern.compile(
|
||||
"(\\s|\\\\\\s)*"
|
||||
);
|
||||
|
||||
private static boolean isValuePrefixChar(char c) {
|
||||
return !Character.isWhitespace(c) && c!=',';
|
||||
}
|
||||
|
||||
private static final PrefixFinder valuePrefixFinder = new PrefixFinder() {
|
||||
protected boolean isPrefixChar(char c) {
|
||||
return isValuePrefixChar(c);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static final PrefixFinder fuzzySearchPrefix = new PrefixFinder() {
|
||||
protected boolean isPrefixChar(char c) {
|
||||
return !Character.isWhitespace(c);
|
||||
}
|
||||
};
|
||||
|
||||
private static final PrefixFinder navigationPrefixFinder = new PrefixFinder() {
|
||||
public String getPrefix(IDocument doc, int offset) {
|
||||
String prefix = super.getPrefix(doc, offset);
|
||||
//Check if character before looks like 'navigation'.. otherwise don't
|
||||
// return a navigationPrefix.
|
||||
char charBefore = getCharBefore(doc, prefix, offset);
|
||||
if (charBefore=='.' || charBefore==']') {
|
||||
return prefix;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private char getCharBefore(IDocument doc, String prefix, int offset) {
|
||||
try {
|
||||
if (prefix!=null) {
|
||||
int offsetBefore = offset-prefix.length()-1;
|
||||
if (offsetBefore>=0) {
|
||||
return doc.getChar(offsetBefore);
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
//ignore
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
protected boolean isPrefixChar(char c) {
|
||||
return !Character.isWhitespace(c) && c!=']' && c!=']' && c!='.';
|
||||
}
|
||||
};
|
||||
|
||||
private FuzzyMap<PropertyInfo> index;
|
||||
private TypeUtil typeUtil;
|
||||
private PropertyCompletionFactory completionFactory;
|
||||
private IDocument doc;
|
||||
private int offset;
|
||||
private boolean preferLowerCaseEnums;
|
||||
private AntlrParser parser;
|
||||
|
||||
PropertiesCompletionProposalsCalculator(FuzzyMap<PropertyInfo> index, TypeUtil typeUtil, PropertyCompletionFactory completionFactory, IDocument doc, int offset, boolean preferLowerCaseEnums) {
|
||||
this.index = index;
|
||||
this.typeUtil = typeUtil;
|
||||
this.completionFactory = completionFactory;
|
||||
this.doc = doc;
|
||||
this.offset = offset;
|
||||
this.preferLowerCaseEnums = preferLowerCaseEnums;
|
||||
this.parser = new AntlrParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create completions proposals in the context of a properties text editor.
|
||||
*/
|
||||
public Collection<ICompletionProposal> calculate() throws BadLocationException {
|
||||
ParseResults parseResults = parser.parse(doc.get());
|
||||
Node node = parseResults.ast.findNode(offset);
|
||||
if (node instanceof Value) {
|
||||
return getValueCompletions((Value)node);
|
||||
} else if (node instanceof Key || node instanceof EmptyLine || node == null) {
|
||||
return getPropertyCompletions();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private Collection<ICompletionProposal> getNavigationProposals() {
|
||||
String navPrefix = navigationPrefixFinder.getPrefix(doc, offset);
|
||||
try {
|
||||
if (navPrefix!=null) {
|
||||
int navOffset = offset-navPrefix.length()-1; //offset of 'nav' operator char (i.e. '.' or ']').
|
||||
navPrefix = fuzzySearchPrefix.getPrefix(doc, navOffset);
|
||||
if (navPrefix!=null && !navPrefix.isEmpty()) {
|
||||
PropertyInfo prop = findLongestValidProperty(index, navPrefix);
|
||||
if (prop!=null) {
|
||||
int regionStart = navOffset-navPrefix.length();
|
||||
Collection<ICompletionProposal> hintProposals = getKeyHintProposals(prop, navOffset);
|
||||
if (CollectionUtil.hasElements(hintProposals)) {
|
||||
return hintProposals;
|
||||
}
|
||||
PropertyNavigator navigator = new PropertyNavigator(doc, null, typeUtil, new DocumentRegion(doc, regionStart, navOffset));
|
||||
Type type = navigator.navigate(regionStart+prop.getId().length(), TypeParser.parse(prop.getType()));
|
||||
if (type!=null) {
|
||||
return getNavigationProposals(type, navOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private Collection<ICompletionProposal> getKeyHintProposals(PropertyInfo prop, int navOffset) {
|
||||
HintProvider hintProvider = prop.getHints(typeUtil, false);
|
||||
if (!HintProviders.isNull(hintProvider)) {
|
||||
String query = textBetween(doc, navOffset+1, offset);
|
||||
List<TypedProperty> hintProperties = hintProvider.getPropertyHints(query);
|
||||
if (CollectionUtil.hasElements(hintProperties)) {
|
||||
return createPropertyProposals(TypeParser.parse(prop.getType()), navOffset, query, hintProperties);
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private String textBetween(IDocument doc, int start, int end) {
|
||||
if (end > doc.getLength()) {
|
||||
end = doc.getLength();
|
||||
}
|
||||
if (start>doc.getLength()) {
|
||||
start = doc.getLength();
|
||||
}
|
||||
if (start<0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
}
|
||||
if (start<end) {
|
||||
try {
|
||||
return doc.get(start, end-start);
|
||||
} catch (BadLocationException e) {
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type Type of the expression leading upto the 'nav' operator
|
||||
* @param navOffset Offset of the nav operator (either ']' or '.'
|
||||
* @param offset Offset of the cursor where CA was requested.
|
||||
*/
|
||||
private Collection<ICompletionProposal> getNavigationProposals(Type type, int navOffset) {
|
||||
try {
|
||||
char navOp = doc.getChar(navOffset);
|
||||
if (navOp=='.') {
|
||||
String prefix = doc.get(navOffset+1, offset-(navOffset+1));
|
||||
EnumCaseMode caseMode = caseMode(prefix);
|
||||
List<TypedProperty> objectProperties = typeUtil.getProperties(type, caseMode, BeanPropertyNameMode.HYPHENATED);
|
||||
//Note: properties editor itself deals with relaxed names. So it expects the properties here to be returned in hyphenated form only.
|
||||
if (objectProperties!=null && !objectProperties.isEmpty()) {
|
||||
return createPropertyProposals(type, navOffset, prefix, objectProperties);
|
||||
}
|
||||
} else {
|
||||
//TODO: other cases ']' or '[' ?
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected Collection<ICompletionProposal> createPropertyProposals(Type type, int navOffset,
|
||||
String prefix, List<TypedProperty> objectProperties) {
|
||||
ArrayList<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
|
||||
for (TypedProperty prop : objectProperties) {
|
||||
double score = FuzzyMatcher.matchScore(prefix, prop.getName());
|
||||
if (score!=0) {
|
||||
Type valueType = prop.getType();
|
||||
String postFix = propertyCompletionPostfix(typeUtil, valueType);
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.delete(navOffset+1, offset);
|
||||
edits.insert(offset, prop.getName()+postFix);
|
||||
proposals.add(
|
||||
completionFactory.beanProperty(doc, null, type, prefix, prop, score, edits, typeUtil)
|
||||
);
|
||||
}
|
||||
}
|
||||
return proposals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the EnumCaseMode used to generate completion candidates based on prefix.
|
||||
*/
|
||||
protected EnumCaseMode caseMode(String prefix) {
|
||||
EnumCaseMode caseMode;
|
||||
if ("".equals(prefix)) {
|
||||
caseMode = preferLowerCaseEnums?EnumCaseMode.LOWER_CASE:EnumCaseMode.ORIGNAL;
|
||||
} else {
|
||||
caseMode = Character.isLowerCase(prefix.charAt(0))?EnumCaseMode.LOWER_CASE:EnumCaseMode.ORIGNAL;
|
||||
}
|
||||
return caseMode;
|
||||
}
|
||||
|
||||
protected static String propertyCompletionPostfix(TypeUtil typeUtil, Type type) {
|
||||
String postfix = "";
|
||||
if (type!=null) {
|
||||
if (typeUtil.isAssignableType(type)) {
|
||||
postfix = "=";
|
||||
} else if (TypeUtil.isBracketable(type)) {
|
||||
postfix = "[";
|
||||
} else if (typeUtil.isDotable(type)) {
|
||||
postfix = ".";
|
||||
}
|
||||
}
|
||||
return postfix;
|
||||
}
|
||||
|
||||
// public static boolean isAssign(char assign) {
|
||||
// return assign==':'||assign=='=';
|
||||
// }
|
||||
//
|
||||
// private KeyValuePair getAstNodeLine(IDocument doc, int offset) {
|
||||
// List<KeyValuePair> pairs = parser.parse(doc.get()).ast.getNodes(KeyValuePair.class);
|
||||
// return findPair(pairs, offset, 0, pairs.size() - 1);
|
||||
// }
|
||||
//
|
||||
// private KeyValuePair findPair(List<KeyValuePair> pairs, int offset, int start, int end) {
|
||||
// if (start == end) {
|
||||
// KeyValuePair pair = pairs.get(start);
|
||||
// if (pair.getOffset() <= offset && offset <= pair.getOffset() + pair.getLength()) {
|
||||
// return pair;
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
// } else if (start < end ) {
|
||||
// int pivotIndex = (start + end) / 2;
|
||||
// KeyValuePair pair = pairs.get(pivotIndex);
|
||||
// if (pair.getOffset() > offset) {
|
||||
// return findPair(pairs, offset, start, pivotIndex - 1);
|
||||
// } else if (offset > pair.getOffset() + pair.getLength()) {
|
||||
// return findPair(pairs, offset, pivotIndex + 1, end);
|
||||
// } else {
|
||||
// return pair;
|
||||
// }
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private HoverInfo getValueHoverInfo(DocumentRegion value) {
|
||||
// try {
|
||||
// String valueString = value.toString();
|
||||
// IDocument doc = value.getDocument();
|
||||
// ITypedRegion valuePartition = getPartition(value.getDocument(), value.getStart());
|
||||
// int valuePartitionStart = valuePartition.getOffset();
|
||||
// String propertyName = fuzzySearchPrefix.getPrefix(doc, valuePartitionStart); //note: no need to skip whitespace backwards.
|
||||
// //because value partition includes whitespace around the assignment
|
||||
//
|
||||
// Type type = getValueType(propertyName);
|
||||
// if (TypeUtil.isArray(type) || TypeUtil.isList(type)) {
|
||||
// //It is useful to provide content assist for the values in the list when entering a list
|
||||
// type = TypeUtil.getDomainType(type);
|
||||
// }
|
||||
// if (TypeUtil.isClass(type)) {
|
||||
// //Special case. We want to provide hoverinfos more liberally than what's suggested for completions (i.e. even class names
|
||||
// //that are not suggested by the hints because they do not meet subtyping constraints should be hoverable and linkable!
|
||||
// StsValueHint hint = StsValueHint.className(valueString, typeUtil);
|
||||
// if (hint!=null) {
|
||||
// return new ValueHintHoverInfo(hint);
|
||||
// }
|
||||
// }
|
||||
// //Hack: pretend to invoke content-assist at the end of the value text. This should provide hints applicable to that value
|
||||
// // then show hoverinfo based on that. That way we can avoid duplication a lot of similar logic to compute hoverinfos and hyperlinks.
|
||||
// Collection<StsValueHint> hints = getValueHints(valueString, propertyName, EnumCaseMode.ALIASED);
|
||||
// if (hints!=null) {
|
||||
// for (StsValueHint h : hints) {
|
||||
// if (valueString.equals(h.getValue())) {
|
||||
// return new ValueHintHoverInfo(h);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (BadLocationException e) {
|
||||
// Log.log(e);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
private Collection<ICompletionProposal> getValueCompletions(Value value) {
|
||||
DocumentRegion valueRegion = createRegion(doc, value).trimStart(SPACES).trimEnd(SPACES);
|
||||
String query = valuePrefixFinder.getPrefix(doc, offset, valueRegion.getStart());
|
||||
int startOfValue = offset - query.length();
|
||||
EnumCaseMode caseMode = caseMode(query);
|
||||
|
||||
// note: no need to skip whitespace backwards.
|
||||
String propertyName = /*fuzzySearchPrefix.getPrefix(doc, pair.getOffset())*/value.getParent().getKey().decode();
|
||||
// because value partition includes whitespace around the assignment
|
||||
if (propertyName != null) {
|
||||
Collection<StsValueHint> valueCompletions = getValueHints(query, propertyName, caseMode);
|
||||
if (valueCompletions != null && !valueCompletions.isEmpty()) {
|
||||
ArrayList<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
|
||||
for (StsValueHint hint : valueCompletions) {
|
||||
String valueCandidate = hint.getValue();
|
||||
double score = FuzzyMatcher.matchScore(query, valueCandidate);
|
||||
if (score != 0) {
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.delete(startOfValue, offset);
|
||||
edits.insert(offset, valueCandidate);
|
||||
proposals.add(completionFactory.valueProposal(valueCandidate, query, getValueType(propertyName),
|
||||
score, edits, new ValueHintHoverInfo(hint))
|
||||
// new ValueProposal(startOfValue, valuePrefix,
|
||||
// valueCandidate, i)
|
||||
);
|
||||
}
|
||||
}
|
||||
return proposals;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private DocumentRegion createRegion(IDocument doc, Node value) {
|
||||
// Trim trailing spaces (there is no leading white space already)
|
||||
int length = value.getLength();
|
||||
try {
|
||||
length = doc.get(value.getOffset(), value.getLength()).trim().length();
|
||||
} catch (BadLocationException e) {
|
||||
// ignore
|
||||
}
|
||||
return new DocumentRegion(doc, value.getOffset(), value.getOffset() + length);
|
||||
}
|
||||
|
||||
private Collection<StsValueHint> getValueHints(String query, String propertyName, EnumCaseMode caseMode) {
|
||||
Type type = getValueType(propertyName);
|
||||
if (TypeUtil.isArray(type) || TypeUtil.isList(type)) {
|
||||
//It is useful to provide content assist for the values in the list when entering a list
|
||||
type = TypeUtil.getDomainType(type);
|
||||
}
|
||||
List<StsValueHint> allHints = new ArrayList<>();
|
||||
{
|
||||
Collection<StsValueHint> hints = typeUtil.getHintValues(type, query, caseMode);
|
||||
if (CollectionUtil.hasElements(hints)) {
|
||||
allHints.addAll(hints);
|
||||
}
|
||||
}
|
||||
{
|
||||
PropertyInfo prop = index.findLongestCommonPrefixEntry(propertyName);
|
||||
if (prop!=null) {
|
||||
HintProvider hintProvider = prop.getHints(typeUtil, false);
|
||||
if (!HintProviders.isNull(hintProvider)) {
|
||||
allHints.addAll(hintProvider.getValueHints(query));
|
||||
}
|
||||
}
|
||||
}
|
||||
return allHints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the value type for a give propertyName.
|
||||
*/
|
||||
protected Type getValueType(String propertyName) {
|
||||
try {
|
||||
PropertyInfo prop = index.get(propertyName);
|
||||
if (prop!=null) {
|
||||
return TypeParser.parse(prop.getType());
|
||||
} else {
|
||||
prop = findLongestValidProperty(index, propertyName);
|
||||
if (prop!=null) {
|
||||
TextDocument doc = new TextDocument(null);
|
||||
doc.setText(propertyName);
|
||||
PropertyNavigator navigator = new PropertyNavigator(doc, null, typeUtil, new DocumentRegion(doc, 0, doc.getLength()));
|
||||
return navigator.navigate(prop.getId().length(), TypeParser.parse(prop.getType()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Match<PropertyInfo>> findMatches(String prefix) {
|
||||
List<Match<PropertyInfo>> matches = index.find(camelCaseToHyphens(prefix));
|
||||
return matches;
|
||||
}
|
||||
|
||||
private Collection<ICompletionProposal> getPropertyCompletions() throws BadLocationException {
|
||||
Collection<ICompletionProposal> navProposals = getNavigationProposals();
|
||||
if (!navProposals.isEmpty()) {
|
||||
return navProposals;
|
||||
}
|
||||
return getFuzzyCompletions();
|
||||
}
|
||||
|
||||
protected Collection<ICompletionProposal> getFuzzyCompletions() {
|
||||
final String prefix = fuzzySearchPrefix.getPrefix(doc, offset);
|
||||
if (prefix != null) {
|
||||
Collection<Match<PropertyInfo>> matches = findMatches(prefix);
|
||||
if (matches!=null && !matches.isEmpty()) {
|
||||
ArrayList<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(matches.size());
|
||||
for (final Match<PropertyInfo> match : matches) {
|
||||
DocumentEdits docEdits;
|
||||
try {
|
||||
docEdits = LazyProposalApplier.from(() -> {
|
||||
Type type = TypeParser.parse(match.data.getType());
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.delete(offset-prefix.length(), offset);
|
||||
edits.insert(offset, match.data.getId() + propertyCompletionPostfix(typeUtil, type));
|
||||
return edits;
|
||||
});
|
||||
proposals.add(completionFactory.property(doc, docEdits, match, typeUtil));
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
return proposals;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// public HoverInfo getHoverInfo(IDocument doc, IRegion _region) {
|
||||
// debug("getHoverInfo("+_region+")");
|
||||
//
|
||||
// //The delegate 'getHoverRegion' for spring propery editor will return smaller word regions.
|
||||
// // we must ensure to use our own region finder to identify correct property name.
|
||||
// ITypedRegion region = getHoverRegion(doc, _region.getOffset());
|
||||
// if (region!=null) {
|
||||
// String contentType = region.getType();
|
||||
// try {
|
||||
// if (contentType.equals(IDocument.DEFAULT_CONTENT_TYPE)) {
|
||||
// debug("hoverRegion = "+region);
|
||||
// PropertyInfo best = findBestHoverMatch(doc.get(region.getOffset(), region.getLength()).trim());
|
||||
// if (best!=null) {
|
||||
// return new SpringPropertyHoverInfo(documentContextFinder.getJavaProject(doc), best);
|
||||
// }
|
||||
// } else if (contentType.equals(IPropertiesFilePartitions.PROPERTY_VALUE)) {
|
||||
// return getValueHoverInfo(new DocumentRegion(doc, region));
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// SpringPropertiesEditorPlugin.log(e);
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// public ITypedRegion getHoverRegion(IDocument document, int offset) {
|
||||
// try {
|
||||
// ITypedRegion candidate = getPartition(document, offset);
|
||||
// if (candidate!=null) {
|
||||
// String type = candidate.getType();
|
||||
// if (IDocument.DEFAULT_CONTENT_TYPE.equals(type)) {
|
||||
// return candidate;
|
||||
// } else if (IPropertiesFilePartitions.PROPERTY_VALUE.equals(type)) {
|
||||
// DocumentRegion valueRegion = new DocumentRegion(document, candidate).trimStart(ASSIGN);
|
||||
// return getValueHoverRegion(valueRegion, valueRegion.toRelative(offset));
|
||||
// }
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// SpringPropertiesEditorPlugin.log(e);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// private ITypedRegion getValueHoverRegion(DocumentRegion r, int offset) {
|
||||
// int len = r.length();
|
||||
// if (offset>=0 && offset<=len) {
|
||||
// int start = offset;
|
||||
// while (start>0 && isValuePrefixChar(r.charAt(start-1))) {
|
||||
// start--;
|
||||
// }
|
||||
// int end = offset;
|
||||
// while (end<len && isValuePrefixChar(r.charAt(end))) {
|
||||
// end++;
|
||||
// }
|
||||
// r = r.subSequence(start, end);
|
||||
// if (!r.isEmpty()) {
|
||||
// return r.asTypedRegion(IPropertiesFilePartitions.PROPERTY_VALUE);
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Search known properties for the best 'match' to show as hover data.
|
||||
// */
|
||||
// private PropertyInfo findBestHoverMatch(String propName) {
|
||||
// //TODO: optimize, should be able to use index's treemap to find this without iterating all entries.
|
||||
// PropertyInfo best = null;
|
||||
// int bestCommonPrefixLen = 0; //We try to pick property with longest common prefix
|
||||
// int bestExtraLen = Integer.MAX_VALUE;
|
||||
// for (PropertyInfo candidate : index) {
|
||||
// int commonPrefixLen = StringUtil.commonPrefixLength(propName, candidate.getId());
|
||||
// int extraLen = candidate.getId().length()-commonPrefixLen;
|
||||
// if (commonPrefixLen==propName.length() && extraLen==0) {
|
||||
// //exact match found, can stop searching for better matches
|
||||
// return candidate;
|
||||
// }
|
||||
// //candidate is better if...
|
||||
// if (commonPrefixLen>bestCommonPrefixLen // it has a longer common prefix
|
||||
// || commonPrefixLen==bestCommonPrefixLen && extraLen<bestExtraLen //or same common prefix but fewer extra chars
|
||||
// ) {
|
||||
// bestCommonPrefixLen = commonPrefixLen;
|
||||
// bestExtraLen = extraLen;
|
||||
// best = candidate;
|
||||
// }
|
||||
// }
|
||||
// return best;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* Find the longest known property that is a prefix of the given name. Here prefix does not mean
|
||||
* 'string prefix' but a prefix in the sense of treating '.' as a kind of separators. So
|
||||
* 'prefix' is not allowed to end in the middle of a 'segment'.
|
||||
*/
|
||||
public static PropertyInfo findLongestValidProperty(FuzzyMap<PropertyInfo> index, String name) {
|
||||
int bracketPos = name.indexOf('[');
|
||||
int endPos = bracketPos>=0?bracketPos:name.length();
|
||||
PropertyInfo prop = null;
|
||||
String prefix = null;
|
||||
while (endPos>0 && prop==null) {
|
||||
prefix = name.substring(0, endPos);
|
||||
String canonicalPrefix = camelCaseToHyphens(prefix);
|
||||
prop = index.get(canonicalPrefix);
|
||||
if (prop==null) {
|
||||
endPos = name.lastIndexOf('.', endPos-1);
|
||||
}
|
||||
}
|
||||
if (prop!=null) {
|
||||
//We should meet caller's expectation that matched properties returned by this method
|
||||
// match the names exactly even if we found them using relaxed name matching.
|
||||
return prop.withId(prefix);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.springframework.ide.vscode.application.properties.completions;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.ide.vscode.application.properties.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.completions.PropertyCompletionFactory;
|
||||
import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtilProvider;
|
||||
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.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.IDocument;;
|
||||
|
||||
/**
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class SpringPropertiesCompletionEngine implements ICompletionEngine {
|
||||
|
||||
private boolean preferLowerCaseEnums = true; //might make sense to make this user configurable
|
||||
|
||||
private SpringPropertyIndexProvider indexProvider;
|
||||
private TypeUtilProvider typeUtilProvider;
|
||||
private PropertyCompletionFactory completionFactory = null;
|
||||
|
||||
/**
|
||||
* Constructor used in 'production'. Wires up stuff properly for running inside a normal
|
||||
* Eclipse runtime.
|
||||
*/
|
||||
public SpringPropertiesCompletionEngine(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder projectFinder) {
|
||||
this.indexProvider = indexProvider;
|
||||
this.typeUtilProvider = typeUtilProvider;
|
||||
this.completionFactory = new PropertyCompletionFactory(projectFinder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create completions proposals in the context of a properties text editor.
|
||||
*/
|
||||
public Collection<ICompletionProposal> getCompletions(IDocument doc, int offset) throws BadLocationException {
|
||||
return new PropertiesCompletionProposalsCalculator(indexProvider.getIndex(doc),
|
||||
typeUtilProvider.getTypeUtil(doc), completionFactory, doc, offset, preferLowerCaseEnums).calculate();
|
||||
}
|
||||
|
||||
public boolean getPreferLowerCaseEnums() {
|
||||
return preferLowerCaseEnums;
|
||||
}
|
||||
|
||||
public void setPreferLowerCaseEnums(boolean preferLowerCaseEnums) {
|
||||
this.preferLowerCaseEnums = preferLowerCaseEnums;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine {
|
||||
private final DelimitedListReconciler commaListReconciler = new DelimitedListReconciler(COMMA, this::reconcileType);
|
||||
private Parser parser = new AntlrParser();
|
||||
|
||||
private boolean recordSyntaxErrors = false;
|
||||
private boolean recordSyntaxErrors;
|
||||
|
||||
public SpringPropertiesReconcileEngine(SpringPropertyIndexProvider provider, TypeUtilProvider typeUtilProvider) {
|
||||
this(provider, typeUtilProvider, true);
|
||||
@@ -103,8 +103,6 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine {
|
||||
}
|
||||
|
||||
results.ast.getNodes(KeyValuePair.class).forEach(pair -> {
|
||||
// Key fullName = pair.getKey();
|
||||
// String keyName = fullName.decode();
|
||||
try {
|
||||
DocumentRegion propertyNameRegion = createRegion(doc, pair.getKey());
|
||||
String keyName = PropertiesFileEscapes.unescape(propertyNameRegion.toString());
|
||||
@@ -184,7 +182,7 @@ public class SpringPropertiesReconcileEngine implements IReconcileEngine {
|
||||
// Trim trailing spaces (there is no leading white space already)
|
||||
int length = value.getLength();
|
||||
try {
|
||||
length = doc.get(value.getOffset(), value.getLength()).trim().length();
|
||||
length = doc.get(value.getOffset(), value.getLength()).length();
|
||||
} catch (BadLocationException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.ide.vscode.application.properties.metadata.CachingVal
|
||||
import org.springframework.ide.vscode.application.properties.metadata.PropertiesLoader;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
@@ -47,6 +48,8 @@ import org.eclipse.lsp4j.Diagnostic;
|
||||
*/
|
||||
public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
private JavaProjectFinder javaProjectFinder;
|
||||
|
||||
@Test public void testReconcileCatchesParseError() throws Exception {
|
||||
Editor editor = newEditor("key\n");
|
||||
editor.assertProblems("key|extraneous input");
|
||||
@@ -66,28 +69,28 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
editor.assertProblems("problem|extraneous input", "another|mismatched input");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testServerPortCompletion() throws Exception {
|
||||
@Test public void testServerPortCompletion() throws Exception {
|
||||
data("server.port", INTEGER, 8080, "Port where server listens for http.");
|
||||
assertCompletion("ser<*>", "server.port=<*>");
|
||||
assertCompletionDisplayString("ser<*>", "server.port : int");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testLoggingLevelCompletion() throws Exception {
|
||||
@Test public void testLoggingLevelCompletion() throws Exception {
|
||||
data("logging.level", "java.util.Map<java.lang.String,java.lang.Object>", null, "Logging level per package.");
|
||||
assertCompletion("lolev<*>","logging.level.<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testListCompletion() throws Exception {
|
||||
@Test public void testListCompletion() throws Exception {
|
||||
data("foo.bars", "java.util.List<java.lang.String>", null, "List of bars in foo.");
|
||||
assertCompletion("foba<*>","foo.bars=<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testInetAddresCompletion() throws Exception {
|
||||
@Test public void testInetAddresCompletion() throws Exception {
|
||||
defaultTestData();
|
||||
assertCompletion("server.add<*>", "server.address=<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testStringArrayCompletion() throws Exception {
|
||||
@Test public void testStringArrayCompletion() throws Exception {
|
||||
data("spring.freemarker.view-names", "java.lang.String[]", null, "White list of view names that can be resolved.");
|
||||
data("some.defaulted.array", "java.lang.String[]", new String[] {"a", "b", "c"} , "Stuff.");
|
||||
|
||||
@@ -95,7 +98,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
assertCompletion("some.d.a<*>", "some.defaulted.array=<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testEmptyPrefixProposalsSortedAlpabetically() throws Exception {
|
||||
@Test public void testEmptyPrefixProposalsSortedAlpabetically() throws Exception {
|
||||
defaultTestData();
|
||||
Editor editor = newEditor("");
|
||||
List<CompletionItem> completions = editor.getCompletions();
|
||||
@@ -110,7 +113,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore @Test public void testValueCompletion() throws Exception {
|
||||
@Test public void testValueCompletion() throws Exception {
|
||||
defaultTestData();
|
||||
assertCompletionsVariations("liquibase.enabled=<*>",
|
||||
"liquibase.enabled=false<*>",
|
||||
@@ -310,7 +313,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testPojoArrayCompletions() throws Exception {
|
||||
@Test public void testPojoArrayCompletions() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("boot-1.2.1-app-properties-list-of-pojo");
|
||||
|
||||
useProject(p);
|
||||
@@ -395,7 +398,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testRelaxedNameContentAssist() throws Exception {
|
||||
@Test public void testRelaxedNameContentAssist() throws Exception {
|
||||
data("foo-bar-zor.enabled", "java.lang.Boolean", null, null);
|
||||
assertCompletion("fooBar<*>", "foo-bar-zor.enabled=<*>");
|
||||
}
|
||||
@@ -458,7 +461,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testEnumPropertyCompletionInsideCommaSeparateList() throws Exception {
|
||||
@Test public void testEnumPropertyCompletionInsideCommaSeparateList() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -481,7 +484,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
assertCompletion("foo.colors=RED,B<*>", "foo.colors=RED,BLUE<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testEnumPropertyCompletion() throws Exception {
|
||||
@Test public void testEnumPropertyCompletion() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -523,7 +526,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testEnumMapValueCompletion() throws Exception {
|
||||
@Test public void testEnumMapValueCompletion() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -559,7 +562,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testEnumMapKeyCompletion() throws Exception {
|
||||
@Test public void testEnumMapKeyCompletion() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -617,7 +620,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testPojoCompletions() throws Exception {
|
||||
@Test public void testPojoCompletions() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -629,11 +632,11 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
assertCompletionsDisplayString("foo.data.",
|
||||
"wavelen : double",
|
||||
"name : String",
|
||||
"next : demo.Color[RED, GREEN, BLUE]",
|
||||
"next : demo.Color[BLUE, GREEN, RED]",
|
||||
"nested : demo.ColorData",
|
||||
"children : List<demo.ColorData>",
|
||||
"mapped-children : Map<String, demo.ColorData>",
|
||||
"color-children : Map<demo.Color[RED, GREEN, BLUE], demo.ColorData>",
|
||||
"color-children : Map<demo.Color[BLUE, GREEN, RED], demo.ColorData>",
|
||||
"tags : List<String>",
|
||||
"funky : boolean"
|
||||
);
|
||||
@@ -675,7 +678,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testListOfAtomicCompletions() throws Exception {
|
||||
@Test public void testListOfAtomicCompletions() throws Exception {
|
||||
data("foo.slist", "java.util.List<java.lang.String>", null, "list of strings");
|
||||
data("foo.ulist", "java.util.List<Unknown>", null, "list of strings");
|
||||
data("foo.dlist", "java.util.List<java.lang.Double>", null, "list of doubles");
|
||||
@@ -684,7 +687,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
assertCompletionsVariations("foo.sl<*>", "foo.slist=<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testMapKeyDotInterpretation() throws Exception {
|
||||
@Test public void testMapKeyDotInterpretation() throws Exception {
|
||||
//Interpretation of '.' changes depending on the domain type (i.e. when domain type is
|
||||
//is a simple type got which '.' navigation is invalid then the '.' is 'eaten' by the key.
|
||||
|
||||
@@ -724,7 +727,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testMapKeyDotInterpretationInPojo() throws Exception {
|
||||
@Test public void testMapKeyDotInterpretationInPojo() throws Exception {
|
||||
//Similar to testMapKeyDotInterpretation but this time maps are not attached to property
|
||||
// directly but via a pojo property
|
||||
|
||||
@@ -797,7 +800,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testEnumsInLowerCaseContentAssist() throws Exception {
|
||||
@Test public void testEnumsInLowerCaseContentAssist() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -840,7 +843,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
assertCompletionsVariations("foo.color-data.red.na<*>", "foo.color-data.red.name=<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testNavigationProposalAfterRelaxedPropertyName() throws Exception {
|
||||
@Test public void testNavigationProposalAfterRelaxedPropertyName() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -849,7 +852,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
assertCompletionsVariations("foo.colorData.red.na<*>", "foo.colorData.red.name=<*>");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testValueProposalAssignedToRelaxedPropertyName() throws Exception {
|
||||
@Test public void testValueProposalAssignedToRelaxedPropertyName() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
|
||||
useProject(p);
|
||||
@@ -983,7 +986,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testCharsetCompletions() throws Exception {
|
||||
@Test public void testCharsetCompletions() throws Exception {
|
||||
data("foobar.encoding", "java.nio.charset.Charset", null, "The charset-encoding to use for foobars");
|
||||
|
||||
assertCompletions(
|
||||
@@ -1001,7 +1004,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testLocaleCompletions() throws Exception {
|
||||
@Test public void testLocaleCompletions() throws Exception {
|
||||
data("foobar.locale", "java.util.Locale", null, "Yada yada");
|
||||
|
||||
assertCompletions(
|
||||
@@ -1019,7 +1022,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testPropertyValueHintCompletions() throws Exception {
|
||||
@Test public void testPropertyValueHintCompletions() throws Exception {
|
||||
//Test that 'value hints' work when property name is associated with 'value' hints.
|
||||
// via boot metadata.
|
||||
|
||||
@@ -1037,7 +1040,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testPropertyListHintCompletions() throws Exception {
|
||||
@Test public void testPropertyListHintCompletions() throws Exception {
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
|
||||
assertCompletion(
|
||||
@@ -1065,7 +1068,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testPropertyMapValueCompletions() throws Exception {
|
||||
@Test public void testPropertyMapValueCompletions() throws Exception {
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
|
||||
assertCompletionsDisplayString(
|
||||
@@ -1093,7 +1096,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testPropertyMapKeyCompletions() throws Exception {
|
||||
@Test public void testPropertyMapKeyCompletions() throws Exception {
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
assertCompletionWithLabel(
|
||||
"logging.level.<*>"
|
||||
@@ -1120,7 +1123,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testHandleAsResourceContentAssist() throws Exception {
|
||||
@Test public void testHandleAsResourceContentAssist() throws Exception {
|
||||
//"name": "my.terms-and-conditions",
|
||||
// "providers": [
|
||||
// {
|
||||
@@ -1144,7 +1147,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testHandleAsListContentAssist() throws Exception {
|
||||
@Test public void testHandleAsListContentAssist() throws Exception {
|
||||
data("my.tosses", "String[]", null, "A sequence of coin tosses")
|
||||
.provider("handle-as", "target", "java.lang.Boolean[]");
|
||||
|
||||
@@ -1179,7 +1182,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
}
|
||||
|
||||
|
||||
@Ignore @Test public void test_STS_3335_completions_list_nested_in_Map_of_String() throws Exception {
|
||||
@Test public void test_STS_3335_completions_list_nested_in_Map_of_String() throws Exception {
|
||||
useProject(createPredefinedMavenProject("boot-1.3.3-sts-4335"));
|
||||
|
||||
assertCompletions(
|
||||
@@ -1201,7 +1204,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testSimpleResourceCompletion() throws Exception {
|
||||
@Test public void testSimpleResourceCompletion() throws Exception {
|
||||
CachingValueProvider.TIMEOUT = Duration.ofSeconds(20);
|
||||
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
@@ -1225,7 +1228,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testClasspathResourceCompletion() throws Exception {
|
||||
@Test public void testClasspathResourceCompletion() throws Exception {
|
||||
CachingValueProvider.TIMEOUT = Duration.ofSeconds(20);
|
||||
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
@@ -1295,7 +1298,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testClasspathResourceCompletionInCommaList() throws Exception {
|
||||
@Test public void testClasspathResourceCompletionInCommaList() throws Exception {
|
||||
CachingValueProvider.TIMEOUT = Duration.ofSeconds(20);
|
||||
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
@@ -1490,7 +1493,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void test_PT_119352965() throws Exception {
|
||||
@Test public void test_PT_119352965() throws Exception {
|
||||
data("some.property", "java.lang.String", null, "Some property to test stuff")
|
||||
.valueHint("SOMETHING", "A value for something")
|
||||
.valueHint("ALTERNATE", "An alternative value");
|
||||
@@ -1558,17 +1561,9 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
@Override
|
||||
protected SimpleLanguageServer newLanguageServer() {
|
||||
return new ApplicationPropertiesLanguageServer(md.getIndexProvider(), typeUtilProvider);
|
||||
// return new ApplicationPropertiesLanguageServer(md.getIndexProvider(), typeUtilProvider) {
|
||||
//
|
||||
// @Override
|
||||
// protected IReconcileEngine getReconcileEngine() {
|
||||
// SpringPropertiesReconcileEngine reconcileEngine = (SpringPropertiesReconcileEngine) super.getReconcileEngine();
|
||||
// reconcileEngine.setRecordSyntaxErrors(false);
|
||||
// return reconcileEngine;
|
||||
// }
|
||||
//
|
||||
// };
|
||||
ApplicationPropertiesLanguageServer server = new ApplicationPropertiesLanguageServer(md.getIndexProvider(), typeUtilProvider, javaProjectFinder);
|
||||
server.setMaxCompletionsNumber(-1);
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ApplicationPropertiesLanguageServerTest {
|
||||
}
|
||||
|
||||
private LanguageServerHarness newHarness() throws Exception {
|
||||
Callable<? extends LanguageServer> f = () -> new ApplicationPropertiesLanguageServer((d) -> null, (d) -> null);
|
||||
Callable<? extends LanguageServer> f = () -> new ApplicationPropertiesLanguageServer((d) -> null, (d) -> null, (d) -> null);
|
||||
return new LanguageServerHarness(f);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"portfinder": "^0.4.0",
|
||||
"vscode-languageclient": "2.5.x",
|
||||
"commons-vscode": "0.1.x"
|
||||
"vscode-languageclient": "2.5.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^1.8.5",
|
||||
|
||||
@@ -27,6 +27,7 @@ public class ApplicationYamlLanguageServer extends SimpleLanguageServer {
|
||||
private YamlASTProvider parser = new YamlParser(yaml);
|
||||
private SpringPropertyIndexProvider indexProvider;
|
||||
private TypeUtilProvider typeUtilProvider;
|
||||
private VscodeCompletionEngineAdapter completionEngine;
|
||||
|
||||
public ApplicationYamlLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder javaProjectFinder) {
|
||||
this.indexProvider = indexProvider;
|
||||
@@ -57,10 +58,14 @@ public class ApplicationYamlLanguageServer extends SimpleLanguageServer {
|
||||
typeUtilProvider,
|
||||
RelaxedNameConfig.COMPLETION_DEFAULTS
|
||||
);
|
||||
VscodeCompletionEngine completionEngine = new VscodeCompletionEngineAdapter(this, yamlCompletionEngine);
|
||||
completionEngine = new VscodeCompletionEngineAdapter(this, yamlCompletionEngine);
|
||||
documents.onCompletion(completionEngine::getCompletions);
|
||||
documents.onCompletionResolve(completionEngine::resolveCompletion);
|
||||
}
|
||||
|
||||
public void setMaxCompletionsNumber(int number) {
|
||||
completionEngine.setMaxCompletionsNumber(number);
|
||||
}
|
||||
|
||||
protected IReconcileEngine getReconcileEngine() {
|
||||
return new ApplicationYamlReconcileEngine(parser, indexProvider, typeUtilProvider);
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.ide.vscode.application.properties.metadata.util.Fuzzy
|
||||
import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap.Match;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.LazyProposalApplier;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ProposalApplier;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
|
||||
@@ -2987,7 +2987,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testPropertyMapKeyCompletions() throws Exception {
|
||||
@Test public void testPropertyMapKeyCompletions() throws Exception {
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
assertCompletionWithLabel(
|
||||
"logging:\n" +
|
||||
@@ -3090,7 +3090,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testSimpleResourceCompletion() throws Exception {
|
||||
@Test public void testSimpleResourceCompletion() throws Exception {
|
||||
CachingValueProvider.TIMEOUT = Duration.ofSeconds(20);
|
||||
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
@@ -3134,7 +3134,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
}
|
||||
|
||||
@Ignore @Test public void testClasspathResourceCompletion() throws Exception {
|
||||
@Test public void testClasspathResourceCompletion() throws Exception {
|
||||
CachingValueProvider.TIMEOUT = Duration.ofSeconds(20);
|
||||
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-app"));
|
||||
@@ -3388,7 +3388,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testHandleAsResourceContentAssist() throws Exception {
|
||||
@Test public void testHandleAsResourceContentAssist() throws Exception {
|
||||
//"name": "my.terms-and-conditions",
|
||||
// "providers": [
|
||||
// {
|
||||
@@ -3413,7 +3413,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testBootBug5905() throws Exception {
|
||||
@Test public void testBootBug5905() throws Exception {
|
||||
useProject(createPredefinedMavenProject("boot-1.3.3-app-with-resource-prop"));
|
||||
|
||||
//Check the metadata reflects the 'handle-as':
|
||||
@@ -3542,7 +3542,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
@Override
|
||||
protected SimpleLanguageServer newLanguageServer() {
|
||||
return new ApplicationYamlLanguageServer(md.getIndexProvider(), typeUtilProvider, javaProjectFinder);
|
||||
ApplicationYamlLanguageServer server = new ApplicationYamlLanguageServer(md.getIndexProvider(), typeUtilProvider, javaProjectFinder);
|
||||
server.setMaxCompletionsNumber(-1);
|
||||
return server;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user