Skip to content

Commit

Permalink
xsd:enumeration autocomplete don't work for text node
Browse files Browse the repository at this point in the history
  • Loading branch information
angelozerr committed Apr 13, 2020
1 parent 565e869 commit b8afc81
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,25 @@ public static void placeholders(int index, String text, StringBuilder snippets)
}

/**
* Returns the LSP choices snippets content.
*
* @param index
* @param values
* @param index the snippet index.
* @param values the values for the choice.
* @return the LSP choices snippets content.
*
* @see https://github.com/Microsoft/language-server-protocol/blob/master/snippetSyntax.md#choice
*/
public static String choice(int index, Collection<String> values) {
StringBuilder snippets = new StringBuilder();
choice(index, values, snippets);
return snippets.toString();
}

/**
* Add LSP choices snippets in the given snippets content.
*
* @param index the snippet index.
* @param values the values for the choice.
* @return
*
* @see https://github.com/Microsoft/language-server-protocol/blob/master/snippetSyntax.md#choice
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ default String getName(String prefix) {
*/
Collection<String> getEnumerationValues();

/**
* Returns the documentation for the given enumeration value and null otherwise.
*
* @param value the enumeration value.
* @return the documentation for the given enumeration value and null otherwise.
*/
String getValueDocumentation(String value);

