Skip to content

Commit

Permalink
Support joinCommentLines setting with experimental formatter
Browse files Browse the repository at this point in the history
Signed-off-by: Jessica He <jhe@redhat.com>
  • Loading branch information
JessicaJHee committed Jul 18, 2022
1 parent 0b0b63b commit 742c1db
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*******************************************************************************
* Copyright (c) 2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.services.format;

import java.util.List;

import org.eclipse.lemminx.dom.DOMComment;
import org.eclipse.lsp4j.TextEdit;

/**
* DOM text formatter.
*
* @author Angelo ZERR
*
*/
public class DOMCommentFormatter {

private final XMLFormatterDocumentNew formatterDocument;

public DOMCommentFormatter(XMLFormatterDocumentNew formatterDocument) {
this.formatterDocument = formatterDocument;
}

public void formatComment(DOMComment commentNode, XMLFormattingConstraints parentConstraints,
List<TextEdit> edits) {

// Check the comment is closed properly
if (commentNode.getEnd() == commentNode.getEndContent()) {
return;
}
FormatElementCategory formatElementCategory = parentConstraints.getFormatElementCategory();
String text = formatterDocument.getText();
int availableLineWidth = parentConstraints.getAvailableLineWidth();

int spaceStart = -1;
int spaceEnd = -1;

int indentLevel = parentConstraints.getIndentLevel();

switch (formatElementCategory) {
case PreserveSpace:
// Preserve existing spaces
break;
case MixedContent:
break;
case IgnoreSpace:
// remove spaces and indent hasLineBreak
int start = commentNode.getStart();
int leftWhitespaceOffset = start > 0 ? start - 1 : 0;
while (leftWhitespaceOffset > 0 && Character.isWhitespace(text.charAt(leftWhitespaceOffset))) {
leftWhitespaceOffset--;
}
boolean addLineSeparator = formatterDocument.hasLineBreak(leftWhitespaceOffset, start);
if (addLineSeparator) {
replaceLeftSpacesWithIndentation(indentLevel, start,
addLineSeparator, edits);
} else if (start != 0 && isLineSeparator(text.charAt(start-1))) {
replaceLeftSpacesWithOneSpace(leftWhitespaceOffset, start, edits);
}
case NormalizeSpace:
break;
}

if (isJoinCommentLines()) {
for (int i = commentNode.getStartContent(); i < commentNode.getEndContent(); i++) {
char c = text.charAt(i);
if (Character.isWhitespace(c)) {
// Whitespaces...
if (spaceStart == -1) {
spaceStart = i;
} else {
spaceEnd = i;
}
} else {
// Text content...
int contentStart = i;
while (i < commentNode.getEnd() + 1 && !Character.isWhitespace(text.charAt(i + 1))) {
i++;
}

int contentEnd = i;
availableLineWidth -= (contentEnd - contentStart);
if (availableLineWidth <= 0) {
if (spaceStart != -1) {
insertLineBreak(spaceStart, contentStart, edits);
availableLineWidth = getMaxLineWidth();
}
} else {
replaceSpacesWithOneSpace(spaceStart, spaceEnd, edits);
spaceStart = -1;
spaceEnd = -1;
}
}
}
replaceSpacesWithOneSpace(spaceStart, spaceEnd, edits);

}
}

private boolean isJoinCommentLines() {
return formatterDocument.getSharedSettings().getFormattingSettings().isJoinCommentLines();
}

private int getMaxLineWidth() {
return formatterDocument.getMaxLineWidth();
}

private void insertLineBreak(int start, int end, List<TextEdit> edits) {
formatterDocument.insertLineBreak(start, end, edits);
}

private void replaceSpacesWithOneSpace(int spaceStart, int spaceEnd, List<TextEdit> edits) {
formatterDocument.replaceSpacesWithOneSpace(spaceStart, spaceEnd, edits);
}

private int replaceLeftSpacesWithIndentation(int indentLevel, int offset, boolean addLineSeparator,
List<TextEdit> edits) {
return formatterDocument.replaceLeftSpacesWithIndentation(indentLevel, offset, addLineSeparator, edits);
}

private void replaceLeftSpacesWithOneSpace(int from, int to, List<TextEdit> edits) {
formatterDocument.replaceLeftSpacesWithOneSpace(from, to, edits);
}

private static boolean isLineSeparator(char c) {
return c == '\r' || c == '\n';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DOMProcessingInstruction;
import org.eclipse.lemminx.dom.DOMText;
import org.eclipse.lemminx.dom.DOMComment;
import org.eclipse.lemminx.services.extensions.format.IFormatterParticipant;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lsp4j.Position;
Expand Down Expand Up @@ -66,6 +67,8 @@ public class XMLFormatterDocumentNew {

private final DOMTextFormatter textFormatter;

private final DOMCommentFormatter commentFormatter;

private final Collection<IFormatterParticipant> formatterParticipants;

private int startOffset = -1;
Expand Down Expand Up @@ -96,6 +99,8 @@ public XMLFormatterDocumentNew(DOMDocument xmlDocument, Range range, SharedSetti
this.elementFormatter = new DOMElementFormatter(this, attributeFormatter);
this.processingInstructionFormatter = new DOMProcessingInstructionFormatter(this, attributeFormatter);
this.textFormatter = new DOMTextFormatter(this);
this.commentFormatter = new DOMCommentFormatter(this);


}

Expand Down Expand Up @@ -264,36 +269,42 @@ private void format(DOMNode child, XMLFormattingConstraints parentConstraints, i

switch (child.getNodeType()) {

case Node.DOCUMENT_TYPE_NODE:
DOMDocumentType docType = (DOMDocumentType) child;
docTypeFormatter.formatDocType(docType, parentConstraints, start, end, edits);
break;

case Node.DOCUMENT_NODE:
DOMDocument document = (DOMDocument) child;
formatChildren(document, parentConstraints, start, end, edits);
break;

case DOMNode.PROCESSING_INSTRUCTION_NODE:
DOMProcessingInstruction processingInstruction = (DOMProcessingInstruction) child;
processingInstructionFormatter.formatProcessingInstruction(processingInstruction, parentConstraints, edits);
break;

case Node.ELEMENT_NODE:
DOMElement element = (DOMElement) child;
elementFormatter.formatElement(element, parentConstraints, start, end, edits);
break;

case Node.TEXT_NODE:
DOMText textNode = (DOMText) child;
textFormatter.formatText(textNode, parentConstraints, edits);
break;

default:
// unknown, so just leave alone for now but make sure to update
// available line width
int width = updateLineWidthWithLastLine(child, parentConstraints.getAvailableLineWidth());
parentConstraints.setAvailableLineWidth(width);
case Node.DOCUMENT_TYPE_NODE:
DOMDocumentType docType = (DOMDocumentType) child;
docTypeFormatter.formatDocType(docType, parentConstraints, start, end, edits);
break;

case Node.DOCUMENT_NODE:
DOMDocument document = (DOMDocument) child;
formatChildren(document, parentConstraints, start, end, edits);
break;

case DOMNode.PROCESSING_INSTRUCTION_NODE:
DOMProcessingInstruction processingInstruction = (DOMProcessingInstruction) child;
processingInstructionFormatter.formatProcessingInstruction(processingInstruction, parentConstraints,
edits);
break;

case Node.ELEMENT_NODE:
DOMElement element = (DOMElement) child;
elementFormatter.formatElement(element, parentConstraints, start, end, edits);
break;

case Node.TEXT_NODE:
DOMText textNode = (DOMText) child;
textFormatter.formatText(textNode, parentConstraints, edits);
break;

case Node.COMMENT_NODE:
DOMComment commentNode = (DOMComment) child;
commentFormatter.formatComment(commentNode, parentConstraints, edits);
break;

default:
// unknown, so just leave alone for now but make sure to update
// available line width
int width = updateLineWidthWithLastLine(child, parentConstraints.getAvailableLineWidth());
parentConstraints.setAvailableLineWidth(width);
}
}

Expand Down Expand Up @@ -329,7 +340,7 @@ void replaceLeftSpacesWith(int leftLimit, int to, String replacement, List<TextE
createTextEditIfNeeded(from, to, replacement, edits);
}

void replaceQuoteWithPreferred(int from, int to, String replacement, List<TextEdit> edits){
void replaceQuoteWithPreferred(int from, int to, String replacement, List<TextEdit> edits) {
createTextEditIfNeeded(from, to, replacement, edits);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,6 @@ public void testComment() throws BadLocationException {
assertFormat(expected, expected);
}

@Disabled
@Test
public void testComment2() throws BadLocationException {
String content = "<!-- CommentText --><!-- Comment2 --><a>Val</a>";
Expand Down Expand Up @@ -584,7 +583,6 @@ public void testCommentWithRange() throws BadLocationException {
assertFormat(content, expected);
}

@Disabled
@Test
public void testJoinCommentLines() throws BadLocationException {
String content = "<!--" + lineSeparator() + //
Expand All @@ -599,7 +597,6 @@ public void testJoinCommentLines() throws BadLocationException {
assertFormat(content, expected, settings);
}

@Disabled
@Test
public void testUnclosedEndTagTrailingComment() throws BadLocationException {
String content = "<root>" + lineSeparator() + //
Expand All @@ -615,7 +612,6 @@ public void testUnclosedEndTagTrailingComment() throws BadLocationException {
assertFormat(content, expected, settings);
}

@Disabled
@Test
public void testJoinCommentLinesNested() throws BadLocationException {
String content = "<a>" + lineSeparator() + //
Expand All @@ -635,15 +631,12 @@ public void testJoinCommentLinesNested() throws BadLocationException {
assertFormat(content, expected, settings);
}

@Disabled
@Test
public void testCommentFormatSameLine() throws BadLocationException {
String content = "<a>" + lineSeparator() + //
" Content" + lineSeparator() + //
"</a> <!-- My Comment -->";
String expected = "<a>" + lineSeparator() + //
" Content" + lineSeparator() + //
"</a> <!-- My Comment -->";
String expected = "<a> Content </a> <!-- My Comment -->";

SharedSettings settings = new SharedSettings();
settings.getFormattingSettings().setJoinCommentLines(true);
Expand Down Expand Up @@ -935,6 +928,9 @@ private static void assertFormat(String unformatted, String actual, TextEdit...
throws BadLocationException {
assertFormat(unformatted, actual, new SharedSettings(), expectedEdits);
}
private static void assertFormat(String unformatted, String expected, SharedSettings sharedSettings) throws BadLocationException {
assertFormat(unformatted, expected, sharedSettings, "test://test.html", (TextEdit[]) null);
}

private static void assertFormat(String unformatted, String expected, SharedSettings sharedSettings,
TextEdit... expectedEdits) throws BadLocationException {
Expand Down

0 comments on commit 742c1db

Please sign in to comment.