WhitespaceManager.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  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 WhitespaceManager::Change::IsBeforeInFile::
  19. operator()(const Change &C1, const Change &C2) const {
  20. return SourceMgr.isBeforeInTranslationUnit(
  21. C1.OriginalWhitespaceRange.getBegin(),
  22. C2.OriginalWhitespaceRange.getBegin());
  23. }
  24. WhitespaceManager::Change::Change(
  25. bool CreateReplacement, SourceRange OriginalWhitespaceRange,
  26. unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
  27. unsigned NewlinesBefore, StringRef PreviousLinePostfix,
  28. StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective,
  29. bool IsStartOfDeclName, bool IsInsideToken)
  30. : CreateReplacement(CreateReplacement),
  31. OriginalWhitespaceRange(OriginalWhitespaceRange),
  32. StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
  33. PreviousLinePostfix(PreviousLinePostfix),
  34. CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
  35. ContinuesPPDirective(ContinuesPPDirective),
  36. IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel),
  37. Spaces(Spaces), IsInsideToken(IsInsideToken), IsTrailingComment(false),
  38. TokenLength(0), PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
  39. StartOfBlockComment(nullptr), IndentationOffset(0) {}
  40. void WhitespaceManager::reset() {
  41. Changes.clear();
  42. Replaces.clear();
  43. }
  44. void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
  45. unsigned IndentLevel, unsigned Spaces,
  46. unsigned StartOfTokenColumn,
  47. bool InPPDirective) {
  48. if (Tok.Finalized)
  49. return;
  50. Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
  51. Changes.push_back(
  52. Change(/*CreateReplacement=*/true, Tok.WhitespaceRange, IndentLevel,
  53. Spaces, StartOfTokenColumn, Newlines, "", "", Tok.Tok.getKind(),
  54. InPPDirective && !Tok.IsFirst,
  55. Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
  56. /*IsInsideToken=*/false));
  57. }
  58. void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
  59. bool InPPDirective) {
  60. if (Tok.Finalized)
  61. return;
  62. Changes.push_back(Change(
  63. /*CreateReplacement=*/false, Tok.WhitespaceRange, /*IndentLevel=*/0,
  64. /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
  65. Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst,
  66. Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
  67. /*IsInsideToken=*/false));
  68. }
  69. void WhitespaceManager::replaceWhitespaceInToken(
  70. const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
  71. StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
  72. unsigned Newlines, unsigned IndentLevel, int Spaces) {
  73. if (Tok.Finalized)
  74. return;
  75. SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
  76. Changes.push_back(Change(
  77. true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)),
  78. IndentLevel, Spaces, std::max(0, Spaces), Newlines, PreviousPostfix,
  79. CurrentPrefix, Tok.is(TT_LineComment) ? tok::comment : tok::unknown,
  80. InPPDirective && !Tok.IsFirst,
  81. Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
  82. /*IsInsideToken=*/Newlines == 0));
  83. }
  84. const tooling::Replacements &WhitespaceManager::generateReplacements() {
  85. if (Changes.empty())
  86. return Replaces;
  87. std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
  88. calculateLineBreakInformation();
  89. alignConsecutiveDeclarations();
  90. alignConsecutiveAssignments();
  91. alignTrailingComments();
  92. alignEscapedNewlines();
  93. generateChanges();
  94. return Replaces;
  95. }
  96. void WhitespaceManager::calculateLineBreakInformation() {
  97. Changes[0].PreviousEndOfTokenColumn = 0;
  98. Change *LastOutsideTokenChange = &Changes[0];
  99. for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
  100. unsigned OriginalWhitespaceStart =
  101. SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
  102. unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
  103. Changes[i - 1].OriginalWhitespaceRange.getEnd());
  104. Changes[i - 1].TokenLength = OriginalWhitespaceStart -
  105. PreviousOriginalWhitespaceEnd +
  106. Changes[i].PreviousLinePostfix.size() +
  107. Changes[i - 1].CurrentLinePrefix.size();
  108. // If there are multiple changes in this token, sum up all the changes until
  109. // the end of the line.
  110. if (Changes[i - 1].IsInsideToken)
  111. LastOutsideTokenChange->TokenLength +=
  112. Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
  113. else
  114. LastOutsideTokenChange = &Changes[i - 1];
  115. Changes[i].PreviousEndOfTokenColumn =
  116. Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
  117. Changes[i - 1].IsTrailingComment =
  118. (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof ||
  119. (Changes[i].IsInsideToken && Changes[i].Kind == tok::comment)) &&
  120. Changes[i - 1].Kind == tok::comment;
  121. }
  122. // FIXME: The last token is currently not always an eof token; in those
  123. // cases, setting TokenLength of the last token to 0 is wrong.
  124. Changes.back().TokenLength = 0;
  125. Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
  126. const WhitespaceManager::Change *LastBlockComment = nullptr;
  127. for (auto &Change : Changes) {
  128. // Reset the IsTrailingComment flag for changes inside of trailing comments
  129. // so they don't get realigned later.
  130. if (Change.IsInsideToken)
  131. Change.IsTrailingComment = false;
  132. Change.StartOfBlockComment = nullptr;
  133. Change.IndentationOffset = 0;
  134. if (Change.Kind == tok::comment) {
  135. LastBlockComment = &Change;
  136. } else if (Change.Kind == tok::unknown) {
  137. if ((Change.StartOfBlockComment = LastBlockComment))
  138. Change.IndentationOffset =
  139. Change.StartOfTokenColumn -
  140. Change.StartOfBlockComment->StartOfTokenColumn;
  141. } else {
  142. LastBlockComment = nullptr;
  143. }
  144. }
  145. }
  146. // Align a single sequence of tokens, see AlignTokens below.
  147. template <typename F>
  148. static void
  149. AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
  150. SmallVector<WhitespaceManager::Change, 16> &Changes) {
  151. bool FoundMatchOnLine = false;
  152. int Shift = 0;
  153. for (unsigned i = Start; i != End; ++i) {
  154. if (Changes[i].NewlinesBefore > 0) {
  155. FoundMatchOnLine = false;
  156. Shift = 0;
  157. }
  158. // If this is the first matching token to be aligned, remember by how many
  159. // spaces it has to be shifted, so the rest of the changes on the line are
  160. // shifted by the same amount
  161. if (!FoundMatchOnLine && Matches(Changes[i])) {
  162. FoundMatchOnLine = true;
  163. Shift = Column - Changes[i].StartOfTokenColumn;
  164. Changes[i].Spaces += Shift;
  165. }
  166. assert(Shift >= 0);
  167. Changes[i].StartOfTokenColumn += Shift;
  168. if (i + 1 != Changes.size())
  169. Changes[i + 1].PreviousEndOfTokenColumn += Shift;
  170. }
  171. }
  172. // Walk through all of the changes and find sequences of matching tokens to
  173. // align. To do so, keep track of the lines and whether or not a matching token
  174. // was found on a line. If a matching token is found, extend the current
  175. // sequence. If the current line cannot be part of a sequence, e.g. because
  176. // there is an empty line before it or it contains only non-matching tokens,
  177. // finalize the previous sequence.
  178. template <typename F>
  179. static void AlignTokens(const FormatStyle &Style, F &&Matches,
  180. SmallVector<WhitespaceManager::Change, 16> &Changes) {
  181. unsigned MinColumn = 0;
  182. unsigned MaxColumn = UINT_MAX;
  183. // Line number of the start and the end of the current token sequence.
  184. unsigned StartOfSequence = 0;
  185. unsigned EndOfSequence = 0;
  186. // Keep track of the nesting level of matching tokens, i.e. the number of
  187. // surrounding (), [], or {}. We will only align a sequence of matching
  188. // token that share the same scope depth.
  189. //
  190. // FIXME: This could use FormatToken::NestingLevel information, but there is
  191. // an outstanding issue wrt the brace scopes.
  192. unsigned NestingLevelOfLastMatch = 0;
  193. unsigned NestingLevel = 0;
  194. // Keep track of the number of commas before the matching tokens, we will only
  195. // align a sequence of matching tokens if they are preceded by the same number
  196. // of commas.
  197. unsigned CommasBeforeLastMatch = 0;
  198. unsigned CommasBeforeMatch = 0;
  199. // Whether a matching token has been found on the current line.
  200. bool FoundMatchOnLine = false;
  201. // Aligns a sequence of matching tokens, on the MinColumn column.
  202. //
  203. // Sequences start from the first matching token to align, and end at the
  204. // first token of the first line that doesn't need to be aligned.
  205. //
  206. // We need to adjust the StartOfTokenColumn of each Change that is on a line
  207. // containing any matching token to be aligned and located after such token.
  208. auto AlignCurrentSequence = [&] {
  209. if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
  210. AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
  211. Changes);
  212. MinColumn = 0;
  213. MaxColumn = UINT_MAX;
  214. StartOfSequence = 0;
  215. EndOfSequence = 0;
  216. };
  217. for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
  218. if (Changes[i].NewlinesBefore != 0) {
  219. CommasBeforeMatch = 0;
  220. EndOfSequence = i;
  221. // If there is a blank line, or if the last line didn't contain any
  222. // matching token, the sequence ends here.
  223. if (Changes[i].NewlinesBefore > 1 || !FoundMatchOnLine)
  224. AlignCurrentSequence();
  225. FoundMatchOnLine = false;
  226. }
  227. if (Changes[i].Kind == tok::comma) {
  228. ++CommasBeforeMatch;
  229. } else if (Changes[i].Kind == tok::r_brace ||
  230. Changes[i].Kind == tok::r_paren ||
  231. Changes[i].Kind == tok::r_square) {
  232. --NestingLevel;
  233. } else if (Changes[i].Kind == tok::l_brace ||
  234. Changes[i].Kind == tok::l_paren ||
  235. Changes[i].Kind == tok::l_square) {
  236. // We want sequences to skip over child scopes if possible, but not the
  237. // other way around.
  238. NestingLevelOfLastMatch = std::min(NestingLevelOfLastMatch, NestingLevel);
  239. ++NestingLevel;
  240. }
  241. if (!Matches(Changes[i]))
  242. continue;
  243. // If there is more than one matching token per line, or if the number of
  244. // preceding commas, or the scope depth, do not match anymore, end the
  245. // sequence.
  246. if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch ||
  247. NestingLevel != NestingLevelOfLastMatch)
  248. AlignCurrentSequence();
  249. CommasBeforeLastMatch = CommasBeforeMatch;
  250. NestingLevelOfLastMatch = NestingLevel;
  251. FoundMatchOnLine = true;
  252. if (StartOfSequence == 0)
  253. StartOfSequence = i;
  254. unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
  255. int LineLengthAfter = -Changes[i].Spaces;
  256. for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
  257. LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
  258. unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
  259. // If we are restricted by the maximum column width, end the sequence.
  260. if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
  261. CommasBeforeLastMatch != CommasBeforeMatch) {
  262. AlignCurrentSequence();
  263. StartOfSequence = i;
  264. }
  265. MinColumn = std::max(MinColumn, ChangeMinColumn);
  266. MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
  267. }
  268. EndOfSequence = Changes.size();
  269. AlignCurrentSequence();
  270. }
  271. void WhitespaceManager::alignConsecutiveAssignments() {
  272. if (!Style.AlignConsecutiveAssignments)
  273. return;
  274. AlignTokens(Style,
  275. [&](const Change &C) {
  276. // Do not align on equal signs that are first on a line.
  277. if (C.NewlinesBefore > 0)
  278. return false;
  279. // Do not align on equal signs that are last on a line.
  280. if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
  281. return false;
  282. return C.Kind == tok::equal;
  283. },
  284. Changes);
  285. }
  286. void WhitespaceManager::alignConsecutiveDeclarations() {
  287. if (!Style.AlignConsecutiveDeclarations)
  288. return;
  289. // FIXME: Currently we don't handle properly the PointerAlignment: Right
  290. // The * and & are not aligned and are left dangling. Something has to be done
  291. // about it, but it raises the question of alignment of code like:
  292. // const char* const* v1;
  293. // float const* v2;
  294. // SomeVeryLongType const& v3;
  295. AlignTokens(Style, [](Change const &C) { return C.IsStartOfDeclName; },
  296. Changes);
  297. }
  298. void WhitespaceManager::alignTrailingComments() {
  299. unsigned MinColumn = 0;
  300. unsigned MaxColumn = UINT_MAX;
  301. unsigned StartOfSequence = 0;
  302. bool BreakBeforeNext = false;
  303. unsigned Newlines = 0;
  304. for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
  305. if (Changes[i].StartOfBlockComment)
  306. continue;
  307. Newlines += Changes[i].NewlinesBefore;
  308. if (!Changes[i].IsTrailingComment)
  309. continue;
  310. unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
  311. unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
  312. // If we don't create a replacement for this change, we have to consider
  313. // it to be immovable.
  314. if (!Changes[i].CreateReplacement)
  315. ChangeMaxColumn = ChangeMinColumn;
  316. if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
  317. ChangeMaxColumn -= 2;
  318. // If this comment follows an } in column 0, it probably documents the
  319. // closing of a namespace and we don't want to align it.
  320. bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
  321. Changes[i - 1].Kind == tok::r_brace &&
  322. Changes[i - 1].StartOfTokenColumn == 0;
  323. bool WasAlignedWithStartOfNextLine = false;
  324. if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
  325. unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
  326. Changes[i].OriginalWhitespaceRange.getEnd());
  327. for (unsigned j = i + 1; j != e; ++j) {
  328. if (Changes[j].Kind == tok::comment ||
  329. Changes[j].Kind == tok::unknown)
  330. // Skip over comments and unknown tokens. "unknown tokens are used for
  331. // the continuation of multiline comments.
  332. continue;
  333. unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
  334. Changes[j].OriginalWhitespaceRange.getEnd());
  335. // The start of the next token was previously aligned with the
  336. // start of this comment.
  337. WasAlignedWithStartOfNextLine =
  338. CommentColumn == NextColumn ||
  339. CommentColumn == NextColumn + Style.IndentWidth;
  340. break;
  341. }
  342. }
  343. if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
  344. alignTrailingComments(StartOfSequence, i, MinColumn);
  345. MinColumn = ChangeMinColumn;
  346. MaxColumn = ChangeMinColumn;
  347. StartOfSequence = i;
  348. } else if (BreakBeforeNext || Newlines > 1 ||
  349. (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
  350. // Break the comment sequence if the previous line did not end
  351. // in a trailing comment.
  352. (Changes[i].NewlinesBefore == 1 && i > 0 &&
  353. !Changes[i - 1].IsTrailingComment) ||
  354. WasAlignedWithStartOfNextLine) {
  355. alignTrailingComments(StartOfSequence, i, MinColumn);
  356. MinColumn = ChangeMinColumn;
  357. MaxColumn = ChangeMaxColumn;
  358. StartOfSequence = i;
  359. } else {
  360. MinColumn = std::max(MinColumn, ChangeMinColumn);
  361. MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
  362. }
  363. BreakBeforeNext =
  364. (i == 0) || (Changes[i].NewlinesBefore > 1) ||
  365. // Never start a sequence with a comment at the beginning of
  366. // the line.
  367. (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
  368. Newlines = 0;
  369. }
  370. alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
  371. }
  372. void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
  373. unsigned Column) {
  374. for (unsigned i = Start; i != End; ++i) {
  375. int Shift = 0;
  376. if (Changes[i].IsTrailingComment) {
  377. Shift = Column - Changes[i].StartOfTokenColumn;
  378. }
  379. if (Changes[i].StartOfBlockComment) {
  380. Shift = Changes[i].IndentationOffset +
  381. Changes[i].StartOfBlockComment->StartOfTokenColumn -
  382. Changes[i].StartOfTokenColumn;
  383. }
  384. assert(Shift >= 0);
  385. Changes[i].Spaces += Shift;
  386. if (i + 1 != End)
  387. Changes[i + 1].PreviousEndOfTokenColumn += Shift;
  388. Changes[i].StartOfTokenColumn += Shift;
  389. }
  390. }
  391. void WhitespaceManager::alignEscapedNewlines() {
  392. unsigned MaxEndOfLine =
  393. Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
  394. unsigned StartOfMacro = 0;
  395. for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
  396. Change &C = Changes[i];
  397. if (C.NewlinesBefore > 0) {
  398. if (C.ContinuesPPDirective) {
  399. MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
  400. } else {
  401. alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
  402. MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
  403. StartOfMacro = i;
  404. }
  405. }
  406. }
  407. alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
  408. }
  409. void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
  410. unsigned Column) {
  411. for (unsigned i = Start; i < End; ++i) {
  412. Change &C = Changes[i];
  413. if (C.NewlinesBefore > 0) {
  414. assert(C.ContinuesPPDirective);
  415. if (C.PreviousEndOfTokenColumn + 1 > Column)
  416. C.EscapedNewlineColumn = 0;
  417. else
  418. C.EscapedNewlineColumn = Column;
  419. }
  420. }
  421. }
  422. void WhitespaceManager::generateChanges() {
  423. for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
  424. const Change &C = Changes[i];
  425. if (i > 0) {
  426. assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
  427. C.OriginalWhitespaceRange.getBegin() &&
  428. "Generating two replacements for the same location");
  429. }
  430. if (C.CreateReplacement) {
  431. std::string ReplacementText = C.PreviousLinePostfix;
  432. if (C.ContinuesPPDirective)
  433. appendNewlineText(ReplacementText, C.NewlinesBefore,
  434. C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
  435. else
  436. appendNewlineText(ReplacementText, C.NewlinesBefore);
  437. appendIndentText(ReplacementText, C.IndentLevel, std::max(0, C.Spaces),
  438. C.StartOfTokenColumn - std::max(0, C.Spaces));
  439. ReplacementText.append(C.CurrentLinePrefix);
  440. storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
  441. }
  442. }
  443. }
  444. void WhitespaceManager::storeReplacement(SourceRange Range,
  445. StringRef Text) {
  446. unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
  447. SourceMgr.getFileOffset(Range.getBegin());
  448. // Don't create a replacement, if it does not change anything.
  449. if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
  450. WhitespaceLength) == Text)
  451. return;
  452. Replaces.insert(tooling::Replacement(
  453. SourceMgr, CharSourceRange::getCharRange(Range), Text));
  454. }
  455. void WhitespaceManager::appendNewlineText(std::string &Text,
  456. unsigned Newlines) {
  457. for (unsigned i = 0; i < Newlines; ++i)
  458. Text.append(UseCRLF ? "\r\n" : "\n");
  459. }
  460. void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
  461. unsigned PreviousEndOfTokenColumn,
  462. unsigned EscapedNewlineColumn) {
  463. if (Newlines > 0) {
  464. unsigned Offset =
  465. std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
  466. for (unsigned i = 0; i < Newlines; ++i) {
  467. Text.append(EscapedNewlineColumn - Offset - 1, ' ');
  468. Text.append(UseCRLF ? "\\\r\n" : "\\\n");
  469. Offset = 0;
  470. }
  471. }
  472. }
  473. void WhitespaceManager::appendIndentText(std::string &Text,
  474. unsigned IndentLevel, unsigned Spaces,
  475. unsigned WhitespaceStartColumn) {
  476. switch (Style.UseTab) {
  477. case FormatStyle::UT_Never:
  478. Text.append(Spaces, ' ');
  479. break;
  480. case FormatStyle::UT_Always: {
  481. unsigned FirstTabWidth =
  482. Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
  483. // Indent with tabs only when there's at least one full tab.
  484. if (FirstTabWidth + Style.TabWidth <= Spaces) {
  485. Spaces -= FirstTabWidth;
  486. Text.append("\t");
  487. }
  488. Text.append(Spaces / Style.TabWidth, '\t');
  489. Text.append(Spaces % Style.TabWidth, ' ');
  490. break;
  491. }
  492. case FormatStyle::UT_ForIndentation:
  493. if (WhitespaceStartColumn == 0) {
  494. unsigned Indentation = IndentLevel * Style.IndentWidth;
  495. // This happens, e.g. when a line in a block comment is indented less than
  496. // the first one.
  497. if (Indentation > Spaces)
  498. Indentation = Spaces;
  499. unsigned Tabs = Indentation / Style.TabWidth;
  500. Text.append(Tabs, '\t');
  501. Spaces -= Tabs * Style.TabWidth;
  502. }
  503. Text.append(Spaces, ' ');
  504. break;
  505. }
  506. }
  507. } // namespace format
  508. } // namespace clang