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 47ba73d commit ead786b
Show file tree
Hide file tree
Showing 24 changed files with 601 additions and 213 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,11 @@ public Collection<CMDocument> findCMDocument(DOMDocument xmlDocument, String nam
}

/**
* Returns the declared documents which match the given DOM document and null
* otherwise.
* Returns the declared documents which match the given DOM document.
*
* @param xmlDocument the DOM document.
* @param namespaceURI the namespace URI
* @return the declared documents which match the given DOM document and null
* otherwise.
* @return the declared documents which match the given DOM document.
*/
public Collection<CMDocument> findCMDocument(DOMDocument xmlDocument, String namespaceURI, boolean withInternal) {
Collection<CMDocument> documents = new ArrayList<>();
Expand Down
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.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lemminx.utils.MarkupContentFactory;
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, ISharedSettingsRequest support) {
private static Hover getCacheWarningHover(CacheResourceDownloadingException e, ISharedSettingsRequest 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 @@ -21,6 +21,21 @@
*/
public class EntitiesDocumentationUtils {

public static enum EntityOriginType {

PREDEFINED("Predefined"), LOCAL("Local"), EXTERNAL("External");

private final String label;

private EntityOriginType(String label) {
this.label = label;
}

public String getLabel() {
return label;
}
}

/**
* Predefined entities.
*
Expand All @@ -37,6 +52,10 @@ private PredefinedEntity(String value) {
this.value = value;
}

public String getName() {
return name();
}

public String getValue() {
return value;
}
Expand All @@ -50,14 +69,13 @@ private EntitiesDocumentationUtils() {
*
* @param entityName the entity name.
* @param entityValue the entity value.
* @param external true if it's an external entity and false otherwise.
* @param predefined true if it's an predefined entity and false otherwise.
* @param type the entity type (local, external or predefined)
* @param markdown true if the documentation can be formatted as markdown and
* false otherwise.
* @return the entity documentation.
*/
public static MarkupContent getDocumentation(String entityName, String entityValue, boolean external,
boolean predefined, boolean markdown) {
public static MarkupContent getDocumentation(String entityName, String entityValue, EntityOriginType type,
boolean markdown) {
StringBuilder documentation = new StringBuilder();

// Title
Expand All @@ -73,10 +91,7 @@ public static MarkupContent getDocumentation(String entityName, String entityVal
if (entityValue != null && !entityValue.isEmpty()) {
addParameter("Value", entityValue, documentation, markdown);
}
addParameter("External", String.valueOf(external), documentation, markdown);
addParameter("Predefined", String.valueOf(predefined), documentation, markdown);

documentation.append(System.lineSeparator());
addParameter("Type", type.getLabel(), documentation, markdown);
return new MarkupContent(markdown ? MarkupKind.MARKDOWN : MarkupKind.PLAINTEXT, documentation.toString());
}

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
Expand Up @@ -20,6 +20,7 @@
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.extensions.entities.EntitiesDocumentationUtils.EntityOriginType;
import org.eclipse.lemminx.extensions.entities.EntitiesDocumentationUtils.PredefinedEntity;
import org.eclipse.lemminx.services.extensions.CompletionParticipantAdapter;
import org.eclipse.lemminx.services.extensions.ICompletionRequest;
Expand Down Expand Up @@ -52,22 +53,21 @@ public void onXMLContent(ICompletionRequest request, ICompletionResponse respons
// There is the '&' character before the offset where completion was triggered
boolean markdown = request.canSupportMarkupKind(MarkupKind.MARKDOWN);
DOMDocument document = request.getXMLDocument();
collectInternalEntityProposals(document, range, markdown, response);
collectLocalEntityProposals(document, range, markdown, response);
collectExternalEntityProposals(document, range, markdown, request, response);
collectPredefinedEntityProposals(range, markdown, request, response);
collectPredefinedEntityProposals(range, markdown, response);
}

/**
* Collect internal entities.
* Collect local entities declared in the DOCTYPE.
*
* @param document the DOM document.
* @param entityRange the entity range.
* @param markdown true if the documentation can be formatted as markdown and
* false otherwise.
* @param request the completion request.
* @param response the completion response.
*/
private static void collectInternalEntityProposals(DOMDocument document, Range entityRange, boolean markdown,
private static void collectLocalEntityProposals(DOMDocument document, Range entityRange, boolean markdown,
ICompletionResponse response) {
DOMDocumentType docType = document.getDoctype();
if (docType == null) {
Expand All @@ -78,8 +78,8 @@ private static void collectInternalEntityProposals(DOMDocument document, Range e
Entity entity = (Entity) entities.item(i);
if (entity.getNodeName() != null) {
// provide completion for the locally declared entity
fillCompletion(entity.getNodeName(), entity.getNotationName(), false, false, entityRange, markdown,
response);
fillCompletion(entity.getNodeName(), entity.getNotationName(), EntityOriginType.LOCAL, entityRange,
markdown, response);
}
}
}
Expand All @@ -103,8 +103,8 @@ private static void collectExternalEntityProposals(DOMDocument document, Range e
for (Entity entity : entities) {
if (entity.getNodeName() != null) {
// provide completion for the external declared entity
fillCompletion(entity.getNodeName(), entity.getNotationName(), true, false, entityRange, markdown,
response);
fillCompletion(entity.getNodeName(), entity.getNotationName(), EntityOriginType.EXTERNAL,
entityRange, markdown, response);
}
}
}
Expand All @@ -116,21 +116,20 @@ private static void collectExternalEntityProposals(DOMDocument document, Range e
* @param entityRange the entity range.
* @param markdown true if the documentation can be formatted as markdown and
* false otherwise.
* @param request the completion request.
* @param response the completion response.
*
* @see https://www.w3.org/TR/xml/#sec-predefined-ent
*/
private void collectPredefinedEntityProposals(Range entityRange, boolean markdown, ICompletionRequest request,
ICompletionResponse response) {
private void collectPredefinedEntityProposals(Range entityRange, boolean markdown, ICompletionResponse response) {
PredefinedEntity[] entities = PredefinedEntity.values();
for (PredefinedEntity entity : entities) {
fillCompletion(entity.name(), entity.getValue(), false, true, entityRange, markdown, response);
fillCompletion(entity.getName(), entity.getValue(), EntityOriginType.PREDEFINED, entityRange, markdown,
response);
}
}

private static void fillCompletion(String name, String entityValue, boolean external, boolean predefined,
Range entityRange, boolean markdown, ICompletionResponse response) {
private static void fillCompletion(String name, String entityValue, EntityOriginType type, Range entityRange,
boolean markdown, ICompletionResponse response) {
String entityName = "&" + name + ";";
CompletionItem item = new CompletionItem();
item.setLabel(entityName);
Expand All @@ -139,8 +138,7 @@ private static void fillCompletion(String name, String entityValue, boolean exte
String insertText = entityName;
item.setFilterText(insertText);
item.setTextEdit(new TextEdit(entityRange, insertText));
item.setDocumentation(
EntitiesDocumentationUtils.getDocumentation(name, entityValue, external, predefined, markdown));
item.setDocumentation(EntitiesDocumentationUtils.getDocumentation(name, entityValue, type, markdown));
response.addCompletionItem(item);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,28 @@ protected void doFindDefinition(IDefinitionRequest request, List<LocationLink> l
if (!node.isText()) {
return;
}
// definition is done in a text node, check if it's a referenced entity
// Definition 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) {
String entityName = entityRange.getName();
Range range = entityRange.getRange();
searchInInternalEntities(entityName, range, document, locations, cancelChecker);
searchInLocalEntities(entityName, range, document, locations, cancelChecker);
searchInExternalEntities(entityName, range, document, locations, request, cancelChecker);
}
}

/**
* Search the given entity name in the internal entities.
* Search the given entity name in the local entities.
*
* @param document the DOM document.
* @param entityName the entity name.
* @param entityRange the entity range.
* @param locations the location links
* @param cancelChecker the cancel checker.
*/
private static void searchInInternalEntities(String entityName, Range entityRange, DOMDocument document,
private static void searchInLocalEntities(String entityName, Range entityRange, DOMDocument document,
List<LocationLink> locations, CancelChecker cancelChecker) {
DOMDocumentType docType = document.getDoctype();
if (docType == null) {
Expand Down
Loading

0 comments on commit ead786b

Please sign in to comment.