Skip to content

Commit

Permalink
XSI Nil completion
Browse files Browse the repository at this point in the history
Fixes eclipse-lemminx#247

Signed-off-by: Nikolas Komonen <[email protected]>
  • Loading branch information
NikolasKomonen committed Mar 7, 2019
1 parent 4797365 commit 4fac701
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,15 @@ private static <T> int findFirst(List<T> array, Function<T, Boolean> p) {
}

public DOMAttr getAttributeNode(String name) {
return getAttributeNode(null, name);
}

public DOMAttr getAttributeNode(String prefix, String suffix) {
String name = "";
if(prefix != null) {
name = prefix + ":";
}
name += suffix;
if (!hasAttributes()) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.eclipse.lsp4xml.extensions.contentmodel.participants.ContentModelHoverParticipant;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.diagnostics.ContentModelDiagnosticsParticipant;
import org.eclipse.lsp4xml.extensions.contentmodel.settings.ContentModelSettings;
import org.eclipse.lsp4xml.services.XSISchemaModel;
import org.eclipse.lsp4xml.services.extensions.ICompletionParticipant;
import org.eclipse.lsp4xml.services.extensions.IHoverParticipant;
import org.eclipse.lsp4xml.services.extensions.IXMLExtension;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,7 @@ private void fillWithChildrenElementDeclaration(DOMElement element, Collection<C
@Override
public void onAttributeName(boolean generateValue, Range fullRange, ICompletionRequest request,
ICompletionResponse response) throws Exception {
if (request.getXMLDocument().hasSchemaInstancePrefix()) {
XSISchemaModel.computeCompletionResponses(request, response, fullRange, request.getXMLDocument(),
generateValue);
}

// otherwise, manage completion based on XML Schema, DTD.
DOMElement parentElement = request.getNode().isElement() ? (DOMElement) request.getNode() : null;
if (parentElement == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ private int generate(Collection<CMAttributeDeclaration> attributes, int level, i
return snippetIndex;
}

/**
* Creates the string value for a CompletionItem TextEdit
*
* Can create an enumerated TextEdit if given multiple values.
*/
public static String generateAttributeValue(String defaultValue, Collection<String> enumerationValues,
boolean canSupportSnippets, int snippetIndex, boolean withQuote) {
StringBuilder value = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.eclipse.lsp4xml.extensions.xsi;

import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4xml.services.XSISchemaModel;
import org.eclipse.lsp4xml.services.extensions.CompletionParticipantAdapter;
import org.eclipse.lsp4xml.services.extensions.ICompletionRequest;
import org.eclipse.lsp4xml.services.extensions.ICompletionResponse;

/**
* XSICompletionParticipant
*/
public class XSICompletionParticipant extends CompletionParticipantAdapter {

@Override
public void onAttributeName(boolean generateValue, Range fullRange, ICompletionRequest request,
ICompletionResponse response) throws Exception {
if (request.getXMLDocument().hasSchemaInstancePrefix()) {
XSISchemaModel.computeCompletionResponses(request, response, fullRange, request.getXMLDocument(),
generateValue);
}
}

@Override
public void onAttributeValue(String valuePrefix, Range fullRange, boolean addQuotes, ICompletionRequest request,
ICompletionResponse response) throws Exception {
XSISchemaModel.computeValueCompletionResponses(request, response, fullRange, request.getXMLDocument());
}

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

import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4xml.services.extensions.ICompletionParticipant;
import org.eclipse.lsp4xml.services.extensions.IXMLExtension;
import org.eclipse.lsp4xml.services.extensions.XMLExtensionsRegistry;
import org.eclipse.lsp4xml.services.extensions.save.ISaveContext;

/**
* Plugin to handle `xsi` attributes or a namespace with the value of:
* "http://www.w3.org/2001/XMLSchema-instance"
*
* Loaded by service loader in 'resources' folder.
*/
public class XSISchemaPlugin implements IXMLExtension {

ICompletionParticipant completionParticipant = new XSICompletionParticipant();

@Override
public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerCompletionParticipant(completionParticipant);
}

@Override
public void stop(XMLExtensionsRegistry registry) {
registry.unregisterCompletionParticipant(completionParticipant);
}

@Override
public void doSave(ISaveContext context) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@

import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4xml.commons.BadLocationException;
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.extensions.contentmodel.utils.XMLGenerator;
import org.eclipse.lsp4xml.services.extensions.ICompletionRequest;
import org.eclipse.lsp4xml.services.extensions.ICompletionResponse;
import org.eclipse.lsp4xml.services.extensions.IHoverRequest;
Expand All @@ -31,6 +35,7 @@
* https://www.w3.org/2001/XMLSchema-instance
*/
public class XSISchemaModel {

private static String lineSeparator = System.lineSeparator();
public static final String TYPE_DOC = "Specifies the type of an element. This attribute labels an element as a particular type, even though there might not be an element declaration in the schema binding that element to the type.";
public static final String NIL_DOC = "Indicates if an element should contain content. Valid values are `true` or `false`";
Expand Down Expand Up @@ -74,7 +79,7 @@ public static void computeCompletionResponses(ICompletionRequest request,
boolean schemaLocationExists = document.hasSchemaLocation();
boolean noNamespaceSchemaLocationExists = document.hasNoNamespaceSchemaLocation();
//Indicates that no values are allowed inside an XML element
if(!attributeAlreadyExists(nodeAtOffset, actualPrefix, "nil")) {
if(!hasAttribute(nodeAtOffset, actualPrefix, "nil")) {
documentation = NIL_DOC;
name = actualPrefix + ":nil";
createCompletionItem(name, isSnippetsSupported, generateValue, editRange, StringUtils.TRUE,
Expand All @@ -84,7 +89,7 @@ public static void computeCompletionResponses(ICompletionRequest request,
//a content type which does not require or even necessarily allow empty content.
//An element may be ·valid· without content if it has the attribute xsi:nil with
//the value true.
if(!attributeAlreadyExists(nodeAtOffset, actualPrefix, "type")) {
if(!hasAttribute(nodeAtOffset, actualPrefix, "type")) {
documentation = TYPE_DOC;
name = actualPrefix + ":type";
createCompletionItem(name, isSnippetsSupported, generateValue, editRange, null, null, documentation, response);
Expand Down Expand Up @@ -114,8 +119,78 @@ private static void createCompletionItem(String attrName, boolean canSupportSnip
response.addCompletionItem(item);
}

private static boolean attributeAlreadyExists(DOMElement root, String actualPrefix, String suffix) {
return root.getAttributeNode(actualPrefix + ":" + suffix) != null;
public static void computeValueCompletionResponses(ICompletionRequest request,
ICompletionResponse response, Range editRange, DOMDocument document) throws BadLocationException {

DOMElement rootElement = document.getDocumentElement();
int offset = document.offsetAt(editRange.getStart());
boolean inRootElement = false;
DOMElement nodeAtOffset = (DOMElement) document.findNodeAt(offset);
if(rootElement.equals(nodeAtOffset)) {
inRootElement = true;
}

boolean isSnippetsSupported = request.getCompletionSettings().isCompletionSnippetsSupported();
String actualPrefix = document.getSchemaInstancePrefix();
String name;
String documentation;

if(hasAttribute(nodeAtOffset, actualPrefix, "nil")) {
nodeAtOffset.getAttribute("nil");

}

}

private static void createCompletionItemValue(boolean canSupportSnippets, String defaultValue,
Collection<String> enumerationValues, Range editRange, DOMDocument document, ICompletionResponse response) {

String text = document.getText();
int startOffset;
int endOffset;
try {
endOffset = document.offsetAt(editRange.getEnd());
startOffset = document.offsetAt(editRange.getStart());

if(startOffset == endOffset && startOffset > 0) {
startOffset = startOffset - 1;
}

char c = text.charAt(startOffset);
if(c == '\"' || c == '\'') {
editRange.setStart(document.positionAt(startOffset));
}

c = text.charAt(endOffset);
if(c == '\"' || c == '\'') {
endOffset = endOffset + 1; //end position is non-inclusive
editRange.setEnd(document.positionAt(endOffset));
}
} catch (BadLocationException e) {
e.printStackTrace();
}

String value = XMLGenerator.generateAttributeValue(defaultValue, enumerationValues,
canSupportSnippets, 1, true);

CompletionItem item = new CompletionItem();
item.setTextEdit(new TextEdit(editRange, value));
item.setInsertTextFormat(canSupportSnippets ? InsertTextFormat.Snippet : InsertTextFormat.PlainText);
response.addCompletionItem(item);
}

/**
* Checks if a given root has an attribute with the name:
* {@code prefix:suffix}.
* If no prefix exists, put the name in {@code suffix}
*/
private static boolean hasAttribute(DOMElement root, String prefix, String suffix) {
String attName = "";
if(prefix != null) {
attName = prefix + ":";
}
attName += suffix;
return root.getAttributeNode(attName) != null;
}

public static Hover computeHoverResponse(DOMAttr attribute, IHoverRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ org.eclipse.lsp4xml.extensions.references.XMLReferencesPlugin
org.eclipse.lsp4xml.extensions.xsd.XSDPlugin
org.eclipse.lsp4xml.extensions.dtd.DTDPlugin
org.eclipse.lsp4xml.extensions.xsl.XSLPlugin
org.eclipse.lsp4xml.extensions.catalog.XMLCatalogPlugin
org.eclipse.lsp4xml.extensions.catalog.XMLCatalogPlugin
org.eclipse.lsp4xml.extensions.xsi.XSISchemaPlugin
3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4fac701

Please sign in to comment.