PT #165911996: Switch to LocationLink from Location for definitions

This commit is contained in:
BoykoAlex
2019-10-04 18:34:09 -04:00
parent 2e73b4b66f
commit bb79221cfe
18 changed files with 228 additions and 113 deletions

View File

@@ -51,6 +51,7 @@ import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;
import org.eclipse.lsp4j.Range;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
@@ -466,6 +467,9 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
if (project != null) {
Location location = new Location();
location.setUri(Utils.eclipseIntroUri(project.getElementName(), params.getBindingKey()).toString());
// Set the range because LocationLink needs it to be non-null. The target range
// would highlighted by the eclipse intro URL navigation anyway
location.setRange(new Range());
return location;
}
}

View File

@@ -15,14 +15,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.definition.SimpleDefinitionFinder;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
@@ -34,9 +38,9 @@ import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import reactor.core.publisher.Flux;
public class BoshDefintionFinder extends SimpleDefinitionFinder<SimpleLanguageServer> {
private static final Logger log = LoggerFactory.getLogger(BoshDefintionFinder.class);
//TODO: lots of common code between BoshDefintionFinder and ConcourseDefinitionFinder.
// should be possible to pull up into a common super class.
@@ -69,7 +73,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder<SimpleLanguageSe
}
@Override
public List<Location> handle(TextDocumentPositionParams params) {
public List<LocationLink> handle(TextDocumentPositionParams params) {
try {
TextDocument doc = server.getTextDocumentService().get(params);
if (doc!=null) {
@@ -81,14 +85,19 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder<SimpleLanguageSe
if (type!=null) {
Handler handler = handlers.get(type);
if (handler!=null) {
return handler.handle(refNode, doc, ast);
int start = refNode.getStartMark().getIndex();
int end = refNode.getEndMark().getIndex();
Range originalRange = doc.toRange(start, end - start);
return handler.handle(refNode, doc, ast).stream()
.map(l -> new LocationLink(l.getUri(), l.getRange(), l.getRange(), originalRange))
.collect(Collectors.toList());
}
}
}
}
}
} catch (Exception e) {
Log.log(e);
log.error("", e);;
}
return ImmutableList.of();
}
@@ -129,7 +138,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder<SimpleLanguageSe
try {
return Optional.of(new Location(doc.getUri(), doc.toRange(start, end-start)));
} catch (BadLocationException e) {
Log.log(e);
log.error("", e);;
return Optional.empty();
}
}

View File

@@ -41,7 +41,6 @@ import org.springframework.ide.vscode.bosh.models.ReleaseData;
import org.springframework.ide.vscode.bosh.models.ReleasesModel;
import org.springframework.ide.vscode.bosh.models.StemcellData;
import org.springframework.ide.vscode.bosh.models.StemcellsModel;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.ExternalCommand;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
@@ -1587,7 +1586,8 @@ public class BoshEditorTest {
"- name: some-release\n"
,
"some-release"
)
),
editor.rangeOf("- release: some-release", "some-release")
);
}
@@ -1612,7 +1612,8 @@ public class BoshEditorTest {
editor.assertGotoDefinition(
editor.positionOf("stemcell: windoze", "windoze"),
editor.rangeOf("- alias: windoze", "windoze")
editor.rangeOf("- alias: windoze", "windoze"),
editor.rangeOf("stemcell: windoze", "windoze")
);
}

View File

