Skip to content

Commit

Permalink
XML Completion element apply generate invalid XML content
Browse files Browse the repository at this point in the history
Fixes #1373

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
azerr authored and datho7561 committed Dec 2, 2022
1 parent 52e48fe commit 40afeaf
Show file tree
Hide file tree
Showing 31 changed files with 1,471 additions and 225 deletions.
Expand Up @@ -183,7 +183,7 @@ private synchronized void updateSettings(Object initOptions, boolean initLogs) {
if (newTelemetry != null) {
getTelemetryManager().setEnabled(newTelemetry.isEnabled());
}

XMLFoldingSettings newFolding = xmlClientSettings.getFolding();
if (newFolding != null) {
xmlTextDocumentService.getSharedFoldingSettings().merge(newFolding);
Expand All @@ -198,6 +198,8 @@ private synchronized void updateSettings(Object initOptions, boolean initLogs) {
if (newCompletions != null) {
xmlTextDocumentService.updateCompletionSettings(newCompletions);
}
xmlTextDocumentService.getSharedSettings()
.setLinkedEditingEnabled(xmlClientSettings.isLinkedEditingEnabled());

XMLSymbolSettings newSymbols = xmlClientSettings.getSymbols();
if (newSymbols != null) {
Expand Down
Expand Up @@ -223,8 +223,8 @@ public String getPrefix(String namespaceURI) {
}
if (!StringUtils.isEmpty(namespaceURI)) {
switch (namespaceURI) {
case "http://www.w3.org/XML/1998/namespace":
return "xml";
case "http://www.w3.org/XML/1998/namespace":
return "xml";
}
}
return null;
Expand Down Expand Up @@ -472,29 +472,38 @@ public int getUnclosedStartTagCloseOffset() {
}

@Override
public DOMElement getOrphanEndElement(int offset, String tagName) {
public DOMElement getOrphanEndElement(int offset, String tagName, boolean anyOrphan) {
if (getEnd() <= offset) {
// <employee />|
// <employee /> |
// <employee></employee> |
// check if next sibling node is an element like <\tagName>
return super.getOrphanEndElement(offset, tagName);
return super.getOrphanEndElement(offset, tagName, anyOrphan);
}
if (isSameTag(tagName) && isInStartTag(offset)) {
// <employe|e></employee>
return hasEndTag() ? this : null;
if (anyOrphan) {
if (hasEndTag()) {
return this;
}
} else {
return hasEndTag() ? this : null;
}
}
// search if it exists an end tag
DOMElement orphanEndElement = null;
List<DOMNode> children = getChildren();
for (DOMNode child : children) {
if (child.isElement()) {
DOMElement childElement = (DOMElement) child;
if (childElement.isOrphanEndTagOf(tagName)) {
return childElement;
} else if (orphanEndElement == null && childElement.isOrphanEndTag()) {
orphanEndElement = childElement;
}
}
}
return null;
return anyOrphan ? orphanEndElement : null;
}

/**
Expand Down
Expand Up @@ -739,14 +739,54 @@ public DOMNode getPreviousNonTextSibling() {
return prev;
}

/**
* Returns the orphan end element after the given offset which matches the given
* tagName and null otherwise.
*
* The following sample sample with tagName=foo will returns the <\foo> orphan
* end element:
* <p>
* |
* <\foo>
* </p>
*
* @param offset the offset.
* @param tagName the tag name.
*
* @return the orphan end element after the given offset which matches the given
* tagName and null otherwise.
*/
public DOMElement getOrphanEndElement(int offset, String tagName) {
return getOrphanEndElement(offset, tagName, false);
}

/**
* Returns the orphan end element after the given offset which matches the given
* tagName and the first orphan end element otherwise and null otherwise.
*
* The following sample sample with tagName=bar will returns the <\foo> orphan
* end element:
* <p>
* |
* <\foo>
* </p>
*
* @param offset the offset.
* @param tagName the tag name.
* @param anyOrphan true if any orphan should be returned and false otherwise.
*
* @return the orphan end element after the given offset which matches the given
* tagName and the first orphan end element otherwise and null
* otherwise.
*/
public DOMElement getOrphanEndElement(int offset, String tagName, boolean anyOrphan) {
DOMNode next = getNextSibling();
if (next == null || !next.isElement()) {
return null;
}
// emp| </employe>
DOMElement nextElement = (DOMElement) next;
if (nextElement.isOrphanEndTagOf(tagName)) {
if ((anyOrphan && nextElement.isOrphanEndTag()) || nextElement.isOrphanEndTagOf(tagName)) {
return nextElement;
}
return null;
Expand Down
Expand Up @@ -23,17 +23,16 @@
import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.extensions.contentmodel.model.CMAttributeDeclaration;
import org.eclipse.lemminx.extensions.contentmodel.model.CMDocument;
import org.eclipse.lemminx.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.extensions.contentmodel.participants.completion.AbstractCMCompletionResolver;
import org.eclipse.lemminx.extensions.contentmodel.participants.completion.AttributeNameCompletionResolver;
import org.eclipse.lemminx.extensions.contentmodel.participants.completion.AttributeValueCompletionResolver;
import org.eclipse.lemminx.extensions.contentmodel.participants.completion.ContentModelElementCompletionItem;
import org.eclipse.lemminx.extensions.contentmodel.utils.XMLGenerator;
import org.eclipse.lemminx.services.AttributeCompletionItem;
import org.eclipse.lemminx.services.data.DataEntryField;
import org.eclipse.lemminx.services.extensions.completion.AttributeCompletionItem;
import org.eclipse.lemminx.services.extensions.completion.CompletionParticipantAdapter;
import org.eclipse.lemminx.services.extensions.completion.ICompletionItemResolveParticipant;
import org.eclipse.lemminx.services.extensions.completion.ICompletionRequest;
Expand All @@ -42,7 +41,6 @@
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
Expand Down Expand Up @@ -201,7 +199,8 @@ private static void fillWithChildrenElementDeclaration(DOMElement element, Colle
addTagName(list, tags, request, response);
} else {
for (CMElementDeclaration child : cmElements) {
addCompletionItem(child, element, defaultPrefix, forceUseOfPrefix, request, response, generator, null);
addTagCompletionItem(child, element, defaultPrefix, forceUseOfPrefix, request, response, generator,
null);
}
}
}
Expand All @@ -212,7 +211,8 @@ private static void fillCompletionItem(Collection<CMElementDeclaration> elements
for (CMElementDeclaration child : elements) {
if (!processedElements.contains(child)) {
processedElements.add(child);
addCompletionItem(child, element, defaultPrefix, forceUseOfPrefix, request, response, generator, tags);
addTagCompletionItem(child, element, defaultPrefix, forceUseOfPrefix, request, response, generator,
tags);
fillCompletionItem(child.getElements(), element, defaultPrefix, forceUseOfPrefix, request, response,
generator, tags, processedElements);
}
Expand Down Expand Up @@ -250,39 +250,25 @@ private static void addTagName(NodeList list, Set<String> tags, ICompletionReque
}
}

private static void addCompletionItem(CMElementDeclaration elementDeclaration, DOMElement parentElement,
private static void addTagCompletionItem(CMElementDeclaration elementDeclaration, DOMElement parentElement,
String defaultPrefix, boolean forceUseOfPrefix, ICompletionRequest request, ICompletionResponse response,
XMLGenerator generator, Set<String> tags) {
String prefix = forceUseOfPrefix ? defaultPrefix
: (parentElement != null ? parentElement.getPrefix(elementDeclaration.getNamespace()) : null);
String label = elementDeclaration.getName(prefix);
String tagName = elementDeclaration.getName(prefix);
if (tags != null) {
if (tags.contains(label)) {
if (tags.contains(tagName)) {
return;
} else {
tags.add(label);
tags.add(tagName);
}
}

CompletionItem item = new CompletionItem(label);
item.setFilterText(request.getFilterForStartTagName(label));
item.setKind(CompletionItemKind.Property);
MarkupContent documentation = XMLGenerator.createMarkupContent(elementDeclaration, request);
item.setDocumentation(documentation);
String xml = generator.generate(elementDeclaration, prefix,
isGenerateEndTag(request.getNode(), request.getOffset(), label));
item.setTextEdit(Either.forLeft(new TextEdit(request.getReplaceRange(), xml)));
item.setInsertTextFormat(InsertTextFormat.Snippet);
ContentModelElementCompletionItem item = new ContentModelElementCompletionItem(tagName,
elementDeclaration, generator, request);
response.addCompletionItem(item, true);
}

private static boolean isGenerateEndTag(DOMNode node, int offset, String tagName) {
if (node == null) {
return true;
}
return node.getOrphanEndElement(offset, tagName) == null;
}

@Override
public void onAttributeName(boolean generateValue, ICompletionRequest request, ICompletionResponse response,
CancelChecker cancelChecker)
Expand Down Expand Up @@ -450,12 +436,8 @@ public ICompletionItemResolveParticipant getResolveCompletionItemParticipant(Str
return completionResolvers.get(participantId);
}

private void addResolveData(ICompletionRequest request, CompletionItem item, String participantId) {
DOMDocument document = request.getNode().getOwnerDocument();
JsonObject data = DataEntryField.createData(document.getDocumentURI(),
participantId);
data.addProperty(AbstractCMCompletionResolver.OFFSET_KEY,
request.getOffset());
private static void addResolveData(ICompletionRequest request, CompletionItem item, String participantId) {
JsonObject data = DataEntryField.createCompletionData(request, participantId);
item.setData(data);
}
}

This file was deleted.

Expand Up @@ -13,36 +13,38 @@

import java.util.Collection;

import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.extensions.contentmodel.model.CMAttributeDeclaration;
import org.eclipse.lemminx.extensions.contentmodel.model.CMDocument;
import org.eclipse.lemminx.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.extensions.contentmodel.utils.XMLGenerator;
import org.eclipse.lemminx.services.extensions.completion.AbstractAttributeCompletionResolver;
import org.eclipse.lemminx.services.extensions.completion.ICompletionItemResolverRequest;
import org.eclipse.lemminx.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;

/**
* Resolves the completion item with the documentation of the attribute name
* from the content model.
*/
public class AttributeNameCompletionResolver extends AbstractCMCompletionResolver {
public class AttributeNameCompletionResolver extends AbstractAttributeCompletionResolver {

public static final String PARTICIPANT_ID = AttributeNameCompletionResolver.class.getName();

@Override
protected void addDocumentationToCompletion(ICompletionItemResolverRequest request, CompletionItem toResolve,
DOMElement parentElement, DOMAttr attr) {
protected void resolveCompletionItem(DOMElement element, CompletionItem toResolve,
ICompletionItemResolverRequest request,
CancelChecker cancelChecker) {
String attributeName = request.getUnresolved().getFilterText();
try {
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
Collection<CMDocument> cmDocuments = contentModelManager.findCMDocument(parentElement);
Collection<CMDocument> cmDocuments = contentModelManager.findCMDocument(element);
for (CMDocument cmDocument : cmDocuments) {
CMElementDeclaration cmElement = cmDocument.findCMElement(parentElement,
parentElement.getNamespaceURI());
CMElementDeclaration cmElement = cmDocument.findCMElement(element,
element.getNamespaceURI());
if (cmElement != null) {
MarkupContent documentation = getDocumentationForAttributeValue(cmElement, attributeName, request);
if (documentation != null) {
Expand Down
Expand Up @@ -20,32 +20,37 @@
import org.eclipse.lemminx.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.extensions.contentmodel.utils.XMLGenerator;
import org.eclipse.lemminx.services.extensions.completion.AbstractAttributeCompletionResolver;
import org.eclipse.lemminx.services.extensions.completion.ICompletionItemResolverRequest;
import org.eclipse.lemminx.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;

/**
* Resolves the documentation for the completion of the attribute value from the
* content model.
*
*/
public class AttributeValueCompletionResolver extends AbstractCMCompletionResolver {
public class AttributeValueCompletionResolver extends AbstractAttributeCompletionResolver {

public static final String PARTICIPANT_ID = AttributeValueCompletionResolver.class.getName();

protected void addDocumentationToCompletion(ICompletionItemResolverRequest request, CompletionItem toResolve,
DOMElement parentElement, DOMAttr attr) {
@Override
protected void resolveCompletionItem(DOMElement element, CompletionItem toResolve, ICompletionItemResolverRequest request,
CancelChecker cancelChecker) {
int offset = request.getCompletionOffset();
DOMAttr attr = element.findAttrAt(offset);
if (attr == null) {
return;
}
String attributeValue = toResolve.getLabel();
try {
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
Collection<CMDocument> cmDocuments = contentModelManager.findCMDocument(parentElement);
Collection<CMDocument> cmDocuments = contentModelManager.findCMDocument(element);
for (CMDocument cmDocument : cmDocuments) {
CMElementDeclaration cmElement = cmDocument.findCMElement(parentElement,
parentElement.getNamespaceURI());
CMElementDeclaration cmElement = cmDocument.findCMElement(element,
element.getNamespaceURI());
if (cmElement != null) {
MarkupContent documentation = getDocumentationForAttributeValue(cmElement, attr,
attributeValue, request);
Expand All @@ -68,5 +73,4 @@ private static MarkupContent getDocumentationForAttributeValue(CMElementDeclarat
}
return null;
}

}

0 comments on commit 40afeaf

Please sign in to comment.