<div dir="ltr">Generally makes sense to fix the patch description if the content of the patch changes significantly ;-).</div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, May 11, 2015 at 10:21 AM, Manuel Klimek <span dir="ltr"><<a href="mailto:klimek@google.com" target="_blank">klimek@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: klimek<br>
Date: Mon May 11 03:21:35 2015<br>
New Revision: 236974<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=236974&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=236974&view=rev</a><br>
Log:<br>
Refactor the formatter of clang-format.<br>
<br>
Pull various parts of the UnwrappedLineFormatter into their own<br>
abstractions. NFC.<br>
<br>
There are two things left for subsequent changes (to keep this<br>
reasonably small)<br>
- the UnwrappedLineFormatter now has a bad name<br>
- the UnwrappedLineFormatter::format function is still too large<br>
<br>
Modified:<br>
cfe/trunk/lib/Format/Format.cpp<br>
cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp<br>
cfe/trunk/lib/Format/UnwrappedLineFormatter.h<br>
<br>
Modified: cfe/trunk/lib/Format/Format.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=236974&r1=236973&r2=236974&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=236974&r1=236973&r2=236974&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Format/Format.cpp (original)<br>
+++ cfe/trunk/lib/Format/Format.cpp Mon May 11 03:21:35 2015<br>
@@ -1269,9 +1269,9 @@ public:<br>
ContinuationIndenter Indenter(Style, Tokens.getKeywords(), SourceMgr,<br>
Whitespaces, Encoding,<br>
BinPackInconclusiveFunctions);<br>
- UnwrappedLineFormatter Formatter(&Indenter, &Whitespaces, Style,<br>
- Tokens.getKeywords(), IncompleteFormat);<br>
- Formatter.format(AnnotatedLines, /*DryRun=*/false);<br>
+ UnwrappedLineFormatter(&Indenter, &Whitespaces, Style, Tokens.getKeywords(),<br>
+ IncompleteFormat)<br>
+ .format(AnnotatedLines);<br>
return Whitespaces.generateReplacements();<br>
}<br>
<br>
<br>
Modified: cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp?rev=236974&r1=236973&r2=236974&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp?rev=236974&r1=236973&r2=236974&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp (original)<br>
+++ cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp Mon May 11 03:21:35 2015<br>
@@ -151,8 +151,8 @@ private:<br>
return 0;<br>
if (1 + I[1]->Last->TotalLength > Limit)<br>
return 0;<br>
- if (I[1]->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for,<br>
- tok::kw_while, TT_LineComment))<br>
+ if (I[1]->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for, tok::kw_while,<br>
+ TT_LineComment))<br>
return 0;<br>
// Only inline simple if's (no nested if or else).<br>
if (I + 2 != E && Line.First->is(tok::kw_if) &&<br>
@@ -161,9 +161,10 @@ private:<br>
return 1;<br>
}<br>
<br>
- unsigned tryMergeShortCaseLabels(<br>
- SmallVectorImpl<AnnotatedLine *>::const_iterator I,<br>
- SmallVectorImpl<AnnotatedLine *>::const_iterator E, unsigned Limit) {<br>
+ unsigned<br>
+ tryMergeShortCaseLabels(SmallVectorImpl<AnnotatedLine *>::const_iterator I,<br>
+ SmallVectorImpl<AnnotatedLine *>::const_iterator E,<br>
+ unsigned Limit) {<br>
if (Limit == 0 || I + 1 == E ||<br>
I[1]->First->isOneOf(tok::kw_case, tok::kw_default))<br>
return 0;<br>
@@ -307,40 +308,346 @@ private:<br>
const AdditionalKeywords &Keywords;<br>
};<br>
<br>
-class NoColumnLimitFormatter {<br>
+static void markFinalized(FormatToken *Tok) {<br>
+ for (; Tok; Tok = Tok->Next) {<br>
+ Tok->Finalized = true;<br>
+ for (AnnotatedLine *Child : Tok->Children)<br>
+ markFinalized(Child->First);<br>
+ }<br>
+}<br>
+<br>
+#ifndef NDEBUG<br>
+static void printLineState(const LineState &State) {<br>
+ llvm::dbgs() << "State: ";<br>
+ for (const ParenState &P : State.Stack) {<br>
+ llvm::dbgs() << P.Indent << "|" << P.LastSpace << "|" << P.NestedBlockIndent<br>
+ << " ";<br>
+ }<br>
+ llvm::dbgs() << State.NextToken->TokenText << "\n";<br>
+}<br>
+#endif<br>
+<br>
+/// \brief Base class for classes that format one \c AnnotatedLine.<br>
+class LineFormatter {<br>
+public:<br>
+ LineFormatter(ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces,<br>
+ const FormatStyle &Style,<br>
+ UnwrappedLineFormatter *BlockFormatter)<br>
+ : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style),<br>
+ BlockFormatter(BlockFormatter) {}<br>
+ virtual ~LineFormatter() {}<br>
+<br>
+ /// \brief Formats an \c AnnotatedLine and returns the penalty.<br>
+ ///<br>
+ /// If \p DryRun is \c false, directly applies the changes.<br>
+ virtual unsigned formatLine(const AnnotatedLine &Line, unsigned FirstIndent,<br>
+ bool DryRun) = 0;<br>
+<br>
+protected:<br>
+ /// \brief If the \p State's next token is an r_brace closing a nested block,<br>
+ /// format the nested block before it.<br>
+ ///<br>
+ /// Returns \c true if all children could be placed successfully and adapts<br>
+ /// \p Penalty as well as \p State. If \p DryRun is false, also directly<br>
+ /// creates changes using \c Whitespaces.<br>
+ ///<br>
+ /// The crucial idea here is that children always get formatted upon<br>
+ /// encountering the closing brace right after the nested block. Now, if we<br>
+ /// are currently trying to keep the "}" on the same line (i.e. \p NewLine is<br>
+ /// \c false), the entire block has to be kept on the same line (which is only<br>
+ /// possible if it fits on the line, only contains a single statement, etc.<br>
+ ///<br>
+ /// If \p NewLine is true, we format the nested block on separate lines, i.e.<br>
+ /// break after the "{", format all lines with correct indentation and the put<br>
+ /// the closing "}" on yet another new line.<br>
+ ///<br>
+ /// This enables us to keep the simple structure of the<br>
+ /// \c UnwrappedLineFormatter, where we only have two options for each token:<br>
+ /// break or don't break.<br>
+ bool formatChildren(LineState &State, bool NewLine, bool DryRun,<br>
+ unsigned &Penalty) {<br>
+ const FormatToken *LBrace = State.NextToken->getPreviousNonComment();<br>
+ FormatToken &Previous = *State.NextToken->Previous;<br>
+ if (!LBrace || LBrace->isNot(tok::l_brace) ||<br>
+ LBrace->BlockKind != BK_Block || Previous.Children.size() == 0)<br>
+ // The previous token does not open a block. Nothing to do. We don't<br>
+ // assert so that we can simply call this function for all tokens.<br>
+ return true;<br>
+<br>
+ if (NewLine) {<br>
+ int AdditionalIndent = State.Stack.back().Indent -<br>
+ Previous.Children[0]->Level * Style.IndentWidth;<br>
+<br>
+ Penalty +=<br>
+ BlockFormatter->format(Previous.Children, DryRun, AdditionalIndent,<br>
+ /*FixBadIndentation=*/true);<br>
+ return true;<br>
+ }<br>
+<br>
+ if (Previous.Children[0]->First->MustBreakBefore)<br>
+ return false;<br>
+<br>
+ // Cannot merge multiple statements into a single line.<br>
+ if (Previous.Children.size() > 1)<br>
+ return false;<br>
+<br>
+ // Cannot merge into one line if this line ends on a comment.<br>
+ if (Previous.is(tok::comment))<br>
+ return false;<br>
+<br>
+ // We can't put the closing "}" on a line with a trailing comment.<br>
+ if (Previous.Children[0]->Last->isTrailingComment())<br>
+ return false;<br>
+<br>
+ // If the child line exceeds the column limit, we wouldn't want to merge it.<br>
+ // We add +2 for the trailing " }".<br>
+ if (Style.ColumnLimit > 0 &&<br>
+ Previous.Children[0]->Last->TotalLength + State.Column + 2 ><br>
+ Style.ColumnLimit)<br>
+ return false;<br>
+<br>
+ if (!DryRun) {<br>
+ Whitespaces->replaceWhitespace(<br>
+ *Previous.Children[0]->First,<br>
+ /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1,<br>
+ /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective);<br>
+ }<br>
+ Penalty += formatLine(*Previous.Children[0], State.Column + 1, DryRun);<br>
+<br>
+ State.Column += 1 + Previous.Children[0]->Last->TotalLength;<br>
+ return true;<br>
+ }<br>
+<br>
+ ContinuationIndenter *Indenter;<br>
+<br>
+private:<br>
+ WhitespaceManager *Whitespaces;<br>
+ const FormatStyle &Style;<br>
+ UnwrappedLineFormatter *BlockFormatter;<br>
+};<br>
+<br>
+/// \brief Formatter that keeps the existing line breaks.<br>
+class NoColumnLimitLineFormatter : public LineFormatter {<br>
public:<br>
- NoColumnLimitFormatter(ContinuationIndenter *Indenter,<br>
- UnwrappedLineFormatter *Formatter)<br>
- : Indenter(Indenter), Formatter(Formatter) {}<br>
-<br>
- /// \brief Formats the line starting at \p State, simply keeping all of the<br>
- /// input's line breaking decisions.<br>
- void format(unsigned FirstIndent, const AnnotatedLine *Line) {<br>
+ NoColumnLimitLineFormatter(ContinuationIndenter *Indenter,<br>
+ WhitespaceManager *Whitespaces,<br>
+ const FormatStyle &Style,<br>
+ UnwrappedLineFormatter *BlockFormatter)<br>
+ : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {}<br>
+<br>
+ /// \brief Formats the line, simply keeping all of the input's line breaking<br>
+ /// decisions.<br>
+ unsigned formatLine(const AnnotatedLine &Line, unsigned FirstIndent,<br>
+ bool DryRun) override {<br>
+ assert(!DryRun);<br>
LineState State =<br>
- Indenter->getInitialState(FirstIndent, Line, /*DryRun=*/false);<br>
+ Indenter->getInitialState(FirstIndent, &Line, /*DryRun=*/false);<br>
while (State.NextToken) {<br>
bool Newline =<br>
Indenter->mustBreak(State) ||<br>
(Indenter->canBreak(State) && State.NextToken->NewlinesBefore > 0);<br>
unsigned Penalty = 0;<br>
- Formatter->formatChildren(State, Newline, /*DryRun=*/false, Penalty);<br>
+ formatChildren(State, Newline, /*DryRun=*/false, Penalty);<br>
Indenter->addTokenToState(State, Newline, /*DryRun=*/false);<br>
}<br>
+ return 0;<br>
}<br>
+};<br>
<br>
-private:<br>
- ContinuationIndenter *Indenter;<br>
- UnwrappedLineFormatter *Formatter;<br>
+/// \brief Formatter that puts all tokens into a single line without breaks.<br>
+class NoLineBreakFormatter : public LineFormatter {<br>
+public:<br>
+ NoLineBreakFormatter(ContinuationIndenter *Indenter,<br>
+ WhitespaceManager *Whitespaces, const FormatStyle &Style,<br>
+ UnwrappedLineFormatter *BlockFormatter)<br>
+ : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {}<br>
+<br>
+ /// \brief Puts all tokens into a single line.<br>
+ unsigned formatLine(const AnnotatedLine &Line, unsigned FirstIndent,<br>
+ bool DryRun) {<br>
+ unsigned Penalty = 0;<br>
+ LineState State = Indenter->getInitialState(FirstIndent, &Line, DryRun);<br>
+ while (State.NextToken) {<br>
+ formatChildren(State, /*Newline=*/false, DryRun, Penalty);<br>
+ Indenter->addTokenToState(State, /*Newline=*/false, DryRun);<br>
+ }<br>
+ return Penalty;<br>
+ }<br>
};<br>
<br>
+/// \brief Finds the best way to break lines.<br>
+class OptimizingLineFormatter : public LineFormatter {<br>
+public:<br>
+ OptimizingLineFormatter(ContinuationIndenter *Indenter,<br>
+ WhitespaceManager *Whitespaces,<br>
+ const FormatStyle &Style,<br>
+ UnwrappedLineFormatter *BlockFormatter)<br>
+ : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {}<br>
+<br>
+ /// \brief Formats the line by finding the best line breaks with line lengths<br>
+ /// below the column limit.<br>
+ unsigned formatLine(const AnnotatedLine &Line, unsigned FirstIndent,<br>
+ bool DryRun) {<br>
+ LineState State = Indenter->getInitialState(FirstIndent, &Line, DryRun);<br>
+<br>
+ // If the ObjC method declaration does not fit on a line, we should format<br>
+ // it with one arg per line.<br>
+ if (State.Line->Type == LT_ObjCMethodDecl)<br>
+ State.Stack.back().BreakBeforeParameter = true;<br>
<br>
-static void markFinalized(FormatToken *Tok) {<br>
- for (; Tok; Tok = Tok->Next) {<br>
- Tok->Finalized = true;<br>
- for (AnnotatedLine *Child : Tok->Children)<br>
- markFinalized(Child->First);<br>
+ // Find best solution in solution space.<br>
+ return analyzeSolutionSpace(State, DryRun);<br>
+ }<br>
+<br>
+private:<br>
+ struct CompareLineStatePointers {<br>
+ bool operator()(LineState *obj1, LineState *obj2) const {<br>
+ return *obj1 < *obj2;<br>
+ }<br>
+ };<br>
+<br>
+ /// \brief A pair of <penalty, count> that is used to prioritize the BFS on.<br>
+ ///<br>
+ /// In case of equal penalties, we want to prefer states that were inserted<br>
+ /// first. During state generation we make sure that we insert states first<br>
+ /// that break the line as late as possible.<br>
+ typedef std::pair<unsigned, unsigned> OrderedPenalty;<br>
+<br>
+ /// \brief An edge in the solution space from \c Previous->State to \c State,<br>
+ /// inserting a newline dependent on the \c NewLine.<br>
+ struct StateNode {<br>
+ StateNode(const LineState &State, bool NewLine, StateNode *Previous)<br>
+ : State(State), NewLine(NewLine), Previous(Previous) {}<br>
+ LineState State;<br>
+ bool NewLine;<br>
+ StateNode *Previous;<br>
+ };<br>
+<br>
+ /// \brief An item in the prioritized BFS search queue. The \c StateNode's<br>
+ /// \c State has the given \c OrderedPenalty.<br>
+ typedef std::pair<OrderedPenalty, StateNode *> QueueItem;<br>
+<br>
+ /// \brief The BFS queue type.<br>
+ typedef std::priority_queue<QueueItem, std::vector<QueueItem>,<br>
+ std::greater<QueueItem>> QueueType;<br>
+<br>
+ /// \brief Analyze the entire solution space starting from \p InitialState.<br>
+ ///<br>
+ /// This implements a variant of Dijkstra's algorithm on the graph that spans<br>
+ /// the solution space (\c LineStates are the nodes). The algorithm tries to<br>
+ /// find the shortest path (the one with lowest penalty) from \p InitialState<br>
+ /// to a state where all tokens are placed. Returns the penalty.<br>
+ ///<br>
+ /// If \p DryRun is \c false, directly applies the changes.<br>
+ unsigned analyzeSolutionSpace(LineState &InitialState, bool DryRun) {<br>
+ std::set<LineState *, CompareLineStatePointers> Seen;<br>
+<br>
+ // Increasing count of \c StateNode items we have created. This is used to<br>
+ // create a deterministic order independent of the container.<br>
+ unsigned Count = 0;<br>
+ QueueType Queue;<br>
+<br>
+ // Insert start element into queue.<br>
+ StateNode *Node =<br>
+ new (Allocator.Allocate()) StateNode(InitialState, false, nullptr);<br>
+ Queue.push(QueueItem(OrderedPenalty(0, Count), Node));<br>
+ ++Count;<br>
+<br>
+ unsigned Penalty = 0;<br>
+<br>
+ // While not empty, take first element and follow edges.<br>
+ while (!Queue.empty()) {<br>
+ Penalty = Queue.top().first.first;<br>
+ StateNode *Node = Queue.top().second;<br>
+ if (!Node->State.NextToken) {<br>
+ DEBUG(llvm::dbgs() << "\n---\nPenalty for line: " << Penalty << "\n");<br>
+ break;<br>
+ }<br>
+ Queue.pop();<br>
+<br>
+ // Cut off the analysis of certain solutions if the analysis gets too<br>
+ // complex. See description of IgnoreStackForComparison.<br>
+ if (Count > 10000)<br>
+ Node->State.IgnoreStackForComparison = true;<br>
+<br>
+ if (!Seen.insert(&Node->State).second)<br>
+ // State already examined with lower penalty.<br>
+ continue;<br>
+<br>
+ FormatDecision LastFormat = Node->State.NextToken->Decision;<br>
+ if (LastFormat == FD_Unformatted || LastFormat == FD_Continue)<br>
+ addNextStateToQueue(Penalty, Node, /*NewLine=*/false, &Count, &Queue);<br>
+ if (LastFormat == FD_Unformatted || LastFormat == FD_Break)<br>
+ addNextStateToQueue(Penalty, Node, /*NewLine=*/true, &Count, &Queue);<br>
+ }<br>
+<br>
+ if (Queue.empty()) {<br>
+ // We were unable to find a solution, do nothing.<br>
+ // FIXME: Add diagnostic?<br>
+ DEBUG(llvm::dbgs() << "Could not find a solution.\n");<br>
+ return 0;<br>
+ }<br>
+<br>
+ // Reconstruct the solution.<br>
+ if (!DryRun)<br>
+ reconstructPath(InitialState, Queue.top().second);<br>
+<br>
+ DEBUG(llvm::dbgs() << "Total number of analyzed states: " << Count << "\n");<br>
+ DEBUG(llvm::dbgs() << "---\n");<br>
+<br>
+ return Penalty;<br>
+ }<br>
+<br>
+ /// \brief Add the following state to the analysis queue \c Queue.<br>
+ ///<br>
+ /// Assume the current state is \p PreviousNode and has been reached with a<br>
+ /// penalty of \p Penalty. Insert a line break if \p NewLine is \c true.<br>
+ void addNextStateToQueue(unsigned Penalty, StateNode *PreviousNode,<br>
+ bool NewLine, unsigned *Count, QueueType *Queue) {<br>
+ if (NewLine && !Indenter->canBreak(PreviousNode->State))<br>
+ return;<br>
+ if (!NewLine && Indenter->mustBreak(PreviousNode->State))<br>
+ return;<br>
+<br>
+ StateNode *Node = new (Allocator.Allocate())<br>
+ StateNode(PreviousNode->State, NewLine, PreviousNode);<br>
+ if (!formatChildren(Node->State, NewLine, /*DryRun=*/true, Penalty))<br>
+ return;<br>
+<br>
+ Penalty += Indenter->addTokenToState(Node->State, NewLine, true);<br>
+<br>
+ Queue->push(QueueItem(OrderedPenalty(Penalty, *Count), Node));<br>
+ ++(*Count);<br>
+ }<br>
+<br>
+ /// \brief Applies the best formatting by reconstructing the path in the<br>
+ /// solution space that leads to \c Best.<br>
+ void reconstructPath(LineState &State, StateNode *Best) {<br>
+ std::deque<StateNode *> Path;<br>
+ // We do not need a break before the initial token.<br>
+ while (Best->Previous) {<br>
+ Path.push_front(Best);<br>
+ Best = Best->Previous;<br>
+ }<br>
+ for (std::deque<StateNode *>::iterator I = Path.begin(), E = Path.end();<br>
+ I != E; ++I) {<br>
+ unsigned Penalty = 0;<br>
+ formatChildren(State, (*I)->NewLine, /*DryRun=*/false, Penalty);<br>
+ Penalty += Indenter->addTokenToState(State, (*I)->NewLine, false);<br>
+<br>
+ DEBUG({<br>
+ printLineState((*I)->Previous->State);<br>
+ if ((*I)->NewLine) {<br>
+ llvm::dbgs() << "Penalty for placing "<br>
+ << (*I)->Previous->State.NextToken->Tok.getName() << ": "<br>
+ << Penalty << "\n";<br>
+ }<br>
+ });<br>
+ }<br>
}<br>
-}<br>
+<br>
+ llvm::SpecificBumpPtrAllocator<StateNode> Allocator;<br>
+};<br>
<br>
} // namespace<br>
<br>
@@ -431,17 +738,14 @@ UnwrappedLineFormatter::format(const Sma<br>
<br>
if (TheLine.Last->TotalLength + Indent <= ColumnLimit ||<br>
TheLine.Type == LT_ImportStatement) {<br>
- LineState State = Indenter->getInitialState(Indent, &TheLine, DryRun);<br>
- while (State.NextToken) {<br>
- formatChildren(State, /*Newline=*/false, DryRun, Penalty);<br>
- Indenter->addTokenToState(State, /*Newline=*/false, DryRun);<br>
- }<br>
+ Penalty += NoLineBreakFormatter(Indenter, Whitespaces, Style, this)<br>
+ .formatLine(TheLine, Indent, DryRun);<br>
} else if (Style.ColumnLimit == 0) {<br>
- NoColumnLimitFormatter Formatter(Indenter, this);<br>
- if (!DryRun)<br>
- Formatter.format(Indent, &TheLine);<br>
+ NoColumnLimitLineFormatter(Indenter, Whitespaces, Style, this)<br>
+ .formatLine(TheLine, Indent, DryRun);<br>
} else {<br>
- Penalty += format(TheLine, Indent, DryRun);<br>
+ Penalty += OptimizingLineFormatter(Indenter, Whitespaces, Style, this)<br>
+ .formatLine(TheLine, Indent, DryRun);<br>
}<br>
<br>
if (!TheLine.InPPDirective)<br>
@@ -486,19 +790,6 @@ UnwrappedLineFormatter::format(const Sma<br>
return Penalty;<br>
}<br>
<br>
-unsigned UnwrappedLineFormatter::format(const AnnotatedLine &Line,<br>
- unsigned FirstIndent, bool DryRun) {<br>
- LineState State = Indenter->getInitialState(FirstIndent, &Line, DryRun);<br>
-<br>
- // If the ObjC method declaration does not fit on a line, we should format<br>
- // it with one arg per line.<br>
- if (State.Line->Type == LT_ObjCMethodDecl)<br>
- State.Stack.back().BreakBeforeParameter = true;<br>
-<br>
- // Find best solution in solution space.<br>
- return analyzeSolutionSpace(State, DryRun);<br>
-}<br>
-<br>
void UnwrappedLineFormatter::formatFirstToken(FormatToken &RootToken,<br>
const AnnotatedLine *PreviousLine,<br>
unsigned IndentLevel,<br>
@@ -567,174 +858,5 @@ void UnwrappedLineFormatter::join(Annota<br>
}<br>
}<br>
<br>
-unsigned UnwrappedLineFormatter::analyzeSolutionSpace(LineState &InitialState,<br>
- bool DryRun) {<br>
- std::set<LineState *, CompareLineStatePointers> Seen;<br>
-<br>
- // Increasing count of \c StateNode items we have created. This is used to<br>
- // create a deterministic order independent of the container.<br>
- unsigned Count = 0;<br>
- QueueType Queue;<br>
-<br>
- // Insert start element into queue.<br>
- StateNode *Node =<br>
- new (Allocator.Allocate()) StateNode(InitialState, false, nullptr);<br>
- Queue.push(QueueItem(OrderedPenalty(0, Count), Node));<br>
- ++Count;<br>
-<br>
- unsigned Penalty = 0;<br>
-<br>
- // While not empty, take first element and follow edges.<br>
- while (!Queue.empty()) {<br>
- Penalty = Queue.top().first.first;<br>
- StateNode *Node = Queue.top().second;<br>
- if (!Node->State.NextToken) {<br>
- DEBUG(llvm::dbgs() << "\n---\nPenalty for line: " << Penalty << "\n");<br>
- break;<br>
- }<br>
- Queue.pop();<br>
-<br>
- // Cut off the analysis of certain solutions if the analysis gets too<br>
- // complex. See description of IgnoreStackForComparison.<br>
- if (Count > 10000)<br>
- Node->State.IgnoreStackForComparison = true;<br>
-<br>
- if (!Seen.insert(&Node->State).second)<br>
- // State already examined with lower penalty.<br>
- continue;<br>
-<br>
- FormatDecision LastFormat = Node->State.NextToken->Decision;<br>
- if (LastFormat == FD_Unformatted || LastFormat == FD_Continue)<br>
- addNextStateToQueue(Penalty, Node, /*NewLine=*/false, &Count, &Queue);<br>
- if (LastFormat == FD_Unformatted || LastFormat == FD_Break)<br>
- addNextStateToQueue(Penalty, Node, /*NewLine=*/true, &Count, &Queue);<br>
- }<br>
-<br>
- if (Queue.empty()) {<br>
- // We were unable to find a solution, do nothing.<br>
- // FIXME: Add diagnostic?<br>
- DEBUG(llvm::dbgs() << "Could not find a solution.\n");<br>
- return 0;<br>
- }<br>
-<br>
- // Reconstruct the solution.<br>
- if (!DryRun)<br>
- reconstructPath(InitialState, Queue.top().second);<br>
-<br>
- DEBUG(llvm::dbgs() << "Total number of analyzed states: " << Count << "\n");<br>
- DEBUG(llvm::dbgs() << "---\n");<br>
-<br>
- return Penalty;<br>
-}<br>
-<br>
-#ifndef NDEBUG<br>
-static void printLineState(const LineState &State) {<br>
- llvm::dbgs() << "State: ";<br>
- for (const ParenState &P : State.Stack) {<br>
- llvm::dbgs() << P.Indent << "|" << P.LastSpace << "|" << P.NestedBlockIndent<br>
- << " ";<br>
- }<br>
- llvm::dbgs() << State.NextToken->TokenText << "\n";<br>
-}<br>
-#endif<br>
-<br>
-void UnwrappedLineFormatter::reconstructPath(LineState &State,<br>
- StateNode *Current) {<br>
- std::deque<StateNode *> Path;<br>
- // We do not need a break before the initial token.<br>
- while (Current->Previous) {<br>
- Path.push_front(Current);<br>
- Current = Current->Previous;<br>
- }<br>
- for (std::deque<StateNode *>::iterator I = Path.begin(), E = Path.end();<br>
- I != E; ++I) {<br>
- unsigned Penalty = 0;<br>
- formatChildren(State, (*I)->NewLine, /*DryRun=*/false, Penalty);<br>
- Penalty += Indenter->addTokenToState(State, (*I)->NewLine, false);<br>
-<br>
- DEBUG({<br>
- printLineState((*I)->Previous->State);<br>
- if ((*I)->NewLine) {<br>
- llvm::dbgs() << "Penalty for placing "<br>
- << (*I)->Previous->State.NextToken->Tok.getName() << ": "<br>
- << Penalty << "\n";<br>
- }<br>
- });<br>
- }<br>
-}<br>
-<br>
-void UnwrappedLineFormatter::addNextStateToQueue(unsigned Penalty,<br>
- StateNode *PreviousNode,<br>
- bool NewLine, unsigned *Count,<br>
- QueueType *Queue) {<br>
- if (NewLine && !Indenter->canBreak(PreviousNode->State))<br>
- return;<br>
- if (!NewLine && Indenter->mustBreak(PreviousNode->State))<br>
- return;<br>
-<br>
- StateNode *Node = new (Allocator.Allocate())<br>
- StateNode(PreviousNode->State, NewLine, PreviousNode);<br>
- if (!formatChildren(Node->State, NewLine, /*DryRun=*/true, Penalty))<br>
- return;<br>
-<br>
- Penalty += Indenter->addTokenToState(Node->State, NewLine, true);<br>
-<br>
- Queue->push(QueueItem(OrderedPenalty(Penalty, *Count), Node));<br>
- ++(*Count);<br>
-}<br>
-<br>
-bool UnwrappedLineFormatter::formatChildren(LineState &State, bool NewLine,<br>
- bool DryRun, unsigned &Penalty) {<br>
- const FormatToken *LBrace = State.NextToken->getPreviousNonComment();<br>
- FormatToken &Previous = *State.NextToken->Previous;<br>
- if (!LBrace || LBrace->isNot(tok::l_brace) || LBrace->BlockKind != BK_Block ||<br>
- Previous.Children.size() == 0)<br>
- // The previous token does not open a block. Nothing to do. We don't<br>
- // assert so that we can simply call this function for all tokens.<br>
- return true;<br>
-<br>
- if (NewLine) {<br>
- int AdditionalIndent = State.Stack.back().Indent -<br>
- Previous.Children[0]->Level * Style.IndentWidth;<br>
-<br>
- Penalty += format(Previous.Children, DryRun, AdditionalIndent,<br>
- /*FixBadIndentation=*/true);<br>
- return true;<br>
- }<br>
-<br>
- if (Previous.Children[0]->First->MustBreakBefore)<br>
- return false;<br>
-<br>
- // Cannot merge multiple statements into a single line.<br>
- if (Previous.Children.size() > 1)<br>
- return false;<br>
-<br>
- // Cannot merge into one line if this line ends on a comment.<br>
- if (Previous.is(tok::comment))<br>
- return false;<br>
-<br>
- // We can't put the closing "}" on a line with a trailing comment.<br>
- if (Previous.Children[0]->Last->isTrailingComment())<br>
- return false;<br>
-<br>
- // If the child line exceeds the column limit, we wouldn't want to merge it.<br>
- // We add +2 for the trailing " }".<br>
- if (Style.ColumnLimit > 0 &&<br>
- Previous.Children[0]->Last->TotalLength + State.Column + 2 ><br>
- Style.ColumnLimit)<br>
- return false;<br>
-<br>
- if (!DryRun) {<br>
- Whitespaces->replaceWhitespace(<br>
- *Previous.Children[0]->First,<br>
- /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1,<br>
- /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective);<br>
- }<br>
- Penalty += format(*Previous.Children[0], State.Column + 1, DryRun);<br>
-<br>
- State.Column += 1 + Previous.Children[0]->Last->TotalLength;<br>
- return true;<br>
-}<br>
-<br>
} // namespace format<br>
} // namespace clang<br>
<br>
Modified: cfe/trunk/lib/Format/UnwrappedLineFormatter.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineFormatter.h?rev=236974&r1=236973&r2=236974&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineFormatter.h?rev=236974&r1=236973&r2=236974&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Format/UnwrappedLineFormatter.h (original)<br>
+++ cfe/trunk/lib/Format/UnwrappedLineFormatter.h Mon May 11 03:21:35 2015<br>
@@ -38,65 +38,12 @@ public:<br>
: Indenter(Indenter), Whitespaces(Whitespaces), Style(Style),<br>
Keywords(Keywords), IncompleteFormat(IncompleteFormat) {}<br>
<br>
- unsigned format(const SmallVectorImpl<AnnotatedLine *> &Lines, bool DryRun,<br>
- int AdditionalIndent = 0, bool FixBadIndentation = false);<br>
-<br>
-<br>
- /// \brief If the \p State's next token is an r_brace closing a nested block,<br>
- /// format the nested block before it.<br>
- ///<br>
- /// Returns \c true if all children could be placed successfully and adapts<br>
- /// \p Penalty as well as \p State. If \p DryRun is false, also directly<br>
- /// creates changes using \c Whitespaces.<br>
- ///<br>
- /// The crucial idea here is that children always get formatted upon<br>
- /// encountering the closing brace right after the nested block. Now, if we<br>
- /// are currently trying to keep the "}" on the same line (i.e. \p NewLine is<br>
- /// \c false), the entire block has to be kept on the same line (which is only<br>
- /// possible if it fits on the line, only contains a single statement, etc.<br>
- ///<br>
- /// If \p NewLine is true, we format the nested block on separate lines, i.e.<br>
- /// break after the "{", format all lines with correct indentation and the put<br>
- /// the closing "}" on yet another new line.<br>
- ///<br>
- /// This enables us to keep the simple structure of the<br>
- /// \c UnwrappedLineFormatter, where we only have two options for each token:<br>
- /// break or don't break.<br>
- bool formatChildren(LineState &State, bool NewLine, bool DryRun,<br>
- unsigned &Penalty);<br>
+ /// \brief Format the current block and return the penalty.<br>
+ unsigned format(const SmallVectorImpl<AnnotatedLine *> &Lines,<br>
+ bool DryRun = false, int AdditionalIndent = 0,<br>
+ bool FixBadIndentation = false);<br>
<br>
private:<br>
- /// \brief Formats an \c AnnotatedLine and returns the penalty.<br>
- ///<br>
- /// If \p DryRun is \c false, directly applies the changes.<br>
- unsigned format(const AnnotatedLine &Line, unsigned FirstIndent,<br>
- bool DryRun);<br>
-<br>
- /// \brief An edge in the solution space from \c Previous->State to \c State,<br>
- /// inserting a newline dependent on the \c NewLine.<br>
- struct StateNode {<br>
- StateNode(const LineState &State, bool NewLine, StateNode *Previous)<br>
- : State(State), NewLine(NewLine), Previous(Previous) {}<br>
- LineState State;<br>
- bool NewLine;<br>
- StateNode *Previous;<br>
- };<br>
-<br>
- /// \brief A pair of <penalty, count> that is used to prioritize the BFS on.<br>
- ///<br>
- /// In case of equal penalties, we want to prefer states that were inserted<br>
- /// first. During state generation we make sure that we insert states first<br>
- /// that break the line as late as possible.<br>
- typedef std::pair<unsigned, unsigned> OrderedPenalty;<br>
-<br>
- /// \brief An item in the prioritized BFS search queue. The \c StateNode's<br>
- /// \c State has the given \c OrderedPenalty.<br>
- typedef std::pair<OrderedPenalty, StateNode *> QueueItem;<br>
-<br>
- /// \brief The BFS queue type.<br>
- typedef std::priority_queue<QueueItem, std::vector<QueueItem>,<br>
- std::greater<QueueItem> > QueueType;<br>
-<br>
/// \brief Get the offset of the line relatively to the level.<br>
///<br>
/// For example, 'public:' labels in classes are offset by 1 or 2<br>
@@ -133,44 +80,16 @@ private:<br>
return Style.ColumnLimit - (InPPDirective ? 2 : 0);<br>
}<br>
<br>
- struct CompareLineStatePointers {<br>
- bool operator()(LineState *obj1, LineState *obj2) const {<br>
- return *obj1 < *obj2;<br>
- }<br>
- };<br>
-<br>
- /// \brief Analyze the entire solution space starting from \p InitialState.<br>
- ///<br>
- /// This implements a variant of Dijkstra's algorithm on the graph that spans<br>
- /// the solution space (\c LineStates are the nodes). The algorithm tries to<br>
- /// find the shortest path (the one with lowest penalty) from \p InitialState<br>
- /// to a state where all tokens are placed. Returns the penalty.<br>
- ///<br>
- /// If \p DryRun is \c false, directly applies the changes.<br>
- unsigned analyzeSolutionSpace(LineState &InitialState, bool DryRun = false);<br>
-<br>
- void reconstructPath(LineState &State, StateNode *Current);<br>
-<br>
- /// \brief Add the following state to the analysis queue \c Queue.<br>
- ///<br>
- /// Assume the current state is \p PreviousNode and has been reached with a<br>
- /// penalty of \p Penalty. Insert a line break if \p NewLine is \c true.<br>
- void addNextStateToQueue(unsigned Penalty, StateNode *PreviousNode,<br>
- bool NewLine, unsigned *Count, QueueType *Queue);<br>
-<br>
- ContinuationIndenter *Indenter;<br>
- WhitespaceManager *Whitespaces;<br>
- FormatStyle Style;<br>
- const AdditionalKeywords &Keywords;<br>
-<br>
- llvm::SpecificBumpPtrAllocator<StateNode> Allocator;<br>
-<br>
// Cache to store the penalty of formatting a vector of AnnotatedLines<br>
// starting from a specific additional offset. Improves performance if there<br>
// are many nested blocks.<br>
std::map<std::pair<const SmallVectorImpl<AnnotatedLine *> *, unsigned>,<br>
unsigned> PenaltyCache;<br>
<br>
+ ContinuationIndenter *Indenter;<br>
+ WhitespaceManager *Whitespaces;<br>
+ const FormatStyle &Style;<br>
+ const AdditionalKeywords &Keywords;<br>
bool *IncompleteFormat;<br>
};<br>
} // end namespace format<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@cs.uiuc.edu">cfe-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>