Skip to content

Commit

Permalink
Completion should support markdown for documentation
Browse files Browse the repository at this point in the history
Fix #526

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jul 22, 2019
1 parent 5059e2e commit 0f276b8
Show file tree
Hide file tree
Showing 13 changed files with 392 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4xml.commons.BadLocationException;
Expand Down Expand Up @@ -42,7 +43,6 @@ public class ContentModelCompletionParticipant extends CompletionParticipantAdap
public void onTagOpen(ICompletionRequest request, ICompletionResponse response) throws Exception {
try {
DOMDocument document = request.getXMLDocument();
String schemaURI;
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
DOMElement parentElement = request.getParentElement();
if (parentElement == null) {
Expand All @@ -51,9 +51,7 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response)
// XML Schema is done with pattern and not with XML root element)
CMDocument cmDocument = contentModelManager.findCMDocument(document, null);
if (cmDocument != null) {
schemaURI = cmDocument.getURI();
fillWithChildrenElementDeclaration(null, cmDocument.getElements(), null, false, request, response,
schemaURI);
fillWithChildrenElementDeclaration(null, cmDocument.getElements(), null, false, request, response);
}
return;
}
Expand All @@ -62,14 +60,14 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response)
final CMDocument cmRootDocument = contentModelManager.findCMDocument(parentElement,
parentElement.getNamespaceURI());

schemaURI = cmRootDocument != null ? cmRootDocument.getURI() : null;
CMElementDeclaration cmElement = contentModelManager.findCMElement(parentElement);
String defaultPrefix = null;

if (cmElement != null) {
defaultPrefix = parentElement.getPrefix();
fillWithChildrenElementDeclaration(parentElement, cmElement.getPossibleElements(parentElement, request.getOffset()),
defaultPrefix, false, request, response, schemaURI);
fillWithChildrenElementDeclaration(parentElement,
cmElement.getPossibleElements(parentElement, request.getOffset()), defaultPrefix, false,
request, response);
}
if (parentElement.isDocumentElement()) {
// completion on root document element
Expand All @@ -85,7 +83,7 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response)
CMDocument cmDocument = contentModelManager.findCMDocument(parentElement, namespaceURI);
if (cmDocument != null) {
fillWithChildrenElementDeclaration(parentElement, cmDocument.getElements(), prefix, true,
request, response, cmRootDocument.getURI());
request, response);
}
}
}
Expand All @@ -96,28 +94,26 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response)
if (cmInternalElement != null) {
defaultPrefix = parentElement.getPrefix();
fillWithChildrenElementDeclaration(parentElement,
cmInternalElement.getPossibleElements(parentElement, request.getOffset()), defaultPrefix, false, request,
response, schemaURI);
cmInternalElement.getPossibleElements(parentElement, request.getOffset()), defaultPrefix, false,
request, response);
}
} catch (CacheResourceDownloadingException e) {
// XML Schema, DTD is loading, ignore this error
}
}

