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 26, 2020
1 parent d3dfc02 commit fca0abf
Show file tree
Hide file tree
Showing 16 changed files with 381 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,124 @@
/**
* 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.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.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 {
DOMDocument document = request.getXMLDocument();
int offset = request.getOffset();
String text = document.getText();
int entityReferenceStart = XMLPositionUtility.getEntityReferenceStartOffset(text, offset);
if (entityReferenceStart == -1) {
return null;
}
// the start offset is the amp character, ignore it
entityReferenceStart++;
int entityReferenceEnd = XMLPositionUtility.getEntityReferenceEndOffset(text, offset);
// Find hover of the entity
// abcd &loc|al; --> in this case search local entity
if (text.charAt(entityReferenceEnd - 1) == ';') {
entityReferenceEnd--;
}
String entityName = text.substring(entityReferenceStart, entityReferenceEnd);
Range entityRange = XMLPositionUtility.createRange(entityReferenceStart, entityReferenceEnd, document);
MarkupContent entityContents = searchInInternalEntities(entityName, entityRange, document, request);
if (entityContents == null) {
entityContents = searchInExternalEntities(entityName, entityRange, document, request);
}
if (entityContents != null) {
return new Hover(entityContents, entityRange);
}
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class HoverRequest extends AbstractPositionRequest implements IHoverRequest {

private final XMLHoverSettings settings;

private Range tagRange;
private Range hoverRange;

private boolean open;

Expand All @@ -53,12 +53,12 @@ protected DOMNode findNodeAt(DOMDocument xmlDocument, int offset) {
}

@Override
public Range getTagRange() {
return tagRange;
public Range getHoverRange() {
return hoverRange;
}

public void setTagRange(Range tagRange) {
this.tagRange = tagRange;
public void setHoverRange(Range hoverRange) {
this.hoverRange = hoverRange;
}

@Override
Expand Down
Loading

0 comments on commit fca0abf

Please sign in to comment.