WhitespaceManager.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //===--- WhitespaceManager.cpp - Format C++ code --------------------------===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. ///
  10. /// \file
  11. /// \brief This file implements WhitespaceManager class.
  12. ///
  13. //===----------------------------------------------------------------------===//
  14. #include "WhitespaceManager.h"
  15. #include "llvm/ADT/STLExtras.h"
  16. namespace clang {
  17. namespace format {
  18. bool
  19. WhitespaceManager::Change::IsBeforeInFile::operator()(const Change &C1,
  20. const Change &C2) const {
  21. return SourceMgr.isBeforeInTranslationUnit(
  22. C1.OriginalWhitespaceRange.getBegin(),
  23. C2.OriginalWhitespaceRange.getBegin());
  24. }
  25. WhitespaceManager::Change::Change(
  26. bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
  27. unsigned Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore,
  28. StringRef PreviousLinePostfix, StringRef CurrentLinePrefix,
  29. tok::TokenKind Kind, bool ContinuesPPDirective)
  30. : CreateReplacement(CreateReplacement),
  31. OriginalWhitespaceRange(OriginalWhitespaceRange),
  32. StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
  33. PreviousLinePostfix(PreviousLinePostfix),
  34. CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
  35. ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces) {}
  36. void WhitespaceManager::replaceWhitespace(const FormatToken &Tok,
  37. unsigned Newlines, unsigned Spaces,
  38. unsigned StartOfTokenColumn,
  39. bool InPPDirective) {
  40. Changes.push_back(Change(true, Tok.WhitespaceRange, Spaces,
  41. StartOfTokenColumn, Newlines, "", "",
  42. Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
  43. }
  44. void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
  45. bool InPPDirective) {
  46. Changes.push_back(Change(false, Tok.WhitespaceRange, /*Spaces=*/0,
  47. Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
  48. Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
  49. }
  50. void WhitespaceManager::replaceWhitespaceInToken(
  51. const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
  52. StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
  53. unsigned Newlines, unsigned Spaces) {
  54. Changes.push_back(Change(
  55. true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
  56. Tok.getStartOfNonWhitespace().getLocWithOffset(
  57. Offset + ReplaceChars)),
  58. Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
  59. // If we don't add a newline this change doesn't start a comment. Thus,
  60. // when we align line comments, we don't need to treat this change as one.
  61. // FIXME: We still need to take this change in account to properly
  62. // calculate the new length of the comment and to calculate the changes
  63. // for which to do the alignment when aligning comments.
  64. Tok.Type == TT_LineComment && Newlines > 0 ? tok::comment : tok::unknown,
  65. InPPDirective && !Tok.IsFirst));
  66. }
  67. const tooling::Replacements &WhitespaceManager::generateReplacements() {
  68. if (Changes.empty())
  69. return Replaces;
  70. std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
  71. calculateLineBreakInformation();
  72. alignTrailingComments();
  73. alignEscapedNewlines();
  74. generateChanges();
  75. return Replaces;
  76. }
  77. void WhitespaceManager::calculateLineBreakInformation() {
  78. Changes[0].PreviousEndOfTokenColumn = 0;
  79. for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
  80. unsigned OriginalWhitespaceStart =
  81. SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
  82. unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
  83. Changes[i - 1].OriginalWhitespaceRange.getEnd());
  84. Changes[i - 1].TokenLength = OriginalWhitespaceStart -
  85. PreviousOriginalWhitespaceEnd +
  86. Changes[i].PreviousLinePostfix.size() +
  87. Changes[i - 1].CurrentLinePrefix.size();
  88. Changes[i].PreviousEndOfTokenColumn =
  89. Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
  90. Changes[i - 1].IsTrailingComment =
  91. (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
  92. Changes[i - 1].Kind == tok::comment;
  93. }
  94. // FIXME: The last token is currently not always an eof token; in those
  95. // cases, setting TokenLength of the last token to 0 is wrong.
  96. Changes.back().TokenLength = 0;
  97. Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
  98. }
  99. void WhitespaceManager::alignTrailingComments() {
  100. unsigned MinColumn = 0;
  101. unsigned MaxColumn = UINT_MAX;
  102. unsigned StartOfSequence = 0;
  103. bool BreakBeforeNext = false;
  104. unsigned Newlines = 0;
  105. for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
  106. unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
  107. // FIXME: Correctly handle ChangeMaxColumn in PP directives.
  108. unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
  109. Newlines += Changes[i].NewlinesBefore;
  110. if (Changes[i].IsTrailingComment) {
  111. // If this comment follows an } in column 0, it probably documents the
  112. // closing of a namespace and we don't want to align it.
  113. bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
  114. Changes[i - 1].Kind == tok::r_brace &&
  115. Changes[i - 1].StartOfTokenColumn == 0;
  116. bool WasAlignedWithStartOfNextLine =
  117. // A comment on its own line.
  118. Changes[i].NewlinesBefore == 1 &&
  119. // Not the last line.
  120. i + 1 != e &&
  121. // The start of the next token was previously aligned with
  122. // the start of this comment.
  123. (SourceMgr.getSpellingColumnNumber(
  124. Changes[i].OriginalWhitespaceRange.getEnd()) ==
  125. SourceMgr.getSpellingColumnNumber(
  126. Changes[i + 1].OriginalWhitespaceRange.getEnd())) &&
  127. // Which is not a comment itself.
  128. Changes[i + 1].Kind != tok::comment;
  129. if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
  130. alignTrailingComments(StartOfSequence, i, MinColumn);
  131. MinColumn = ChangeMinColumn;
  132. MaxColumn = ChangeMinColumn;
  133. StartOfSequence = i;
  134. } else if (BreakBeforeNext || Newlines > 1 ||
  135. (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
  136. // Break the comment sequence if the previous line did not end
  137. // in a trailing comment.
  138. (Changes[i].NewlinesBefore == 1 && i > 0 &&
  139. !Changes[i - 1].IsTrailingComment) ||
  140. WasAlignedWithStartOfNextLine) {
  141. alignTrailingComments(StartOfSequence, i, MinColumn);
  142. MinColumn = ChangeMinColumn;
  143. MaxColumn = ChangeMaxColumn;
  144. StartOfSequence = i;
  145. } else {
  146. MinColumn = std::max(MinColumn, ChangeMinColumn);
  147. MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
  148. }
  149. BreakBeforeNext =
  150. (i == 0) || (Changes[i].NewlinesBefore > 1) ||
  151. // Never start a sequence with a comment at the beginning of
  152. // the line.
  153. (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
  154. Newlines = 0;
  155. }
  156. }
  157. alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
  158. }
  159. void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
  160. unsigned Column) {
  161. for (unsigned i = Start; i != End; ++i) {
  162. if (Changes[i].IsTrailingComment) {
  163. assert(Column >= Changes[i].StartOfTokenColumn);
  164. Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
  165. Changes[i].StartOfTokenColumn = Column;
  166. }
  167. }
  168. }
  169. void WhitespaceManager::alignEscapedNewlines() {
  170. unsigned MaxEndOfLine =
  171. Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
  172. unsigned StartOfMacro = 0;
  173. for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
  174. Change &C = Changes[i];
  175. if (C.NewlinesBefore > 0) {
  176. if (C.ContinuesPPDirective) {
  177. MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
  178. } else {
  179. alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
  180. MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
  181. StartOfMacro = i;
  182. }
  183. }
  184. }
  185. alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
  186. }
  187. void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
  188. unsigned Column) {
  189. for (unsigned i = Start; i < End; ++i) {
  190. Change &C = Changes[i];
  191. if (C.NewlinesBefore > 0) {
  192. assert(C.ContinuesPPDirective);
  193. if (C.PreviousEndOfTokenColumn + 1 > Column)
  194. C.EscapedNewlineColumn = 0;
  195. else
  196. C.EscapedNewlineColumn = Column;
  197. }
  198. }
  199. }
  200. void WhitespaceManager::generateChanges() {
  201. for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
  202. const Change &C = Changes[i];
  203. if (C.CreateReplacement) {
  204. std::string ReplacementText = C.PreviousLinePostfix;
  205. if (C.ContinuesPPDirective)
  206. appendNewlineText(ReplacementText, C.NewlinesBefore,
  207. C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
  208. else
  209. appendNewlineText(ReplacementText, C.NewlinesBefore);
  210. appendIndentText(ReplacementText, C.Spaces, C.StartOfTokenColumn - C.Spaces);
  211. ReplacementText.append(C.CurrentLinePrefix);
  212. storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
  213. }
  214. }
  215. }
  216. void WhitespaceManager::storeReplacement(const SourceRange &Range,
  217. StringRef Text) {
  218. unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
  219. SourceMgr.getFileOffset(Range.getBegin());
  220. // Don't create a replacement, if it does not change anything.
  221. if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
  222. WhitespaceLength) == Text)
  223. return;
  224. Replaces.insert(tooling::Replacement(
  225. SourceMgr, CharSourceRange::getCharRange(Range), Text));
  226. }
  227. void WhitespaceManager::appendNewlineText(std::string &Text,
  228. unsigned Newlines) {
  229. for (unsigned i = 0; i < Newlines; ++i)
  230. Text.append(UseCRLF ? "\r\n" : "\n");
  231. }
  232. void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
  233. unsigned PreviousEndOfTokenColumn,
  234. unsigned EscapedNewlineColumn) {
  235. if (Newlines > 0) {
  236. unsigned Offset =
  237. std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
  238. for (unsigned i = 0; i < Newlines; ++i) {
  239. Text.append(std::string(EscapedNewlineColumn - Offset - 1, ' '));
  240. Text.append(UseCRLF ? "\\\r\n" : "\\\n");
  241. Offset = 0;
  242. }
  243. }
  244. }
  245. void WhitespaceManager::appendIndentText(std::string &Text, unsigned Spaces,
  246. unsigned WhitespaceStartColumn) {
  247. if (!Style.UseTab) {
  248. Text.append(std::string(Spaces, ' '));
  249. } else {
  250. unsigned FirstTabWidth =
  251. Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
  252. // Indent with tabs only when there's at least one full tab.
  253. if (FirstTabWidth + Style.TabWidth <= Spaces) {
  254. Spaces -= FirstTabWidth;
  255. Text.append("\t");
  256. }
  257. Text.append(std::string(Spaces / Style.TabWidth, '\t'));
  258. Text.append(std::string(Spaces % Style.TabWidth, ' '));
  259. }
  260. }
  261. } // namespace format
  262. } // namespace clang