private void fillWithChildrenElementDeclaration(DOMElement element, Collection<CMElementDeclaration> cmElements,
String p, boolean forceUseOfPrefix, ICompletionRequest request, ICompletionResponse response,
String schemaURI) throws BadLocationException {
String p, boolean forceUseOfPrefix, ICompletionRequest request, ICompletionResponse response)
throws BadLocationException {
XMLGenerator generator = request.getXMLGenerator();
for (CMElementDeclaration child : cmElements) {
String prefix = forceUseOfPrefix ? p : (element != null ? element.getPrefix(child.getNamespace()) : null);
String label = child.getName(prefix);
CompletionItem item = new CompletionItem(label);
item.setFilterText(request.getFilterForStartTagName(label));
item.setKind(CompletionItemKind.Property);
String detail = XMLGenerator.generateDocumentation(child.getDocumentation(), schemaURI);
if (detail != null) {
item.setDetail(detail);
}
MarkupContent documentation = XMLGenerator.createMarkupContent(child, request);
item.setDocumentation(documentation);
String xml = generator.generate(child, prefix);
item.setTextEdit(new TextEdit(request.getReplaceRange(), xml));
item.setInsertTextFormat(InsertTextFormat.Snippet);
Expand All @@ -141,19 +137,19 @@ public void onAttributeName(boolean generateValue, ICompletionRequest request, I
// Completion on attribute based on external grammar
CMElementDeclaration cmElement = contentModelManager.findCMElement(parentElement);
fillAttributesWithCMAttributeDeclarations(parentElement, fullRange, cmElement, canSupportSnippet,
generateValue, response, formattingSettings);
generateValue, request, response, formattingSettings);
// Completion on attribute based on internal grammar
cmElement = contentModelManager.findInternalCMElement(parentElement);
fillAttributesWithCMAttributeDeclarations(parentElement, fullRange, cmElement, canSupportSnippet,
generateValue, response, formattingSettings);
generateValue, request, response, formattingSettings);
} catch (CacheResourceDownloadingException e) {
// XML Schema, DTD is loading, ignore this error
}
}

private void fillAttributesWithCMAttributeDeclarations(DOMElement parentElement, Range fullRange,
CMElementDeclaration cmElement, boolean canSupportSnippet, boolean generateValue,
ICompletionResponse response, XMLFormattingOptions formattingOptions) {
ICompletionRequest request, ICompletionResponse response, XMLFormattingOptions formattingOptions) {
if (cmElement == null) {
return;
}
Expand All @@ -166,10 +162,8 @@ private void fillAttributesWithCMAttributeDeclarations(DOMElement parentElement,
if (!parentElement.hasAttribute(attrName)) {
CompletionItem item = new AttributeCompletionItem(attrName, canSupportSnippet, fullRange, generateValue,
cmAttribute.getDefaultValue(), cmAttribute.getEnumerationValues(), formattingOptions);
String documentation = cmAttribute.getDocumentation();
if (documentation != null) {
item.setDetail(documentation);
}
MarkupContent documentation = XMLGenerator.createMarkupContent(cmAttribute, cmElement, request);
item.setDocumentation(documentation);
response.addCompletionAttribute(item);
}
}
Expand Down Expand Up @@ -210,11 +204,13 @@ private void fillAttributeValuesWithCMAttributeDeclarations(CMElementDeclaration
item.setKind(CompletionItemKind.Value);
item.setFilterText(insertText);
item.setTextEdit(new TextEdit(fullRange, insertText));
MarkupContent documentation = XMLGenerator.createMarkupContent(cmAttribute, value, cmElement,
request);
item.setDocumentation(documentation);
response.addCompletionItem(item);

});
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMAttributeDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lsp4xml.extensions.contentmodel.utils.XMLGenerator;
import org.eclipse.lsp4xml.extensions.xsi.XSISchemaModel;
import org.eclipse.lsp4xml.services.extensions.HoverParticipantAdapter;
import org.eclipse.lsp4xml.services.extensions.IHoverRequest;
import org.eclipse.lsp4xml.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lsp4xml.utils.MarkdownConverter;
import org.eclipse.lsp4xml.utils.MarkupContentFactory;
import org.eclipse.lsp4xml.utils.MarkupContentFactory.IMarkupKindSupport;

