Skip to content

Commit

Permalink
Prolog now completion only provides valid attributes
Browse files Browse the repository at this point in the history
Minor fix to NoMorePseudoAttributes error range.

Fixes eclipse-lemminx#573

Signed-off-by: Nikolas Komonen <[email protected]>
  • Loading branch information
NikolasKomonen authored and angelozerr committed Nov 6, 2019
1 parent 6e8d4f1 commit 082780c
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,23 @@ public String getAttribute(String name) {
return value;
}

/**
* Returns the attribute at the given index, the order is how the attributes
* appear in the document.
* @param index Starting at 0, index of attribute you want
* @return
*/
public DOMAttr getAttributeAtIndex(int index) {
if(!hasAttributes()) {
return null;
}

if(index > attributeNodes.getLength() - 1) {
return null;
}
return attributeNodes.get(index);
}

public boolean hasAttribute(String name) {
return hasAttributes() && getAttributeNode(name) != null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public enum XMLSyntaxErrorCode implements IXMLErrorCode {
LessthanInAttValue, MarkupEntityMismatch, MarkupNotRecognizedInContent, NameRequiredInReference, OpenQuoteExpected,
PITargetRequired, PseudoAttrNameExpected, QuoteRequiredInXMLDecl, RootElementTypeMustMatchDoctypedecl,
SDDeclInvalid, SpaceRequiredBeforeEncodingInXMLDecl, SpaceRequiredBeforeStandalone, SpaceRequiredInPI,
VersionInfoRequired, VersionNotSupported, XMLDeclUnterminated, CustomETag, PrematureEOF, DoctypeNotAllowed;
VersionInfoRequired, VersionNotSupported, XMLDeclUnterminated, CustomETag, PrematureEOF, DoctypeNotAllowed, NoMorePseudoAttributes;

private final String code;

Expand Down Expand Up @@ -109,6 +109,7 @@ public static Range toLSPRange(XMLLocator location, XMLSyntaxErrorCode code, Obj
String attrName = getString(arguments[1]);
return XMLPositionUtility.selectAttributeNameFromGivenNameAt(attrName, offset, document);
}
case NoMorePseudoAttributes:
case EncodingDeclRequired:
case EqRequiredInXMLDecl:
return XMLPositionUtility.selectAttributeNameAt(offset, document);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.eclipse.lsp4xml.settings.SharedSettings;
import org.eclipse.lsp4xml.settings.XMLFormattingOptions;
import org.eclipse.lsp4xml.utils.StringUtils;
import org.w3c.dom.NamedNodeMap;

/**
* This class holds values that represent the XSI xsd. Can be seen at
Expand Down Expand Up @@ -144,17 +145,35 @@ public static void computeAttributeNameCompletionResponses(ICompletionRequest re
return;
}
boolean isSnippetsSupported = request.isCompletionSnippetsSupported();

if(!prolog.hasAttribute(VERSION_NAME)) {
int attrIndex = getAttributeCompletionPosition(offset, prolog);

if(attrIndex == 0) { // 1st attribute
if(isCurrentAttributeEqual(VERSION_NAME, prolog, 0)) {
return;
}
createCompletionItem(VERSION_NAME, isSnippetsSupported, true, editRange, VERSION_1, VERSION_VALUES, null, response, formattingsSettings);
return;
}

if(!prolog.hasAttribute(ENCODING_NAME)) {
createCompletionItem(ENCODING_NAME, isSnippetsSupported, true, editRange, UTF_8, ENCODING_VALUES, null, response, formattingsSettings);
if(attrIndex == 1) { // 2nd attribute
if(!isCurrentAttributeEqual(ENCODING_NAME, prolog, 1)) {
createCompletionItem(ENCODING_NAME, isSnippetsSupported, true, editRange, UTF_8, ENCODING_VALUES, null, response, formattingsSettings);
} else {
return;
}

if(!isCurrentAttributeEqual(STANDALONE_NAME, prolog, 1)) {
createCompletionItem(STANDALONE_NAME, isSnippetsSupported, true, editRange, YES, STANDALONE_VALUES, null, response, formattingsSettings);
}
return;
}

if(!prolog.hasAttribute(STANDALONE_NAME)) {
createCompletionItem(STANDALONE_NAME, isSnippetsSupported, true, editRange, YES, STANDALONE_VALUES, null, response, formattingsSettings);
if(attrIndex == 2) { // 3rd attribute
DOMAttr attrBefore = prolog.getAttributeAtIndex(1);
if(!STANDALONE_NAME.equals(attrBefore.getName()) && !isCurrentAttributeEqual(STANDALONE_NAME, prolog, 2)) {
createCompletionItem(STANDALONE_NAME, isSnippetsSupported, true, editRange, YES, STANDALONE_VALUES, null, response, formattingsSettings);
}
return;
}

}
Expand Down Expand Up @@ -201,4 +220,70 @@ private static void createCompletionItemsForValues(Collection<String> enumeratio
}
}

/**
* Returns the position the offset is in in relation to the attributes and their order
*
* example:
*
* <element a="1" b="2" | c="3">
*
* This will return 2 since if you insert a new attribute there you can access
* it from the list of attributes with this index.
* @param completionOffset
* @param element
* @return
*/
private static int getAttributeCompletionPosition(int completionOffset, DOMNode element) {

NamedNodeMap attributeList = element.getAttributes();

if(attributeList == null) {
return 0;
}

int attributeListLength = attributeList.getLength();


if(attributeListLength == 0) {
return 0;
}

DOMAttr attr;

for (int i = 0; i < attributeListLength; i++) {
attr = element.getAttributeAtIndex(i);
if(completionOffset <= attr.getStart()) {
return i;
}
}

return attributeListLength;
}

/**
* Returns true if the current attribute in the given position of the element's list of attributes
* equals the provided attributeName
* @param attributeName
* @param element
* @param position
* @return
*/
private static boolean isCurrentAttributeEqual(String attributeName, DOMNode element, int index) {
NamedNodeMap attributeList = element.getAttributes();

if(attributeList == null) {
return false;
}

if(index >= attributeList.getLength()) {
return false;
}

if(attributeName.equals(element.getAttributeAtIndex(index).getName())) {
return true;
}

return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ public void testEqRequiredInXMLDecl() throws Exception {
testDiagnosticsFor(xml, d(0, 6, 0, 14, XMLSyntaxErrorCode.EqRequiredInXMLDecl));
}

@Test
public void testNoMorePseudoAttributes() throws Exception {
String xml = "<?xml version=\"1.0\" standalone=\"yes\" encoding=\"UTF-8\"?><a></a>";
testDiagnosticsFor(xml, d(0, 37, 0, 45, XMLSyntaxErrorCode.NoMorePseudoAttributes));
}

/**
* ETagRequired tests
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,53 @@ public static void runOnceBeforeClass() {
}

@Test
public void completionVersion() throws BadLocationException {
public void completionVersionWithV() throws BadLocationException {
// completion on |
String xml = "<?xml v|?>\r\n" + //
"<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
testCompletionFor(xml, c("version", te(0, 6, 0, 7, "version=\"1.0\""), "version"));
testCompletionFor(xml, 1, c("version", te(0, 6, 0, 7, "version=\"1.0\""), "version"));
}

@Test
public void completionVersion() throws BadLocationException {
// completion on |
String xml = "<?xml |?>\r\n" + //
"<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
testCompletionFor(xml, c("version", te(0, 6, 0, 6, "version=\"1.0\""), "version"));
}

@Test
public void completionEncoding() throws BadLocationException {
public void completionEncodingAndStandalone() throws BadLocationException {
// completion on |
String xml = "<?xml e|?>\r\n" + //
String xml = "<?xml version=\"1.0\" |?>\r\n" + //
"<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
testCompletionFor(xml, c("encoding", te(0, 6, 0, 7, "encoding=\"UTF-8\""), "encoding"));
testCompletionFor(xml, 2,
c("encoding", te(0, 20, 0, 20, "encoding=\"UTF-8\""), "encoding"),
c("standalone", te(0, 20, 0, 20, "standalone=\"yes\""), "standalone"));
}

@Test
public void completionStandalone() throws BadLocationException {
// completion on |
String xml = "<?xml s|?>\r\n" + //
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" |?>\r\n" + //
"<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
testCompletionFor(xml, c("standalone", te(0, 6, 0, 7, "standalone=\"yes\""), "standalone"));
testCompletionFor(xml, 1, c("standalone", te(0, 37, 0, 37, "standalone=\"yes\""), "standalone"));
}

@Test
public void noCompletionsAfterStandalone() throws BadLocationException {
// completion on |
String xml = "<?xml version=\"1.0\" standalone=\"yes\" |?>\r\n" + //
"<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
testCompletionFor(xml, 0, (CompletionItem []) null);
}

@Test
public void completionEncodingBeforeStandalone() throws BadLocationException {
// completion on |
String xml = "<?xml version=\"1.0\" | standalone=\"yes\" ?>\r\n" + //
"<project xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
testCompletionFor(xml, 1, c("encoding", te(0, 20, 0, 20, "encoding=\"UTF-8\""), "encoding"));
}

@Test
Expand Down Expand Up @@ -245,6 +271,10 @@ private void testCompletionFor(String xml, CompletionItem... expectedItems) thro
XMLAssert.testCompletionFor(xml, null, expectedItems);
}

private void testCompletionFor(String xml, int expectedCount, CompletionItem... expectedItems) throws BadLocationException {
XMLAssert.testCompletionFor(xml, expectedCount, expectedItems);
}

private void testCompletionFor(String xml, String fileURI, boolean autoCloseTags, boolean isSnippetsSupported,
CompletionItem... expectedItems) throws BadLocationException {
testCompletionFor(xml, fileURI, formattingSettings,
Expand Down

0 comments on commit 082780c

Please sign in to comment.