10 #include "clang/Basic/FileManager.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/Format/Format.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Tooling/Core/Replacement.h"
15 #include "llvm/Support/Unicode.h"
26 SourceManagerForFile FileSM(
"dummy.cpp",
Code);
27 auto &SM = FileSM.get();
28 FileID FID = SM.getMainFileID();
29 Lexer Lex(FID, SM.getBuffer(FID), SM, format::getFormattingLangOpts(Style));
31 std::vector<char> Brackets;
32 while (!Lex.LexFromRawLexer(Tok)) {
33 switch(Tok.getKind()) {
35 Brackets.push_back(
')');
38 Brackets.push_back(
'}');
41 Brackets.push_back(
']');
44 if (!Brackets.empty() && Brackets.back() ==
')')
48 if (!Brackets.empty() && Brackets.back() ==
'}')
52 if (!Brackets.empty() && Brackets.back() ==
']')
60 Code.append(
"\n// */\n");
61 Code.append(Brackets.rbegin(), Brackets.rend());
64 static StringRef commentMarker(llvm::StringRef
Line) {
65 for (StringRef Marker : {
"///",
"//"}){
66 auto I =
Line.rfind(Marker);
67 if (I != StringRef::npos)
68 return Line.substr(I, Marker.size());
73 llvm::StringRef firstLine(llvm::StringRef
Code) {
74 return Code.take_until([](
char C) {
return C ==
'\n'; });
77 llvm::StringRef lastLine(llvm::StringRef
Code) {
78 llvm::StringRef Rest =
Code;
79 while (!Rest.empty() && Rest.back() !=
'\n')
80 Rest = Rest.drop_back();
81 return Code.substr(Rest.size());
86 llvm::StringRef
Filename =
"<stdin>";
89 tooling::Replacement replacement(llvm::StringRef
Code, llvm::StringRef From,
91 assert(From.begin() >=
Code.begin() && From.end() <=
Code.end());
93 return tooling::Replacement(
Filename, From.data() -
Code.data(),
107 struct IncrementalChanges {
123 IncrementalChanges getIncrementalChangesAfterNewline(llvm::StringRef
Code,
125 IncrementalChanges Result;
132 StringRef Trailing = firstLine(
Code.substr(Cursor));
133 StringRef Indentation = lastLine(
Code.take_front(Cursor));
134 if (Indentation.data() ==
Code.data()) {
135 vlog(
"Typed a newline, but we're still on the first line!");
139 lastLine(
Code.take_front(Indentation.data() -
Code.data() - 1));
140 StringRef NextLine = firstLine(
Code.substr(Cursor + Trailing.size() + 1));
143 StringRef TrailingTrim = Trailing.ltrim();
144 if (
unsigned TrailWS = Trailing.size() - TrailingTrim.size())
145 cantFail(Result.Changes.add(
146 replacement(
Code, StringRef(Trailing.begin(), TrailWS),
"")));
150 StringRef CommentMarker = commentMarker(Leading);
151 bool NewLineIsComment = !commentMarker(Indentation).empty();
152 if (!CommentMarker.empty() &&
153 (NewLineIsComment || !commentMarker(NextLine).empty() ||
154 (!TrailingTrim.empty() && !TrailingTrim.startswith(
"//")))) {
155 using llvm::sys::unicode::columnWidthUTF8;
157 StringRef PreComment =
158 Leading.take_front(CommentMarker.data() - Leading.data());
159 std::string IndentAndComment =
160 (std::string(columnWidthUTF8(PreComment),
' ') + CommentMarker +
" ")
163 Result.Changes.add(replacement(
Code, Indentation, IndentAndComment)));
167 cantFail(Result.Changes.add(replacement(
Code, Indentation,
"")));
171 if (CommentMarker.empty() && Leading.endswith(
"{") &&
172 Trailing.startswith(
"}")) {
174 Result.Changes.add(replacement(
Code, Trailing.take_front(1),
"\n}")));
176 Result.FormatRanges.push_back(
181 Result.FormatRanges.push_back(
188 Result.CursorPlaceholder = !CommentMarker.empty() ?
"ident" :
"//==\nident";
193 IncrementalChanges getIncrementalChanges(llvm::StringRef
Code,
unsigned Cursor,
194 llvm::StringRef InsertedText) {
195 IncrementalChanges Result;
196 if (InsertedText ==
"\n")
197 return getIncrementalChangesAfterNewline(
Code, Cursor);
199 Result.CursorPlaceholder =
" /**/";
206 std::vector<tooling::Replacement>
207 split(
const tooling::Replacements &Replacements,
unsigned OldCursor,
208 unsigned NewCursor) {
209 std::vector<tooling::Replacement> Result;
210 int LengthChange = 0;
211 for (
const tooling::Replacement &R : Replacements) {
212 if (R.getOffset() + R.getLength() <= OldCursor) {
214 LengthChange += R.getReplacementText().size() - R.getLength();
215 }
else if (R.getOffset() < OldCursor) {
216 int ReplacementSplit = NewCursor - LengthChange - R.getOffset();
217 assert(ReplacementSplit >= 0 &&
218 ReplacementSplit <=
int(R.getReplacementText().size()) &&
219 "NewCursor incompatible with OldCursor!");
220 Result.push_back(tooling::Replacement(
221 R.getFilePath(), R.getOffset(), OldCursor - R.getOffset(),
222 R.getReplacementText().take_front(ReplacementSplit)));
223 Result.push_back(tooling::Replacement(
224 R.getFilePath(), OldCursor,
225 R.getLength() - (OldCursor - R.getOffset()),
226 R.getReplacementText().drop_front(ReplacementSplit)));
227 }
else if (R.getOffset() >= OldCursor) {
246 std::vector<tooling::Replacement>
250 getIncrementalChanges(OriginalCode, OriginalCursor, InsertedText);
252 if (InsertedText ==
"\n") {
253 Style.MaxEmptyLinesToKeep = 1000;
254 Style.KeepEmptyLinesAtTheStartOfBlocks =
true;
259 std::string CodeToFormat = cantFail(
260 tooling::applyAllReplacements(OriginalCode,
Incremental.Changes));
261 unsigned Cursor =
Incremental.Changes.getShiftedCodePosition(OriginalCursor);
263 unsigned FormatLimit = Cursor;
265 FormatLimit = std::max(FormatLimit, R.getOffset() + R.getLength());
266 CodeToFormat.resize(FormatLimit);
268 CodeToFormat.insert(Cursor,
Incremental.CursorPlaceholder);
270 closeBrackets(CodeToFormat, Style);
273 std::vector<tooling::Range> RangesToFormat =
Incremental.FormatRanges;
275 for (
auto &R : RangesToFormat) {
276 if (R.getOffset() > Cursor)
281 RangesToFormat.push_back(
284 FormatLimit +=
Incremental.CursorPlaceholder.size();
287 tooling::Replacements FormattingChanges;
288 format::FormattingAttemptStatus Status;
289 for (
const tooling::Replacement &R : format::reformat(
290 Style, CodeToFormat, RangesToFormat,
Filename, &Status)) {
291 if (R.getOffset() + R.getLength() <= FormatLimit)
292 cantFail(FormattingChanges.add(R));
293 else if(R.getOffset() < FormatLimit) {
294 if (R.getReplacementText().empty())
295 cantFail(FormattingChanges.add(tooling::Replacement(
Filename,
296 R.getOffset(), FormatLimit - R.getOffset(),
"")));
299 elog(
"Incremental clang-format edit overlapping cursor @ {0}!\n{1}",
300 Cursor, CodeToFormat);
303 if (!Status.FormatComplete)
304 vlog(
"Incremental format incomplete at line {0}", Status.Line);
309 tooling::Replacements InsertCursorPlaceholder(
311 unsigned FormattedCursorStart =
312 FormattingChanges.getShiftedCodePosition(Cursor),
313 FormattedCursorEnd = FormattingChanges.getShiftedCodePosition(
315 tooling::Replacements RemoveCursorPlaceholder(
316 tooling::Replacement(
Filename, FormattedCursorStart,
317 FormattedCursorEnd - FormattedCursorStart,
""));
327 tooling::Replacements Final;
328 unsigned FinalCursor = OriginalCursor;
330 std::string FinalCode = std::string(OriginalCode);
331 dlog(
"Initial code: {0}", FinalCode);
334 std::vector<std::pair<const char *, const tooling::Replacements *>>{
336 {
"Insert placeholder", &InsertCursorPlaceholder},
337 {
"clang-format", &FormattingChanges},
338 {
"Remove placeholder", &RemoveCursorPlaceholder}}) {
339 Final = Final.merge(*Pass.second);
340 FinalCursor = Pass.second->getShiftedCodePosition(FinalCursor);
343 cantFail(tooling::applyAllReplacements(FinalCode, *Pass.second));
344 dlog(
"After {0}:\n{1}^{2}", Pass.first,
345 StringRef(FinalCode).take_front(FinalCursor),
346 StringRef(FinalCode).drop_front(FinalCursor));
349 return split(Final, OriginalCursor, FinalCursor);
354 const std::vector<tooling::Replacement> &Replacements) {
355 unsigned OriginalOffset =
Offset;
356 for (
const auto &R : Replacements) {
357 if (R.getOffset() + R.getLength() <= OriginalOffset) {
359 Offset += R.getReplacementText().size();
361 }
else if (R.getOffset() < OriginalOffset) {
364 unsigned PositionWithinReplacement =
Offset - R.getOffset();
365 if (PositionWithinReplacement > R.getReplacementText().size()) {
366 Offset += R.getReplacementText().size();
367 Offset -= PositionWithinReplacement;