PT #155474396: Ctrl-Click for Boot properties YAML
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.util;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
|
||||
public interface LanguageSpecific {
|
||||
|
||||
Collection<LanguageId> supportedLanguages();
|
||||
|
||||
}
|
||||
@@ -11,7 +11,9 @@
|
||||
package org.springframework.ide.vscode.commons.yaml.completion;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
@@ -20,6 +22,8 @@ import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
@@ -34,4 +38,12 @@ public interface YamlAssistContext extends YamlNavigable<YamlAssistContext> {
|
||||
Renderable getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion);
|
||||
|
||||
YamlDocument getDocument();
|
||||
|
||||
default List<Location> getDefinitionsForPropertyKey() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
default List<Location> getDefinitionsForPropertyValue(DocumentRegion valueRegion) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.languageserver.starter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -22,8 +25,15 @@ import org.springframework.ide.vscode.commons.languageserver.config.LanguageServ
|
||||
import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.DiagnosticSeverityProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DefinitionHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LanguageSpecific;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(LanguageServerProperties.class)
|
||||
@@ -54,7 +64,31 @@ public class LanguageServerAutoConf {
|
||||
@ConditionalOnBean(DefinitionHandler.class)
|
||||
@Bean
|
||||
InitializingBean registerDefintionHandler(SimpleTextDocumentService documents,
|
||||
DefinitionHandler definitionHandler) {
|
||||
return () -> documents.onDefinition(definitionHandler);
|
||||
List<DefinitionHandler> definitionHandlers) {
|
||||
if (definitionHandlers.size() == 1) {
|
||||
return () -> documents.onDefinition(definitionHandlers.get(0));
|
||||
} else {
|
||||
Map<LanguageId, DefinitionHandler> handlers = new HashMap<>(definitionHandlers.size());
|
||||
for (DefinitionHandler h : definitionHandlers) {
|
||||
Assert.isInstanceOf(LanguageSpecific.class, h, "Only language specific defintion handlers supported!");
|
||||
for (LanguageId l : ((LanguageSpecific)h).supportedLanguages()) {
|
||||
Assert.isTrue(!handlers.containsKey(l), "Multiple definition handlers for the same language not supported!");
|
||||
handlers.put(l, h);
|
||||
}
|
||||
}
|
||||
ImmutableMap<LanguageId, DefinitionHandler> immutableMap = ImmutableMap.copyOf(handlers);
|
||||
return () -> documents.onDefinition((position) -> {
|
||||
TextDocument doc = documents.get(position.getTextDocument().getUri());
|
||||
if (doc != null) {
|
||||
LanguageId language = doc.getLanguageId();
|
||||
DefinitionHandler handler = immutableMap.get(language);
|
||||
if (handler != null) {
|
||||
return handler.handle(position);
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
|
||||
import org.springframework.ide.vscode.boot.common.RelaxedNameConfig;
|
||||
import org.springframework.ide.vscode.boot.java.links.DefaultJavaElementLocationProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.EclipseJavaDocumentUriProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.EclipseJavaElementLocationProvider;
|
||||
@@ -23,10 +25,21 @@ import org.springframework.ide.vscode.boot.java.links.JdtJavaDocumentUriProvider
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LspClient;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
@SpringBootApplication
|
||||
public class BootLanguagServerBootApp {
|
||||
@@ -75,4 +88,27 @@ public class BootLanguagServerBootApp {
|
||||
}
|
||||
}
|
||||
|
||||
@Bean Yaml yaml() {
|
||||
return new Yaml();
|
||||
}
|
||||
|
||||
@Bean YamlASTProvider yamlAstProvider(Yaml yaml) {
|
||||
return new YamlParser(yaml);
|
||||
}
|
||||
|
||||
@Bean YamlStructureProvider yamlStructureProvider() {
|
||||
return YamlStructureProvider.DEFAULT;
|
||||
}
|
||||
|
||||
@Bean YamlAssistContextProvider yamlAssistContextProvider(BootLanguageServerParams params, JavaElementLocationProvider javaElementLocationProvider) {
|
||||
return new YamlAssistContextProvider() {
|
||||
@Override
|
||||
public YamlAssistContext getGlobalAssistContext(YamlDocument ydoc) {
|
||||
IDocument doc = ydoc.getDocument();
|
||||
FuzzyMap<PropertyInfo> index = params.indexProvider.getIndex(doc);
|
||||
return ApplicationYamlAssistContext.global(ydoc, index, new PropertyCompletionFactory(), params.typeUtilProvider.getTypeUtil(doc), RelaxedNameConfig.COMPLETION_DEFAULTS, javaElementLocationProvider);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
|
||||
@@ -26,6 +27,9 @@ import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -36,6 +40,10 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
||||
@Autowired BootLanguageServerParams params;
|
||||
@Autowired SourceLinks sourceLinks;
|
||||
@Autowired CompilationUnitCache cuCache;
|
||||
@Autowired JavaElementLocationProvider javaElementLocationProvider;
|
||||
@Autowired YamlASTProvider parser;
|
||||
@Autowired YamlStructureProvider yamlStructureProvider;
|
||||
@Autowired YamlAssistContextProvider yamlAssistContextProvider;
|
||||
|
||||
private CompositeLanguageServerComponents components;
|
||||
private VscodeCompletionEngineAdapter completionEngineAdapter;
|
||||
@@ -58,7 +66,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
||||
//TODO: ComposableLanguageServer object instance serves no purpose anymore. The constructor really just contains
|
||||
// some server intialization code. Migrate that code and get rid of the ComposableLanguageServer class
|
||||
CompositeLanguageServerComponents.Builder builder = new CompositeLanguageServerComponents.Builder();
|
||||
builder.add(new BootPropertiesLanguageServerComponents(server, (ignore) -> params));
|
||||
builder.add(new BootPropertiesLanguageServerComponents(server, params, javaElementLocationProvider, parser, yamlStructureProvider, yamlAssistContextProvider));
|
||||
builder.add(new BootJavaLanguageServerComponents(server, params, sourceLinks, cuCache));
|
||||
components = builder.build(server);
|
||||
params.projectObserver.addListener(reconcileOpenDocuments(server, components));
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavadocService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectCache;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.app;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
@@ -18,23 +19,32 @@ import org.gradle.internal.impldep.com.google.common.collect.ImmutableList;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.Type;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil;
|
||||
import org.springframework.ide.vscode.boot.properties.hover.PropertiesDefinitionCalculator;
|
||||
import org.springframework.ide.vscode.boot.properties.hover.PropertyFinder;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IMember;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DefinitionHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LanguageSpecific;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
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 org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PropertiesJavaDefinitionHandler implements DefinitionHandler {
|
||||
public class PropertiesJavaDefinitionHandler implements DefinitionHandler, LanguageSpecific {
|
||||
|
||||
@Autowired
|
||||
private SimpleTextDocumentService documents;
|
||||
|
||||
@Autowired
|
||||
private JavaElementLocationProvider javaDocumentLocationProvider;
|
||||
private JavaElementLocationProvider javaElementLocationProvider;
|
||||
|
||||
@Autowired
|
||||
private BootLanguageServerParams params;
|
||||
@@ -47,10 +57,33 @@ public class PropertiesJavaDefinitionHandler implements DefinitionHandler {
|
||||
FuzzyMap<PropertyInfo> index = params.indexProvider.getIndex(doc);
|
||||
int offset;
|
||||
offset = doc.toOffset(position.getPosition());
|
||||
return new PropertiesDefinitionCalculator(javaDocumentLocationProvider, index, typeUtil, doc, offset).calculate();
|
||||
return getDefinitions(index, typeUtil, doc, offset);
|
||||
} catch (BadLocationException e) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Location> getDefinitions(FuzzyMap<PropertyInfo> index, TypeUtil typeUtil, TextDocument doc, int offset) {
|
||||
IJavaProject project = typeUtil.getJavaProject();
|
||||
PropertyFinder propertyFinder = new PropertyFinder(index, typeUtil, doc, offset);
|
||||
Node node = propertyFinder.findNode();
|
||||
if (node instanceof Key) {
|
||||
Collection<IMember> propertyJavaElements = PropertiesDefinitionCalculator.getPropertyJavaElements(propertyFinder, project, ((Key) node).decode());
|
||||
return PropertiesDefinitionCalculator.getLocations(javaElementLocationProvider, project, propertyJavaElements);
|
||||
} else if (node instanceof Value) {
|
||||
Value value = (Value) node;
|
||||
Key key = value.getParent().getKey();
|
||||
Type type = PropertiesDefinitionCalculator.getPropertyType(propertyFinder, key.decode());
|
||||
if (type != null) {
|
||||
return PropertiesDefinitionCalculator.getValueDefinitionLocations(javaElementLocationProvider, typeUtil, type, value.decode());
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LanguageId> supportedLanguages() {
|
||||
return ImmutableList.of(LanguageId.BOOT_PROPERTIES);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.app;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.TextDocumentPositionParams;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DefinitionHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LanguageSpecific;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeId;
|
||||
import org.yaml.snakeyaml.parser.ParserException;
|
||||
import org.yaml.snakeyaml.scanner.ScannerException;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Component
|
||||
public class YamlPropertiesJavaDefinitionHandler implements DefinitionHandler, LanguageSpecific {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(YamlPropertiesJavaDefinitionHandler.class);
|
||||
|
||||
@Autowired
|
||||
private SimpleTextDocumentService documents;
|
||||
|
||||
@Autowired
|
||||
private YamlASTProvider astProvider;
|
||||
|
||||
@Autowired
|
||||
private YamlStructureProvider structureProvider;
|
||||
|
||||
@Autowired
|
||||
private YamlAssistContextProvider assistContextProvider;
|
||||
|
||||
@Override
|
||||
public Collection<LanguageId> supportedLanguages() {
|
||||
return ImmutableList.of(LanguageId.BOOT_PROPERTIES_YAML);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> handle(TextDocumentPositionParams position) {
|
||||
try {
|
||||
TextDocument doc = documents.get(position);
|
||||
int offset = doc.toOffset(position.getPosition());
|
||||
YamlFileAST ast = getAst(doc);
|
||||
if (ast != null) {
|
||||
YamlDocument ymlDoc = new YamlDocument(doc, structureProvider);
|
||||
YamlAssistContext assistContext = assistContextProvider.getGlobalAssistContext(ymlDoc);
|
||||
if (assistContext != null) {
|
||||
List<NodeRef<?>> astPath = ast.findPath(offset);
|
||||
final YamlPath path = YamlPath.fromASTPath(astPath);
|
||||
if (path != null) {
|
||||
YamlPath assistPath = path;
|
||||
if (assistPath.pointsAtKey()) {
|
||||
// When a path points at a key we must tramsform it to a
|
||||
// 'value-terminating path'
|
||||
// to be able to reuse the 'getHoverInfo' method on
|
||||
// YamlAssistContext (as navigation
|
||||
// into 'key' is not defined for YamlAssistContext.
|
||||
String key = path.getLastSegment().toPropString();
|
||||
assistPath = path.dropLast().append(YamlPathSegment.valueAt(key));
|
||||
}
|
||||
assistContext = assistPath.traverse(assistContext);
|
||||
|
||||
if (assistContext != null) {
|
||||
if (path.pointsAtValue()) {
|
||||
return assistContext.getDefinitionsForPropertyValue(getNodeRegion(ast, offset));
|
||||
} else {
|
||||
return assistContext.getDefinitionsForPropertyKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private DocumentRegion getNodeRegion(YamlFileAST ast, int offset) {
|
||||
if (ast != null) {
|
||||
Node n = ast.findNode(offset);
|
||||
if (n != null && n.getNodeId() == NodeId.scalar) {
|
||||
int start = n.getStartMark().getIndex();
|
||||
int end = n.getEndMark().getIndex();
|
||||
return new DocumentRegion(ast.getDocument(), start, end);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private YamlFileAST getAst(IDocument doc) throws Exception {
|
||||
try {
|
||||
return astProvider.getAST(doc);
|
||||
} catch (ParserException | ScannerException e) {
|
||||
// ignore, the user just typed some crap
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014-2017 Pivotal, Inc.
|
||||
* Copyright (c) 2014, 2018 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
@@ -19,7 +19,6 @@ import org.springframework.ide.vscode.boot.metadata.types.TypedProperty;
|
||||
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.ScoreableProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap.Match;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
@@ -27,25 +26,25 @@ import org.springframework.ide.vscode.commons.yaml.hover.YPropertyInfoTemplates;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
|
||||
public class PropertyCompletionFactory {
|
||||
|
||||
|
||||
public ICompletionProposal valueProposal(String value, String query, String niceTypeName, double score, DocumentEdits edits, Renderable info) {
|
||||
return new ScoreableProposal() {
|
||||
|
||||
|
||||
@Override
|
||||
public DocumentEdits getTextEdit() {
|
||||
return edits;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompletionItemKind getKind() {
|
||||
return CompletionItemKind.Value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getBaseScore() {
|
||||
return score;
|
||||
@@ -105,7 +104,7 @@ public class PropertyCompletionFactory {
|
||||
return getBaseDisplayString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
if (property.isDeprecated()) {
|
||||
proposal.deprecate();
|
||||
@@ -113,10 +112,7 @@ public class PropertyCompletionFactory {
|
||||
return proposal;
|
||||
}
|
||||
|
||||
private JavaProjectFinder documentContextFinder;
|
||||
|
||||
public PropertyCompletionFactory(JavaProjectFinder documentContextFinder) {
|
||||
this.documentContextFinder = documentContextFinder;
|
||||
public PropertyCompletionFactory() {
|
||||
}
|
||||
|
||||
private class PropertyProposal extends AbstractPropertyProposal {
|
||||
@@ -171,7 +167,7 @@ public class PropertyCompletionFactory {
|
||||
public Renderable getDocumentation() {
|
||||
return InformationTemplates.createCompletionDocumentation(match.data);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 Pivotal, Inc.
|
||||
* Copyright (c) 2016, 2018 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
@@ -546,6 +546,10 @@ public class TypeUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public Type getMapKeyType(Type mapType) {
|
||||
return isMap(mapType) ? getKeyType(mapType) : null;
|
||||
}
|
||||
|
||||
public boolean isAssignableType(Type type) {
|
||||
return ASSIGNABLE_TYPES.contains(type.getErasure())
|
||||
|| isEnum(type)
|
||||
|
||||
@@ -14,15 +14,12 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.boot.app.BootLanguageServerParams;
|
||||
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
|
||||
import org.springframework.ide.vscode.boot.common.RelaxedNameConfig;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider;
|
||||
import org.springframework.ide.vscode.boot.properties.completions.SpringPropertiesCompletionEngine;
|
||||
import org.springframework.ide.vscode.boot.properties.hover.PropertiesHoverInfoProvider;
|
||||
import org.springframework.ide.vscode.boot.properties.reconcile.SpringPropertiesReconcileEngine;
|
||||
import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext;
|
||||
import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
|
||||
@@ -32,22 +29,16 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -62,10 +53,10 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom
|
||||
|
||||
private static final String YML = ".yml";
|
||||
private static final String PROPERTIES = ".properties";
|
||||
|
||||
|
||||
private static final Set<LanguageId> LANGUAGES = ImmutableSet.of(
|
||||
LanguageId.BOOT_PROPERTIES,
|
||||
LanguageId.BOOT_PROPERTIES_YAML
|
||||
LanguageId.BOOT_PROPERTIES,
|
||||
LanguageId.BOOT_PROPERTIES_YAML
|
||||
);
|
||||
|
||||
private static final YamlCompletionEngineOptions COMPLETION_OPTIONS = new YamlCompletionEngineOptions() {
|
||||
@@ -77,35 +68,28 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom
|
||||
private final JavaProjectFinder javaProjectFinder;
|
||||
private final SpringPropertyIndexProvider indexProvider;
|
||||
private final TypeUtilProvider typeUtilProvider;
|
||||
private final RelaxedNameConfig relaxedNameConfig = RelaxedNameConfig.COMPLETION_DEFAULTS;
|
||||
|
||||
private final PropertyCompletionFactory completionFactory;
|
||||
|
||||
// For yaml
|
||||
private final Yaml yaml = new Yaml();
|
||||
private final YamlASTProvider parser = new YamlParser(yaml);
|
||||
private final YamlStructureProvider yamlStructureProvider= YamlStructureProvider.DEFAULT;
|
||||
private final YamlStructureProvider yamlStructureProvider;
|
||||
private YamlAssistContextProvider yamlAssistContextProvider;
|
||||
private final SimpleLanguageServer server;
|
||||
private YamlASTProvider parser;
|
||||
|
||||
public BootPropertiesLanguageServerComponents(SimpleLanguageServer server, LSFactory<BootLanguageServerParams> _params) {
|
||||
public BootPropertiesLanguageServerComponents(
|
||||
SimpleLanguageServer server,
|
||||
BootLanguageServerParams serverParams,
|
||||
JavaElementLocationProvider javaElementLocationProvider,
|
||||
YamlASTProvider parser,
|
||||
YamlStructureProvider yamlStructureProvider,
|
||||
YamlAssistContextProvider yamlAssistContextProvider) {
|
||||
this.server = server;
|
||||
BootLanguageServerParams serverParams = _params.create(server);
|
||||
|
||||
this.parser = parser;
|
||||
this.indexProvider = serverParams.indexProvider;
|
||||
this.typeUtilProvider = serverParams.typeUtilProvider;
|
||||
this.javaProjectFinder = serverParams.projectFinder;
|
||||
this.projectObserver = serverParams.projectObserver;
|
||||
|
||||
this.completionFactory = new PropertyCompletionFactory(javaProjectFinder);
|
||||
this.yamlAssistContextProvider = new YamlAssistContextProvider() {
|
||||
@Override
|
||||
public YamlAssistContext getGlobalAssistContext(YamlDocument ydoc) {
|
||||
IDocument doc = ydoc.getDocument();
|
||||
FuzzyMap<PropertyInfo> index = indexProvider.getIndex(doc);
|
||||
return ApplicationYamlAssistContext.global(ydoc, index, completionFactory, typeUtilProvider.getTypeUtil(doc), relaxedNameConfig);
|
||||
}
|
||||
};
|
||||
this.yamlStructureProvider = yamlStructureProvider;
|
||||
this.yamlAssistContextProvider = yamlAssistContextProvider;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public class SpringPropertiesCompletionEngine implements ICompletionEngine {
|
||||
public SpringPropertiesCompletionEngine(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder projectFinder) {
|
||||
this.indexProvider = indexProvider;
|
||||
this.typeUtilProvider = typeUtilProvider;
|
||||
this.completionFactory = new PropertyCompletionFactory(projectFinder);
|
||||
this.completionFactory = new PropertyCompletionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,17 +22,15 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo.PropertySource;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.Type;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeParser;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IField;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IMember;
|
||||
import org.springframework.ide.vscode.commons.java.IMethod;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
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;
|
||||
|
||||
@@ -40,117 +38,57 @@ public class PropertiesDefinitionCalculator {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PropertiesDefinitionCalculator.class);
|
||||
|
||||
final private PropertyFinder propertyFinder;
|
||||
final private IJavaProject project;
|
||||
final private JavaElementLocationProvider javaElementLocationProvider;
|
||||
|
||||
public PropertiesDefinitionCalculator(JavaElementLocationProvider javaElementLocationProvider, FuzzyMap<PropertyInfo> index, TypeUtil typeUtil, IDocument doc, int offset) {
|
||||
this.javaElementLocationProvider = javaElementLocationProvider;
|
||||
this.propertyFinder = new PropertyFinder(index, typeUtil, doc, offset);
|
||||
this.project = typeUtil.getJavaProject();
|
||||
public static List<Location> getLocations(JavaElementLocationProvider locationProvider, IJavaProject project,
|
||||
Collection<IMember> propertyJavaElements) {
|
||||
return propertyJavaElements.stream().map(member -> locationProvider.findLocation(project, member))
|
||||
.filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Location> calculate() {
|
||||
Node node = propertyFinder.findNode();
|
||||
if (node instanceof Key) {
|
||||
Collection<IMember> propertyJavaElements = getPropertyJavaElements(((Key) node).decode());
|
||||
return getPropertyDefintion(propertyJavaElements);
|
||||
} else if (node instanceof Value) {
|
||||
Value value = (Value) node;
|
||||
Key key = value.getParent().getKey();
|
||||
|
||||
String javaTypeFqName = getPropertyJavaTypeFqName(key.decode());
|
||||
|
||||
if (javaTypeFqName != null) {
|
||||
|
||||
// Class reference value link
|
||||
if ("java.lang.Class".equals(javaTypeFqName)) {
|
||||
IType javaType = project.findType(value.decode());
|
||||
if (javaType != null) {
|
||||
Location location = findLocation(javaType);
|
||||
if (location != null) {
|
||||
return ImmutableList.of(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IType javaType = project.findType(javaTypeFqName);
|
||||
if (javaType != null) {
|
||||
// Enum value link
|
||||
if (javaType.isEnum()) {
|
||||
String enumValue = StringUtil.hyphensToUpperCase(StringUtil.camelCaseToHyphens(value.decode()));
|
||||
ImmutableList.Builder<Location> list = ImmutableList.builder();
|
||||
javaType.getFields().forEach(field -> {
|
||||
if (field.getElementName().equals(enumValue)) {
|
||||
Location location = findLocation(field);
|
||||
if (location != null) {
|
||||
list.add(location);
|
||||
}
|
||||
}
|
||||
});
|
||||
return list.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private List<Location> getPropertyDefintion(Collection<IMember> propertyJavaElements) {
|
||||
return propertyJavaElements.stream().map(this::findLocation).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Location findLocation(IMember element) {
|
||||
return javaElementLocationProvider.findLocation(project, element);
|
||||
}
|
||||
|
||||
private String getPropertyJavaTypeFqName(String propertyKey) {
|
||||
public static Type getPropertyType(PropertyFinder propertyFinder, String propertyKey) {
|
||||
PropertyInfo best = propertyFinder.findBestHoverMatch(propertyKey);
|
||||
if (best != null) {
|
||||
String type = best.getType();
|
||||
// Trim down generic type if present
|
||||
int idx = type == null ? -1 : type.indexOf('<');
|
||||
return idx < 0 ? type : type.substring(0, idx);
|
||||
return TypeParser.parse(best.getType());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Collection<IMember> getPropertyJavaElements(String propertyKey) {
|
||||
public static Collection<IMember> getPropertyJavaElements(PropertyFinder propertyFinder, IJavaProject project, String propertyKey) {
|
||||
PropertyInfo best = propertyFinder.findBestHoverMatch(propertyKey);
|
||||
if (best != null) {
|
||||
List<PropertySource> sources = best.getSources();
|
||||
if (sources != null) {
|
||||
ImmutableList.Builder<IMember> elements = ImmutableList.builder();
|
||||
for (PropertySource source : sources) {
|
||||
String typeName = source.getSourceType();
|
||||
if (typeName!=null) {
|
||||
IType type = project.findType(typeName);
|
||||
IMethod method = null;
|
||||
if (type!=null) {
|
||||
String methodSig = source.getSourceMethod();
|
||||
if (methodSig!=null) {
|
||||
method = getMethod(type, methodSig);
|
||||
} else {
|
||||
method = getSetter(type, best);
|
||||
}
|
||||
}
|
||||
if (method!=null) {
|
||||
elements.add(method);
|
||||
} else if (type!=null) {
|
||||
elements.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return elements.build();
|
||||
}
|
||||
return getPropertyJavaElement(project, best);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private IMethod getMethod(IType type, String methodSig) {
|
||||
public static Collection<IMember> getPropertyJavaElement(IJavaProject project, PropertyInfo property) {
|
||||
List<PropertySource> sources = property.getSources();
|
||||
ImmutableList.Builder<IMember> elements = ImmutableList.builder();
|
||||
if (sources != null) {
|
||||
for (PropertySource source : sources) {
|
||||
String typeName = source.getSourceType();
|
||||
if (typeName!=null) {
|
||||
IType type = project.findType(typeName);
|
||||
IMethod method = null;
|
||||
if (type!=null) {
|
||||
String methodSig = source.getSourceMethod();
|
||||
if (methodSig!=null) {
|
||||
method = getMethod(type, methodSig);
|
||||
} else {
|
||||
method = getPropertyMethod(type, property.getName());
|
||||
}
|
||||
}
|
||||
if (method!=null) {
|
||||
elements.add(method);
|
||||
} else if (type!=null) {
|
||||
elements.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return elements.build();
|
||||
}
|
||||
|
||||
private static IMethod getMethod(IType type, String methodSig) {
|
||||
String name = getMethodName(methodSig);
|
||||
//TODO: This code assumes 0 arguments, which is the case currently for all
|
||||
// 'real' data in spring jars.
|
||||
@@ -184,10 +122,9 @@ public class PropertiesDefinitionCalculator {
|
||||
* Attempt to find corresponding setter method for a given property.
|
||||
* @return setter method, or null if not found.
|
||||
*/
|
||||
private IMethod getSetter(IType type, PropertyInfo propertyInfo) {
|
||||
private static IMethod getAccessor(IType type, String getOrSet, String propName) {
|
||||
try {
|
||||
String propName = propertyInfo.getName();
|
||||
String setterName = "set"
|
||||
String setterName = getOrSet
|
||||
+Character.toUpperCase(propName.charAt(0))
|
||||
+toCamelCase(propName.substring(1));
|
||||
String sloppySetterName = setterName.toLowerCase();
|
||||
@@ -209,11 +146,22 @@ public class PropertiesDefinitionCalculator {
|
||||
}
|
||||
}
|
||||
|
||||
public static IMethod getPropertyMethod(IType type, String propName) {
|
||||
String[] accessors = { "set", "get", "is" };
|
||||
for (String a : accessors) {
|
||||
IMethod propertyMethod = getAccessor(type, a, propName);
|
||||
if (propertyMethod != null) {
|
||||
return propertyMethod;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hyphened name to camel case name. It is
|
||||
* safe to call this on an already camel-cased name.
|
||||
*/
|
||||
private String toCamelCase(String name) {
|
||||
private static String toCamelCase(String name) {
|
||||
if (name.isEmpty()) {
|
||||
return name;
|
||||
} else {
|
||||
@@ -234,4 +182,58 @@ public class PropertiesDefinitionCalculator {
|
||||
}
|
||||
}
|
||||
|
||||
public static IField getEnumField(IType type, String value) {
|
||||
String[] enumValues = {
|
||||
value,
|
||||
StringUtil.hyphensToUpperCase(value),
|
||||
StringUtil.hyphensToUpperCase(StringUtil.camelCaseToHyphens(value))
|
||||
};
|
||||
for (String enumValue : enumValues) {
|
||||
IField field = type.getField(enumValue);
|
||||
if (field != null) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<Location> getEnumValueDefinitionLocation(JavaElementLocationProvider javaElementLocationProvider, IJavaProject project, Type type, String value) {
|
||||
IType javaType = project.findType(type.getErasure());
|
||||
if (javaType != null) {
|
||||
IField field = getEnumField(javaType, value);
|
||||
if (field != null) {
|
||||
Location location = javaElementLocationProvider.findLocation(project, field);
|
||||
if (location != null) {
|
||||
return ImmutableList.of(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private static List<Location> getClassValueDefinitionLocation(JavaElementLocationProvider javaElementLocationProvider, IJavaProject project, String value) {
|
||||
IType javaType = project.findType(value);
|
||||
if (javaType != null) {
|
||||
Location location = javaElementLocationProvider.findLocation(project, javaType);
|
||||
if (location != null) {
|
||||
return ImmutableList.of(location);
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public static List<Location> getValueDefinitionLocations(JavaElementLocationProvider javaElementLocationProvider, TypeUtil typeUtil, Type type, String value) {
|
||||
IJavaProject project = typeUtil.getJavaProject();
|
||||
|
||||
if (TypeUtil.isClass(type)) {
|
||||
return getClassValueDefinitionLocation(javaElementLocationProvider, project, value);
|
||||
}
|
||||
|
||||
if (typeUtil.isEnum(type)) {
|
||||
return getEnumValueDefinitionLocation(javaElementLocationProvider, project, type, value);
|
||||
}
|
||||
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ 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.Node;
|
||||
|
||||
class PropertyFinder {
|
||||
public class PropertyFinder {
|
||||
|
||||
final FuzzyMap<PropertyInfo> index;
|
||||
final TypeUtil typeUtil;
|
||||
@@ -29,7 +29,7 @@ class PropertyFinder {
|
||||
final int offset;
|
||||
final AntlrParser parser;
|
||||
|
||||
PropertyFinder(FuzzyMap<PropertyInfo> index, TypeUtil typeUtil, IDocument doc, int offset) {
|
||||
public PropertyFinder(FuzzyMap<PropertyInfo> index, TypeUtil typeUtil, IDocument doc, int offset) {
|
||||
this.index = index;
|
||||
this.typeUtil = typeUtil;
|
||||
this.doc = doc;
|
||||
@@ -37,12 +37,12 @@ class PropertyFinder {
|
||||
this.parser = new AntlrParser();
|
||||
}
|
||||
|
||||
Node findNode() {
|
||||
public Node findNode() {
|
||||
ParseResults parseResults = parser.parse(doc.get());
|
||||
return parseResults.ast.findNode(offset);
|
||||
}
|
||||
|
||||
DocumentRegion createRegion(Node value) {
|
||||
public DocumentRegion createRegion(Node value) {
|
||||
// Trim trailing spaces (there is no leading white space already)
|
||||
int length = value.getLength();
|
||||
try {
|
||||
@@ -56,7 +56,7 @@ class PropertyFinder {
|
||||
/**
|
||||
* Search known properties for the best 'match' to show as hover data.
|
||||
*/
|
||||
PropertyInfo findBestHoverMatch(String propName) {
|
||||
public PropertyInfo findBestHoverMatch(String propName) {
|
||||
PropertyInfo propertyInfo = index.get(propName);
|
||||
if (propertyInfo == null) {
|
||||
propertyInfo = SpringPropertyIndex.findLongestValidProperty(index, propName);
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.common.InformationTemplates;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
|
||||
import org.springframework.ide.vscode.boot.common.RelaxedNameConfig;
|
||||
import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.metadata.IndexNavigator;
|
||||
@@ -41,10 +43,13 @@ import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.BeanPropertyN
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypedProperty;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.PropertyDocUtils;
|
||||
import org.springframework.ide.vscode.boot.properties.hover.PropertiesDefinitionCalculator;
|
||||
import org.springframework.ide.vscode.commons.java.IField;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaElement;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IMember;
|
||||
import org.springframework.ide.vscode.commons.java.IMethod;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
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;
|
||||
@@ -97,10 +102,13 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
|
||||
public final TypeUtil typeUtil;
|
||||
|
||||
public ApplicationYamlAssistContext(YamlDocument doc, int documentSelector, YamlPath contextPath, TypeUtil typeUtil, RelaxedNameConfig conf) {
|
||||
public final JavaElementLocationProvider javaElementLocationProvider;
|
||||
|
||||
public ApplicationYamlAssistContext(YamlDocument doc, int documentSelector, YamlPath contextPath, TypeUtil typeUtil, RelaxedNameConfig conf, JavaElementLocationProvider javaElementLocationProvider) {
|
||||
super(doc, documentSelector, contextPath);
|
||||
this.typeUtil = typeUtil;
|
||||
this.conf = conf;
|
||||
this.javaElementLocationProvider = javaElementLocationProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,8 +138,8 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
*/
|
||||
protected abstract Type getType();
|
||||
|
||||
public static ApplicationYamlAssistContext subdocument(YamlDocument doc, int documentSelector, FuzzyMap<PropertyInfo> index, PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf) {
|
||||
return new IndexContext(doc, documentSelector, YamlPath.EMPTY, IndexNavigator.with(index), completionFactory, typeUtil, conf);
|
||||
public static ApplicationYamlAssistContext subdocument(YamlDocument doc, int documentSelector, FuzzyMap<PropertyInfo> index, PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf, JavaElementLocationProvider javaElementLocationProvider) {
|
||||
return new IndexContext(doc, documentSelector, YamlPath.EMPTY, IndexNavigator.with(index), completionFactory, typeUtil, conf, javaElementLocationProvider);
|
||||
}
|
||||
|
||||
private static class TypeContext extends ApplicationYamlAssistContext {
|
||||
@@ -142,8 +150,8 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
private HintProvider hints;
|
||||
|
||||
public TypeContext(ApplicationYamlAssistContext parent, YamlPath contextPath, Type type,
|
||||
PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf, HintProvider hints) {
|
||||
super(parent.getDocument(), parent.documentSelector, contextPath, typeUtil, conf);
|
||||
PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf, HintProvider hints, JavaElementLocationProvider javaElementLocationProvider) {
|
||||
super(parent.getDocument(), parent.documentSelector, contextPath, typeUtil, conf, javaElementLocationProvider);
|
||||
this.parent = parent;
|
||||
this.completionFactory = completionFactory;
|
||||
this.type = type;
|
||||
@@ -341,7 +349,7 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
private AbstractYamlAssistContext contextWith(YamlPathSegment s, Type nextType) {
|
||||
if (nextType!=null) {
|
||||
return new TypeContext(this, contextPath.append(s), nextType, completionFactory, typeUtil, conf,
|
||||
new YamlPath(s).traverse(hints));
|
||||
new YamlPath(s).traverse(hints), javaElementLocationProvider);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -382,6 +390,42 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> getDefinitionsForPropertyKey() {
|
||||
if (parent instanceof IndexContext) {
|
||||
//this context is in fact an 'alias' of its parent, representing the
|
||||
// point in the context hierarchy where a we transition from navigating
|
||||
// the index to navigating type/bean properties
|
||||
return parent.getDefinitionsForPropertyKey();
|
||||
} else {
|
||||
String propName = contextPath.getBeanPropertyName();
|
||||
Type parentType = parent.getType();
|
||||
IJavaProject javaProject = typeUtil.getJavaProject();
|
||||
Type keyType = typeUtil.getMapKeyType(parentType);
|
||||
if (keyType != null) {
|
||||
String keyValue = contextPath.getLastSegment().toPropString();
|
||||
return PropertiesDefinitionCalculator.getValueDefinitionLocations(javaElementLocationProvider, typeUtil, keyType, keyValue);
|
||||
} else {
|
||||
IType javaType = javaProject.findType(parentType.getErasure());
|
||||
if (javaType != null) {
|
||||
IMethod method = PropertiesDefinitionCalculator.getPropertyMethod(javaType, propName);
|
||||
if (method != null) {
|
||||
Location location = javaElementLocationProvider.findLocation(javaProject, method);
|
||||
if (location != null) {
|
||||
return ImmutableList.of(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> getDefinitionsForPropertyValue(DocumentRegion valueRegion) {
|
||||
return PropertiesDefinitionCalculator.getValueDefinitionLocations(javaElementLocationProvider, typeUtil, type, valueRegion.toString().trim());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class IndexContext extends ApplicationYamlAssistContext {
|
||||
@@ -390,8 +434,8 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
PropertyCompletionFactory completionFactory;
|
||||
|
||||
public IndexContext(YamlDocument doc, int documentSelector, YamlPath contextPath, IndexNavigator indexNav,
|
||||
PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf) {
|
||||
super(doc, documentSelector, contextPath, typeUtil, conf);
|
||||
PropertyCompletionFactory completionFactory, TypeUtil typeUtil, RelaxedNameConfig conf, JavaElementLocationProvider javaElementLocationProvider) {
|
||||
super(doc, documentSelector, contextPath, typeUtil, conf, javaElementLocationProvider);
|
||||
this.indexNav = indexNav;
|
||||
this.completionFactory = completionFactory;
|
||||
}
|
||||
@@ -467,11 +511,11 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
}
|
||||
}
|
||||
if (subIndex.getExtensionCandidate()!=null) {
|
||||
return new IndexContext(getDocument(), documentSelector, contextPath.append(s), subIndex, completionFactory, typeUtil, conf);
|
||||
return new IndexContext(getDocument(), documentSelector, contextPath.append(s), subIndex, completionFactory, typeUtil, conf, javaElementLocationProvider);
|
||||
} else if (subIndex.getExactMatch()!=null) {
|
||||
IndexContext asIndexContext = new IndexContext(getDocument(), documentSelector, contextPath.append(s), subIndex, completionFactory, typeUtil, conf);
|
||||
IndexContext asIndexContext = new IndexContext(getDocument(), documentSelector, contextPath.append(s), subIndex, completionFactory, typeUtil, conf, javaElementLocationProvider);
|
||||
PropertyInfo prop = subIndex.getExactMatch();
|
||||
return new TypeContext(asIndexContext, contextPath.append(s), TypeParser.parse(prop.getType()), completionFactory, typeUtil, conf, prop.getHints(typeUtil, true));
|
||||
return new TypeContext(asIndexContext, contextPath.append(s), TypeParser.parse(prop.getType()), completionFactory, typeUtil, conf, prop.getHints(typeUtil, true), javaElementLocationProvider);
|
||||
}
|
||||
}
|
||||
//Unsuported navigation => no context for assist
|
||||
@@ -502,6 +546,23 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> getDefinitionsForPropertyKey() {
|
||||
PropertyInfo prop = indexNav.getExactMatch();
|
||||
if (prop != null) {
|
||||
IJavaProject project = typeUtil.getJavaProject();
|
||||
Collection<IMember> elements = PropertiesDefinitionCalculator.getPropertyJavaElement(project, prop);
|
||||
return PropertiesDefinitionCalculator.getLocations(javaElementLocationProvider, project, elements);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> getDefinitionsForPropertyValue(DocumentRegion valueRegion) {
|
||||
// Shouldn't be reaching this point. TypeContext should be supplying the value definition
|
||||
return super.getDefinitionsForPropertyValue(valueRegion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getHoverInfo(YamlPathSegment lastSegment) {
|
||||
// TODO Auto-generated method stub
|
||||
@@ -521,11 +582,11 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
|
||||
return null;
|
||||
}
|
||||
|
||||
public static YamlAssistContext global(YamlDocument doc, final FuzzyMap<PropertyInfo> index, final PropertyCompletionFactory completionFactory, final TypeUtil typeUtil, final RelaxedNameConfig conf) {
|
||||
public static YamlAssistContext global(YamlDocument doc, final FuzzyMap<PropertyInfo> index, final PropertyCompletionFactory completionFactory, final TypeUtil typeUtil, final RelaxedNameConfig conf, JavaElementLocationProvider javaElementLocationProvider) {
|
||||
return new TopLevelAssistContext() {
|
||||
@Override
|
||||
protected YamlAssistContext getDocumentContext(int documentSelector) {
|
||||
return subdocument(doc, documentSelector, index, completionFactory, typeUtil, conf);
|
||||
return subdocument(doc, documentSelector, index, completionFactory, typeUtil, conf, javaElementLocationProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.bootiful;
|
||||
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
@@ -6,12 +16,14 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ide.vscode.boot.app.BootLanguageServerParams;
|
||||
import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider;
|
||||
import org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
@@ -64,4 +76,8 @@ import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
return SourceLinkFactory.NO_SOURCE_LINKS;
|
||||
}
|
||||
|
||||
@Bean DefinitionLinkAsserts definitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, CompilationUnitCache cuCache) {
|
||||
return new DefinitionLinkAsserts(javaDocumentUriProvider, cuCache);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,33 +13,18 @@ package org.springframework.ide.vscode.boot.test;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.ide.vscode.boot.properties.reconcile.ApplicationPropertiesProblemType.PROP_DUPLICATE_KEY;
|
||||
import static org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts.field;
|
||||
import static org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts.method;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.ClasspathTestUtil.getOutputFolder;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
|
||||
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
|
||||
import org.eclipse.jdt.core.dom.EnumDeclaration;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -51,24 +36,17 @@ import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.PropertyEditorTestConf;
|
||||
import org.springframework.ide.vscode.boot.editor.harness.AbstractPropsEditorTest;
|
||||
import org.springframework.ide.vscode.boot.editor.harness.StyledStringMatcher;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.metadata.CachingValueProvider;
|
||||
import org.springframework.ide.vscode.boot.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.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.ProjectCustomizer;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
/**
|
||||
@@ -82,13 +60,7 @@ import com.google.common.io.Files;
|
||||
public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
@Autowired
|
||||
CompilationUnitCache cuCache;
|
||||
|
||||
@Autowired
|
||||
SimpleTextDocumentService docService;
|
||||
|
||||
@Autowired
|
||||
JavaDocumentUriProvider javaDocumentUriProvider;
|
||||
private DefinitionLinkAsserts definitionLinkAsserts;
|
||||
|
||||
@Configuration static class TestConf {
|
||||
@Bean LanguageId defaultLanguageId() {
|
||||
@@ -317,258 +289,20 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
"flyway.init-sqls=a,b,c\n"
|
||||
);
|
||||
|
||||
assertLinkTargets(editor, "server", p,
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "server", p,
|
||||
method("org.springframework.boot.autoconfigure.web.ServerProperties", "setPort", "java.lang.Integer"));
|
||||
|
||||
assertLinkTargets(editor, "data", p,
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "data", p,
|
||||
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "hikariDataSource"),
|
||||
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "tomcatDataSource"),
|
||||
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "dbcpDataSource")
|
||||
);
|
||||
|
||||
assertLinkTargets(editor, "flyway", p, method("org.springframework.boot.autoconfigure.flyway.FlywayProperties", "setInitSqls", "java.util.List"));
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "flyway", p, method("org.springframework.boot.autoconfigure.flyway.FlywayProperties", "setInitSqls", "java.util.List"));
|
||||
System.out.println("<<< testHyperlinkTargets");
|
||||
}
|
||||
|
||||
void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, JavaMethod... methods) throws Exception {
|
||||
Set<Location> expectedLocations = Arrays.stream(methods).map(method -> {
|
||||
try {
|
||||
return getLocation(project, method);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}).collect(Collectors.toSet());
|
||||
|
||||
editor.assertLinkTargets(hoverOver, expectedLocations);
|
||||
}
|
||||
|
||||
void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, String typeFqName) throws Exception {
|
||||
|
||||
Location expectedLocation = getLocation(project, typeFqName);
|
||||
|
||||
System.out.println("Expected Location: " + expectedLocation);
|
||||
|
||||
editor.assertLinkTargets(hoverOver, ImmutableSet.of(expectedLocation));
|
||||
}
|
||||
|
||||
void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, JavaField field) throws Exception {
|
||||
|
||||
Location expectedLocation = getLocation(project, field);
|
||||
|
||||
System.out.println("Expected Location: " + expectedLocation);
|
||||
|
||||
editor.assertLinkTargets(hoverOver, ImmutableSet.of(expectedLocation));
|
||||
}
|
||||
|
||||
static private JavaMethod method(String fqClassName, String methodName, String... params) {
|
||||
return new JavaMethod(fqClassName, methodName, params);
|
||||
}
|
||||
|
||||
static private JavaField field(String fqClassName, String name) {
|
||||
return new JavaField(fqClassName, name);
|
||||
}
|
||||
|
||||
static class JavaMethod {
|
||||
public final String fqName;
|
||||
public final String methodName;
|
||||
public final String[] params;
|
||||
public JavaMethod(String fqClassName, String methodName, String... params) {
|
||||
this.fqName = fqClassName;
|
||||
this.methodName = methodName;
|
||||
this.params = params;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JavaMethod [fqName=" + fqName + ", methodName=" + methodName + ", params=" + Arrays.toString(params)
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
static class JavaField {
|
||||
public final String fqName;
|
||||
public final String fieldName;
|
||||
public JavaField(String fqName, String fieldName) {
|
||||
super();
|
||||
this.fqName = fqName;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JavaField [fqName=" + fqName + ", fieldName=" + fieldName + "]";
|
||||
}
|
||||
}
|
||||
|
||||
private Location getLocation(IJavaProject project, String fqName) throws Exception {
|
||||
Location loc = new Location();
|
||||
Optional<URL> sourceUrl = SourceLinks.source(project, fqName);
|
||||
if (sourceUrl.isPresent()) {
|
||||
|
||||
URI docUri = javaDocumentUriProvider.docUri(project, fqName);
|
||||
loc.setUri(docUri.toString());
|
||||
|
||||
String typeName = fqName.substring(fqName.lastIndexOf('.') + 1);
|
||||
URI sourceUri = sourceUrl.get().toURI();
|
||||
Range r = cuCache.withCompilationUnit(project, sourceUri, (cu) -> {
|
||||
try {
|
||||
TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA);
|
||||
doc.setText(cuCache.fetchContent(sourceUri));
|
||||
AtomicReference<Range> range = new AtomicReference<>(null);
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
private boolean proceessTypeNode(TextDocument doc, String typeName,
|
||||
AtomicReference<Range> range, AbstractTypeDeclaration node) {
|
||||
SimpleName nameNode = node.getName();
|
||||
if (nameNode.getIdentifier().equals(typeName)) {
|
||||
try {
|
||||
range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength()));
|
||||
return false;
|
||||
} catch (BadLocationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
return proceessTypeNode(doc, typeName, range, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumDeclaration node) {
|
||||
return proceessTypeNode(doc, typeName, range, node);
|
||||
}
|
||||
|
||||
});
|
||||
return range.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Couldn't find " + fqName);
|
||||
}
|
||||
loc.setRange(r);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
private Location getLocation(IJavaProject project, JavaMethod method) throws Exception {
|
||||
Location loc = new Location();
|
||||
Optional<URL> sourceUrl = SourceLinks.source(project, method.fqName);
|
||||
if (sourceUrl.isPresent()) {
|
||||
|
||||
URI docUri = javaDocumentUriProvider.docUri(project, method.fqName);
|
||||
loc.setUri(docUri.toString());
|
||||
|
||||
URI sourceUri = sourceUrl.get().toURI();
|
||||
Range r = cuCache.withCompilationUnit(project, sourceUri, (cu) -> {
|
||||
try {
|
||||
AtomicReference<Range> range = new AtomicReference<>(null);
|
||||
TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA);
|
||||
doc.setText(cuCache.fetchContent(sourceUri));
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
SimpleName nameNode = node.getName();
|
||||
if (nameNode.getIdentifier().equals(method.methodName)) {
|
||||
if (node.parameters().size() != method.params.length) {
|
||||
return false;
|
||||
}
|
||||
int i = 0;
|
||||
for (Object _p : node.parameters()) {
|
||||
if (_p instanceof SingleVariableDeclaration) {
|
||||
SingleVariableDeclaration p = (SingleVariableDeclaration) _p;
|
||||
String fqName = p.getType().resolveBinding().getErasure().getQualifiedName();
|
||||
if (!fqName.equals(method.params[i++])) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength()));
|
||||
} catch (BadLocationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
return range.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Couldn't find " + method);
|
||||
}
|
||||
loc.setRange(r);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
private Location getLocation(IJavaProject project, JavaField field) throws Exception {
|
||||
Location loc = new Location();
|
||||
|
||||
Optional<URL> sourceUrl = SourceLinks.source(project, field.fqName);
|
||||
if (sourceUrl.isPresent()) {
|
||||
|
||||
URI sourceUri = sourceUrl.get().toURI();
|
||||
|
||||
URI docUri = javaDocumentUriProvider.docUri(project, field.fqName);
|
||||
loc.setUri(docUri.toString());
|
||||
|
||||
Range r = cuCache.withCompilationUnit(project, sourceUri, (cu) -> {
|
||||
try {
|
||||
AtomicReference<Range> range = new AtomicReference<>(null);
|
||||
TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA);
|
||||
doc.setText(cuCache.fetchContent(sourceUri));
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
boolean foundType = false;
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumConstantDeclaration node) {
|
||||
if (foundType) {
|
||||
SimpleName nameNode = node.getName();
|
||||
if (nameNode.getIdentifier().equals(field.fieldName)) {
|
||||
try {
|
||||
range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength()));
|
||||
} catch (BadLocationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumDeclaration node) {
|
||||
if (node.getName().getIdentifier()
|
||||
.equals(field.fqName.substring(field.fqName.lastIndexOf('.') + 1))) {
|
||||
foundType = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
return range.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Couldn't find " + field);
|
||||
}
|
||||
loc.setRange(r);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
@Test public void testHyperlinkTargetsLoggingLevel() throws Exception {
|
||||
System.out.println(">>> testHyperlinkTargetsLoggingLevel");
|
||||
@@ -580,7 +314,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
"logging.level.com.acme=INFO\n"
|
||||
);
|
||||
|
||||
assertLinkTargets(editor, "level", p, "org.springframework.boot.logging.LoggingApplicationListener");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "level", p, "org.springframework.boot.logging.LoggingApplicationListener");
|
||||
System.out.println("<<< testHyperlinkTargetsLoggingLevel");
|
||||
}
|
||||
|
||||
@@ -1702,7 +1436,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
"spring.data.mongodb.field-naming-strategy=org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n" +
|
||||
"#more stuff"
|
||||
);
|
||||
assertLinkTargets(editor, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", project, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", project, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
|
||||
|
||||
//Linking should also work for types that aren't valid based on the constraints
|
||||
|
||||
@@ -1713,7 +1447,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
"spring.data.mongodb.field-naming-strategy=java.lang.String\n" +
|
||||
"#more stuff"
|
||||
);
|
||||
assertLinkTargets(editor, "java.lang.String", project, "java.lang.String");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "java.lang.String", project, "java.lang.String");
|
||||
|
||||
// Instead of java.lang.String
|
||||
editor = newEditor(
|
||||
@@ -1721,7 +1455,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
"spring.data.mongodb.field-naming-strategy=org.springframework.core.io.Resource\n" +
|
||||
"#more stuff"
|
||||
);
|
||||
assertLinkTargets(editor, "org.springframework.core.io.Resource", project, "org.springframework.core.io.Resource");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "org.springframework.core.io.Resource", project, "org.springframework.core.io.Resource");
|
||||
|
||||
}
|
||||
|
||||
@@ -1894,12 +1628,12 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
editor = newEditor(
|
||||
"my.background: RED"
|
||||
);
|
||||
assertLinkTargets(editor, "RED", project, field("demo.Color", "RED"));
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, field("demo.Color", "RED"));
|
||||
|
||||
editor = newEditor(
|
||||
"my.background=red"
|
||||
);
|
||||
assertLinkTargets(editor, "red", project, field("demo.Color", "RED"));
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "red", project, field("demo.Color", "RED"));
|
||||
}
|
||||
|
||||
@Test public void testEnumInPojoField() throws Exception {
|
||||
@@ -1911,8 +1645,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
|
||||
editor = newEditor(
|
||||
"my.screen.background=green"
|
||||
);
|
||||
assertLinkTargets(editor, "background", project, method("com.example.demo.MyProperties$Screen", "getScreen"));
|
||||
assertLinkTargets(editor, "green", project, field("com.example.demo.Color", "GREEN"));
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "background", project, method("com.example.demo.MyProperties$Screen", "getScreen"));
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "green", project, field("com.example.demo.Color", "GREEN"));
|
||||
}
|
||||
|
||||
@Test public void testNoHoverForUnrecognizedProperty() throws Exception {
|
||||
|
||||
@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.boot.test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts.field;
|
||||
import static org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts.method;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.Editor.INDENTED_COMPLETION;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -20,10 +22,10 @@ import java.util.Optional;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -34,6 +36,7 @@ import org.springframework.ide.vscode.boot.editor.harness.StyledStringMatcher;
|
||||
import org.springframework.ide.vscode.boot.metadata.CachingValueProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.RunnableWithException;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
@@ -52,6 +55,9 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
@Import(PropertyEditorTestConf.class)
|
||||
public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
|
||||
@Autowired
|
||||
private DefinitionLinkAsserts definitionLinkAsserts;
|
||||
|
||||
@Configuration static class TestConf {
|
||||
@Bean LanguageId defaultLanguageId() {
|
||||
return LanguageId.BOOT_PROPERTIES_YAML;
|
||||
@@ -384,7 +390,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
}
|
||||
|
||||
|
||||
@Ignore @Test public void testUserDefinedHoversandLinkTargets() throws Exception {
|
||||
@Ignore @Test public void testUserDefinedHovers() throws Exception {
|
||||
useProject(createPredefinedMavenProject("enums-boot-1.3.2-app"));
|
||||
data("foo.link-tester", "demo.LinkTestSubject", null, "for testing different Pojo link cases");
|
||||
Editor editor = newEditor(
|
||||
@@ -407,12 +413,30 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
editor.assertHoverContains("name", "Set the name"); // javadoc from setter
|
||||
editor.assertHoverContains("next", "Get the next"); // javadoc from getter
|
||||
|
||||
editor.assertLinkTargets("data", "demo.FooProperties.setdata(ColorData)");
|
||||
editor.assertLinkTargets("wavelen", "demo.ColorData.setWavelen(double)");
|
||||
|
||||
}
|
||||
|
||||
@Ignore @Test public void testHyperlinkTargets() throws Exception {
|
||||
@Test public void testUserDefinedLinkTargets() throws Exception {
|
||||
MavenJavaProject project = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
useProject(project);
|
||||
data("foo.link-tester", "demo.LinkTestSubject", null, "for testing different Pojo link cases");
|
||||
Editor editor = newEditor(
|
||||
"#A comment at the start\n" +
|
||||
"foo:\n" +
|
||||
" data:\n" +
|
||||
" wavelen: 666\n" +
|
||||
" name: foo\n" +
|
||||
" next: green\n" +
|
||||
" link-tester:\n" +
|
||||
" has-it-all: nice\n" +
|
||||
" strange: weird\n" +
|
||||
" getter-only: getme\n"
|
||||
);
|
||||
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "data", project, method("demo.FooProperties", "setData", "demo.ColorData"));
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "wavelen", project, method("demo.ColorData", "setWavelen", "double"));
|
||||
}
|
||||
|
||||
@Test public void testHyperlinkTargets() throws Exception {
|
||||
IJavaProject p = createPredefinedMavenProject("tricky-getters-boot-1.3.1-app");
|
||||
useProject(p);
|
||||
|
||||
@@ -426,16 +450,16 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" init-sqls: a,b,c\n"
|
||||
);
|
||||
|
||||
editor.assertLinkTargets("port",
|
||||
"org.springframework.boot.autoconfigure.web.ServerProperties.setPort(Integer)"
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "port", p,
|
||||
method("org.springframework.boot.autoconfigure.web.ServerProperties", "setPort", "java.lang.Integer")
|
||||
);
|
||||
editor.assertLinkTargets("login-",
|
||||
"org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.hikariDataSource()",
|
||||
"org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.tomcatDataSource()",
|
||||
"org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.dbcpDataSource()"
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "login-", p,
|
||||
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "hikariDataSource"),
|
||||
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "tomcatDataSource"),
|
||||
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "dbcpDataSource")
|
||||
);
|
||||
editor.assertLinkTargets("init-sql",
|
||||
"org.springframework.boot.autoconfigure.flyway.FlywayProperties.setInitSqls(List<String>)");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "init-sql", p,
|
||||
method("org.springframework.boot.autoconfigure.flyway.FlywayProperties", "setInitSqls", "java.util.List"));
|
||||
}
|
||||
|
||||
@Test public void testReconcile() throws Exception {
|
||||
@@ -3552,9 +3576,10 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void testClassReferenceInValueLink() throws Exception {
|
||||
@Test public void testClassReferenceInValueLink() throws Exception {
|
||||
Editor editor;
|
||||
useProject(createPredefinedMavenProject("empty-boot-1.3.0-with-mongo"));
|
||||
MavenJavaProject project = createPredefinedMavenProject("empty-boot-1.3.0-with-mongo");
|
||||
useProject(project);
|
||||
|
||||
editor = newEditor(
|
||||
"spring:\n" +
|
||||
@@ -3562,7 +3587,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" mongodb:\n" +
|
||||
" field-naming-strategy: org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n"
|
||||
);
|
||||
editor.assertLinkTargets("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", project, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
|
||||
|
||||
editor = newEditor(
|
||||
"spring:\n" +
|
||||
@@ -3571,7 +3596,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" field-naming-strategy:\n" +
|
||||
" org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n"
|
||||
);
|
||||
editor.assertLinkTargets("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", project, "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
|
||||
|
||||
//Linking should also work for types that aren't valid based on the constraints
|
||||
editor = newEditor(
|
||||
@@ -3581,7 +3606,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" field-naming-strategy: java.lang.String\n" +
|
||||
"#more stuff"
|
||||
);
|
||||
editor.assertLinkTargets("java.lang.String", "java.lang.String");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "java.lang.String", project, "java.lang.String");
|
||||
}
|
||||
|
||||
@Test public void test_STS_3335_reconcile_list_nested_in_Map_of_String() throws Exception {
|
||||
@@ -3716,27 +3741,29 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
editor.assertHoverContains("RED", "Hot and delicious");
|
||||
}
|
||||
|
||||
@Ignore @Test public void testHyperLinkEnumValue() throws Exception {
|
||||
@Test public void testHyperLinkEnumValue() throws Exception {
|
||||
Editor editor;
|
||||
useProject(createPredefinedMavenProject("enums-boot-1.3.2-app"));
|
||||
MavenJavaProject project = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
useProject(project);
|
||||
data("my.background", "demo.Color", null, "Color to use as default background.");
|
||||
|
||||
editor = newEditor(
|
||||
"my:\n" +
|
||||
" background: RED"
|
||||
);
|
||||
editor.assertLinkTargets("RED", "demo.Color.RED");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, field("demo.Color", "RED"));
|
||||
|
||||
editor = newEditor(
|
||||
"my:\n" +
|
||||
" background: red"
|
||||
);
|
||||
editor.assertLinkTargets("red", "demo.Color.RED");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "red", project, field("demo.Color", "RED"));
|
||||
}
|
||||
|
||||
@Ignore @Test public void testHyperLinkEnumValueInMapKey() throws Exception {
|
||||
@Test public void testHyperLinkEnumValueInMapKey() throws Exception {
|
||||
Editor editor;
|
||||
useProject(createPredefinedMavenProject("enums-boot-1.3.2-app"));
|
||||
MavenJavaProject project = createPredefinedMavenProject("enums-boot-1.3.2-app");
|
||||
useProject(project);
|
||||
data("my.color.map", "java.util.Map<demo.Color,java.lang.String>", null, "Pretty names for the colors.");
|
||||
|
||||
editor = newEditor(
|
||||
@@ -3746,8 +3773,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" RED: Rood\n" +
|
||||
" green: Groen\n"
|
||||
);
|
||||
editor.assertLinkTargets("RED", "demo.Color.RED");
|
||||
editor.assertLinkTargets("green", "demo.Color.GREEN");
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, field("demo.Color", "RED"));
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "green", project, field("demo.Color", "GREEN"));
|
||||
|
||||
editor = newEditor(
|
||||
"spring:\n" +
|
||||
@@ -3755,8 +3782,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" serialization:\n" +
|
||||
" INDENT_OUTPUT: true"
|
||||
);
|
||||
editor.assertLinkTargets("INDENT_OUTPUT",
|
||||
"com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT"
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "INDENT_OUTPUT", project,
|
||||
field("com.fasterxml.jackson.databind.SerializationFeature", "INDENT_OUTPUT")
|
||||
);
|
||||
|
||||
editor = newEditor(
|
||||
@@ -3765,8 +3792,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" serialization:\n" +
|
||||
" indent-output: true"
|
||||
);
|
||||
editor.assertLinkTargets("indent-output",
|
||||
"com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT"
|
||||
definitionLinkAsserts.assertLinkTargets(editor, "indent-output", project,
|
||||
field("com.fasterxml.jackson.databind.SerializationFeature", "INDENT_OUTPUT")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
|
||||
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
|
||||
import org.eclipse.jdt.core.dom.EnumDeclaration;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public class DefinitionLinkAsserts {
|
||||
|
||||
private JavaDocumentUriProvider javaDocumentUriProvider;
|
||||
private CompilationUnitCache cuCache;
|
||||
|
||||
public static JavaMethod method(String fqClassName, String methodName, String... params) {
|
||||
return new JavaMethod(fqClassName, methodName, params);
|
||||
}
|
||||
|
||||
public static JavaField field(String fqClassName, String name) {
|
||||
return new JavaField(fqClassName, name);
|
||||
}
|
||||
|
||||
public static class JavaMethod {
|
||||
public final String fqName;
|
||||
public final String methodName;
|
||||
public final String[] params;
|
||||
public JavaMethod(String fqClassName, String methodName, String... params) {
|
||||
this.fqName = fqClassName;
|
||||
this.methodName = methodName;
|
||||
this.params = params;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JavaMethod [fqName=" + fqName + ", methodName=" + methodName + ", params=" + Arrays.toString(params)
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
public static class JavaField {
|
||||
public final String fqName;
|
||||
public final String fieldName;
|
||||
public JavaField(String fqName, String fieldName) {
|
||||
super();
|
||||
this.fqName = fqName;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JavaField [fqName=" + fqName + ", fieldName=" + fieldName + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public DefinitionLinkAsserts(JavaDocumentUriProvider javaDocumentUriProvider, CompilationUnitCache cuCache) {
|
||||
this.javaDocumentUriProvider = javaDocumentUriProvider;
|
||||
this.cuCache = cuCache;
|
||||
|
||||
}
|
||||
|
||||
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, JavaMethod... methods) throws Exception {
|
||||
Set<Location> expectedLocations = Arrays.stream(methods).map(method -> {
|
||||
try {
|
||||
return getLocation(project, method);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}).collect(Collectors.toSet());
|
||||
|
||||
editor.assertLinkTargets(hoverOver, expectedLocations);
|
||||
}
|
||||
|
||||
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, String typeFqName) throws Exception {
|
||||
|
||||
Location expectedLocation = getLocation(project, typeFqName);
|
||||
|
||||
System.out.println("Expected Location: " + expectedLocation);
|
||||
|
||||
editor.assertLinkTargets(hoverOver, ImmutableSet.of(expectedLocation));
|
||||
}
|
||||
|
||||
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, JavaField field) throws Exception {
|
||||
|
||||
Location expectedLocation = getLocation(project, field);
|
||||
|
||||
System.out.println("Expected Location: " + expectedLocation);
|
||||
|
||||
editor.assertLinkTargets(hoverOver, ImmutableSet.of(expectedLocation));
|
||||
}
|
||||
|
||||
private Location getLocation(IJavaProject project, String fqName) throws Exception {
|
||||
Location loc = new Location();
|
||||
Optional<URL> sourceUrl = SourceLinks.source(project, fqName);
|
||||
if (sourceUrl.isPresent()) {
|
||||
|
||||
URI docUri = javaDocumentUriProvider.docUri(project, fqName);
|
||||
loc.setUri(docUri.toString());
|
||||
|
||||
String typeName = fqName.substring(fqName.lastIndexOf('.') + 1);
|
||||
URI sourceUri = sourceUrl.get().toURI();
|
||||
Range r = cuCache.withCompilationUnit(project, sourceUri, (cu) -> {
|
||||
try {
|
||||
TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA);
|
||||
doc.setText(cuCache.fetchContent(sourceUri));
|
||||
AtomicReference<Range> range = new AtomicReference<>(null);
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
private boolean proceessTypeNode(TextDocument doc, String typeName,
|
||||
AtomicReference<Range> range, AbstractTypeDeclaration node) {
|
||||
SimpleName nameNode = node.getName();
|
||||
if (nameNode.getIdentifier().equals(typeName)) {
|
||||
try {
|
||||
range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength()));
|
||||
return false;
|
||||
} catch (BadLocationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
return proceessTypeNode(doc, typeName, range, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumDeclaration node) {
|
||||
return proceessTypeNode(doc, typeName, range, node);
|
||||
}
|
||||
|
||||
});
|
||||
return range.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Couldn't find " + fqName);
|
||||
}
|
||||
loc.setRange(r);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
private Location getLocation(IJavaProject project, JavaMethod method) throws Exception {
|
||||
Location loc = new Location();
|
||||
Optional<URL> sourceUrl = SourceLinks.source(project, method.fqName);
|
||||
if (sourceUrl.isPresent()) {
|
||||
|
||||
URI docUri = javaDocumentUriProvider.docUri(project, method.fqName);
|
||||
loc.setUri(docUri.toString());
|
||||
|
||||
URI sourceUri = sourceUrl.get().toURI();
|
||||
Range r = cuCache.withCompilationUnit(project, sourceUri, (cu) -> {
|
||||
try {
|
||||
AtomicReference<Range> range = new AtomicReference<>(null);
|
||||
TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA);
|
||||
doc.setText(cuCache.fetchContent(sourceUri));
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
SimpleName nameNode = node.getName();
|
||||
if (nameNode.getIdentifier().equals(method.methodName)) {
|
||||
if (node.parameters().size() != method.params.length) {
|
||||
return false;
|
||||
}
|
||||
int i = 0;
|
||||
for (Object _p : node.parameters()) {
|
||||
if (_p instanceof SingleVariableDeclaration) {
|
||||
SingleVariableDeclaration p = (SingleVariableDeclaration) _p;
|
||||
String fqName = p.getType().resolveBinding().getErasure().getQualifiedName();
|
||||
if (!fqName.equals(method.params[i++])) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength()));
|
||||
} catch (BadLocationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
return range.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Couldn't find " + method);
|
||||
}
|
||||
loc.setRange(r);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
private Location getLocation(IJavaProject project, JavaField field) throws Exception {
|
||||
Location loc = new Location();
|
||||
|
||||
Optional<URL> sourceUrl = SourceLinks.source(project, field.fqName);
|
||||
if (sourceUrl.isPresent()) {
|
||||
|
||||
URI sourceUri = sourceUrl.get().toURI();
|
||||
|
||||
URI docUri = javaDocumentUriProvider.docUri(project, field.fqName);
|
||||
loc.setUri(docUri.toString());
|
||||
|
||||
Range r = cuCache.withCompilationUnit(project, sourceUri, (cu) -> {
|
||||
try {
|
||||
AtomicReference<Range> range = new AtomicReference<>(null);
|
||||
TextDocument doc = new TextDocument(sourceUrl.get().toString(), LanguageId.JAVA);
|
||||
doc.setText(cuCache.fetchContent(sourceUri));
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
boolean foundType = false;
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumConstantDeclaration node) {
|
||||
if (foundType) {
|
||||
SimpleName nameNode = node.getName();
|
||||
if (nameNode.getIdentifier().equals(field.fieldName)) {
|
||||
try {
|
||||
range.set(doc.toRange(nameNode.getStartPosition(), nameNode.getLength()));
|
||||
} catch (BadLocationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumDeclaration node) {
|
||||
if (node.getName().getIdentifier()
|
||||
.equals(field.fqName.substring(field.fqName.lastIndexOf('.') + 1))) {
|
||||
foundType = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
return range.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
if (r == null) {
|
||||
throw new IllegalStateException("Couldn't find " + field);
|
||||
}
|
||||
loc.setRange(r);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user