📄 sourceformatter.java
字号:
}
appendEssentialNewLine();
// Insert our indent:
appendIndent(depth);
// Write the rest of the line including any indent greater than the first line's indent:
appendLineKeepWhiteSpace(end,depth);
}
assert index==end;
}
private boolean appendTextRemoveIndentation(final int end) throws IOException {
assert index<end;
appendLineKeepWhiteSpace(end,0);
if (index==end) return true;
while (index!=end) {
// Skip over the original indent:
while (true) {
final char ch=sourceText.charAt(index);
if (!(ch==' ' || ch=='\t')) break;
if (++index==end) return false;
}
appendEssentialNewLine();
// Write the rest of the line including any indent greater than the first line's indent:
appendLineKeepWhiteSpace(end,0);
}
assert index==end;
return false;
}
private int getStartOfLinePos(final int end, final boolean atStartOfLine) {
// returns the starting position of the next complete line containing text, or -1 if texts starts on the current line (hence not a complete line).
// sets index to the start of the text following the returned position, or end, whichever comes first.
int startOfLinePos=atStartOfLine ? index : -1;
while (true) {
final char ch=sourceText.charAt(index);
if (ch=='\n' || ch=='\r') {
startOfLinePos=index+1;
} else if (!(ch==' ' || ch=='\t')) break;
if (++index==end) break;
}
return startOfLinePos;
}
private void appendSpecifiedTextInline(final CharSequence text, int depth) throws IOException {
final int textLength=text.length();
int i=appendSpecifiedLine(text,0);
if (i<textLength) {
final int subsequentLineDepth=depth+1;
do {
while (Segment.isWhiteSpace(text.charAt(i))) if (++i>=textLength) return; // trim whitespace.
appendEssentialNewLine();
appendIndent(subsequentLineDepth);
i=appendSpecifiedLine(text,i);
} while (i<textLength);
}
}
private int appendSpecifiedLine(final CharSequence text, int i) throws IOException {
// Writes the first line from the specified text starting from the specified position.
// The line break characters are not written.
// Returns the position following the first line break character(s), or text.length() if the text contains no line breaks.
final int textLength=text.length();
while (true) {
final char ch=text.charAt(i);
if (ch=='\r') {
final int nexti=i+1;
if (nexti<textLength && text.charAt(nexti)=='\n') return i+2;
}
if (ch=='\n') return i+1;
appendable.append(ch);
if (++i>=textLength) return i;
}
}
private boolean appendTextInline(final int end, int depth, final boolean increaseIndentAfterFirstLineBreak) throws IOException {
// returns true if all text was on one line, otherwise false
assert index<end;
appendLineKeepWhiteSpace(end,depth);
if (index==end) return true;
final int subsequentLineDepth=increaseIndentAfterFirstLineBreak ? depth+1 : depth;
do {
while (Segment.isWhiteSpace(sourceText.charAt(index))) if (++index==end) return false; // trim whitespace.
appendEssentialNewLine(); // essential because we might be inside a tag attribute value. If new lines in normal text aren't required this method wouldn't have been called.
appendIndent(subsequentLineDepth);
appendLineKeepWhiteSpace(end,subsequentLineDepth);
} while (index<end);
assert index==end;
return false;
}
private void appendLineKeepWhiteSpace(final int end, final int depth) throws IOException {
// Writes the first line from the source text starting from index, ending at the specified end position.
// The line break characters are not written.
// Sets index to the position following the first line break character(s), or end if the text contains no line breaks, guaranteed index<=end.
// Any tags encountered are written using the appendTag method, whose output may include line breaks.
assert index<end;
updateNextTag();
while (true) {
while (nextTag!=null && index==nextTag.begin) {
appendTag(nextTag,depth,end);
if (index==end) return;
}
final char ch=sourceText.charAt(index);
if (ch=='\r') {
final int nextindex=index+1;
if (nextindex<end && sourceText.charAt(nextindex)=='\n') {
index+=2;
assert index<=end;
return;
}
}
if (ch=='\n') {
index++;
assert index<=end;
return;
}
appendable.append(ch);
if (++index==end) return;
}
}
private void appendTextCollapseWhiteSpace(final int end, final int depth) throws IOException {
assert index<end;
boolean lastWasWhiteSpace=false;
updateNextTag();
while (index<end) {
while (nextTag!=null && index==nextTag.begin) {
if (lastWasWhiteSpace) {
appendable.append(' ');
lastWasWhiteSpace=false;
}
appendTag(nextTag,depth,end);
if (index==end) return;
}
final char ch=sourceText.charAt(index++);
if (Segment.isWhiteSpace(ch)) {
lastWasWhiteSpace=true;
} else {
if (lastWasWhiteSpace) {
appendable.append(' ');
lastWasWhiteSpace=false;
}
appendable.append(ch);
}
}
if (lastWasWhiteSpace) appendable.append(' ');
assert index==end;
}
private void appendContentPreformatted(final int end, final int depth) throws IOException {
assert index<end;
updateNextTag();
do {
while (nextTag!=null && index==nextTag.begin) {
appendTag(nextTag,depth,end);
if (index==end) return;
}
appendable.append(sourceText.charAt(index));
} while (++index<end);
assert index==end;
}
private void appendTag(final Tag tag, final int depth, final int end) throws IOException {
// sets index to last position written
assert index==tag.begin;
assert index<end;
nextTag=tag.getNextTag();
final int tagEnd=(tag.end<end) ? tag.end : end;
assert index<tagEnd;
if (tag.getTagType()==StartTagType.COMMENT || tag.getTagType()==StartTagType.CDATA_SECTION || tag.getTagType().isServerTag()) {
appendTextPreserveIndentation(tagEnd,depth);
} else if (tidyTags) {
final String tidyTag=tag.tidy();
if ((tag instanceof StartTag) && ((StartTag)tag).getAttributes()!=null)
appendable.append(tidyTag);
else
appendSpecifiedTextInline(tidyTag,depth);
index=tagEnd;
} else {
appendTextInline(tagEnd,depth,true); // Write tag keeping linefeeds. This will add an indent to any attribute values containing linefeeds, but the normal situation where line breaks are between attributes will look nice.
}
if (end<=tag.end || !(tag instanceof StartTag)) {
assert index<=end;
return;
}
if ((tag.name==HTMLElementName.SCRIPT && !indentScriptElements) || tag.getTagType().isServerTag()) {
// NOTE SERVER ELEMENTS CONTAINING NON-INLINE TAGS WILL NOT FORMAT PROPERLY. NEED TO INVESTIGATE INCLUDING SUCH SERVER ELEMENTS IN DOCUMENT HIERARCHY.
// this is a script or server start tag, we may need to append the whole element:
final Element element=tag.getElement();
final EndTag endTag=element.getEndTag();
if (endTag==null) {
assert index<=end;
return;
}
final int contentEnd=(end<endTag.begin) ? end : endTag.begin;
boolean singleLineContent=true;
if (index!=contentEnd) {
// elementContainsMarkup should be made into a TagType property one day.
// for the time being assume all server element content is code, although this is not true for some Mason elements.
final boolean elementContainsMarkup=false;
if (elementContainsMarkup) {
singleLineContent=appendTextInline(contentEnd,depth+1,false);
} else {
singleLineContent=appendTextPreserveIndentation(contentEnd,depth);
}
}
if (endTag.begin>=end) {
assert index<=end;
return;
}
if (!singleLineContent) {
appendEssentialNewLine(); // some server or client side scripting languages might need the final new line
appendIndent(depth);
}
assert index==endTag.begin;
appendTag(endTag,depth,end);
}
assert index<=end;
}
private void appendIndent(final int depth) throws IOException {
if (!removeLineBreaks) for (int x=0; x<depth; x++) appendable.append(indentString);
}
private void appendFormattingNewLine() throws IOException {
if (!removeLineBreaks) appendable.append(newLine);
}
private void appendEssentialNewLine() throws IOException {
appendable.append(newLine);
}
private boolean containsOnlyInlineLevelChildElements(final Element element) {
// returns true if the element contains only inline-level elements except for SCRIPT elements.
final Collection<Element> childElements=element.getChildElements();
if (childElements.isEmpty()) return true;
for (Element childElement : childElements) {
final String elementName=childElement.getName();
if (elementName==HTMLElementName.SCRIPT || !HTMLElements.getInlineLevelElementNames().contains(elementName)) return false;
if (!containsOnlyInlineLevelChildElements(childElement)) return false;
}
return true;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -