Improved qualified name resolution in LSP editor.
Change-Id: Ic5e375907fcd99cb9b6b1a0d46d725ffc9bf3420
diff --git a/plugins/org.eclipse.acceleo.aql.ls/src/org/eclipse/acceleo/aql/ls/services/textdocument/AcceleoLocationLinkResolver.java b/plugins/org.eclipse.acceleo.aql.ls/src/org/eclipse/acceleo/aql/ls/services/textdocument/AcceleoLocationLinkResolver.java
index b0071b1..b6eb90b 100644
--- a/plugins/org.eclipse.acceleo.aql.ls/src/org/eclipse/acceleo/aql/ls/services/textdocument/AcceleoLocationLinkResolver.java
+++ b/plugins/org.eclipse.acceleo.aql.ls/src/org/eclipse/acceleo/aql/ls/services/textdocument/AcceleoLocationLinkResolver.java
@@ -19,6 +19,7 @@
import org.eclipse.acceleo.ASTNode;
import org.eclipse.acceleo.Module;
import org.eclipse.acceleo.aql.location.AcceleoLocationLinkToAcceleo;
+import org.eclipse.acceleo.aql.location.AcceleoLocationLinkToSourceLocation;
import org.eclipse.acceleo.aql.location.aql.AqlLocationLinkToAny;
import org.eclipse.acceleo.aql.location.aql.AqlLocationLinkToAql;
import org.eclipse.acceleo.aql.location.common.AbstractLocationLink;
@@ -81,6 +82,8 @@
locationLink = this.transform((AqlLocationLinkToAql)locationLinkToTransform);
} else if (locationLinkToTransform instanceof AqlLocationLinkToAny) {
locationLink = this.transform((AqlLocationLinkToAny)locationLinkToTransform);
+ } else if (locationLinkToTransform instanceof AcceleoLocationLinkToSourceLocation) {
+ locationLink = this.transform((AcceleoLocationLinkToSourceLocation)locationLinkToTransform);
} else {
throw new UnsupportedOperationException("Unsupported " + AbstractLocationLink.class
.getCanonicalName() + " implementation: " + locationLinkToTransform.toString());
@@ -111,6 +114,27 @@
}
/**
+ * Transforms an {@link AcceleoLocationLinkToSourceLocation} from Acceleo into a corresponding
+ * {@link LocationLink} for LSP4J.
+ *
+ * @param acceleoLocationLinkToSourceLocation
+ * the (non-{@code null}) {@link AcceleoLocationLinkToSourceLocation} to transform.
+ * @return the {@link LocationLink} corresponding to {@code acceleoLocationLinkToSourceLocation}.
+ */
+ private LocationLink transform(AcceleoLocationLinkToSourceLocation acceleoLocationLinkToSourceLocation) {
+ ASTNode linkOrigin = acceleoLocationLinkToSourceLocation.getOrigin();
+ AcceleoTextDocument originTextDocument = getAcceleoTextDocumentContaining(linkOrigin);
+ ASTNode linkOriginEquivalent = AcceleoAstUtils.getSelfOrEquivalentOf(linkOrigin, originTextDocument
+ .getAcceleoAstResult());
+
+ Range originSelectionRange = AcceleoLanguageServerPositionUtils.getRangeOf(linkOriginEquivalent,
+ originTextDocument.getAcceleoAstResult());
+
+ ISourceLocation targetSourceLocation = acceleoLocationLinkToSourceLocation.getDestination();
+ return this.createLocationLinkFromRangeToSourceLocation(originSelectionRange, targetSourceLocation);
+ }
+
+ /**
* Provides the {@link AcceleoTextDocument} containing the given Acceleo {@link ASTNode}.
*
* @param astNode
@@ -336,7 +360,6 @@
destinationNode, destinationAcceleoAstResult);
Range targetRange = AcceleoLanguageServerPositionUtils.getRangeOf(
destinationNodeInDestinationTextDocument, destinationAcceleoAstResult);
- // FIXME: we probably only want to select part of the target.
Range targetSelectionRange = AcceleoLanguageServerPositionUtils.getIdentifierRangeOf(
destinationNodeInDestinationTextDocument, destinationTextDocument.getAcceleoAstResult());
@@ -367,6 +390,36 @@
}
/**
+ * Creates a {@link LocationLink} from the given {@link Range} to the given Acceleo
+ * {@link ISourceLocation} destination.
+ *
+ * @param originSelectionRange
+ * the (non-{@code null}) origin selection {@link Range}.
+ * @param targetSourceLocation
+ * the (non-{@code null}) destination {@link ISourceLocation}.
+ * @return the {@link LocationLink} from {@code originSelectionRange} to {@code targetSourceLocation}.
+ */
+ private LocationLink createLocationLinkFromRangeToSourceLocation(Range originSelectionRange,
+ ISourceLocation targetSourceLocation) {
+
+ final Position rangeStart = new Position(targetSourceLocation.getRange().getStart().getLine(),
+ targetSourceLocation.getRange().getStart().getColumn());
+ final Position rangeEnd = new Position(targetSourceLocation.getRange().getEnd().getLine(),
+ targetSourceLocation.getRange().getEnd().getColumn());
+ final Range targetRange = new Range(rangeStart, rangeEnd);
+
+ final Position identifierStart = new Position(targetSourceLocation.getIdentifierRange().getStart()
+ .getLine(), targetSourceLocation.getIdentifierRange().getStart().getColumn());
+ final Position identifierEnd = new Position(targetSourceLocation.getIdentifierRange().getEnd()
+ .getLine(), targetSourceLocation.getIdentifierRange().getEnd().getColumn());
+ final Range targetSelectionRange = new Range(identifierStart, identifierEnd);
+
+ final LocationLink locationLink = new LocationLink(targetSourceLocation.getSourceURL().toString(),
+ targetRange, targetSelectionRange, originSelectionRange);
+ return locationLink;
+ }
+
+ /**
* Creates a {@link LocationLink} from the given {@link Range} to the given Java {@link Class}
* destination.
*
diff --git a/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/location/AcceleoDefinitionLocator.java b/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/location/AcceleoDefinitionLocator.java
index 1cc855e..48efdea 100644
--- a/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/location/AcceleoDefinitionLocator.java
+++ b/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/location/AcceleoDefinitionLocator.java
@@ -28,6 +28,7 @@
import org.eclipse.acceleo.Variable;
import org.eclipse.acceleo.aql.location.common.AbstractLocationLink;
import org.eclipse.acceleo.query.runtime.namespace.IQualifiedNameQueryEnvironment;
+import org.eclipse.acceleo.query.runtime.namespace.ISourceLocation;
import org.eclipse.acceleo.util.AcceleoSwitch;
/**
@@ -103,16 +104,20 @@
*/
@Override
public List<AbstractLocationLink<?, ?>> caseModuleReference(ModuleReference moduleReference) {
- final Object resolved = queryEnvironment.getLookupEngine().getResolver().resolve(moduleReference
- .getQualifiedName());
- if (resolved instanceof Module) {
- return Collections.singletonList(new AcceleoLocationLinkToAcceleo(moduleReference,
- (Module)resolved));
+ final List<AbstractLocationLink<?, ?>> res;
+
+ final ISourceLocation sourceLocation = queryEnvironment.getLookupEngine().getResolver()
+ .getSourceLocation(moduleReference.getQualifiedName());
+ if (sourceLocation != null) {
+ res = Collections.singletonList(new AcceleoLocationLinkToSourceLocation(moduleReference,
+ sourceLocation));
} else {
// Could not resolve the module reference, which means that we will not be able to find the
// definition of the referenced module.
- return null;
+ res = null;
}
+
+ return res;
}
@Override
diff --git a/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/location/AcceleoLocationLinkToSourceLocation.java b/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/location/AcceleoLocationLinkToSourceLocation.java
new file mode 100644
index 0000000..56d67cc
--- /dev/null
+++ b/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/location/AcceleoLocationLinkToSourceLocation.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.acceleo.aql.location;
+
+import org.eclipse.acceleo.ASTNode;
+import org.eclipse.acceleo.query.runtime.namespace.ISourceLocation;
+
+/**
+ * An {@link AbstractAcceleoLocationLink} that points to an {@link ASTNode}.
+ *
+ * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
+ */
+public class AcceleoLocationLinkToSourceLocation extends AbstractAcceleoLocationLink<ISourceLocation> {
+
+ /**
+ * The constructor.
+ *
+ * @param fromSemanticElement
+ * the (non-{@code null}) origin {@link ASTNode}.
+ * @param sourceLocation
+ * the (non-{@code null}) destination {@link ISourceLocation}.
+ */
+ public AcceleoLocationLinkToSourceLocation(ASTNode fromSemanticElement, ISourceLocation sourceLocation) {
+ super(fromSemanticElement, sourceLocation);
+ }
+
+}
diff --git a/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/parser/ModuleLoader.java b/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/parser/ModuleLoader.java
index bfbab51..58f356e 100644
--- a/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/parser/ModuleLoader.java
+++ b/plugins/org.eclipse.acceleo.aql/src/org/eclipse/acceleo/aql/parser/ModuleLoader.java
@@ -198,4 +198,47 @@
return res;
}
+ @Override
+ public ISourceLocation getSourceLocation(IQualifiedNameResolver resolver, String qualifiedName) {
+ final ISourceLocation res;
+
+ final Object resolved = resolver.resolve(qualifiedName);
+ if (resolved instanceof Module) {
+ final Module module = (Module)resolved;
+ final URL sourceURL = resolver.getSourceURL(qualifiedName);
+
+ final int identifierStartLine = module.getAst().getIdentifierStartLine(module);
+ final int identifierStartColumn = module.getAst().getIdentifierStartColumn(module);
+ final int identifierStartPosition = module.getAst().getIdentifierStartPosition(module);
+ final IPosition identifierStart = new Position(identifierStartLine, identifierStartColumn,
+ identifierStartPosition);
+
+ final int identifierEndLine = module.getAst().getIdentifierEndLine(module);
+ final int identifierEndColumn = module.getAst().getIdentifierEndColumn(module);
+ final int identifierEndPosition = module.getAst().getIdentifierEndPosition(module);
+ final IPosition identifierEnd = new Position(identifierEndLine, identifierEndColumn,
+ identifierEndPosition);
+
+ final IRange identifierRange = new Range(identifierStart, identifierEnd);
+
+ final int startLine = module.getAst().getStartLine(module);
+ final int startColumn = module.getAst().getStartColumn(module);
+ final int startPosition = module.getAst().getStartPosition(module);
+ final IPosition start = new Position(startLine, startColumn, startPosition);
+
+ final int endLine = module.getAst().getEndLine(module);
+ final int endColumn = module.getAst().getEndColumn(module);
+ final int endPosition = module.getAst().getEndPosition(module);
+ final IPosition end = new Position(endLine, endColumn, endPosition);
+
+ final IRange range = new Range(start, end);
+
+ res = new SourceLocation(sourceURL, identifierRange, range);
+ } else {
+ res = null;
+ }
+
+ return res;
+ }
+
}
diff --git a/query/plugins/org.eclipse.acceleo.query.ide.jdt/src/org/eclipse/acceleo/query/ide/jdt/EclipseJDTJavaLoader.java b/query/plugins/org.eclipse.acceleo.query.ide.jdt/src/org/eclipse/acceleo/query/ide/jdt/EclipseJDTJavaLoader.java
index 4d2ae89..0dfb04c 100644
--- a/query/plugins/org.eclipse.acceleo.query.ide.jdt/src/org/eclipse/acceleo/query/ide/jdt/EclipseJDTJavaLoader.java
+++ b/query/plugins/org.eclipse.acceleo.query.ide.jdt/src/org/eclipse/acceleo/query/ide/jdt/EclipseJDTJavaLoader.java
@@ -117,6 +117,68 @@
return res;
}
+ @Override
+ public ISourceLocation getSourceLocation(IQualifiedNameResolver resolver, String qualifiedName) {
+ ISourceLocation res = null;
+
+ IPosition identifierStart = new Position(0, 0, 0);
+ IPosition identifierEnd = new Position(0, 0, 0);
+ final IRange identifierRange;
+
+ IPosition start = new Position(0, 0, 0);
+ IPosition end = new Position(0, 0, 0);
+ final IRange range;
+
+ final Object resolved = resolver.resolve(qualifiedName);
+ if (resolved instanceof Class<?>) {
+ URL sourceURL = null;
+ if (resolver instanceof EclipseJDTQualifiedNameResolver) {
+ final IJavaProject project = ((EclipseJDTQualifiedNameResolver)resolver).getProject();
+ try {
+ final IType type = project.findType(((Class<?>)resolved).getCanonicalName());
+ if (type != null) {
+ type.getOpenable().open(new NullProgressMonitor());
+ sourceURL = type.getResource().getLocationURI().toURL();
+ final ISourceRange classIdentifierRange = type.getNameRange();
+ final ISourceRange sourceRange = type.getSourceRange();
+
+ final ASTParser parser = ASTParser.newParser(AST.JLS10);
+ parser.setSource(type.getCompilationUnit());
+ final CompilationUnit cu = (CompilationUnit)parser.createAST(null);
+ final int identifierStartOffset = classIdentifierRange.getOffset();
+ identifierStart = new Position(cu.getLineNumber(identifierStartOffset) - 1, cu
+ .getColumnNumber(identifierStartOffset), identifierStartOffset);
+ final int identifierEndOffset = identifierStartOffset + classIdentifierRange
+ .getLength();
+ identifierEnd = new Position(cu.getLineNumber(identifierEndOffset) - 1, cu
+ .getColumnNumber(identifierEndOffset), identifierEndOffset);
+
+ final int startOffset = sourceRange.getOffset();
+ start = new Position(cu.getLineNumber(startOffset) - 1, cu.getColumnNumber(
+ startOffset), startOffset);
+ final int endOffset = startOffset + sourceRange.getLength();
+ end = new Position(cu.getLineNumber(endOffset) - 1, cu.getColumnNumber(endOffset),
+ endOffset);
+
+ identifierRange = new Range(identifierStart, identifierEnd);
+ range = new Range(start, end);
+ res = new SourceLocation(sourceURL, identifierRange, range);
+ } else {
+ res = null;
+ }
+ } catch (JavaModelException | MalformedURLException e) {
+ // nothing to do here
+ }
+ } else {
+ // nothing to do here
+ }
+ } else {
+ res = null;
+ }
+
+ return res;
+ }
+
private String[] getParamterTypes(Method method) {
final List<String> res = new ArrayList<String>();
diff --git a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/ClassLoaderQualifiedNameResolver.java b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/ClassLoaderQualifiedNameResolver.java
index aed64ae..fa162e2 100644
--- a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/ClassLoaderQualifiedNameResolver.java
+++ b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/ClassLoaderQualifiedNameResolver.java
@@ -162,6 +162,20 @@
return res;
}
+ @Override
+ public ISourceLocation getSourceLocation(String qualifiedName) {
+ ISourceLocation res = null;
+
+ for (ILoader loader : loaders) {
+ res = loader.getSourceLocation(this, qualifiedName);
+ if (res != null) {
+ break;
+ }
+ }
+
+ return res;
+ }
+
/**
* Loads the {@link Object} from the given qualified name.
*
diff --git a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/JavaLoader.java b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/JavaLoader.java
index d076f09..caba2c2 100644
--- a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/JavaLoader.java
+++ b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/namespace/JavaLoader.java
@@ -68,4 +68,9 @@
return null;
}
+ @Override
+ public ISourceLocation getSourceLocation(IQualifiedNameResolver resolver, String qualifiedName) {
+ return null;
+ }
+
}
diff --git a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/ILoader.java b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/ILoader.java
index 212dc75..b5eae37 100644
--- a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/ILoader.java
+++ b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/ILoader.java
@@ -114,4 +114,15 @@
*/
ISourceLocation getSourceLocation(IQualifiedNameResolver resolver, IService<?> service);
+ /**
+ * Gets the {@link ISourceLocation} for the given qualified name.
+ *
+ * @param resolver
+ * the {@link IQualifiedNameResolver}
+ * @param qualifiedName
+ * the qualified name
+ * @return the {@link ISourceLocation} for the given qualified name if any, <code>null</code> otherwise
+ */
+ ISourceLocation getSourceLocation(IQualifiedNameResolver resolver, String qualifiedName);
+
}
diff --git a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/IQualifiedNameResolver.java b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/IQualifiedNameResolver.java
index f62da72..36827b6 100644
--- a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/IQualifiedNameResolver.java
+++ b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/namespace/IQualifiedNameResolver.java
@@ -61,6 +61,15 @@
ISourceLocation getSourceLocation(IService<?> service);
/**
+ * Gets the {@link ISourceLocation} for the given qualified name.
+ *
+ * @param qualifiedName
+ * the qualified name
+ * @return the {@link ISourceLocation} for the given qualified name if any, <code>null</code> otherwise
+ */
+ ISourceLocation getSourceLocation(String qualifiedName);
+
+ /**
* Clears the cache for the given {@link Set} of qualified names.
*
* @param qualifiedNames