Skip to content

Commit

Permalink
Add support for textDocument/typeDefinition from XML to XMLSchema/DTD
Browse files Browse the repository at this point in the history
Fix #371

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jul 10, 2019
1 parent 0c018a2 commit 24a79dc
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ public void triggerValidationIfNeeded() {
private boolean codeActionLiteralSupport;
private boolean hierarchicalDocumentSymbolSupport;
private boolean definitionLinkSupport;

private boolean typeDefinitionLinkSupport;

public XMLTextDocumentService(XMLLanguageServer xmlLanguageServer) {
this.xmlLanguageServer = xmlLanguageServer;
DOMParser parser = DOMParser.getInstance();
Expand All @@ -157,6 +158,9 @@ public void updateClientCapabilities(ClientCapabilities capabilities, ExtendedCl
definitionLinkSupport = textDocumentClientCapabilities.getDefinition() != null
&& textDocumentClientCapabilities.getDefinition().getLinkSupport() != null
&& textDocumentClientCapabilities.getDefinition().getLinkSupport();
typeDefinitionLinkSupport = textDocumentClientCapabilities.getTypeDefinition() != null
&& textDocumentClientCapabilities.getTypeDefinition().getLinkSupport() != null
&& textDocumentClientCapabilities.getTypeDefinition().getLinkSupport();
}
if (extendedClientCapabilities != null) {
// Extended client capabilities
Expand Down Expand Up @@ -304,6 +308,23 @@ public CompletableFuture<Either<List<? extends Location>, List<? extends Locatio
return Either.forLeft(locations);
});
}

@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> typeDefinition(
TextDocumentPositionParams params) {
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
if (typeDefinitionLinkSupport) {
return Either.forRight(
getXMLLanguageService().findTypeDefinition(xmlDocument, params.getPosition(), cancelChecker));
}
List<? extends Location> locations = getXMLLanguageService()
.findTypeDefinition(xmlDocument, params.getPosition(), cancelChecker) //
.stream() //
.map(locationLink -> XMLPositionUtility.toLocation(locationLink)) //
.collect(Collectors.toList());
return Either.forLeft(locations);
});
}

