Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Prolog now completion only provides valid attributes #9

Merged
merged 1 commit into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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