Skip to content

Commit

Permalink
Add alignWithFirstAttr option to xml.format.splitAttributes
Browse files Browse the repository at this point in the history
Signed-off-by: Jessica He <jhe@redhat.com>
  • Loading branch information
JessicaJHee authored and datho7561 committed Jul 26, 2023
1 parent d808ae7 commit 2b0fe29
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 88 deletions.
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.lemminx.services.format.XMLFormatterDocument;
import org.eclipse.lemminx.services.format.XMLFormattingConstraints;
import org.eclipse.lemminx.settings.XMLFormattingOptions;
import org.eclipse.lemminx.settings.XMLFormattingOptions.SplitAttributes;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lemminx.utils.XMLBuilder;
import org.eclipse.lsp4j.TextEdit;
Expand Down Expand Up @@ -188,9 +189,14 @@ public boolean formatAttributeValue(DOMAttr attr, XMLFormatterDocument formatter
int indentSpaceOffset;
int startOfLineOffset = formatterDocument.getLineAtOffset(attr.getOwnerElement().getStart());

if (formattingOptions.isSplitAttributes()) {
indentSpaceOffset = (attrValueStart + 1) - attr.getNodeAttrName().getStart()
+ formattingOptions.getSplitAttributesIndentSize() * tabSize;
if (formattingOptions.getSplitAttributes() != SplitAttributes.preserve) {
if (formattingOptions.getSplitAttributes() == SplitAttributes.splitNewLine) {
indentSpaceOffset = (attrValueStart + 1) - attr.getNodeAttrName().getStart()
+ formattingOptions.getSplitAttributesIndentSize() * tabSize;
} else {
indentSpaceOffset = (attrValueStart + 1) - attr.getNodeAttrName().getStart()
+ attr.getOwnerElement().getTagName().length() + 2;
}
} else if (formattingOptions.isPreserveAttributeLineBreaks()) {
indentSpaceOffset = attrValueStart - formatterDocument.getOffsetWithPreserveLineBreaks(startOfLineOffset,
attrValueStart, tabSize, formattingOptions.isInsertSpaces());
Expand All @@ -212,7 +218,7 @@ public boolean formatAttributeValue(DOMAttr attr, XMLFormatterDocument formatter
availableLineWidth -= i - lastAttrValueTermIndex;
lastAttrValueTermIndex = i;
if (availableLineWidth < 0 && formatterDocument.isMaxLineWidthSupported()
&& !formattingOptions.isSplitAttributes()) {
&& formattingOptions.getSplitAttributes() == SplitAttributes.preserve) {
indentSpaceOffset = (attrValueStart + 1) - attr.getNodeAttrName().getStart()
+ (parentConstraints.getIndentLevel() + 1) * tabSize;
}
Expand Down
Expand Up @@ -14,7 +14,9 @@
import java.util.List;

import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.settings.EnforceQuoteStyle;
import org.eclipse.lemminx.settings.XMLFormattingOptions.SplitAttributes;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lsp4j.TextEdit;

Expand All @@ -33,6 +35,7 @@ public DOMAttributeFormatter(XMLFormatterDocument formatterDocument) {
}

public void formatAttribute(DOMAttr attr, int prevOffset, boolean singleAttribute, boolean useSettings,
boolean isFirstAttr,
XMLFormattingConstraints parentConstraints, List<TextEdit> edits) {
int indentLevel = parentConstraints.getIndentLevel();
// 1) format before attribute name : indent left of the attribute name
Expand All @@ -44,10 +47,14 @@ public void formatAttribute(DOMAttr attr, int prevOffset, boolean singleAttribut
if (isPreserveAttributeLineBreaks() && hasLineBreak(prevOffset, attr.getStart())) {
replaceLeftSpacesWithIndentation(indentLevel + 1, prevOffset, attr.getStart(), true, edits);
alreadyIndented = true;
} else if (isSplitAttributes() && !singleAttribute) {
} else if (getSplitAttributes() == SplitAttributes.splitNewLine && !singleAttribute) {
replaceLeftSpacesWithIndentation(indentLevel + getSplitAttributesIndentSize(), prevOffset,
attr.getStart(), true, edits);
alreadyIndented = true;
} else if (getSplitAttributes() == SplitAttributes.alignWithFirstAttr && !isFirstAttr) {
replaceLeftSpacesWithIndentationWithOffsetSpaces(getFirstAttrOffset(attr.getOwnerElement(), indentLevel), prevOffset,
attr.getStart(), edits);
alreadyIndented = true;
}
}

Expand All @@ -74,7 +81,7 @@ public void formatAttribute(DOMAttr attr, int prevOffset, boolean singleAttribut
int availableLineWidth = parentConstraints.getAvailableLineWidth();
if (isPreserveAttributeLineBreaks() && hasLineBreak(prevOffset, attr.getStart())) {
availableLineWidth = getMaxLineWidth() - getTabSize() * (indentLevel + 1);
} else if (isSplitAttributes() && !singleAttribute) {
} else if (getSplitAttributes() == SplitAttributes.splitNewLine && !singleAttribute) {
availableLineWidth = getMaxLineWidth()
- getTabSize() * (indentLevel + getSplitAttributesIndentSize());
} else {
Expand All @@ -93,7 +100,7 @@ public void formatAttribute(DOMAttr attr, int prevOffset, boolean singleAttribut
int from = prevOffset;
int to = attr.getStart();
if (isMaxLineWidthSupported() && parentConstraints.getAvailableLineWidth() < 0
&& !isSplitAttributes()) {
&& getSplitAttributes() == SplitAttributes.preserve) {
replaceLeftSpacesWithIndentation(indentLevel + 1, from, to, true, edits);
int attrValuelength = attr.getValue() != null ? attr.getValue().length() : 0;
parentConstraints.setAvailableLineWidth(
Expand Down Expand Up @@ -129,6 +136,13 @@ public void formatAttribute(DOMAttr attr, int prevOffset, boolean singleAttribut
}
}

private int getFirstAttrOffset(DOMElement ownerElement, int indentLevel) {
return getTabSize() * indentLevel + ownerElement.getTagName().length() + 2 /*
* +1 for '<', +1 for space between
* element name and first attr name
*/;
}

private void formatAttributeValue(DOMAttr attr, XMLFormattingConstraints parentConstraints, List<TextEdit> edits) {
formatterDocument.formatAttributeValue(attr, parentConstraints, edits);
}
Expand All @@ -137,6 +151,11 @@ private void replaceQuoteWithPreferred(int from, int to, List<TextEdit> edits) {
formatterDocument.replaceQuoteWithPreferred(from, to, edits);
}

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

private void replaceLeftSpacesWithOneSpace(int from, int to, List<TextEdit> edits) {
formatterDocument.replaceLeftSpacesWithOneSpace(from, to, edits);
}
Expand All @@ -150,8 +169,8 @@ private void removeLeftSpaces(int from, int to, List<TextEdit> edits) {
formatterDocument.removeLeftSpaces(from, to, edits);
}

private boolean isSplitAttributes() {
return formatterDocument.getSharedSettings().getFormattingSettings().isSplitAttributes();
private SplitAttributes getSplitAttributes() {
return formatterDocument.getSharedSettings().getFormattingSettings().getSplitAttributes();
}

private int getSplitAttributesIndentSize() {
Expand Down
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lemminx.settings.XMLFormattingOptions.EmptyElements;
import org.eclipse.lemminx.settings.XMLFormattingOptions.SplitAttributes;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lsp4j.TextEdit;

Expand Down Expand Up @@ -214,9 +215,12 @@ private int formatAttributes(DOMElement element, XMLFormattingConstraints parent
// <foo| attr1="" attr2="">.
int prevOffset = element.getOffsetAfterStartTag();
boolean singleAttribute = attributes.size() == 1;
boolean isFirstAttr = true;
for (DOMAttr attr : attributes) {
// Format current attribute
attributeFormatter.formatAttribute(attr, prevOffset, singleAttribute, true, parentConstraints, edits);
attributeFormatter.formatAttribute(attr, prevOffset, singleAttribute, true, isFirstAttr,
parentConstraints, edits);
isFirstAttr = false;
// set the previous offset with end of the current attribute:
// <foo attr1=""| attr2="".
prevOffset = attr.getEnd();
Expand Down Expand Up @@ -267,9 +271,15 @@ && hasLineBreak(getLastAttribute(element).getEnd(), startTagClose)) {
}
} else if (shouldFormatClosingBracketNewLine(element)) {
int indentLevel = parentConstraints.getIndentLevel();
replaceLeftSpacesWithIndentation(indentLevel + getSplitAttributesIndentSize(), startTagOpen, startTagClose,
true, edits);
return (indentLevel + getSplitAttributesIndentSize()) * getTabSize();
if (getSplitAttributes() == SplitAttributes.splitNewLine) {
replaceLeftSpacesWithIndentation(indentLevel + getSplitAttributesIndentSize(), startTagOpen,
startTagClose, true, edits);
return (indentLevel + getSplitAttributesIndentSize()) * getTabSize();
} else { /* splitAttributes == alignWithFirstAttr */
int indentOffset = indentLevel * getTabSize() + element.getTagName().length() + 2;
replaceLeftSpacesWithIndentationWithOffsetSpaces(indentOffset, startTagOpen, startTagClose, edits);
return indentOffset;
}
}
if (element.isSelfClosed()) {
if (spaceBeforeEmptyCloseTag) {
Expand Down Expand Up @@ -380,7 +390,7 @@ private boolean shouldFormatClosingBracketNewLine(DOMElement element) {
boolean isSingleAttribute = element.getAttributeNodes() != null ? element.getAttributeNodes().size() == 1
: true;
return (formatterDocument.getSharedSettings().getFormattingSettings().getClosingBracketNewLine()
&& isSplitAttributes() && !isSingleAttribute);
&& getSplitAttributes() != SplitAttributes.preserve && !isSingleAttribute);
}

private void replaceLeftSpacesWith(int from, int to, String replace, List<TextEdit> edits) {
Expand All @@ -398,6 +408,11 @@ private void replaceLeftSpacesWithIndentationPreservedNewLines(int spaceStart, i
edits);
}

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

private void removeLeftSpaces(int from, int to, List<TextEdit> edits) {
formatterDocument.removeLeftSpaces(from, to, edits);
}
Expand Down Expand Up @@ -439,8 +454,8 @@ private boolean isPreserveAttributeLineBreaks() {
return formatterDocument.getSharedSettings().getFormattingSettings().isPreserveAttributeLineBreaks();
}

private boolean isSplitAttributes() {
return formatterDocument.getSharedSettings().getFormattingSettings().isSplitAttributes();
private SplitAttributes getSplitAttributes() {
return formatterDocument.getSharedSettings().getFormattingSettings().getSplitAttributes();
}

private int getSplitAttributesIndentSize() {
Expand Down
Expand Up @@ -57,7 +57,7 @@ public void formatProcessingInstruction(DOMProcessingInstruction processingInstr
List<DOMAttr> attributes = processingInstruction.getAttributeNodes();
boolean singleAttribute = attributes.size() == 1;
for (DOMAttr attr : attributes) {
attributeFormatter.formatAttribute(attr, prevOffset, singleAttribute, false, parentConstraints, edits);
attributeFormatter.formatAttribute(attr, prevOffset, singleAttribute, false, false, parentConstraints, edits);
prevOffset = attr.getEnd();
}
}
Expand Down
Expand Up @@ -34,6 +34,7 @@
import org.eclipse.lemminx.services.extensions.format.IFormatterParticipant;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lemminx.settings.XMLFormattingOptions.EmptyElements;
import org.eclipse.lemminx.settings.XMLFormattingOptions.SplitAttributes;
import org.eclipse.lemminx.utils.XMLBuilder;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
Expand Down Expand Up @@ -554,7 +555,7 @@ private void formatAttributes(DOMElement element) throws BadLocationException {
prevOffset = attr.getEnd();
}
if ((this.sharedSettings.getFormattingSettings().getClosingBracketNewLine()
&& this.sharedSettings.getFormattingSettings().isSplitAttributes()) && !isSingleAttribute) {
&& this.sharedSettings.getFormattingSettings().getSplitAttributes() == SplitAttributes.splitNewLine) && !isSingleAttribute) {
xmlBuilder.linefeed();
// Indent by tag + splitAttributesIndentSize to match with attribute indent
// level
Expand Down
Expand Up @@ -59,7 +59,11 @@ public class XMLFormattingOptions extends org.eclipse.lemminx.settings.LSPFormat
private boolean legacy;
private int maxLineWidth;

private boolean splitAttributes;
public static enum SplitAttributes {
preserve, splitNewLine, alignWithFirstAttr;
}

private String splitAttributes;
private boolean joinCDATALines;
private boolean formatComments;
private boolean joinCommentLines;
Expand Down Expand Up @@ -150,7 +154,7 @@ private void initializeDefaultSettings() {
super.setTabSize(DEFAULT_TAB_SIZE);
super.setInsertSpaces(true);
super.setTrimFinalNewlines(true);
this.setSplitAttributes(false);
this.setSplitAttributes(SplitAttributes.preserve);
this.setJoinCDATALines(false);
this.setFormatComments(true);
this.setJoinCommentLines(false);
Expand Down Expand Up @@ -193,12 +197,19 @@ public XMLFormattingOptions(FormattingOptions options) {
this(options, true);
}

public boolean isSplitAttributes() {
return splitAttributes;
public SplitAttributes getSplitAttributes() {
String value = splitAttributes;
if ((value != null)) {
try {
return SplitAttributes.valueOf(value);
} catch (Exception e) {
}
}
return SplitAttributes.preserve;
}

public void setSplitAttributes(final boolean splitAttributes) {
this.splitAttributes = splitAttributes;
public void setSplitAttributes(SplitAttributes splitAttributes) {
this.splitAttributes = splitAttributes.name();
}

public boolean isJoinCDATALines() {
Expand Down Expand Up @@ -347,7 +358,7 @@ public void setPreserveAttributeLineBreaks(final boolean preserveAttributeLineBr
* @return the value of preserveAttrLineBreaks
*/
public boolean isPreserveAttributeLineBreaks() {
if (this.isSplitAttributes()) {
if (this.getSplitAttributes() != SplitAttributes.preserve) {
// splitAttributes overrides preserveAttrLineBreaks
return false;
}
Expand Down Expand Up @@ -438,7 +449,7 @@ public XMLFormattingOptions merge(XMLFormattingOptions formattingOptions) {
setTrimTrailingWhitespace(formattingOptions.isTrimTrailingWhitespace());
setLegacy(formattingOptions.isLegacy());
setMaxLineWidth(formattingOptions.getMaxLineWidth());
setSplitAttributes(formattingOptions.isSplitAttributes());
setSplitAttributes(formattingOptions.getSplitAttributes());
setJoinCDATALines(formattingOptions.isJoinCDATALines());
setFormatComments(formattingOptions.isFormatComments());
setJoinCommentLines(formattingOptions.isJoinCommentLines());
Expand Down
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.lemminx.services.extensions.format.IFormatterParticipant;
import org.eclipse.lemminx.settings.EnforceQuoteStyle;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lemminx.settings.XMLFormattingOptions.SplitAttributes;

/**
* XML content builder utilities.
Expand Down Expand Up @@ -171,7 +172,7 @@ public XMLBuilder addPrologAttribute(DOMAttr attr) {
* @return
*/
public XMLBuilder addAttribute(String name, String value, int level, boolean surroundWithQuotes) {
if (isSplitAttributes()) {
if (getSplitAttributes()== SplitAttributes.splitNewLine) {
linefeed();
indent(level + sharedSettings.getFormattingSettings().getSplitAttributesIndentSize());
} else {
Expand All @@ -187,7 +188,7 @@ public XMLBuilder addAttribute(DOMAttr attr, int level) {
}

private XMLBuilder addAttribute(DOMAttr attr, int level, boolean surroundWithQuotes) {
if (isSplitAttributes()) {
if (getSplitAttributes()== SplitAttributes.splitNewLine) {
linefeed();
indent(level + sharedSettings.getFormattingSettings().getSplitAttributesIndentSize());
} else {
Expand Down Expand Up @@ -529,8 +530,8 @@ private boolean isJoinCDATALines() {
return sharedSettings.getFormattingSettings().isJoinCDATALines();
}

private boolean isSplitAttributes() {
return sharedSettings.getFormattingSettings().isSplitAttributes();
private SplitAttributes getSplitAttributes() {
return sharedSettings.getFormattingSettings().getSplitAttributes();
}

private boolean isInsertSpaces() {
Expand Down
Expand Up @@ -18,6 +18,7 @@
import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.extensions.xsi.settings.XSISchemaLocationSplit;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lemminx.settings.XMLFormattingOptions.SplitAttributes;
import org.junit.jupiter.api.Test;

/**
Expand Down Expand Up @@ -180,7 +181,7 @@ private static SharedSettings createSettings() {
SharedSettings settings = new SharedSettings();
settings.getFormattingSettings().setInsertSpaces(true);
settings.getFormattingSettings().setTabSize(2);
settings.getFormattingSettings().setSplitAttributes(true);
settings.getFormattingSettings().setSplitAttributes(SplitAttributes.splitNewLine);
settings.getFormattingSettings().setPreserveEmptyContent(true);
settings.getFormattingSettings().setLegacy(true);
return settings;
Expand Down

0 comments on commit 2b0fe29

Please sign in to comment.