@Override
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@
import org.eclipse.lsp4xml.extensions.contentmodel.participants.ContentModelCompletionParticipant;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.ContentModelDocumentLinkParticipant;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.ContentModelHoverParticipant;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.ContentModelTypeDefinitionParticipant;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.diagnostics.ContentModelDiagnosticsParticipant;
import org.eclipse.lsp4xml.extensions.contentmodel.settings.ContentModelSettings;
import org.eclipse.lsp4xml.services.extensions.ICodeActionParticipant;
import org.eclipse.lsp4xml.services.extensions.ICompletionParticipant;
import org.eclipse.lsp4xml.services.extensions.IDocumentLinkParticipant;
import org.eclipse.lsp4xml.services.extensions.IHoverParticipant;
import org.eclipse.lsp4xml.services.extensions.ITypeDefinitionParticipant;
import org.eclipse.lsp4xml.services.extensions.IXMLExtension;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;
import org.eclipse.lsp4xml.services.extensions.diagnostics.IDiagnosticsParticipant;
import org.eclipse.lsp4xml.services.extensions.save.ISaveContext;
import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager;
import org.eclipse.lsp4xml.utils.DOMUtils;
Expand All @@ -32,22 +37,25 @@ public class ContentModelPlugin implements IXMLExtension {

private final IHoverParticipant hoverParticipant;

private final ContentModelDiagnosticsParticipant diagnosticsParticipant;
private final IDiagnosticsParticipant diagnosticsParticipant;

private final ContentModelCodeActionParticipant codeActionParticipant;
private final ICodeActionParticipant codeActionParticipant;

private final ContentModelDocumentLinkParticipant documentLinkParticipant;
private final IDocumentLinkParticipant documentLinkParticipant;

private final ITypeDefinitionParticipant typeDefinitionParticipant;

ContentModelManager contentModelManager;

private ContentModelSettings cmSettings;

public ContentModelPlugin() {
completionParticipant = new ContentModelCompletionParticipant();
hoverParticipant = new ContentModelHoverParticipant();
diagnosticsParticipant = new ContentModelDiagnosticsParticipant(this);
codeActionParticipant = new ContentModelCodeActionParticipant();
documentLinkParticipant = new ContentModelDocumentLinkParticipant();
typeDefinitionParticipant = new ContentModelTypeDefinitionParticipant();
}

@Override
Expand Down Expand Up @@ -96,8 +104,7 @@ private void updateSettings(ContentModelSettings settings, ISaveContext context)
}
if (settings.getFileAssociations() != null) {
// Update XML file associations
boolean fileAssociationsChanged = contentModelManager
.setFileAssociations(settings.getFileAssociations());
boolean fileAssociationsChanged = contentModelManager.setFileAssociations(settings.getFileAssociations());
if (fileAssociationsChanged) {
// Validate all opened XML files
context.collectDocumentToValidate(d -> {
Expand Down Expand Up @@ -127,6 +134,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerDiagnosticsParticipant(diagnosticsParticipant);
registry.registerCodeActionParticipant(codeActionParticipant);
registry.registerDocumentLinkParticipant(documentLinkParticipant);
registry.registerTypeDefinitionParticipant(typeDefinitionParticipant);
}

@Override
Expand All @@ -136,6 +144,7 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterDiagnosticsParticipant(diagnosticsParticipant);
registry.unregisterCodeActionParticipant(codeActionParticipant);
registry.unregisterDocumentLinkParticipant(documentLinkParticipant);
registry.unregisterTypeDefinitionParticipant(typeDefinitionParticipant);
}

public ContentModelSettings getContentModelSettings() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.eclipse.lsp4xml.extensions.contentmodel.participants;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.List;

import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4xml.dom.DOMAttr;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.dom.DOMElement;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.dom.DOMParser;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMDocument;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lsp4xml.extensions.xsd.utils.XSDUtils;
import org.eclipse.lsp4xml.services.extensions.ITypeDefinitionParticipant;
import org.eclipse.lsp4xml.services.extensions.ITypeDefinitionRequest;
import org.eclipse.lsp4xml.uriresolver.URIResolverExtensionManager;
import org.eclipse.lsp4xml.utils.URIUtils;
import org.eclipse.lsp4xml.utils.XMLPositionUtility;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.common.io.Files;

public class ContentModelTypeDefinitionParticipant implements ITypeDefinitionParticipant {

@Override
public void findTypeDefinition(ITypeDefinitionRequest request, List<LocationLink> locations,
CancelChecker cancelChecker) {
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
DOMNode node = request.getNode();
if (node == null) {
return;
}
if (node.isElement()) {
DOMElement element = (DOMElement) node;
CMDocument cmDocument = contentModelManager.findCMDocument(element.getOwnerDocument(),
element.getNamespaceURI());
if (cmDocument != null) {
CMElementDeclaration elementDeclaration = cmDocument.findCMElement(element, element.getNamespaceURI());
if (elementDeclaration != null) {
String documentURI = cmDocument.getURI();
if (URIUtils.isFileResource(documentURI)) {
URIResolverExtensionManager resolverExtensionManager = request
.getComponent(URIResolverExtensionManager.class);
try {

DOMDocument schema = DOMParser.getInstance().parse(
Files.asCharSource(new File(new URI(documentURI).getPath()), Charset.defaultCharset()).read(),
documentURI, resolverExtensionManager);
NodeList children = schema.getDocumentElement().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Element elt = (Element) n;
if (XSDUtils.isXSElement(elt)) {
if (element.getLocalName().equals(elt.getAttribute("name"))) {
DOMAttr targetAttr = (DOMAttr) elt.getAttributeNode("name");
LocationLink location = XMLPositionUtility.createLocationLink(element,
targetAttr.getNodeAttrValue());
locations.add(location);
}
}
}
}

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.err.println(elementDeclaration);
}
}

} else if (node.isAttribute()) {
DOMAttr attr = (DOMAttr) node;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*******************************************************************************
* Copyright (c) 2019 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lsp4xml.services;

import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4xml.commons.BadLocationException;
import org.eclipse.lsp4xml.dom.DOMAttr;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.services.extensions.ITypeDefinitionRequest;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;

/**
* Type definition request implementation.
*
*/
class TypeDefinitionRequest extends AbstractPositionRequest implements ITypeDefinitionRequest {

private final XMLExtensionsRegistry extensionsRegistry;

public TypeDefinitionRequest(DOMDocument xmlDocument, Position position, XMLExtensionsRegistry extensionsRegistry)
throws BadLocationException {
super(xmlDocument, position);
this.extensionsRegistry = extensionsRegistry;
}

@Override
protected DOMNode findNodeAt(DOMDocument xmlDocument, int offset) {
DOMNode node = xmlDocument.findNodeAt(offset);
if (node != null && node.isElement()) {
DOMAttr attr = xmlDocument.findAttrAt(node, offset);
if (attr != null) {
return attr;
}
}
return node;
}

@Override
public <T> T getComponent(Class clazz) {
return extensionsRegistry.getComponent(clazz);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public void checkCanceled() {
private final XMLFoldings foldings;
private final XMLDocumentLink documentLink;
private final XMLDefinition definition;
private final XMLTypeDefinition typeDefinition;
private final XMLReference reference;
private final XMLCodeLens codelens;
private final XMLCodeActions codeActions;
Expand All @@ -89,6 +90,7 @@ public XMLLanguageService() {
this.foldings = new XMLFoldings(this);
this.documentLink = new XMLDocumentLink(this);
this.definition = new XMLDefinition(this);
this.typeDefinition = new XMLTypeDefinition(this);
this.reference = new XMLReference(this);
this.codelens = new XMLCodeLens(this);
this.codeActions = new XMLCodeActions(this);
Expand Down Expand Up @@ -213,6 +215,11 @@ public List<? extends LocationLink> findDefinition(DOMDocument xmlDocument, Posi
return definition.findDefinition(xmlDocument, position, cancelChecker);
}

public List<? extends LocationLink> findTypeDefinition(DOMDocument xmlDocument, Position position,
CancelChecker cancelChecker) {
return typeDefinition.findTypeDefinition(xmlDocument, position, cancelChecker);
}

public List<? extends Location> findReferences(DOMDocument xmlDocument, Position position, ReferenceContext context,
CancelChecker cancelChecker) {
return reference.findReferences(xmlDocument, position, context, cancelChecker);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (c) 2019 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lsp4xml.services;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4xml.commons.BadLocationException;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.services.extensions.ITypeDefinitionParticipant;
import org.eclipse.lsp4xml.services.extensions.ITypeDefinitionRequest;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;

/**
* XML type definition support.
*
*/
class XMLTypeDefinition {

private static final Logger LOGGER = Logger.getLogger(XMLTypeDefinition.class.getName());

private final XMLExtensionsRegistry extensionsRegistry;

public XMLTypeDefinition(XMLExtensionsRegistry extensionsRegistry) {
this.extensionsRegistry = extensionsRegistry;
}

public List<? extends LocationLink> findTypeDefinition(DOMDocument document, Position position,
CancelChecker cancelChecker) {
ITypeDefinitionRequest request = null;
try {
request = new TypeDefinitionRequest(document, position, extensionsRegistry);
} catch (BadLocationException e) {
LOGGER.log(Level.SEVERE, "Failed creating TypeDefinitionRequest", e);
return Collections.emptyList();
}
List<LocationLink> locations = new ArrayList<>();
for (ITypeDefinitionParticipant participant : extensionsRegistry.getTypeDefinitionParticipants()) {
participant.findTypeDefinition(request, locations, cancelChecker);
}
return locations;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2019 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lsp4xml.services.extensions;

import java.util.List;

import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;

/**
* Type Definition participant API.
*
*/
public interface ITypeDefinitionParticipant {

/**
* Find type definition.
*
* @param document
* @param position
* @param locations
* @param cancelChecker
*/
void findTypeDefinition(ITypeDefinitionRequest request, List<LocationLink> locations, CancelChecker cancelChecker);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.eclipse.lsp4xml.services.extensions;

public interface ITypeDefinitionRequest extends IPositionRequest {

}
Loading

0 comments on commit 24a79dc

Please sign in to comment.