diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java index 755eabb3cf..5992251a3d 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/DOMAttr.java @@ -108,9 +108,8 @@ public DOMElement getOwnerElement() { } /* - * (non-Javadoc) * - * @see org.w3c.dom.Attr#getValue() + * Returns the attribute's value without quotes. */ @Override public String getValue() { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/Constants.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/Constants.java index 0d68e95973..015302f871 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/Constants.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/Constants.java @@ -60,17 +60,22 @@ public class Constants { public static final Pattern ELEMENT_NAME_REGEX = Pattern.compile("^[_:\\w][_:\\w-.\\d]*"); - public static final Pattern ATTRIBUTE_NAME_REGEX = Pattern.compile("^[^\\s\"'<>/=\\x00-\\x0F\\x7F\\x80-\\x9F]*"); + public static final Pattern ATTRIBUTE_NAME_REGEX = Pattern.compile("^[^\\s\\?\"'<>/=\\x00-\\x0F\\x7F\\x80-\\x9F]*"); public static final Pattern ATTRIBUTE_VALUE_REGEX = Pattern.compile("^(\"[^\"]*\"?)|(\'[^\']*\'?)"); public static final Pattern URL_VALUE_REGEX = Pattern.compile("^(\"|\')[^<>\"]*(\"|\')"); - public static final Pattern PROLOG_NAME_OPTIONS = Pattern.compile("^(xml|xml-stylesheet)"); + public static final Pattern PROLOG_NAME_OPTIONS = Pattern.compile("^(xml)[\\s<>?]?"); + + public static final Pattern PI_TAG_NAME = Pattern.compile("^[a-zA-Z0-9]+"); + + //Add comming processing instructions that are defined to have attributes as content + public static final Pattern PI_WITH_VARIABLES = Pattern.compile("^(xml-stylesheet)[\\s<>?]?"); public static final Pattern DOCTYPE_KIND_OPTIONS = Pattern.compile("^(PUBLIC|SYSTEM)([\\s<>\"'])"); - public static final Pattern PI_TAG_NAME = Pattern.compile("^[a-zA-Z0-9]+"); + public static final Pattern DTD_ELEMENT_CATEGORY = Pattern.compile("^(EMPTY|ANY)([\\s<>\"'])"); diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/XMLScanner.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/XMLScanner.java index fc158c7769..6af4056555 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/XMLScanner.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/parser/XMLScanner.java @@ -135,7 +135,11 @@ TokenType internalScan() { state = ScannerState.WithinTag; return finishToken(offset, TokenType.PrologName); } - if (PI_TAG_NAME.matcher(name).matches()) { // {name} eg: m2e + // if(PI_WITH_VARIABLES.matcher(name).matches()) { // name eg: xml-stylesheet + // state = ScannerState.WithinTag; + // return finishToken(offset, TokenType.PIName); + // } + if (ATTRIBUTE_NAME_REGEX.matcher(name).matches()) { // {name} eg: m2e state = ScannerState.WithinPI; return finishToken(offset, TokenType.PIName); } @@ -296,7 +300,7 @@ TokenType internalScan() { return finishToken(offset, TokenType.Whitespace); } - if (stream.advanceIfChar(_EQS)) { + if (stream.advanceIfChar(_EQS)) { // = state = ScannerState.BeforeAttributeValue; return finishToken(offset, TokenType.DelimiterAssign); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFormatter.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFormatter.java index 6d245ddf39..75fa102f8d 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFormatter.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFormatter.java @@ -124,11 +124,8 @@ private void format(DOMNode node, int level, int end, XMLBuilder xml) { DOMAttr singleAttribute = attributes.get(0); xml.addSingleAttribute(singleAttribute.getName(), singleAttribute.getOriginalValue()); } else { - int attributeIndex = 0; for (DOMAttr attr : attributes) { - String attributeName = attr.getName(); xml.addAttribute(attr, level); - attributeIndex++; } } } @@ -188,7 +185,13 @@ private void format(DOMNode node, int level, int end, XMLBuilder xml) { } else if (node.isProcessingInstruction()) { DOMProcessingInstruction processingInstruction = (DOMProcessingInstruction) node; xml.startPrologOrPI(processingInstruction.getTarget()); - xml.addContentPI(processingInstruction.getData()); + if(processingInstruction.hasAttributes()) { + addAttributes(processingInstruction, xml); + } + else { + xml.addContentPI(processingInstruction.getData()); + } + xml.endPrologOrPI(); if (level == 0) { xml.linefeed(); @@ -197,19 +200,7 @@ private void format(DOMNode node, int level, int end, XMLBuilder xml) { DOMProcessingInstruction processingInstruction = (DOMProcessingInstruction) node; xml.startPrologOrPI(processingInstruction.getTarget()); if (node.hasAttributes()) { - // generate attributes - String[] attributes = new String[3]; - attributes[0] = "version"; - attributes[1] = "encoding"; - attributes[2] = "standalone"; - - for (String name : attributes) { - String value = node.getAttribute(name); - if (value == null) { - continue; - } - xml.addSingleAttribute(name, value, true); - } + addAttributes(node, xml); } xml.endPrologOrPI(); xml.linefeed(); @@ -354,4 +345,18 @@ private static boolean isPreviousSiblingNodeType(DOMNode node, short nodeType) { DOMNode previousNode = node.getPreviousSibling(); return previousNode != null && previousNode.getNodeType() == nodeType; } + + /** + * Will add all attributes, to the given builder, on a single line + */ + private static void addAttributes(DOMNode node, XMLBuilder xml) { + List attrs = node.getAttributeNodes(); + if(attrs == null) { + return; + } + for (DOMAttr attr : attrs) { + xml.addAttributesOnSingleLine(attr, true); + } + xml.appendSpace(); + } } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLBuilder.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLBuilder.java index 477477a193..711bc07579 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLBuilder.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/utils/XMLBuilder.java @@ -38,6 +38,11 @@ public XMLBuilder(XMLFormattingOptions formattingOptions, String whitespacesInde this.xml = new StringBuilder(); } + public XMLBuilder appendSpace() { + xml.append(" "); + return this; + } + public XMLBuilder startElement(String prefix, String name, boolean close) { xml.append("<"); if (prefix != null && !prefix.isEmpty()) { @@ -113,6 +118,13 @@ public XMLBuilder addSingleAttribute(String name, String value, boolean surround return this; } + public XMLBuilder addAttributesOnSingleLine(DOMAttr attr, Boolean surroundWithQuotes) { + xml.append(" "); + addAttributeContents(attr.getName(), attr.hasDelimiter(), attr.getValue(), surroundWithQuotes); + + return this; + } + public XMLBuilder addAttribute(String name, String value, int level) { return addAttribute(name, value, level, false); } @@ -156,7 +168,7 @@ public XMLBuilder addAttribute(DOMAttr attr, int level, boolean surroundWithQuot } /** - * Builds the attribute name, '=', and value. + * Builds the attribute {name, '=', and value}. * * Never puts quotes around unquoted values unless indicated to by 'surroundWithQuotes' */ diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java index ff2a1b1739..3d107306ef 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/dom/DOMParserTest.java @@ -16,6 +16,7 @@ import org.eclipse.lsp4xml.dom.DOMDocumentType.DocumentTypeKind; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; /** @@ -292,6 +293,27 @@ public void testPI() { assertDocument("", html); } + @Ignore + @Test + public void testPIXMLStyleSheet() { + DOMNode processingInstruction = createPINode("xml-stylesheet", 6, 60, true, ""); + insertIntoAttributes(processingInstruction, "href", "\"my-style.css\""); + insertIntoAttributes(processingInstruction, "type", "\"text/css\""); + DOMNode html = createElement("html", 0, 60, 67, true); + html.addChild(processingInstruction); + + assertDocument("", html); + } + + @Test + public void testPIXMLStyleSheetMispelled() { + // This PI name is not recognized by the regex and considers the attributes as content. + DOMNode processingInstruction = createPINode("xml-stylesheetBAD", 6, 63, true, "href=\"my-style.css\" type=\"text/css\""); + DOMNode html = createElement("html", 0, 63, 70, true); + html.addChild(processingInstruction); + assertDocument("", html); + } + @Test public void testPISpaces() { DOMNode processingInstruction = createPINode("m2e", 6, 28, true, "he haa"); diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/services/XMLFormatterTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/services/XMLFormatterTest.java index 9d9db1f0f9..a198bf6a69 100644 --- a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/services/XMLFormatterTest.java +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/services/XMLFormatterTest.java @@ -121,14 +121,14 @@ public void range2() throws BadLocationException { @Test public void testProlog() throws BadLocationException { String content = "" + lineSeparator(); - String expected = "" + lineSeparator(); + String expected = "" + lineSeparator(); format(content, expected); } @Test public void testProlog2() throws BadLocationException { String content = "bb"; - String expected = "" + lineSeparator() + // + String expected = "" + lineSeparator() + // "bb"; format(content, expected); } @@ -136,7 +136,17 @@ public void testProlog2() throws BadLocationException { @Test public void testProlog3() throws BadLocationException { String content = "c"; - String expected = "" + lineSeparator() + // + String expected = "" + lineSeparator() + // + "" + lineSeparator() + // + " c" + lineSeparator() + // + ""; + format(content, expected); + } + + @Test + public void testProlog4WithUnknownVariable() throws BadLocationException { + String content = "c"; + String expected = "" + lineSeparator() + // "" + lineSeparator() + // " c" + lineSeparator() + // ""; @@ -152,6 +162,38 @@ public void testPI() throws BadLocationException { format(content, expected); } + @Ignore + @Test + public void testDefinedPIWithVariables() throws BadLocationException { + String content = ""; + String expected = + "" + lineSeparator() + // + " " + lineSeparator() + // + ""; + format(content, expected); + } + + @Ignore + @Test + public void testDefinedPIWithJustAttributeNames() throws BadLocationException { + String content = ""; + String expected = + "" + lineSeparator() + // + " " + lineSeparator() + // + ""; + format(content, expected); + } + + @Test + public void testPIWithVariables() throws BadLocationException { + String content = ""; + String expected = + "" + lineSeparator() + // + " " + lineSeparator() + // + ""; + format(content, expected); + } + @Test public void testSplitAttributesSingle() throws BadLocationException { String content = ""; @@ -204,7 +246,7 @@ public void testNestedAttributesNoSplit() throws BadLocationException { @Test public void testSplitAttributesProlog() throws BadLocationException { String content = ""; - String expected = "" + lineSeparator(); + String expected = "" + lineSeparator(); XMLFormattingOptions formattingOptions = createDefaultFormattingOptions(); formattingOptions.setSplitAttributes(true); format(content, expected, formattingOptions); @@ -1174,7 +1216,7 @@ public void testContentFormatting6() throws BadLocationException { " \r\n" + ""; String expected = - "\r\n" + + "\r\n" + "\r\n" + " \r\n" +