/**
* Returns the owner document URI where the element is declared.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 Angelo ZERR
* Copyright (c) 2018-2020 Angelo ZERR
* 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
Expand All @@ -13,6 +13,7 @@
package org.eclipse.lemminx.extensions.contentmodel.participants;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

Expand All @@ -35,6 +36,7 @@
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;
import org.eclipse.lsp4j.TextEdit;
import org.w3c.dom.Document;
Expand Down Expand Up @@ -334,4 +336,45 @@ private void fillAttributeValuesWithCMAttributeDeclarations(CMElementDeclaration
}
}

@Override
public void onXMLContent(ICompletionRequest request, ICompletionResponse response) throws Exception {
try {
ContentModelManager contentModelManager = request.getComponent(ContentModelManager.class);
DOMElement parentElement = request.getParentElement();
if (parentElement != null) {
CMElementDeclaration elementDeclaration = contentModelManager.findCMElement(parentElement);
Collection<String> values = elementDeclaration != null ? elementDeclaration.getEnumerationValues()
: Collections.emptyList();
if (!values.isEmpty()) {
DOMDocument document = parentElement.getOwnerDocument();
int startOffset = parentElement.getStartTagCloseOffset() + 1;
Position start = parentElement.getOwnerDocument().positionAt(startOffset);
Position end = request.getPosition();
int endOffset = parentElement.getEndTagOpenOffset();
if (endOffset > 0) {
end = document.positionAt(endOffset);
}
int completionOffset = request.getOffset();
String tokenStart = StringUtils.getWhitespaces(document.getText(), startOffset,
completionOffset);
Range fullRange = new Range(start, end);
values.forEach(value -> {
CompletionItem item = new CompletionItem();
item.setLabel(value);
String insertText = value; // request.getInsertAttrValue(value);
item.setLabel(value);
item.setKind(CompletionItemKind.Value);
item.setFilterText(tokenStart + insertText);
item.setTextEdit(new TextEdit(fullRange, insertText));
MarkupContent documentation = XMLGenerator.createMarkupContent(elementDeclaration, value,
request);
item.setDocumentation(documentation);
response.addCompletionItem(item);
});
}
}
} catch (CacheResourceDownloadingException e) {
// XML Schema, DTD is loading, ignore this error
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 Angelo ZERR
* Copyright (c) 2018-2020 Angelo ZERR
* 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
Expand All @@ -21,8 +21,8 @@
import org.eclipse.lemminx.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lemminx.settings.XMLFormattingOptions;
import org.eclipse.lemminx.utils.MarkupContentFactory;
import org.eclipse.lemminx.utils.XMLBuilder;
import org.eclipse.lemminx.utils.MarkupContentFactory.IMarkupKindSupport;
import org.eclipse.lemminx.utils.XMLBuilder;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.MarkupKind;

Expand Down Expand Up @@ -124,6 +124,15 @@ private int generate(CMElementDeclaration elementDeclaration, String prefix, int
xml.selfCloseElement();
} else {
xml.closeStartElement();
Collection<String> values = elementDeclaration.getEnumerationValues();
if (!values.isEmpty()) {
if (canSupportSnippets) {
snippetIndex++;
xml.addContent(SnippetsBuilder.choice(snippetIndex, values));
} else {
xml.addContent(values.iterator().next());
}
}
if (canSupportSnippets) {
snippetIndex++;
xml.addContent(SnippetsBuilder.tabstops(snippetIndex));
Expand Down Expand Up @@ -323,4 +332,23 @@ public static MarkupContent createMarkupContent(CMAttributeDeclaration cmAttribu
}
return null;
}

/**
* Returns a markup content for element text documentation and null otherwise.
*
* @param cmElement element declaration.
* @param textContent the text content.
* @param support markup kind support.
*
* @return a markup content for element text documentation and null otherwise.
*/
public static MarkupContent createMarkupContent(CMElementDeclaration cmElement, String textContent,
IMarkupKindSupport support) {
String documentation = XMLGenerator.generateDocumentation(cmElement.getValueDocumentation(textContent),
cmElement.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 @@ -131,6 +131,11 @@ public Collection<String> getEnumerationValues() {
return null;
}

@Override
public String getValueDocumentation(String textContent) {
return null;
}

public int getIndex() {
return index;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ private static int getComplexTypeOffset(XSComplexTypeDefinition complexType, Sch
// - fCTLocators array of locator
// - fComplexTypeDecls array of XSComplexTypeDecl

// As it's not an API, we must use Java Reflection to get thoses 2 arrays
// As it's not an API, we must use Java Reflection to get those 2 arrays
Field f = SchemaGrammar.class.getDeclaredField("fCTLocators");
f.setAccessible(true);
SimpleLocator[] fCTLocators = (SimpleLocator[]) f.get(grammar);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,24 @@ public boolean isEmpty() {
@Override
public Collection<String> getEnumerationValues() {
XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition();
if (typeDefinition != null && typeDefinition.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
return CMXSDDocument.getEnumerationValues((XSSimpleTypeDefinition) typeDefinition);
if (typeDefinition != null) {
XSSimpleTypeDefinition simpleDefinition = null;
if (typeDefinition.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
simpleDefinition = (XSSimpleTypeDefinition) typeDefinition;
} else if (typeDefinition.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
simpleDefinition = ((XSComplexTypeDefinition) typeDefinition).getSimpleType();
}
return CMXSDDocument.getEnumerationValues(simpleDefinition);
}
return Collections.emptyList();
}

@Override
public String getValueDocumentation(String value) {
// FIXME: implement xsd:enumeration for Text node.
return null;
}

XSElementDeclaration getElementDeclaration() {
return elementDeclaration;
}
Expand All @@ -416,5 +428,4 @@ public String getDocumentURI() {
SchemaGrammar schemaGrammar = document.getOwnerSchemaGrammar(elementDeclaration);
return CMXSDDocument.getSchemaURI(schemaGrammar);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,21 @@ public static String normalizeSpace(String str) {
* @return the start whitespaces of the given line text.
*/
public static String getStartWhitespaces(String lineText) {
return getWhitespaces(lineText, 0, lineText.length());
}

/**
* Returns the whitespaces from the given range start/end of the given text.
*
* @param start the range start
* @param end the range end
* @param text the text
* @return the whitespaces from the given range start/end of the given text.
*/
public static String getWhitespaces(String text, int start, int end) {
StringBuilder whitespaces = new StringBuilder();
char[] chars = lineText.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
for (int i = start; i < end; i++) {
char c = text.charAt(i);
if (Character.isWhitespace(c)) {
whitespaces.append(c);
} else {
Expand Down Expand Up @@ -266,11 +277,10 @@ public static int getOffsetAfterWhitespace(String text, int endOffset) {
}

/**
* Returns the number of consecutive whitespace characters in front
* of text
* Returns the number of consecutive whitespace characters in front of text
*
* @param text String of interest
* @return the number of consecutive whitespace characters in front
* of text
* @return the number of consecutive whitespace characters in front of text
*/
public static int getFrontWhitespaceLength(String text) {

Expand All @@ -287,14 +297,13 @@ public static int getFrontWhitespaceLength(String text) {
}

/**
* Returns the number of consecutive whitespace characters from
* the end of text
* Returns the number of consecutive whitespace characters from the end of text
*
* @param text String of interest
* @return the number of consecutive whitespace characters from
* the end of text
* @return the number of consecutive whitespace characters from the end of text
*/
public static int getTrailingWhitespaceLength(String text) {

if (StringUtils.isWhitespace(text)) {
return text.length();
}
Expand Down Expand Up @@ -397,7 +406,7 @@ public static int findExprBeforeAt(String text, String expr, int offset) {
}

public static String getString(Object obj) {
if(obj != null) {
if (obj != null) {
return obj.toString();
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,40 @@ public void completionOnAttributeValue2() throws BadLocationException {
XMLAssert.testCompletionFor(xml, null, "src/test/resources/invoice.xml", null, c("credit", "credit"),
c("debit", "debit"), c("cash", "cash"));
}

@Test
public void completionOnTextWithEnumeration() throws BadLocationException {
String xml = "<team xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"team_namespace\" xsi:schemaLocation=\"team_namespace xsd/team.xsd\">\r\n"
+ //
" <member>\r\n" + //
" <skills>\r\n" + //
" |\r\n" + //
" </skills>";
// Completion on skills Text node
// - without snippet
XMLAssert.testCompletionFor(xml, null, "src/test/resources/team.xml", null, c("skill", "<skill>Java</skill>"));
// - with snippet
testCompletionSnippetSupporytFor(xml, "src/test/resources/team.xml", null, c("skill", "<skill>${1|Java,Node,XML|}$2</skill>$0"));

xml = "<team xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"team_namespace\" xsi:schemaLocation=\"team_namespace xsd/team.xsd\">\r\n"
+ //
" <member>\r\n" + //
" <skills>\r\n" + //
" <skill>|</skill>\r\n" + //
" </skills>";
// Completion on skill Text node
XMLAssert.testCompletionFor(xml, null, "src/test/resources/team.xml", null, c("Java", "Java"), c("Node", "Node"), c("XML", "XML"));

xml = "<team xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"team_namespace\" xsi:schemaLocation=\"team_namespace xsd/team.xsd\">\r\n"
+ //
" <member>\r\n" + //
" <skills>\r\n" + //
" <skill> |</skill>\r\n" + //
" </skills>";
// Completion on skill Text node
XMLAssert.testCompletionFor(xml, null, "src/test/resources/team.xml", null, c("Java", "Java"), c("Node", "Node"), c("XML", "XML"));

}

@Test
public void schemaLocationWithElementAndAttributeCompletion() throws BadLocationException {
Expand Down Expand Up @@ -1019,4 +1053,17 @@ private void testCompletionMarkdownSupporytFor(String xml, CompletionItem... exp
XMLAssert.testCompletionFor(new XMLLanguageService(), xml, "src/test/resources/catalogs/catalog.xml", null,
null, null, completionSettings, expectedItems);
}

private void testCompletionSnippetSupporytFor(String xml, String fileURI, Integer expectedCount,
CompletionItem... expectedItems)
throws BadLocationException {
XMLCompletionSettings completionSettings = new XMLCompletionSettings();
CompletionCapabilities completionCapabilities = new CompletionCapabilities();
CompletionItemCapabilities completionItem = new CompletionItemCapabilities(true);
completionItem.setDocumentationFormat(Arrays.asList(MarkupKind.MARKDOWN));
completionCapabilities.setCompletionItem(completionItem);
completionSettings.setCapabilities(completionCapabilities);
XMLAssert.testCompletionFor(new XMLLanguageService(), xml, null, null,
fileURI, null, completionSettings, expectedItems);
}
}

0 comments on commit b8afc81

Please sign in to comment.