@@ -13,11 +13,13 @@ package org.springframework.ide.vscode.commons.languageserver.definition;
import java.util.Collections;
import java.util.List;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.DefinitionHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
@@ -27,6 +29,8 @@ import com.google.common.collect.ImmutableList;
* @author Kris De Volder
*/
public class SimpleDefinitionFinder<T extends SimpleLanguageServer> implements DefinitionHandler {
private static final Logger log = LoggerFactory.getLogger(SimpleDefinitionFinder.class);
protected final T server;
@@ -35,7 +39,7 @@ public class SimpleDefinitionFinder<T extends SimpleLanguageServer> implements D
}
@Override
public List<Location> handle(TextDocumentPositionParams params) {
public List<LocationLink> handle(TextDocumentPositionParams params) {
try {
TextDocument doc = server.getTextDocumentService().get(params);
if (doc != null) {
@@ -50,19 +54,18 @@ public class SimpleDefinitionFinder<T extends SimpleLanguageServer> implements D
end++;
}
String word = doc.textBetween(start, end);
Log.log("Looking for definition of '"+word+"'");
String text = doc.get();
int def = text.indexOf(word);
if (def>=0) {
Location loc = new Location(params.getTextDocument().getUri(),
doc.toRange(def, word.length())
Range targetRange = doc.toRange(def, word.length());
LocationLink link = new LocationLink(params.getTextDocument().getUri(),
targetRange, targetRange, doc.toRange(start, end - start)
);
Log.log("definition: "+loc);
return ImmutableList.of(loc);
return ImmutableList.of(link);
}
}
} catch (Exception e) {
Log.log(e);
log.error("", e);
}
return Collections.emptyList();
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2018 Pivotal, Inc.
* Copyright (c) 2017, 2019 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
@@ -12,10 +12,10 @@ package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.List;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.TextDocumentPositionParams;
@FunctionalInterface
public interface DefinitionHandler {
List<Location> handle(TextDocumentPositionParams position);
List<LocationLink> handle(TextDocumentPositionParams position);
}

View File

@@ -361,12 +361,12 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
DefinitionHandler h = this.definitionHandler;
if (h != null) {
return async.invoke(() -> {
List<Location> locations = h.handle(position);
List<LocationLink> locations = h.handle(position);
if (locations==null) {
// vscode client does not like to recieve null result. See: https://github.com/spring-projects/sts4/issues/309
locations = ImmutableList.of();
}
return Either.forLeft(locations);
return Either.forRight(locations);
});
}
return CompletableFuture.completedFuture(Either.forLeft(ImmutableList.of()));

View File

@@ -39,7 +39,7 @@ import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.MarkedString;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.Position;
@@ -745,7 +745,7 @@ public class Editor {
return "Editor(\n"+getText()+"\n)";
}
public void assertLinkTargets(String hoverOver, Set<Location> expectedLocations) throws Exception {
public void assertLinkTargets(String hoverOver, Set<LocationLink> expectedLocations) throws Exception {
int pos = getRawText().indexOf(hoverOver);
if (pos>=0) {
pos += hoverOver.length() / 2;
@@ -753,7 +753,7 @@ public class Editor {
assertTrue("Not found in editor: '"+hoverOver+"'", pos>=0);
TextDocumentPositionParams params = new TextDocumentPositionParams(new TextDocumentIdentifier(getUri()), doc.toPosition(pos));
List<? extends Location> definitions = harness.getDefinitions(params);
List<? extends LocationLink> definitions = harness.getDefinitions(params);
assertEquals(ImmutableSet.copyOf(expectedLocations), ImmutableSet.copyOf(definitions));
}
@@ -766,7 +766,7 @@ public class Editor {
assertTrue("Not found in editor: '"+hoverOver+"'", pos>=0);
TextDocumentPositionParams params = new TextDocumentPositionParams(new TextDocumentIdentifier(getUri()), doc.toPosition(pos));
List<? extends Location> definitions = harness.getDefinitions(params);
List<? extends LocationLink> definitions = harness.getDefinitions(params);
assertTrue(definitions == null || definitions.isEmpty());
}
@@ -822,12 +822,12 @@ public class Editor {
ignoredTypes.add(type.toString());
}
public void assertGotoDefinition(Position pos, Range expectedTarget) throws Exception {
public void assertGotoDefinition(Position pos, Range expectedTarget, Range highlightRange) throws Exception {
TextDocumentIdentifier textDocumentId = doc.getId();
TextDocumentPositionParams params = new TextDocumentPositionParams(textDocumentId, textDocumentId.getUri(), pos);
List<? extends Location> defs = harness.getDefinitions(params);
List<? extends LocationLink> defs = harness.getDefinitions(params);
assertEquals(1, defs.size());
assertEquals(new Location(textDocumentId.getUri(), expectedTarget), defs.get(0));
assertEquals(new LocationLink(textDocumentId.getUri(), expectedTarget, expectedTarget, highlightRange), defs.get(0));
}
/**

View File

@@ -35,7 +35,6 @@ import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -75,6 +74,7 @@ import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MessageActionItem;
import org.eclipse.lsp4j.MessageParams;
@@ -107,7 +107,6 @@ import org.springframework.ide.vscode.commons.languageserver.completion.Document
import org.springframework.ide.vscode.commons.languageserver.util.LanguageServerTestListener;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.WorkspaceSymbolHandler;
import org.springframework.ide.vscode.commons.protocol.CursorMovement;
import org.springframework.ide.vscode.commons.protocol.HighlightParams;
import org.springframework.ide.vscode.commons.protocol.ProgressParams;
@@ -727,9 +726,9 @@ public class LanguageServerHarness {
assertEquals(expected, completion.getLabel());
}
public List<? extends Location> getDefinitions(TextDocumentPositionParams params) throws Exception {
public List<? extends LocationLink> getDefinitions(TextDocumentPositionParams params) throws Exception {
waitForReconcile(); //goto definitions relies on reconciler infos! Must wait or race condition breaking tests occasionally.
return getServer().getTextDocumentService().definition(params).get().getLeft();
return getServer().getTextDocumentService().definition(params).get().getRight();
}
public static void assertDocumentation(String expected, CompletionItem completion) {

View File

@@ -14,13 +14,17 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.definition.SimpleDefinitionFinder;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
@@ -34,6 +38,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<SimpleLanguageServer> {
private static final Logger log = LoggerFactory.getLogger(ConcourseDefinitionFinder.class);
@FunctionalInterface
private interface Handler {
@@ -84,7 +90,7 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<SimpleLang
}
@Override
public List<Location> handle(TextDocumentPositionParams params) {
public List<LocationLink> handle(TextDocumentPositionParams params) {
try {
TextDocument doc = server.getTextDocumentService().get(params);
if (doc!=null) {
@@ -96,14 +102,19 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<SimpleLang
if (type!=null) {
Handler handler = handlers.get(type);
if (handler!=null) {
return handler.handle(refNode, doc, ast);
int start = refNode.getStartMark().getIndex();
int end = refNode.getEndMark().getIndex();
Range originalRange = doc.toRange(start, end - start);
return handler.handle(refNode, doc, ast).stream()
.map(l -> new LocationLink(l.getUri(), l.getRange(), l.getRange(), originalRange))
.collect(Collectors.toList());
}
}
}
}
}
} catch (Exception e) {
Log.log(e);
log.error("", e);;
}
return ImmutableList.of();
}
@@ -114,7 +125,7 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<SimpleLang
try {
return Optional.of(new Location(doc.getUri(), doc.toRange(start, end-start)));
} catch (BadLocationException e) {
Log.log(e);
log.error("", e);;
return Optional.empty();
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* Copyright (c) 2016, 2019 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
@@ -46,7 +46,6 @@ import org.springframework.ide.vscode.languageserver.testharness.CodeAction;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
import org.springframework.ide.vscode.languageserver.testharness.SynchronizationPoint;
import org.springframework.test.annotation.Repeat;
import org.springframework.test.context.junit4.SpringRunner;
import com.google.common.collect.ImmutableList;
@@ -3161,7 +3160,8 @@ public class ConcourseEditorTest {
);
editor.assertGotoDefinition(editor.positionOf("get: my-git", "my-git"),
editor.rangeOf("- name: my-git", "my-git")
editor.rangeOf("- name: my-git", "my-git"),
editor.rangeOf("get: my-git", "my-git")
);
}
@@ -3184,7 +3184,8 @@ public class ConcourseEditorTest {
" - prepare-stuff\n"
);
editor.assertGotoDefinition(editor.positionOf("- prepare-stuff", "prepare-stuff"),
editor.rangeOf("- name: prepare-stuff", "prepare-stuff")
editor.rangeOf("- name: prepare-stuff", "prepare-stuff"),
editor.rangeOf("- prepare-stuff", "prepare-stuff")
);
}
@@ -3199,7 +3200,8 @@ public class ConcourseEditorTest {
" type: slack-notification\n"
);
editor.assertGotoDefinition(editor.positionOf("type: slack-notification", "slack-notification"),
editor.rangeOf("- name: slack-notification", "slack-notification")
editor.rangeOf("- name: slack-notification", "slack-notification"),
editor.rangeOf("type: slack-notification", "slack-notification")
);
}

View File

@@ -12,10 +12,14 @@ package org.springframework.ide.vscode.boot.app;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.gradle.internal.impldep.com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
@@ -40,6 +44,8 @@ import org.springframework.stereotype.Component;
@Component
public class PropertiesJavaDefinitionHandler implements DefinitionHandler, LanguageSpecific {
private static final Logger log = LoggerFactory.getLogger(PropertiesJavaDefinitionHandler.class);
@Autowired
private SimpleTextDocumentService documents;
@@ -54,7 +60,7 @@ public class PropertiesJavaDefinitionHandler implements DefinitionHandler, Langu
private BootLanguageServerParams params;
@Override
public List<Location> handle(TextDocumentPositionParams position) {
public List<LocationLink> handle(TextDocumentPositionParams position) {
try {
TextDocument doc = documents.get(position);
TypeUtil typeUtil = params.typeUtilProvider.getTypeUtil(sourceLinks, doc);
@@ -67,24 +73,57 @@ public class PropertiesJavaDefinitionHandler implements DefinitionHandler, Langu
}
}
private List<Location> getDefinitions(FuzzyMap<PropertyInfo> index, TypeUtil typeUtil, TextDocument doc, int offset) {
private List<LocationLink> 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(typeUtil, 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());
try {
Range selectionRange = doc.toRange(node.getOffset(), node.getLength());
if (node instanceof Key) {
String propertyKey = ((Key) node).decode();
Collection<IMember> propertyJavaElements = PropertiesDefinitionCalculator.getPropertyJavaElements(typeUtil, propertyFinder, project, propertyKey);
// Attempt to highlight only chunk of the key for which definition is found
Range range = adjustedHighlightRangeForKey(doc, selectionRange, propertyFinder.findBestHoverMatch(propertyKey));
return PropertiesDefinitionCalculator.getLocations(javaElementLocationProvider, project, propertyJavaElements).stream()
.map(l -> new LocationLink(l.getUri(), l.getRange(), l.getRange(), range))
.collect(Collectors.toList());
} else if (node instanceof Value) {
Value value = (Value) node;
Key key = value.getParent().getKey();
Type type = PropertiesDefinitionCalculator.getPropertyType(propertyFinder, key.decode());
if (type != null) {
// Trim spaces from the value node text
Range range = trimHighlightRange(selectionRange, doc);
return PropertiesDefinitionCalculator.getValueDefinitionLocations(javaElementLocationProvider, typeUtil, type, value.decode()).stream()
.map(l -> new LocationLink(l.getUri(), l.getRange(), l.getRange(), range))
.collect(Collectors.toList());
}
}
} catch (BadLocationException e) {
log.error("", e);
}
return ImmutableList.of();
}
private Range adjustedHighlightRangeForKey(TextDocument doc, Range range, PropertyInfo propertyInfo) throws BadLocationException {
Range adjustedRange = trimHighlightRange(range, doc);
int start = doc.toOffset(range.getStart());
String id = propertyInfo.getId();
if (id.equals(doc.get(start, id.length()))) {
adjustedRange.setEnd(doc.toPosition(start + id.length()));
}
return adjustedRange;
}
private Range trimHighlightRange(Range range, TextDocument doc) throws BadLocationException {
int start = doc.toOffset(range.getStart());
for (; start < doc.getLength() && Character.isWhitespace(doc.getChar(start)); start++) {}
int end = start;
for (; end < doc.getLength() && !Character.isWhitespace(doc.getChar(end)); end++) {}
return doc.toRange(start, end - start);
}
@Override
public Collection<LanguageId> supportedLanguages() {
return ImmutableList.of(LanguageId.BOOT_PROPERTIES);

View File

@@ -48,6 +48,7 @@ import java.util.List;
import java.util.Map;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4xml.dom.DOMAttr;
import org.eclipse.lsp4xml.dom.DOMDocument;
@@ -139,7 +140,7 @@ public class XmlBeansConfigDefinitionHandler implements DefinitionHandler, Langu
}
@Override
public List<Location> handle(TextDocumentPositionParams position) {
public List<LocationLink> handle(TextDocumentPositionParams position) {
try {
if (config.isSpringXMLSupportEnabled() && config.areXmlHyperlinksEnabled()) {
TextDocument doc = documents.get(position);
@@ -175,11 +176,15 @@ public class XmlBeansConfigDefinitionHandler implements DefinitionHandler, Langu
List<? extends XMLHyperlinkProvider> providers = hyperlinkProviders.get(key);
if (providers != null) {
ImmutableList.Builder<Location> listBuilder = ImmutableList.builder();
ImmutableList.Builder<LocationLink> listBuilder = ImmutableList.builder();
for (XMLHyperlinkProvider provider : providers) {
Location location = provider.getDefinition(doc, namespace, node, attributeAt);
if (location != null) {
listBuilder.add(location);
int start = attributeAt.getNodeAttrValue().getStart() + 1;
int end = attributeAt.getNodeAttrValue().getEnd() - 1;
listBuilder.add(new LocationLink(location.getUri(),
location.getRange(), location.getRange(),
doc.toRange(start, Math.max(0, end - start))));
}
}
return listBuilder.build();

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* Copyright (c) 2018, 2019 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
@@ -12,8 +12,10 @@ package org.springframework.ide.vscode.boot.app;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -65,7 +67,7 @@ public class YamlPropertiesJavaDefinitionHandler implements DefinitionHandler, L
}
@Override
public List<Location> handle(TextDocumentPositionParams position) {
public List<LocationLink> handle(TextDocumentPositionParams position) {
try {
TextDocument doc = documents.get(position);
int offset = doc.toOffset(position.getPosition());
@@ -90,13 +92,21 @@ public class YamlPropertiesJavaDefinitionHandler implements DefinitionHandler, L
assistContext = assistPath.traverse(assistContext);
if (assistContext != null) {
Node node = astPath.get(astPath.size() - 1).get();
int start = node.getStartMark().getIndex();
int end = node.getEndMark().getIndex();
Range originalRange = doc.toRange(start, end - start);
if (path.pointsAtValue()) {
DocumentRegion nodeRegion = getNodeRegion(ast, offset);
if (nodeRegion != null) {
return assistContext.getDefinitionsForPropertyValue(nodeRegion);
return assistContext.getDefinitionsForPropertyValue(nodeRegion).stream()
.map(l -> new LocationLink(l.getUri(), l.getRange(), l.getRange(), originalRange))
.collect(Collectors.toList());
}
} else {
return assistContext.getDefinitionsForPropertyKey();
return assistContext.getDefinitionsForPropertyKey().stream()
.map(l -> new LocationLink(l.getUri(), l.getRange(), l.getRange(), originalRange))
.collect(Collectors.toList());
}
}
}

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.links;
import java.net.URI;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Range;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.IMember;
@@ -26,6 +27,7 @@ public class EclipseJavaElementLocationProvider implements JavaElementLocationPr
} else {
Location location = new Location();
location.setUri(uri.toString());
location.setRange(new Range());
return location;
}
}

View File

@@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.junit.Before;
@@ -95,7 +96,7 @@ public class XmlBeansHyperlinkTest {
"</beans>\n",
UriUtil.toUri(xmlFilePath.toFile()).toString()
);
definitionLinkAsserts.assertLinkTargets(editor, "u.t.r.SimpleObj", project, "u.t.r.SimpleObj");
definitionLinkAsserts.assertLinkTargets(editor, "u.t.r.SimpleObj", project, editor.rangeOf("u.t.r.SimpleObj", "u.t.r.SimpleObj"), "u.t.r.SimpleObj");
}
@Test
@@ -113,7 +114,9 @@ public class XmlBeansHyperlinkTest {
"</beans>\n",
UriUtil.toUri(xmlFilePath.toFile()).toString()
);
definitionLinkAsserts.assertLinkTargets(editor, "age", project, DefinitionLinkAsserts.method("u.t.r.TestBean", "setAge", "int"));
definitionLinkAsserts.assertLinkTargets(editor, "age", project,
editor.rangeOf("<property name=\"age\" value=\"10\" />", "age"),
DefinitionLinkAsserts.method("u.t.r.TestBean", "setAge", "int"));
}
@Test
@@ -131,7 +134,9 @@ public class XmlBeansHyperlinkTest {
"</beans>\n",
UriUtil.toUri(xmlFilePath.toFile()).toString()
);
definitionLinkAsserts.assertLinkTargets(editor, "message", project, DefinitionLinkAsserts.method("u.t.r.SuperTestBean", "setMessage", "java.lang.String"));
definitionLinkAsserts.assertLinkTargets(editor, "message", project,
editor.rangeOf("<property name=\"message\" value=\"Hello\" />", "message"),
DefinitionLinkAsserts.method("u.t.r.SuperTestBean", "setMessage", "java.lang.String"));
}
@Test
@@ -150,9 +155,13 @@ public class XmlBeansHyperlinkTest {
UriUtil.toUri(xmlFilePath.toFile()).toString()
);
Path rootContextFilePath = Paths.get(project.getLocationUri()).resolve("src/main/webapp/WEB-INF/spring/root-context.xml");
Location expectedLocation = new Location();
expectedLocation.setUri(UriUtil.toUri(rootContextFilePath.toFile()).toString());
expectedLocation.setRange(new Range(new Position(6,7), new Position(6, 21)));
Range targetRange = new Range(new Position(6,7), new Position(6, 21));
LocationLink expectedLocation = new LocationLink(
UriUtil.toUri(rootContextFilePath.toFile()).toString(),
targetRange,
targetRange,
editor.rangeOf("name=\"simple\" ref=\"simpleObj\"", "simpleObj")
);
editor.assertLinkTargets("simpleObj", Collections.singleton(expectedLocation));
}
@@ -216,9 +225,13 @@ public class XmlBeansHyperlinkTest {
UriUtil.toUri(xmlFilePath.toFile()).toString()
);
Path rootContextFilePath = Paths.get(project.getLocationUri()).resolve("src/main/webapp/WEB-INF/spring/root-context.xml");
Location expectedLocation = new Location();
expectedLocation.setUri(UriUtil.toUri(rootContextFilePath.toFile()).toString());
expectedLocation.setRange(new Range(new Position(6,7), new Position(6, 21)));
Range targetRange = new Range(new Position(6,7), new Position(6, 21));
LocationLink expectedLocation = new LocationLink(
UriUtil.toUri(rootContextFilePath.toFile()).toString(),
targetRange,
targetRange,
editor.rangeOf("name=\"simple\" ref=\"simpleObj\"", "simpleObj")
);
editor.assertLinkTargets("simpleObj", Collections.singleton(expectedLocation));
}

View File

@@ -395,16 +395,17 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
"flyway.init-sqls=a,b,c\n"
);
definitionLinkAsserts.assertLinkTargets(editor, "server", p,
definitionLinkAsserts.assertLinkTargets(editor, "server", p, editor.rangeOf("server.port", "server.port"),
method("org.springframework.boot.autoconfigure.web.ServerProperties", "setPort", "java.lang.Integer"));
definitionLinkAsserts.assertLinkTargets(editor, "data", p,
definitionLinkAsserts.assertLinkTargets(editor, "data", p, editor.rangeOf("spring.datasource.login-timeout", "spring.datasource.login-timeout"),
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "hikariDataSource"),
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "tomcatDataSource"),
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "dbcpDataSource")
);
definitionLinkAsserts.assertLinkTargets(editor, "flyway", p, method("org.springframework.boot.autoconfigure.flyway.FlywayProperties", "setInitSqls", "java.util.List"));
definitionLinkAsserts.assertLinkTargets(editor, "flyway", p, editor.rangeOf("flyway.init-sqls", "flyway.init-sqls"),
method("org.springframework.boot.autoconfigure.flyway.FlywayProperties", "setInitSqls", "java.util.List"));
System.out.println("<<< testHyperlinkTargets");
}
@@ -420,7 +421,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
"logging.level.com.acme=INFO\n"
);
definitionLinkAsserts.assertLinkTargets(editor, "level", p, "org.springframework.boot.logging.LoggingApplicationListener");
definitionLinkAsserts.assertLinkTargets(editor, "level", p, editor.rangeOf("logging.level", "logging.level"),
"org.springframework.boot.logging.LoggingApplicationListener");
System.out.println("<<< testHyperlinkTargetsLoggingLevel");
}
@@ -1642,7 +1644,9 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
"spring.data.mongodb.field-naming-strategy=org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n" +
"#more stuff"
);
definitionLinkAsserts.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,
editor.rangeOf("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy"),
"org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
//Linking should also work for types that aren't valid based on the constraints
@@ -1653,7 +1657,9 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
"spring.data.mongodb.field-naming-strategy=java.lang.String\n" +
"#more stuff"
);
definitionLinkAsserts.assertLinkTargets(editor, "java.lang.String", project, "java.lang.String");
definitionLinkAsserts.assertLinkTargets(editor, "java.lang.String", project,
editor.rangeOf("java.lang.String", "java.lang.String"),
"java.lang.String");
// Instead of java.lang.String
editor = newEditor(
@@ -1661,7 +1667,9 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
"spring.data.mongodb.field-naming-strategy=org.springframework.core.io.Resource\n" +
"#more stuff"
);
definitionLinkAsserts.assertLinkTargets(editor, "org.springframework.core.io.Resource", project, "org.springframework.core.io.Resource");
definitionLinkAsserts.assertLinkTargets(editor, "org.springframework.core.io.Resource", project,
editor.rangeOf("org.springframework.core.io.Resource", "org.springframework.core.io.Resource"),
"org.springframework.core.io.Resource");
}
@@ -1834,12 +1842,12 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
editor = newEditor(
"my.background: RED"
);
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, field("demo.Color", "RED"));
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, editor.rangeOf("RED", "RED"), field("demo.Color", "RED"));
editor = newEditor(
"my.background=red"
);
definitionLinkAsserts.assertLinkTargets(editor, "red", project, field("demo.Color", "RED"));
definitionLinkAsserts.assertLinkTargets(editor, "red", project, editor.rangeOf("red", "red"), field("demo.Color", "RED"));
}
@Test public void testEnumInPojoField() throws Exception {
@@ -1851,8 +1859,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest {
editor = newEditor(
"my.screen.background=green"
);
definitionLinkAsserts.assertLinkTargets(editor, "background", project, method("com.example.demo.MyProperties$Screen", "getScreen"));
definitionLinkAsserts.assertLinkTargets(editor, "green", project, field("com.example.demo.Color", "GREEN"));
definitionLinkAsserts.assertLinkTargets(editor, "background", project, editor.rangeOf("my.screen.background", "my.screen.background"), method("com.example.demo.MyProperties$Screen", "getScreen"));
definitionLinkAsserts.assertLinkTargets(editor, "green", project, editor.rangeOf("green", "green"), field("com.example.demo.Color", "GREEN"));
}
@Test public void testNoHoverForUnrecognizedProperty() throws Exception {

View File

@@ -26,7 +26,6 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -631,8 +630,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
" 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"));
definitionLinkAsserts.assertLinkTargets(editor, "data", project, editor.rangeOf("data:", "data"), method("demo.FooProperties", "setData", "demo.ColorData"));
definitionLinkAsserts.assertLinkTargets(editor, "wavelen", project, editor.rangeOf("wavelen:", "wavelen"), method("demo.ColorData", "setWavelen", "double"));
}
@Test
@@ -653,11 +652,11 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
//io.spring.initializr.metadata.DefaultMetadataElement.setDefault(boolean)
//io.spring.initializr.metadata.MetadataElement.setId(String)
//io.spring.initializr.metadata.MetadataElement.setName(String)
definitionLinkAsserts.assertLinkTargets(editor, "name", project, method("io.spring.initializr.metadata.MetadataElement", "setName", "java.lang.String"));
definitionLinkAsserts.assertLinkTargets(editor, "id", project, method("io.spring.initializr.metadata.MetadataElement", "setId", "java.lang.String"));
definitionLinkAsserts.assertLinkTargets(editor, "default", project, method("io.spring.initializr.metadata.DefaultMetadataElement", "setDefault", "boolean"));
definitionLinkAsserts.assertLinkTargets(editor, "name", project, editor.rangeOf("name:", "name"), method("io.spring.initializr.metadata.MetadataElement", "setName", "java.lang.String"));
definitionLinkAsserts.assertLinkTargets(editor, "id", project, editor.rangeOf("id:", "id"), method("io.spring.initializr.metadata.MetadataElement", "setId", "java.lang.String"));
definitionLinkAsserts.assertLinkTargets(editor, "default", project, editor.rangeOf("default:", "default"), method("io.spring.initializr.metadata.DefaultMetadataElement", "setDefault", "boolean"));
definitionLinkAsserts.assertLinkTargets(editor, "bogus", project /*NONE*/);
definitionLinkAsserts.assertLinkTargets(editor, "bogus", project, editor.rangeOf("bogus", "bogus") /*NONE*/);
}
@Test public void testHyperlinkTargets() throws Exception {
@@ -675,14 +674,17 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
);
definitionLinkAsserts.assertLinkTargets(editor, "port", p,
editor.rangeOf("port", "port"),
method("org.springframework.boot.autoconfigure.web.ServerProperties", "setPort", "java.lang.Integer")
);
definitionLinkAsserts.assertLinkTargets(editor, "login-", p,
editor.rangeOf("login-timeout", "login-timeout"),
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "hikariDataSource"),
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "tomcatDataSource"),
method("org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata", "dbcpDataSource")
);
definitionLinkAsserts.assertLinkTargets(editor, "init-sql", p,
editor.rangeOf("init-sqls", "init-sqls"),
method("org.springframework.boot.autoconfigure.flyway.FlywayProperties", "setInitSqls", "java.util.List"));
}
@@ -3858,7 +3860,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
" mongodb:\n" +
" field-naming-strategy: org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n"
);
definitionLinkAsserts.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,
editor.rangeOf("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy"),
"org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
editor = newEditor(
"spring:\n" +
@@ -3867,7 +3871,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
" field-naming-strategy:\n" +
" org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n"
);
definitionLinkAsserts.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,
editor.rangeOf("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy"),
"org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy");
//Linking should also work for types that aren't valid based on the constraints
editor = newEditor(
@@ -3877,7 +3883,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
" field-naming-strategy: java.lang.String\n" +
"#more stuff"
);
definitionLinkAsserts.assertLinkTargets(editor, "java.lang.String", project, "java.lang.String");
definitionLinkAsserts.assertLinkTargets(editor, "java.lang.String", project, editor.rangeOf("java.lang.String", "java.lang.String"), "java.lang.String");
}
@Test public void test_STS_3335_reconcile_list_nested_in_Map_of_String() throws Exception {
@@ -4022,13 +4028,13 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
"my:\n" +
" background: RED"
);
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, field("demo.Color", "RED"));
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, editor.rangeOf("RED", "RED"), field("demo.Color", "RED"));
editor = newEditor(
"my:\n" +
" background: red"
);
definitionLinkAsserts.assertLinkTargets(editor, "red", project, field("demo.Color", "RED"));
definitionLinkAsserts.assertLinkTargets(editor, "red", project, editor.rangeOf("red", "red"), field("demo.Color", "RED"));
}
@Test public void testHyperLinkEnumValueInMapKey() throws Exception {
@@ -4044,8 +4050,8 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
" RED: Rood\n" +
" green: Groen\n"
);
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, field("demo.Color", "RED"));
definitionLinkAsserts.assertLinkTargets(editor, "green", project, field("demo.Color", "GREEN"));
definitionLinkAsserts.assertLinkTargets(editor, "RED", project, editor.rangeOf("RED", "RED"), field("demo.Color", "RED"));
definitionLinkAsserts.assertLinkTargets(editor, "green", project, editor.rangeOf("green", "green"), field("demo.Color", "GREEN"));
editor = newEditor(
"spring:\n" +
@@ -4054,6 +4060,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
" INDENT_OUTPUT: true"
);
definitionLinkAsserts.assertLinkTargets(editor, "INDENT_OUTPUT", project,
editor.rangeOf("INDENT_OUTPUT", "INDENT_OUTPUT"),
field("com.fasterxml.jackson.databind.SerializationFeature", "INDENT_OUTPUT")
);
@@ -4064,6 +4071,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
" indent-output: true"
);
definitionLinkAsserts.assertLinkTargets(editor, "indent-output", project,
editor.rangeOf("indent-output", "indent-output"),
field("com.fasterxml.jackson.databind.SerializationFeature", "INDENT_OUTPUT")
);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* Copyright (c) 2018, 2019 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
@@ -27,6 +27,7 @@ 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.LocationLink;
import org.eclipse.lsp4j.Range;
import org.springframework.ide.vscode.boot.java.links.JavaDocumentUriProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
@@ -88,34 +89,34 @@ public class DefinitionLinkAsserts {
}
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, JavaMethod... methods) throws Exception {
Set<Location> expectedLocations = Arrays.stream(methods).map(method -> {
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, Range highlightRange, JavaMethod... methods) throws Exception {
Set<LocationLink> expectedLocations = Arrays.stream(methods).map(method -> {
try {
return getLocation(project, method);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}).collect(Collectors.toSet());
}).map(l -> new LocationLink(l.getUri(), l.getRange(), l.getRange(), highlightRange)).collect(Collectors.toSet());
editor.assertLinkTargets(hoverOver, expectedLocations);
}
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, String typeFqName) throws Exception {
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, Range highlightRange, String typeFqName) throws Exception {
Location expectedLocation = getLocation(project, typeFqName);
Location l = getLocation(project, typeFqName);
LocationLink link = new LocationLink(l.getUri(), l.getRange(), l.getRange(), highlightRange);
System.out.println("Expected Location: " + expectedLocation);
editor.assertLinkTargets(hoverOver, ImmutableSet.of(expectedLocation));
editor.assertLinkTargets(hoverOver, ImmutableSet.of(link));
}
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, JavaField field) throws Exception {
public void assertLinkTargets(Editor editor, String hoverOver, IJavaProject project, Range highlightRange, JavaField field) throws Exception {
Location expectedLocation = getLocation(project, field);
Location l = getLocation(project, field);
System.out.println("Expected Location: " + expectedLocation);
LocationLink link = new LocationLink(l.getUri(), l.getRange(), l.getRange(), highlightRange);
editor.assertLinkTargets(hoverOver, ImmutableSet.of(expectedLocation));
editor.assertLinkTargets(hoverOver, ImmutableSet.of(link));
}
private Location getLocation(IJavaProject project, String fqName) throws Exception {