/**
* Extension to support XML hover based on content model (XML Schema completion,
Expand All @@ -37,44 +39,35 @@ public Hover onTag(IHoverRequest hoverRequest) throws Exception {
DOMElement node = (DOMElement) hoverRequest.getNode();
CMElementDeclaration cmElement = contentModelManager.findCMElement(node);
if (cmElement != null) {
String doc = cmElement.getDocumentation();
if (doc != null && doc.length() > 0) {
String markdown = MarkdownConverter.convert(doc);
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.MARKDOWN);
content.setValue(markdown);
MarkupContent content = XMLGenerator.createMarkupContent(cmElement, hoverRequest);
if (content != null) {
return new Hover(content, hoverRequest.getTagRange());
}
}
} catch (CacheResourceDownloadingException e) {
return getCacheWarningHover(e);
return getCacheWarningHover(e, hoverRequest);
}
return null;
}

@Override
public Hover onAttributeName(IHoverRequest hoverRequest) throws Exception {

DOMAttr attribute = (DOMAttr) hoverRequest.getNode();

try {
ContentModelManager contentModelManager = hoverRequest.getComponent(ContentModelManager.class);
CMElementDeclaration cmElement = contentModelManager.findCMElement(attribute.getOwnerElement());
if (cmElement != null) {
String attributeName = attribute.getName();
CMAttributeDeclaration cmAttribute = cmElement.findCMAttribute(attributeName);
if (cmAttribute != null) {
String doc = cmAttribute.getDocumentation();
if (doc != null && doc.length() > 0) {
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.PLAINTEXT);
content.setValue(doc);
return new Hover(content);
MarkupContent content = XMLGenerator.createMarkupContent(cmAttribute, cmElement, hoverRequest);
if (content != null) {
return new Hover(content, hoverRequest.getTagRange());
}
}
}
} catch (CacheResourceDownloadingException e) {
return getCacheWarningHover(e);
return getCacheWarningHover(e, hoverRequest);
}
return null;
}
Expand All @@ -83,44 +76,41 @@ public Hover onAttributeName(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.
// Attempts to compute specifically for XSI related attributes since
// the XSD itself does not have enough information. Should create a mock XSD
// eventually.
Hover temp = XSISchemaModel.computeHoverResponse(attribute, hoverRequest);
if(temp != null) {
if (temp != null) {
return temp;
}

try {
ContentModelManager contentModelManager = hoverRequest.getComponent(ContentModelManager.class);

CMElementDeclaration cmElement = contentModelManager.findCMElement(attribute.getOwnerElement());
if (cmElement != null) {
String attributeName = attribute.getName();
CMAttributeDeclaration cmAttribute = cmElement.findCMAttribute(attributeName);

String attributeValue = attribute.getValue();
if (cmAttribute != null) {
String doc = cmAttribute.getValueDocumentation(attributeValue);
if (doc != null && doc.length() > 0) {
String markdown = MarkdownConverter.convert(doc);
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.MARKDOWN);
content.setValue(markdown);
return new Hover(content);
MarkupContent content = XMLGenerator.createMarkupContent(cmAttribute, attributeValue, cmElement,
hoverRequest);
if (content != null) {
return new Hover(content, hoverRequest.getTagRange());
}
}
}
} catch (CacheResourceDownloadingException e) {
return getCacheWarningHover(e);
return getCacheWarningHover(e, hoverRequest);
}
return null;
}

private Hover getCacheWarningHover(CacheResourceDownloadingException e) {
private static Hover getCacheWarningHover(CacheResourceDownloadingException e, IMarkupKindSupport support) {
// Here cache is enabled and some XML Schema, DTD, etc are loading
MarkupContent content = new MarkupContent();
content.setKind(MarkupKind.MARKDOWN);
content.setValue("Cannot process " + (e.isDTD() ? "DTD" : "XML Schema") + " hover: " + e.getMessage());
MarkupContent content = MarkupContentFactory.createMarkupContent(
"Cannot process " + (e.isDTD() ? "DTD" : "XML Schema") + " hover: " + e.getMessage(),
MarkupKind.MARKDOWN, support);
return new Hover(content);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
import java.util.Collection;
import java.util.List;

import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;
import org.eclipse.lsp4xml.commons.SnippetsBuilder;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMAttributeDeclaration;
import org.eclipse.lsp4xml.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lsp4xml.settings.SharedSettings;
import org.eclipse.lsp4xml.settings.XMLFormattingOptions;
import org.eclipse.lsp4xml.utils.StringUtils;
import org.eclipse.lsp4xml.utils.MarkupContentFactory;
import org.eclipse.lsp4xml.utils.MarkupContentFactory.IMarkupKindSupport;
import org.eclipse.lsp4xml.utils.XMLBuilder;

/**
Expand Down Expand Up @@ -223,15 +225,27 @@ public static String generateAttributeValue(String defaultValue, Collection<Stri
* @param schemaURI
* @return
*/
public static String generateDocumentation(String documentation, String schemaURI) {
public static String generateDocumentation(String documentation, String schemaURI, boolean html) {
StringBuilder doc = new StringBuilder(documentation != null ? documentation : "");
if (schemaURI != null) {
if (doc.length() != 0) {
doc.append(System.lineSeparator());
doc.append(System.lineSeparator());
}
if (html) {
doc.append("<p>");
}
doc.append("Source: ");
if (html) {
doc.append("<a href=\"");
doc.append(schemaURI);
doc.append("\">");
}
doc.append(getFileName(schemaURI));
if (html) {
doc.append("</a>");
doc.append("</p>");
}
}
return doc.length() > 0 ? doc.toString() : null;
}
Expand All @@ -253,4 +267,58 @@ private static String getFileName(String schemaURI) {
return schemaURI.substring(index + 1, schemaURI.length());
}

/**
* Returns a markup content for element documentation and null otherwise.
*
* @param cmElement
* @param support
* @return a markup content for element documentation and null otherwise.
*/
public static MarkupContent createMarkupContent(CMElementDeclaration cmElement, IMarkupKindSupport support) {
String documentation = XMLGenerator.generateDocumentation(cmElement.getDocumentation(),
cmElement.getDocumentURI(), support.canSupportMarkupKind(MarkupKind.MARKDOWN));
if (documentation != null) {
return MarkupContentFactory.createMarkupContent(documentation, MarkupKind.MARKDOWN, support);
}
return null;
}

/**
* Returns a markup content for attribute name documentation and null otherwise.
*
* @param cmAttribute
* @param ownerElement
* @param support
* @return a markup content for attribute name documentation and null otherwise.
*/
public static MarkupContent createMarkupContent(CMAttributeDeclaration cmAttribute,
CMElementDeclaration ownerElement, IMarkupKindSupport support) {
String documentation = XMLGenerator.generateDocumentation(cmAttribute.getDocumentation(),
ownerElement.getDocumentURI(), support.canSupportMarkupKind(MarkupKind.MARKDOWN));
if (documentation != null) {
return MarkupContentFactory.createMarkupContent(documentation, MarkupKind.MARKDOWN, support);
}
return null;
}

/**
* Returns a markup content for attribute value documentation and null
* otherwise.
*
* @param cmAttribute
* @param attributeValue
* @param ownerElement
* @param support
* @return a markup content for attribute value documentation and null
* otherwise.
*/
public static MarkupContent createMarkupContent(CMAttributeDeclaration cmAttribute, String attributeValue,
CMElementDeclaration ownerElement, IMarkupKindSupport support) {
String documentation = XMLGenerator.generateDocumentation(cmAttribute.getValueDocumentation(attributeValue),
ownerElement.getDocumentURI(), support.canSupportMarkupKind(MarkupKind.MARKDOWN));
if (documentation != null) {
return MarkupContentFactory.createMarkupContent(documentation, MarkupKind.MARKDOWN, support);
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,14 @@ private String getQuotation() {
String quotation = formattingSettings != null ? formattingSettings.getQuotationAsString() : null;
return StringUtils.isEmpty(quotation) ? XMLFormattingOptions.DEFAULT_QUOTATION : quotation;
}
}

@Override
public boolean canSupportMarkupKind(String kind) {
return completionSettings != null && completionSettings.getCompletionCapabilities() != null
&& completionSettings.getCompletionCapabilities().getCompletionItem() != null
&& completionSettings.getCompletionCapabilities().getCompletionItem().getDocumentationFormat() != null
&& completionSettings.getCompletionCapabilities().getCompletionItem().getDocumentationFormat()
.contains(kind);
}

}
Loading

0 comments on commit 0f276b8

Please sign in to comment.