Skip to content

Commit

Permalink
Add support for textDocument/completion for xs:element/@name /
Browse files Browse the repository at this point in the history
xs:extension/@base

Fix #451

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jun 24, 2019
1 parent 472d146 commit c8193b2
Show file tree
Hide file tree
Showing 27 changed files with 932 additions and 338 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public short getNodeType() {
public DOMAttr getOwnerAttr() {
return ownerAttr;
}

@Override
public DOMDocument getOwnerDocument() {
return ownerAttr.getOwnerDocument();
}
}

public DOMAttr(String name, DOMNode ownerElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class DOMDocument extends DOMNode implements Document {
private boolean hasNamespaces;
private Map<String, String> externalSchemaLocation;
private String schemaInstancePrefix;
private String schemaPrefix;
private boolean hasExternalGrammar;
private CancelChecker cancelChecker;

Expand All @@ -64,11 +65,11 @@ public DOMDocument(TextDocument textDocument, URIResolverExtensionManager resolv
this.resolverExtensionManager = resolverExtensionManager;
resetGrammar();
}

public void setCancelChecker(CancelChecker cancelChecker) {
this.cancelChecker = cancelChecker;
}

public CancelChecker getCancelChecker() {
return cancelChecker;
}
Expand Down Expand Up @@ -246,6 +247,7 @@ private synchronized void initializeReferencedSchema() {
return;
}
schemaInstancePrefix = null;
schemaPrefix = null;
// Search if document element root declares namespace with "xmlns".
if (documentElement.hasAttributes()) {
for (DOMAttr attr : documentElement.getAttributeNodes()) {
Expand All @@ -254,19 +256,23 @@ private synchronized void initializeReferencedSchema() {
if (attributeName.equals("xmlns") || attributeName.startsWith("xmlns:")) //$NON-NLS-1$ //$NON-NLS-2$
{
hasNamespaces = true;
}
String attributeValue = documentElement.getAttribute(attributeName);
if (attributeValue != null && attributeValue.startsWith("http://www.w3.org/") //$NON-NLS-1$
&& attributeValue.endsWith("/XMLSchema-instance")) //$NON-NLS-1$
{
schemaInstancePrefix = attributeName.equals("xmlns") ? "" : getUnprefixedName(attributeName); //$NON-NLS-1$ //$NON-NLS-2$
String attributeValue = documentElement.getAttribute(attributeName);
if (attributeValue != null && attributeValue.startsWith("http://www.w3.org/")) {
if (attributeValue.endsWith("/XMLSchema-instance")) {
schemaInstancePrefix = attributeName.equals("xmlns") ? ""
: getUnprefixedName(attributeName);
} else if (attributeValue.endsWith("/XMLSchema")) {
schemaPrefix = attributeName.equals("xmlns") ? "" : getUnprefixedName(attributeName);
}
}
}
}
}
if (schemaInstancePrefix != null) {
// DOM document can declared xsi:noNamespaceSchemaLocation and xsi:schemaLocation both even it's not valid
// DOM document can declared xsi:noNamespaceSchemaLocation and
// xsi:schemaLocation both even it's not valid
noNamespaceSchemaLocation = createNoNamespaceSchemaLocation(documentElement, schemaInstancePrefix);
schemaLocation = createSchemaLocation(documentElement, schemaInstancePrefix);
schemaLocation = createSchemaLocation(documentElement, schemaInstancePrefix);
}
}
}
Expand Down Expand Up @@ -788,21 +794,21 @@ public boolean isDTD() {
}

/**
* Returns true if 'offset' is within an internal DOCTYPE dtd.
* Else false.
* Returns true if 'offset' is within an internal DOCTYPE dtd. Else false.
*
* @param offset
* @return
* @return
*/
public boolean isWithinInternalDTD(int offset) {
DOMDocumentType doctype = this.getDoctype();
if(doctype != null && doctype.internalSubset != null) {
if (doctype != null && doctype.internalSubset != null) {
return offset > doctype.internalSubset.start && offset < doctype.internalSubset.end;
}
return false;
}

public Range getTrimmedRange(Range range) {
if(range != null) {
if (range != null) {
return getTrimmedRange(range.getStart().getCharacter(), range.getEnd().getCharacter());
}
return null;
Expand All @@ -812,16 +818,16 @@ public Range getTrimmedRange(Range range) {
public Range getTrimmedRange(int start, int end) {
String text = getText();
char c = text.charAt(start);
while(Character.isWhitespace(c)) {
while (Character.isWhitespace(c)) {
start++;
c = text.charAt(start);
}
if(start == end) {
if (start == end) {
return null;
}
end--;
c = text.charAt(end);
while(Character.isWhitespace(c)) {
while (Character.isWhitespace(c)) {
end--;
c = text.charAt(end);
}
Expand All @@ -847,36 +853,47 @@ public Collection<DOMNode> findDTDAttrList(String elementName) {
}

/**
* Given a schema URI, this will return true if the given schemaURI
* matches the one defined in this DOMDocument(xml document).
* Given a schema URI, this will return true if the given schemaURI matches the
* one defined in this DOMDocument(xml document).
*
* It will check either xsi:schemaLocation or xsi:noNamespaceSchemaLocation.
*/
public boolean usesSchema(String xsdURI) {
String rootURI = URI.create(textDocument.getUri()).getPath(); //remove "file://" if exists
if(rootURI == null || xsdURI == null) {
String rootURI = URI.create(textDocument.getUri()).getPath(); // remove "file://" if exists
if (rootURI == null || xsdURI == null) {
return false;
}

Path rootPath = Paths.get(rootURI).getParent();
Path rootPath = Paths.get(rootURI).getParent();
xsdURI = URI.create(xsdURI).getPath();
Path xsdPath = Paths.get(xsdURI);

if(schemaLocation != null) {
return schemaLocation.usesSchema(rootPath ,xsdPath);
}
else if (noNamespaceSchemaLocation != null) {
if (schemaLocation != null) {
return schemaLocation.usesSchema(rootPath, xsdPath);
} else if (noNamespaceSchemaLocation != null) {
String noNamespaceURI = URI.create(noNamespaceSchemaLocation.getLocation()).getPath();
Path noNamespacePath = Paths.get(noNamespaceURI).normalize();

if(!noNamespacePath.isAbsolute()) {
if (!noNamespacePath.isAbsolute()) {
noNamespacePath = rootPath.resolve(noNamespacePath);
}
return xsdPath.equals(noNamespacePath);
}
return false;
}


/**
* Returns the XML Schema prefix (ex : 'xs' for
* xmlns:xs="http://www.w3.org/2001/XMLSchema")
*
* @return the XML Schema prefix (ex : 'xs' for
* xmlns:xs="http://www.w3.org/2001/XMLSchema")
*/
public String getSchemaPrefix() {
initializeReferencedSchemaIfNeeded();
return schemaPrefix;
}

private void checkCanceled() {
if (cancelChecker != null) {
cancelChecker.checkCanceled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import org.eclipse.lsp4xml.services.extensions.CompletionParticipantAdapter;
import org.eclipse.lsp4xml.services.extensions.ICompletionRequest;
import org.eclipse.lsp4xml.services.extensions.ICompletionResponse;
import org.eclipse.lsp4xml.settings.SharedSettings;
import org.eclipse.lsp4xml.settings.XMLFormattingOptions;
import org.eclipse.lsp4xml.uriresolver.CacheResourceDownloadingException;

/**
Expand All @@ -43,7 +43,6 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response)
try {
DOMDocument document = request.getXMLDocument();
String schemaURI;
String fileURI;
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
DOMElement parentElement = request.getParentElement();
CMDocument cmDocument;
Expand All @@ -54,18 +53,19 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response)
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,
schemaURI);
}
return;
}
// Try to retrieve XML Schema/DTD element declaration for the parent element
// where completion was triggered.
cmDocument = contentModelManager.findCMDocument(parentElement, parentElement.getNamespaceURI());

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

if (cmElement != null) {
defaultPrefix = parentElement.getPrefix();
fillWithChildrenElementDeclaration(parentElement, cmElement.getElements(), defaultPrefix, false,
Expand Down Expand Up @@ -100,8 +100,8 @@ public void onTagOpen(ICompletionRequest request, ICompletionResponse response)
}

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,
String schemaURI) throws BadLocationException {
XMLGenerator generator = request.getXMLGenerator();
for (CMElementDeclaration child : cmElements) {
String prefix = forceUseOfPrefix ? p : (element != null ? element.getPrefix(child.getNamespace()) : null);
Expand All @@ -121,32 +121,34 @@ private void fillWithChildrenElementDeclaration(DOMElement element, Collection<C
}

@Override
public void onAttributeName(boolean generateValue, Range fullRange, ICompletionRequest request,
ICompletionResponse response, SharedSettings settings) throws Exception {
public void onAttributeName(boolean generateValue, ICompletionRequest request,
ICompletionResponse response) throws Exception {
// otherwise, manage completion based on XML Schema, DTD.
DOMElement parentElement = request.getNode().isElement() ? (DOMElement) request.getNode() : null;
if (parentElement == null) {
return;
}
try {
Range fullRange = request.getReplaceRange();
boolean canSupportSnippet = request.getCompletionSettings().isCompletionSnippetsSupported();
XMLFormattingOptions formattingSettings = request.getFormattingSettings();
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
// Completion on attribute based on external grammar
CMElementDeclaration cmElement = contentModelManager.findCMElement(parentElement);
fillAttributesWithCMAttributeDeclarations(parentElement, fullRange, cmElement, canSupportSnippet,
generateValue, response, settings);
generateValue, response, formattingSettings);
// Completion on attribute based on internal grammar
cmElement = contentModelManager.findInternalCMElement(parentElement);
fillAttributesWithCMAttributeDeclarations(parentElement, fullRange, cmElement, canSupportSnippet,
generateValue, response, settings);
generateValue, 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, SharedSettings settings) {
ICompletionResponse response, XMLFormattingOptions formattingOptions) {
if (cmElement == null) {
return;
}
Expand All @@ -158,7 +160,7 @@ private void fillAttributesWithCMAttributeDeclarations(DOMElement parentElement,
String attrName = cmAttribute.getName();
if (!parentElement.hasAttribute(attrName)) {
CompletionItem item = new AttributeCompletionItem(attrName, canSupportSnippet, fullRange, generateValue,
cmAttribute.getDefaultValue(), cmAttribute.getEnumerationValues(), settings);
cmAttribute.getDefaultValue(), cmAttribute.getEnumerationValues(), formattingOptions);
String documentation = cmAttribute.getDocumentation();
if (documentation != null) {
item.setDetail(documentation);
Expand All @@ -169,8 +171,8 @@ private void fillAttributesWithCMAttributeDeclarations(DOMElement parentElement,
}

@Override
public void onAttributeValue(String valuePrefix, Range fullRange, boolean addQuotes, ICompletionRequest request,
ICompletionResponse response, SharedSettings settings) throws Exception {
public void onAttributeValue(String valuePrefix, ICompletionRequest request,
ICompletionResponse response) throws Exception {
DOMElement parentElement = request.getNode().isElement() ? (DOMElement) request.getNode() : null;
if (parentElement == null) {
return;
Expand All @@ -194,11 +196,17 @@ private void fillAttributeValuesWithCMAttributeDeclarations(CMElementDeclaration
String attributeName = request.getCurrentAttributeName();
CMAttributeDeclaration cmAttribute = cmElement.findCMAttribute(attributeName);
if (cmAttribute != null) {
Range fullRange = request.getReplaceRange();
cmAttribute.getEnumerationValues().forEach(value -> {
CompletionItem item = new CompletionItem();
item.setLabel(value);
String insertText = request.getInsertAttrValue(value);
item.setLabel(value);
item.setKind(CompletionItemKind.Value);
response.addCompletionAttribute(item);
item.setFilterText(insertText);
item.setTextEdit(new TextEdit(fullRange, insertText));
response.addCompletionItem(item);

});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@ public static String generateAttributeValue(String defaultValue, Collection<Stri
* Can create an enumerated TextEdit if given a collection of values.
*/
public static String generateAttributeValue(String defaultValue, Collection<String> enumerationValues,
boolean canSupportSnippets, int snippetIndex, boolean withQuote, SharedSettings settings) {
boolean canSupportSnippets, int snippetIndex, boolean withQuote, XMLFormattingOptions formattingSettings) {
StringBuilder value = new StringBuilder();
String quotation = "\"";
if (withQuote) {
if (settings != null) {
quotation = settings.formattingSettings.getQuotationAsString();
if (formattingSettings != null) {
quotation = formattingSettings.getQuotationAsString();
}
value.append("=" + quotation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.eclipse.lsp4xml.services.extensions.CompletionParticipantAdapter;
import org.eclipse.lsp4xml.services.extensions.ICompletionRequest;
import org.eclipse.lsp4xml.services.extensions.ICompletionResponse;
import org.eclipse.lsp4xml.settings.SharedSettings;
import org.eclipse.lsp4xml.utils.CompletionSortTextHelper;
import org.eclipse.lsp4xml.utils.FilesUtils;
import org.eclipse.lsp4xml.utils.StringUtils;
Expand All @@ -44,15 +43,15 @@ public class FilePathCompletionParticipant extends CompletionParticipantAdapter
public static final String FILE_SCHEME = "file";

@Override
public void onAttributeValue(String valuePrefix, Range fullRange, boolean addQuotes, ICompletionRequest request,
ICompletionResponse response, SharedSettings settings) throws Exception {
public void onAttributeValue(String valuePrefix,
ICompletionRequest request, ICompletionResponse response) throws Exception {

DOMDocument xmlDocument = request.getXMLDocument();
String text = xmlDocument.getText();

Range fullRange = request.getReplaceRange();

// Get full attribute value range
// + 1 since it includes the quotations
int documentStartOffset = xmlDocument.offsetAt(fullRange.getStart()) + 1;
int documentStartOffset = xmlDocument.offsetAt(fullRange.getStart());

String fullAttributeValue = valuePrefix;
if (isEmpty(fullAttributeValue)) {
Expand Down Expand Up @@ -234,8 +233,4 @@ private void createFilePathCompletionItem(File f, Range replaceRange, ICompletio
response.addCompletionItem(item);
}





}
Loading

0 comments on commit c8193b2

Please sign in to comment.