WhitespaceManager.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  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(
  41. Change(true, Tok.WhitespaceRange, Spaces, StartOfTokenColumn, Newlines,
  42. "", "", Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
  43. }
  44. void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
  45. bool InPPDirective) {
  46. Changes.push_back(
  47. Change(false, Tok.WhitespaceRange, /*Spaces=*/0,
  48. SourceMgr.getSpellingColumnNumber(Tok.Tok.getLocation()) - 1,
  49. Tok.NewlinesBefore, "", "", Tok.Tok.getKind(),
  50. InPPDirective && !Tok.IsFirst));
  51. }
  52. void WhitespaceManager::replaceWhitespaceInToken(
  53. const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
  54. StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
  55. unsigned Newlines, unsigned Spaces) {
  56. Changes.push_back(Change(
  57. true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
  58. Tok.getStartOfNonWhitespace().getLocWithOffset(
  59. Offset + ReplaceChars)),
  60. Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
  61. // FIXME: Unify token adjustment, so we don't split it between
  62. // BreakableToken and the WhitespaceManager. That would also allow us to
  63. // correctly store a tok::TokenKind instead of rolling our own enum.
  64. tok::unknown, InPPDirective && !Tok.IsFirst));
  65. }
  66. const tooling::Replacements &WhitespaceManager::generateReplacements() {
  67. if (Changes.empty())
  68. return Replaces;
  69. std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
  70. calculateLineBreakInformation();
  71. alignTrailingComments();
  72. alignEscapedNewlines();
  73. generateChanges();
  74. return Replaces;
  75. }
  76. void WhitespaceManager::calculateLineBreakInformation() {
  77. Changes[0].PreviousEndOfTokenColumn = 0;
  78. for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
  79. unsigned OriginalWhitespaceStart =
  80. SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
  81. unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
  82. Changes[i - 1].OriginalWhitespaceRange.getEnd());
  83. Changes[i - 1].TokenLength =
  84. OriginalWhitespaceStart - PreviousOriginalWhitespaceEnd +
  85. Changes[i].PreviousLinePostfix.size() +
  86. Changes[i - 1].CurrentLinePrefix.size();
  87. Changes[i].PreviousEndOfTokenColumn =
  88. Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
  89. Changes[i - 1].IsTrailingComment =
  90. (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
  91. Changes[i - 1].Kind == tok::comment;
  92. }
  93. // FIXME: The last token is currently not always an eof token; in those
  94. // cases, setting TokenLength of the last token to 0 is wrong.
  95. Changes.back().TokenLength = 0;
  96. Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
  97. }
  98. void WhitespaceManager::alignTrailingComments() {
  99. unsigned MinColumn = 0;
  100. unsigned MaxColumn = UINT_MAX;
  101. unsigned StartOfSequence = 0;
  102. bool BreakBeforeNext = false;
  103. unsigned Newlines = 0;
  104. for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
  105. unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
  106. // FIXME: Correctly handle ChangeMaxColumn in PP directives.
  107. unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
  108. Newlines += Changes[i].NewlinesBefore;
  109. if (Changes[i].IsTrailingComment) {
  110. bool WasAlignedWithStartOfNextLine =
  111. // A comment on its own line.
  112. Changes[i].NewlinesBefore == 1 &&
  113. // Not the last line.
  114. i + 1 != e &&
  115. // The start of the next token was previously aligned with
  116. // the start of this comment.
  117. (SourceMgr.getSpellingColumnNumber(
  118. Changes[i].OriginalWhitespaceRange.getEnd()) ==
  119. SourceMgr.getSpellingColumnNumber(
  120. Changes[i + 1].OriginalWhitespaceRange.getEnd())) &&
  121. // Which is not a comment itself.
  122. Changes[i + 1].Kind != tok::comment;
  123. if (BreakBeforeNext || Newlines > 1 ||
  124. (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
  125. // Break the comment sequence if the previous line did not end
  126. // in a trailing comment.
  127. (Changes[i].NewlinesBefore == 1 && i > 0 &&
  128. !Changes[i - 1].IsTrailingComment) ||
  129. WasAlignedWithStartOfNextLine) {
  130. alignTrailingComments(StartOfSequence, i, MinColumn);
  131. MinColumn = ChangeMinColumn;
  132. MaxColumn = ChangeMaxColumn;
  133. StartOfSequence = i;
  134. } else {
  135. MinColumn = std::max(MinColumn, ChangeMinColumn);
  136. MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
  137. }
  138. BreakBeforeNext =
  139. (i == 0) || (Changes[i].NewlinesBefore > 1) ||
  140. // Never start a sequence with a comment at the beginning of
  141. // the line.
  142. (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
  143. Newlines = 0;
  144. }
  145. }
  146. alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
  147. }
  148. void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
  149. unsigned Column) {
  150. for (unsigned i = Start; i != End; ++i) {
  151. if (Changes[i].IsTrailingComment) {
  152. assert(Column >= Changes[i].StartOfTokenColumn);
  153. Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
  154. Changes[i].StartOfTokenColumn = Column;
  155. }
  156. }
  157. }
  158. void WhitespaceManager::alignEscapedNewlines() {
  159. unsigned MaxEndOfLine = 0;
  160. unsigned StartOfMacro = 0;
  161. for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
  162. Change &C = Changes[i];
  163. if (C.NewlinesBefore > 0) {
  164. if (C.ContinuesPPDirective) {
  165. if (Style.AlignEscapedNewlinesLeft)
  166. MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
  167. else
  168. MaxEndOfLine = Style.ColumnLimit;
  169. } else {
  170. alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
  171. MaxEndOfLine = 0;
  172. StartOfMacro = i;
  173. }
  174. }
  175. }
  176. alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
  177. }
  178. void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
  179. unsigned Column) {
  180. for (unsigned i = Start; i < End; ++i) {
  181. Change &C = Changes[i];
  182. if (C.NewlinesBefore > 0) {
  183. assert(C.ContinuesPPDirective);
  184. if (C.PreviousEndOfTokenColumn + 1 > Column)
  185. C.EscapedNewlineColumn = 0;
  186. else
  187. C.EscapedNewlineColumn = Column;
  188. }
  189. }
  190. }
  191. void WhitespaceManager::generateChanges() {
  192. for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
  193. const Change &C = Changes[i];
  194. if (C.CreateReplacement) {
  195. std::string ReplacementText =
  196. C.PreviousLinePostfix +
  197. (C.ContinuesPPDirective
  198. ? getNewlineText(C.NewlinesBefore, C.Spaces,
  199. C.PreviousEndOfTokenColumn,
  200. C.EscapedNewlineColumn)
  201. : getNewlineText(C.NewlinesBefore, C.Spaces)) +
  202. C.CurrentLinePrefix;
  203. storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
  204. }
  205. }
  206. }
  207. void WhitespaceManager::storeReplacement(const SourceRange &Range,
  208. StringRef Text) {
  209. unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
  210. SourceMgr.getFileOffset(Range.getBegin());
  211. // Don't create a replacement, if it does not change anything.
  212. if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
  213. WhitespaceLength) ==
  214. Text)
  215. return;
  216. Replaces.insert(tooling::Replacement(
  217. SourceMgr, CharSourceRange::getCharRange(Range), Text));
  218. }
  219. std::string WhitespaceManager::getNewlineText(unsigned Newlines,
  220. unsigned Spaces) {
  221. return std::string(Newlines, '\n') + getIndentText(Spaces);
  222. }
  223. std::string WhitespaceManager::getNewlineText(unsigned Newlines,
  224. unsigned Spaces,
  225. unsigned PreviousEndOfTokenColumn,
  226. unsigned EscapedNewlineColumn) {
  227. std::string NewlineText;
  228. if (Newlines > 0) {
  229. unsigned Offset =
  230. std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
  231. for (unsigned i = 0; i < Newlines; ++i) {
  232. NewlineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
  233. NewlineText += "\\\n";
  234. Offset = 0;
  235. }
  236. }
  237. return NewlineText + getIndentText(Spaces);
  238. }
  239. std::string WhitespaceManager::getIndentText(unsigned Spaces) {
  240. if (!Style.UseTab)
  241. return std::string(Spaces, ' ');
  242. return std::string(Spaces / Style.IndentWidth, '\t') +
  243. std::string(Spaces % Style.IndentWidth, ' ');
  244. }
  245. } // namespace format
  246. } // namespace clang