Skip to content

Commit

Permalink
Hover for referenced entities
Browse files Browse the repository at this point in the history
Fixes eclipse#663

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed May 27, 2020
1 parent b0dd7ce commit bd7d58d
Show file tree
Hide file tree
Showing 16 changed files with 421 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/
package org.eclipse.lemminx.extensions.contentmodel.participants;

import static org.eclipse.lemminx.utils.MarkupContentFactory.aggregateContent;
import static org.eclipse.lemminx.utils.MarkupContentFactory.createHover;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -32,6 +32,7 @@
import org.eclipse.lemminx.utils.MarkupContentFactory;
import org.eclipse.lemminx.utils.MarkupContentFactory.IMarkupKindSupport;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;

Expand All @@ -42,7 +43,7 @@
public class ContentModelHoverParticipant extends HoverParticipantAdapter {

@Override
public String onTag(IHoverRequest hoverRequest) throws Exception {
public Hover onTag(IHoverRequest hoverRequest) throws Exception {
try {
ContentModelManager contentModelManager = hoverRequest.getComponent(ContentModelManager.class);
DOMElement element = (DOMElement) hoverRequest.getNode();
Expand All @@ -52,22 +53,22 @@ public String onTag(IHoverRequest hoverRequest) throws Exception {
return null;
}
// Compute element declaration documentation from bound grammars
List<String> contentValues = new ArrayList<>();
List<MarkupContent> contentValues = new ArrayList<>();
for (CMDocument cmDocument : cmDocuments) {
CMElementDeclaration cmElement = cmDocument.findCMElement(element);
if (cmElement != null) {
MarkupContent content = XMLGenerator.createMarkupContent(cmElement, hoverRequest);
fillHoverContent(content, contentValues);
}
}
return aggregateContent(contentValues, hoverRequest);
return createHover(contentValues);
} catch (CacheResourceDownloadingException e) {
return getCacheWarningHover(e, hoverRequest);
}
}

@Override
public String onAttributeName(IHoverRequest hoverRequest) throws Exception {
public Hover onAttributeName(IHoverRequest hoverRequest) throws Exception {
DOMAttr attribute = (DOMAttr) hoverRequest.getNode();
DOMElement element = attribute.getOwnerElement();
try {
Expand All @@ -79,7 +80,7 @@ public String onAttributeName(IHoverRequest hoverRequest) throws Exception {
}
String attributeName = attribute.getName();
// Compute attribute name declaration documentation from bound grammars
List<String> contentValues = new ArrayList<>();
List<MarkupContent> contentValues = new ArrayList<>();
for (CMDocument cmDocument : cmDocuments) {
CMElementDeclaration cmElement = cmDocument.findCMElement(element);
if (cmElement != null) {
Expand All @@ -90,20 +91,20 @@ public String onAttributeName(IHoverRequest hoverRequest) throws Exception {
}
}
}
return aggregateContent(contentValues, hoverRequest);
return createHover(contentValues);
} catch (CacheResourceDownloadingException e) {
return getCacheWarningHover(e, hoverRequest);
}
}

@Override
public String onAttributeValue(IHoverRequest hoverRequest) throws Exception {
public Hover onAttributeValue(IHoverRequest hoverRequest) throws Exception {
DOMAttr attribute = (DOMAttr) hoverRequest.getNode();

// Attempts to compute specifically for XSI related attributes since
// the XSD itself does not have enough information. Should create a mock XSD
// eventually.
String temp = XSISchemaModel.computeHoverResponse(attribute, hoverRequest);
Hover temp = XSISchemaModel.computeHoverResponse(attribute, hoverRequest);
if (temp != null) {
return temp;
}
Expand All @@ -119,7 +120,7 @@ public String onAttributeValue(IHoverRequest hoverRequest) throws Exception {
String attributeName = attribute.getName();
String attributeValue = attribute.getValue();
// Compute attribute value declaration documentation from bound grammars
List<String> contentValues = new ArrayList<>();
List<MarkupContent> contentValues = new ArrayList<>();
for (CMDocument cmDocument : cmDocuments) {
CMElementDeclaration cmElement = cmDocument.findCMElement(element);
if (cmElement != null) {
Expand All @@ -131,23 +132,23 @@ public String onAttributeValue(IHoverRequest hoverRequest) throws Exception {
}
}
}
return aggregateContent(contentValues, hoverRequest);
return createHover(contentValues);
} catch (CacheResourceDownloadingException e) {
return getCacheWarningHover(e, hoverRequest);
}
}

private static String getCacheWarningHover(CacheResourceDownloadingException e, IMarkupKindSupport support) {
private static Hover getCacheWarningHover(CacheResourceDownloadingException e, IMarkupKindSupport support) {
// Here cache is enabled and some XML Schema, DTD, etc are loading
MarkupContent content = MarkupContentFactory.createMarkupContent(
"Cannot process " + (e.isDTD() ? "DTD" : "XML Schema") + " hover: " + e.getMessage(),
MarkupKind.MARKDOWN, support);
return content.getValue();
return new Hover(content);
}

private static void fillHoverContent(MarkupContent content, List<String> hoverContent) {
private static void fillHoverContent(MarkupContent content, List<MarkupContent> contents) {
if (content != null && !StringUtils.isEmpty(content.getValue())) {
hoverContent.add(content.getValue());
contents.add(content);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

import org.eclipse.lemminx.extensions.entities.participants.EntitiesCompletionParticipant;
import org.eclipse.lemminx.extensions.entities.participants.EntitiesDefinitionParticipant;
import org.eclipse.lemminx.extensions.entities.participants.EntitiesHoverParticipant;
import org.eclipse.lemminx.services.extensions.ICompletionParticipant;
import org.eclipse.lemminx.services.extensions.IDefinitionParticipant;
import org.eclipse.lemminx.services.extensions.IHoverParticipant;
import org.eclipse.lemminx.services.extensions.IXMLExtension;
import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry;
import org.eclipse.lemminx.services.extensions.save.ISaveContext;
Expand All @@ -30,18 +32,23 @@ public class EntitiesPlugin implements IXMLExtension {

private IDefinitionParticipant definitionParticipant;

private IHoverParticipant hoverParticipant;

@Override
public void start(InitializeParams params, XMLExtensionsRegistry registry) {
completionParticipant = new EntitiesCompletionParticipant();
registry.registerCompletionParticipant(completionParticipant);
definitionParticipant = new EntitiesDefinitionParticipant();
registry.registerDefinitionParticipant(definitionParticipant);
hoverParticipant = new EntitiesHoverParticipant();
registry.registerHoverParticipant(hoverParticipant);
}

@Override
public void stop(XMLExtensionsRegistry registry) {
registry.unregisterCompletionParticipant(completionParticipant);
registry.unregisterDefinitionParticipant(definitionParticipant);
registry.unregisterHoverParticipant(hoverParticipant);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* Copyright (c) 2020 Red Hat, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*/
package org.eclipse.lemminx.extensions.entities.participants;

import java.util.Collection;
import java.util.List;

import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMDocumentType;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DTDEntityDecl;
import org.eclipse.lemminx.extensions.contentmodel.model.CMDocument;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.extensions.entities.EntitiesDocumentationUtils;
import org.eclipse.lemminx.services.extensions.HoverParticipantAdapter;
import org.eclipse.lemminx.services.extensions.IHoverRequest;
import org.eclipse.lemminx.utils.XMLPositionUtility;
import org.eclipse.lemminx.utils.XMLPositionUtility.EntityReferenceRange;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;
import org.eclipse.lsp4j.Range;
import org.w3c.dom.Entity;
import org.w3c.dom.NamedNodeMap;

/**
* Entities hover used in a text node (ex : &amp;).
*
*/
public class EntitiesHoverParticipant extends HoverParticipantAdapter {

@Override
public Hover onText(IHoverRequest request) throws Exception {
DOMNode node = request.getNode();
if (!node.isText()) {
return null;
}
// Hover is done in a text node, check if it's a entity reference
DOMDocument document = request.getXMLDocument();
int offset = request.getOffset();
EntityReferenceRange entityRange = XMLPositionUtility.selectEntityReference(offset, document);
if (entityRange == null) {
return null;
}
// The hovered text follows the entity reference syntax (ex : &amp;)
String entityName = entityRange.getName();
Range range = entityRange.getRange();
// Try to find the entity
MarkupContent entityContents = searchInEntities(entityName, range, document, request);
if (entityContents != null) {
return new Hover(entityContents, range);
}
return null;
}

/**
* Returns the markup content of the given entity name in the predefined,
* internal and external entities and null otherwise.
*
* @param entityName the entity name to search.
* @param range the hovered range.
* @param document the DOM document
* @param request the hover request.
* @return the markup content of the given entity name in the predefined,
* internal and external entities and null otherwise.
*/
private static MarkupContent searchInEntities(String entityName, Range range, DOMDocument document,
IHoverRequest request) {
MarkupContent entityContents = searchInPredefinedEntities(entityName, range, document, request);
if (entityContents != null) {
return entityContents;
}
entityContents = searchInInternalEntities(entityName, range, document, request);
if (entityContents != null) {
return entityContents;
}
return searchInExternalEntities(entityName, range, document, request);
}

/**
* Returns the markup content of the given entity name in the predefined
* entities and null otherwise.
*
* @param entityName the entity name to search.
* @param range the hovered range.
* @param document the DOM document
* @param request the hover request.
* @return the markup content of the given entity name in the predefined
* entities and null otherwise.
*/
private static MarkupContent searchInPredefinedEntities(String entityName, Range range, DOMDocument document,
IHoverRequest request) {
// TODO Auto-generated method stub
return null;
}

/**
* Search the given entity name in the internal entities.
*
* @param document the DOM document.
* @param entityName the entity name.
* @param entityRange the entity range.
* @param locations the location links
*/
private static MarkupContent searchInInternalEntities(String entityName, Range entityRange, DOMDocument document,
IHoverRequest request) {
DOMDocumentType docType = document.getDoctype();
if (docType == null) {
return null;
}
// Loop for entities declared in the DOCTYPE of the document
NamedNodeMap entities = docType.getEntities();
for (int i = 0; i < entities.getLength(); i++) {
DTDEntityDecl entity = (DTDEntityDecl) entities.item(i);
if (entityName.equals(entity.getName())) {
boolean markdown = request.canSupportMarkupKind(MarkupKind.MARKDOWN);
return EntitiesDocumentationUtils.getDocumentation(entity.getName(), entity.getNotationName(), true,
false, markdown);
}
}
return null;
}

/**
* Search the given entity name in the external entities.
*
* @param document the DOM document.
* @param entityName the entity name.
* @param entityRange the entity range.
* @param locations the location links
* @param request the definition request.
* @return
*/
private static MarkupContent searchInExternalEntities(String entityName, Range entityRange, DOMDocument document,
IHoverRequest request) {
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
Collection<CMDocument> cmDocuments = contentModelManager.findCMDocument(document, null, false);
for (CMDocument cmDocument : cmDocuments) {
List<Entity> entities = cmDocument.getEntities();
for (Entity ent : entities) {
DTDEntityDecl entity = (DTDEntityDecl) ent;
if (entityName.equals(entity.getName())) {
boolean markdown = request.canSupportMarkupKind(MarkupKind.MARKDOWN);
return EntitiesDocumentationUtils.getDocumentation(entity.getName(), entity.getNotationName(), true,
false, markdown);
}
}
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,17 @@
import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.services.extensions.HoverParticipantAdapter;
import org.eclipse.lemminx.services.extensions.IHoverRequest;
import org.eclipse.lsp4j.Hover;

/**
* XSIHoverParticipant
*/
public class XSIHoverParticipant extends HoverParticipantAdapter{
public class XSIHoverParticipant extends HoverParticipantAdapter {

@Override
public String onAttributeName(IHoverRequest request) throws Exception {

public Hover onAttributeName(IHoverRequest request) throws Exception {
DOMAttr attribute = (DOMAttr) request.getNode();
return XSISchemaModel.computeHoverResponse(attribute, request);
}

@Override
public String onAttributeValue(IHoverRequest request) throws Exception {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;
import org.eclipse.lsp4j.Range;
Expand Down Expand Up @@ -212,7 +213,7 @@ private static boolean hasAttribute(DOMElement root, String name) {
return hasAttribute(root, null, name);
}

public static String computeHoverResponse(DOMAttr attribute, IHoverRequest request) {
public static Hover computeHoverResponse(DOMAttr attribute, IHoverRequest request) {

String name = attribute.getName();
if(!name.startsWith(request.getXMLDocument().getSchemaInstancePrefix() + ":")) {
Expand Down Expand Up @@ -246,6 +247,9 @@ else if(name.endsWith(":type")) {
}
}

return doc;
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.MARKDOWN);
content.setValue(doc);
return new Hover(content);
}
}
Loading

0 comments on commit bd7d58d

Please sign in to comment.