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 <[email protected]>
  • 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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 2b0fe29

Please sign in to comment.