Skip to content

Commit

Permalink
Preserve whitespace on format
Browse files Browse the repository at this point in the history
Fixes #350

Signed-off-by: Nikolas Komonen <nikolaskomonen@gmail.com>
  • Loading branch information
NikolasKomonen authored and fbricon committed May 7, 2019
1 parent d3a3d61 commit 589c17c
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,28 @@ public abstract class DOMCharacterData extends DOMNode implements org.w3c.dom.Ch

private boolean isWhitespace;

private int newLineCount;

private String delimiter;

public DOMCharacterData(int start, int end, DOMDocument ownerDocument) {
super(start, end, ownerDocument);
}

public boolean hasMultiLine() {
return getData().contains(getDelimiter());
}

public String getDelimiter() {
if(delimiter != null) {
return delimiter;
}
try {
String delimiter = getOwnerDocument().getTextDocument().lineDelimiter(0);
return getData().contains(delimiter);
delimiter = getOwnerDocument().getTextDocument().lineDelimiter(0);
return delimiter;
} catch (BadLocationException e) {
return getData().contains(lineSeparator());
delimiter = lineSeparator();
return delimiter;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@ public DOMDocument parse(String text, String uri, URIResolverExtensionManager re
return parse(new TextDocument(text, uri), resolverExtensionManager);
}

public DOMDocument parse(String text, String uri, URIResolverExtensionManager resolverExtensionManager, boolean ignoreWhitespaceContent) {
return parse(new TextDocument(text, uri), resolverExtensionManager, ignoreWhitespaceContent);
}

public DOMDocument parse(TextDocument document, URIResolverExtensionManager resolverExtensionManager) {
return parse(document, resolverExtensionManager, true);
}

public DOMDocument parse(TextDocument document, URIResolverExtensionManager resolverExtensionManager, boolean ignoreWhitespaceContent) {
boolean isDTD = DOMUtils.isDTD(document.getUri());
boolean inDTDInternalSubset = false;
String text = document.getText();
Expand Down Expand Up @@ -320,8 +328,8 @@ else if((curr.isClosed())) {
case Content: {
// FIXME: don't use getTokenText (substring) to know if the content is only
// spaces or line feed (scanner should know that).

if (curr instanceof DTDDeclNode) {
boolean currIsDeclNode = curr instanceof DTDDeclNode;
if (currIsDeclNode) {
curr.end = scanner.getTokenOffset() - 1;
while(!curr.isDoctype()) {
curr = curr.getParentNode();
Expand All @@ -334,12 +342,22 @@ else if((curr.isClosed())) {

String content = scanner.getTokenText();
if(StringUtils.isWhitespace(content)) {
if(curr.hasChildNodes()) {
if(ignoreWhitespaceContent) {
if(curr.hasChildNodes()) {
break;
}

tempWhitespaceContent = textNode;
break;

}
textNode.setWhitespace(true);
tempWhitespaceContent = textNode;
break;
else if(!currIsDeclNode) {
textNode.setWhitespace(true);
}
else {
break;
}

}

curr.addChild(textNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
package org.eclipse.lsp4xml.services;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -72,7 +71,7 @@ public List<? extends TextEdit> format(TextDocument document, Range range, XMLFo
// Parse the content to format to create an XML document with full data (CData,
// comments, etc)
String text = document.getText().substring(start, end);
DOMDocument doc = DOMParser.getInstance().parse(text, document.getUri(), null);
DOMDocument doc = DOMParser.getInstance().parse(text, document.getUri(), null, false);

// Format the content
XMLBuilder xml = new XMLBuilder(formattingOptions, "", document.lineDelimiter(startPosition.getLine()));
Expand All @@ -98,7 +97,7 @@ private void format(DOMNode node, int level, int end, XMLBuilder xml) {
doLineFeed = false;
} else {
doLineFeed = !(node.isComment() && ((DOMComment) node).isCommentSameLineEndTag())
&& (!node.isText() || ((DOMText) node).hasSiblings());
&& (!node.isText() || (!((DOMText) node).isWhitespace() && ((DOMText) node).hasSiblings()));

//&& (!isPreviousSiblingNodeType(node, DOMNode.TEXT_NODE) || !((DOMText) node.getPreviousSibling()).endsWithNewLine())
}
Expand Down Expand Up @@ -209,7 +208,7 @@ private void format(DOMNode node, int level, int end, XMLBuilder xml) {

// Generate content
String content = textNode.getData();
xml.addContent(content, textNode.isWhitespace(), textNode.hasSiblings());
xml.addContent(content, textNode.isWhitespace(), textNode.hasSiblings(), textNode.getDelimiter(), level);
return;
} else if (node.isDoctype()) {
boolean isDTD = node.getOwnerDocument().isDTD();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class XMLFormattingOptions extends FormattingOptions {
private static final String SPACE_BEFORE_EMPTY_CLOSE_TAG = "spaceBeforeEmptyCloseTag";
private static final String QUOTATIONS = "quotations";
private static final String JOIN_CONTENT_LINES = "joinContentLines";
private static final String PRESERVED_NEWLINES = "preservedNewlines";

// Values for QUOTATIONS
public static final String DOUBLE_QUOTES_VALUE = "doubleQuotes";
Expand Down Expand Up @@ -66,6 +67,7 @@ public void initializeDefaultSettings() {
this.setSpaceBeforeEmptyCloseTag(true);
this.setQuotations(DOUBLE_QUOTES_VALUE);
this.setPreserveEmptyContent(false);
this.setPreservedNewlines(2);
}

public XMLFormattingOptions(int tabSize, boolean insertSpaces, boolean initializeDefaultSettings) {
Expand Down Expand Up @@ -245,6 +247,20 @@ public boolean isPreserveEmptyContent() {
}
}

public void setPreservedNewlines(final int preservedNewlines) {
this.putNumber(XMLFormattingOptions.PRESERVED_NEWLINES, preservedNewlines);
}

public int getPreservedNewlines() {

final Number value = this.getNumber(XMLFormattingOptions.PRESERVED_NEWLINES);
if ((value != null)) {
return value.intValue();
} else {
return 2;
}
}

public XMLFormattingOptions merge(FormattingOptions formattingOptions) {
formattingOptions.entrySet().stream().forEach(entry -> {
String key = entry.getKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,43 @@ public static String lTrim(String value) {
return value.substring(i, len);
}


/**
* Given a string that is only whitespace,
* this will return the amount of newline characters.
*
* If the newLineCounter becomes > newLineLimit, then the value of
* newLineLimit is always returned.
* @param text
* @param isWhitespace
* @param delimiter
* @return
*/
public static int getNumberOfNewLines(String text, boolean isWhitespace, String delimiter, int newLineLimit) {
if(!isWhitespace){
return 0;
}

int newLineCounter = 0;
boolean delimiterHasTwoCharacters = delimiter.length() == 2;
for(int i = 0; newLineCounter <= newLineLimit && i < text.length(); i++) {
String c;
if(delimiterHasTwoCharacters) {
if(i + 1 < text.length()) {
c = text.substring(i, i + 2);
if(delimiter.equals(c)) {
newLineCounter++;
i++; //skip the second char of the delimiter
}
}
}
else {
c = String.valueOf(text.charAt(i));
if(delimiter.equals(c)) {
newLineCounter++;
}
}
}
return newLineCounter;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

import org.eclipse.lsp4xml.dom.DOMAttr;
import org.eclipse.lsp4xml.dom.DOMComment;
import org.eclipse.lsp4xml.dom.DOMDocumentType;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.dom.DTDDeclNode;
import org.eclipse.lsp4xml.settings.XMLFormattingOptions;

Expand Down Expand Up @@ -222,10 +220,10 @@ public XMLBuilder linefeed() {
}

public XMLBuilder addContent(String text) {
return addContent(text, false, false);
return addContent(text, false, false, null, 0);
}

public XMLBuilder addContent(String text, Boolean isWhitespaceContent, Boolean hasSiblings) {
public XMLBuilder addContent(String text, Boolean isWhitespaceContent, Boolean hasSiblings, String delimiter, int level) {
if(!isWhitespaceContent) {
if(isJoinContentLines()) {
text = StringUtils.normalizeSpace(text);
Expand All @@ -238,6 +236,17 @@ else if(hasSiblings) {
else if (!hasSiblings && isPreserveEmptyContent()) {
xml.append(text);
}
else if(hasSiblings) {
int preservedNewLines = getPreservedNewlines();
if(preservedNewLines > 0) {
int newLineCount = StringUtils.getNumberOfNewLines(text, isWhitespaceContent, delimiter, preservedNewLines);
for (int i = 0; i < newLineCount - 1; i++) { // - 1 because the node after will insert a delimiter
xml.append(delimiter);
this.indent(level);
}
}

}
return this;
}

Expand Down Expand Up @@ -383,4 +392,11 @@ private boolean isPreserveEmptyContent() {
return formattingOptions != null && formattingOptions.isPreserveEmptyContent();
}

private int getPreservedNewlines() {
if(formattingOptions != null) {
return formattingOptions.getPreservedNewlines();
}
return 2; // default
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.util.ArrayList;

import org.eclipse.lsp4j.FormattingOptions;
import org.eclipse.lsp4xml.dom.DOMDocumentType.DocumentTypeKind;
import org.junit.Assert;
import org.junit.Ignore;
Expand Down Expand Up @@ -411,6 +412,34 @@ public void testUnclosedEndTagWithTrailingComment() {
assertDocument("<root> <a>Content</a <!-- comment --> </root>", root);
}

@Test
public void testWhitespaceIsParsed() {
DOMNode textNodeBefore = createTextNode("\r\n\r\n", 3, 7, true);
DOMNode a = createElement("a", 0, 18, 22, true);
DOMNode b = createElement("b", 7, 10, 14, true);
DOMNode textNodeAfter = createTextNode("\r\n\r\n", 14, 18, true);
a.addChild(textNodeBefore);
a.addChild(b);
a.addChild(textNodeAfter);


assertDocument("<a>\r\n\r\n<b></b>\r\n\r\n</a>", a, false);
}

@Test
public void testPreserveWhitespaceContent() {

DOMNode a = createElement("a", 0, 14, 18, true);
DOMNode b = createElement("b", 3, 10, 14, true);
DOMNode whitespaceContent = createTextNode("\r\n\r\n", 6, 10, true);

a.addChild(b);
b.addChild(whitespaceContent);


assertDocument("<a><b>\r\n\r\n</b></a>", a);
}

@Test
public void elementOffsets() {
DOMDocument document = DOMParser.getInstance().parse("<a></a>", null, null);
Expand Down Expand Up @@ -988,6 +1017,12 @@ private static void assertDocument(String input, DOMNode expectedNode) {
compareTrees(expectedNode, actualNode);
}

private static void assertDocument(String input, DOMNode expectedNode, boolean ignoreWhitespace) {
DOMDocument document = DOMParser.getInstance().parse(input, "uri", null, ignoreWhitespace);
DOMNode actualNode = document.getChild(0);
compareTrees(expectedNode, actualNode);
}

private static void compareTrees(DOMNode expectedNode, DOMNode actualNode) {
if (expectedNode.isElement()) {
assertEquals(((DOMElement) expectedNode).getTagName(), ((DOMElement) actualNode).getTagName());
Expand Down
Loading

0 comments on commit 589c17c

Please sign in to comment.