1 //===--- Format.cpp -----------------------------------------*- C++-*------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 #include "Format.h" 9 #include "Logger.h" 10 #include "clang/Basic/SourceManager.h" 11 #include "clang/Format/Format.h" 12 #include "clang/Lex/Lexer.h" 13 #include "clang/Tooling/Core/Replacement.h" 14 #include "llvm/Support/Unicode.h" 15 16 namespace clang { 17 namespace clangd { 18 namespace { 19 20 /// Append closing brackets )]} to \p Code to make it well-formed. 21 /// Clang-format conservatively refuses to format files with unmatched brackets 22 /// as it isn't sure where the errors are and so can't correct. 23 /// When editing, it's reasonable to assume code before the cursor is complete. 24 void closeBrackets(std::string &Code, const format::FormatStyle &Style) { 25 SourceManagerForFile FileSM("dummy.cpp", Code); 26 auto &SM = FileSM.get(); 27 FileID FID = SM.getMainFileID(); 28 Lexer Lex(FID, SM.getBuffer(FID), SM, format::getFormattingLangOpts(Style)); 29 Token Tok; 30 std::vector<char> Brackets; 31 while (!Lex.LexFromRawLexer(Tok)) { 32 switch(Tok.getKind()) { 33 case tok::l_paren: 34 Brackets.push_back(')'); 35 break; 36 case tok::l_brace: 37 Brackets.push_back('}'); 38 break; 39 case tok::l_square: 40 Brackets.push_back(']'); 41 break; 42 case tok::r_paren: 43 if (!Brackets.empty() && Brackets.back() == ')') 44 Brackets.pop_back(); 45 break; 46 case tok::r_brace: 47 if (!Brackets.empty() && Brackets.back() == '}') 48 Brackets.pop_back(); 49 break; 50 case tok::r_square: 51 if (!Brackets.empty() && Brackets.back() == ']') 52 Brackets.pop_back(); 53 break; 54 default: 55 continue; 56 } 57 } 58 // Attempt to end any open comments first. 59 Code.append("\n// */\n"); 60 Code.append(Brackets.rbegin(), Brackets.rend()); 61 } 62 63 static StringRef commentMarker(llvm::StringRef Line) { 64 for (StringRef Marker : {"///", "//"}){ 65 auto I = Line.rfind(Marker); 66 if (I != StringRef::npos) 67 return Line.substr(I, Marker.size()); 68 } 69 return ""; 70 } 71 72 llvm::StringRef firstLine(llvm::StringRef Code) { 73 return Code.take_until([](char C) { return C == '\n'; }); 74 } 75 76 llvm::StringRef lastLine(llvm::StringRef Code) { 77 llvm::StringRef Rest = Code; 78 while (!Rest.empty() && Rest.back() != '\n') 79 Rest = Rest.drop_back(); 80 return Code.substr(Rest.size()); 81 } 82 83 // Filename is needed for tooling::Replacement and some overloads of reformat(). 84 // Its value should not affect the outcome. We use the default from reformat(). 85 llvm::StringRef Filename = "<stdin>"; 86 87 // tooling::Replacement from overlapping StringRefs: From must be part of Code. 88 tooling::Replacement replacement(llvm::StringRef Code, llvm::StringRef From, 89 llvm::StringRef To) { 90 assert(From.begin() >= Code.begin() && From.end() <= Code.end()); 91 // The filename is required but ignored. 92 return tooling::Replacement(Filename, From.data() - Code.data(), 93 From.size(), To); 94 } 95 96 // High-level representation of incremental formatting changes. 97 // The changes are made in two steps. 98 // 1) a (possibly-empty) set of changes synthesized by clangd (e.g. adding 99 // comment markers when splitting a line comment with a newline). 100 // 2) a selective clang-format run: 101 // - the "source code" passed to clang format is the code up to the cursor, 102 // a placeholder for the cursor, and some closing brackets 103 // - the formatting is restricted to the cursor and (possibly) other ranges 104 // (e.g. the old line when inserting a newline). 105 // - changes before the cursor are applied, those after are discarded. 106 struct IncrementalChanges { 107 // Changes that should be applied before running clang-format. 108 tooling::Replacements Changes; 109 // Ranges of the original source code that should be clang-formatted. 110 // The CursorProxyText will also be formatted. 111 std::vector<tooling::Range> FormatRanges; 112 // The source code that should stand in for the cursor when clang-formatting. 113 // e.g. after inserting a newline, a line-comment at the cursor is used to 114 // ensure that the newline is preserved. 115 std::string CursorPlaceholder; 116 }; 117 118 // After a newline: 119 // - we continue any line-comment that was split 120 // - we format the old line in addition to the cursor 121 // - we represent the cursor with a line comment to preserve the newline 122 IncrementalChanges getIncrementalChangesAfterNewline(llvm::StringRef Code, 123 unsigned Cursor) { 124 IncrementalChanges Result; 125 // Before newline, code looked like: 126 // leading^trailing 127 // After newline, code looks like: 128 // leading 129 // indentation^trailing 130 // Where indentation was added by the editor. 131 StringRef Trailing = firstLine(Code.substr(Cursor)); 132 StringRef Indentation = lastLine(Code.take_front(Cursor)); 133 if (Indentation.data() == Code.data()) { 134 vlog("Typed a newline, but we're still on the first line!"); 135 return Result; 136 } 137 StringRef Leading = 138 lastLine(Code.take_front(Indentation.data() - Code.data() - 1)); 139 StringRef NextLine = firstLine(Code.substr(Cursor + Trailing.size() + 1)); 140 141 // Strip leading whitespace on trailing line. 142 StringRef TrailingTrim = Trailing.ltrim(); 143 if (unsigned TrailWS = Trailing.size() - TrailingTrim.size()) 144 cantFail(Result.Changes.add( 145 replacement(Code, StringRef(Trailing.begin(), TrailWS), ""))); 146 147 // If we split a comment, replace indentation with a comment marker. 148 // If the editor made the new line a comment, also respect that. 149 StringRef CommentMarker = commentMarker(Leading); 150 bool NewLineIsComment = !commentMarker(Indentation).empty(); 151 if (!CommentMarker.empty() && 152 (NewLineIsComment || !commentMarker(NextLine).empty() || 153 (!TrailingTrim.empty() && !TrailingTrim.startswith("//")))) { 154 using llvm::sys::unicode::columnWidthUTF8; 155 // We indent the new comment to match the previous one. 156 StringRef PreComment = 157 Leading.take_front(CommentMarker.data() - Leading.data()); 158 std::string IndentAndComment = 159 (std::string(columnWidthUTF8(PreComment), ' ') + CommentMarker + " ") 160 .str(); 161 cantFail( 162 Result.Changes.add(replacement(Code, Indentation, IndentAndComment))); 163 } else { 164 // Remove any indentation and let clang-format re-add it. 165 // This prevents the cursor marker dragging e.g. an aligned comment with it. 166 cantFail(Result.Changes.add(replacement(Code, Indentation, ""))); 167 } 168 169 // If we put a the newline inside a {} pair, put } on its own line... 170 if (CommentMarker.empty() && Leading.endswith("{") && 171 Trailing.startswith("}")) { 172 cantFail( 173 Result.Changes.add(replacement(Code, Trailing.take_front(1), "\n}"))); 174 // ...and format it. 175 Result.FormatRanges.push_back( 176 tooling::Range(Trailing.data() - Code.data() + 1, 1)); 177 } 178 179 // Format the whole leading line. 180 Result.FormatRanges.push_back( 181 tooling::Range(Leading.data() - Code.data(), Leading.size())); 182 183 // We use a comment to represent the cursor, to preserve the newline. 184 // A trailing identifier improves parsing of e.g. for without braces. 185 // Exception: if the previous line has a trailing comment, we can't use one 186 // as the cursor (they will be aligned). But in this case we don't need to. 187 Result.CursorPlaceholder = !CommentMarker.empty() ? "ident" : "//==\nident"; 188 189 return Result; 190 } 191 192 IncrementalChanges getIncrementalChanges(llvm::StringRef Code, unsigned Cursor, 193 llvm::StringRef InsertedText) { 194 IncrementalChanges Result; 195 if (InsertedText == "\n") 196 return getIncrementalChangesAfterNewline(Code, Cursor); 197 198 Result.CursorPlaceholder = " /**/"; 199 return Result; 200 } 201 202 // Returns equivalent replacements that preserve the correspondence between 203 // OldCursor and NewCursor. If OldCursor lies in a replaced region, that 204 // replacement will be split. 205 std::vector<tooling::Replacement> 206 split(const tooling::Replacements &Replacements, unsigned OldCursor, 207 unsigned NewCursor) { 208 std::vector<tooling::Replacement> Result; 209 int LengthChange = 0; 210 for (const tooling::Replacement &R : Replacements) { 211 if (R.getOffset() + R.getLength() <= OldCursor) { // before cursor 212 Result.push_back(R); 213 LengthChange += R.getReplacementText().size() - R.getLength(); 214 } else if (R.getOffset() < OldCursor) { // overlaps cursor 215 int ReplacementSplit = NewCursor - LengthChange - R.getOffset(); 216 assert(ReplacementSplit >= 0 && 217 ReplacementSplit <= int(R.getReplacementText().size()) && 218 "NewCursor incompatible with OldCursor!"); 219 Result.push_back(tooling::Replacement( 220 R.getFilePath(), R.getOffset(), OldCursor - R.getOffset(), 221 R.getReplacementText().take_front(ReplacementSplit))); 222 Result.push_back(tooling::Replacement( 223 R.getFilePath(), OldCursor, 224 R.getLength() - (OldCursor - R.getOffset()), 225 R.getReplacementText().drop_front(ReplacementSplit))); 226 } else if (R.getOffset() >= OldCursor) { // after cursor 227 Result.push_back(R); 228 } 229 } 230 return Result; 231 } 232 233 } // namespace 234 235 // We're simulating the following sequence of changes: 236 // - apply the pre-formatting edits (see getIncrementalChanges) 237 // - insert a placeholder for the cursor 238 // - format some of the resulting code 239 // - remove the cursor placeholder again 240 // The replacements we return are produced by composing these. 241 // 242 // The text we actually pass to clang-format is slightly different from this, 243 // e.g. we have to close brackets. We ensure these differences are *after* 244 // all the regions we want to format, and discard changes in them. 245 std::vector<tooling::Replacement> 246 formatIncremental(llvm::StringRef OriginalCode, unsigned OriginalCursor, 247 llvm::StringRef InsertedText, format::FormatStyle Style) { 248 IncrementalChanges Incremental = 249 getIncrementalChanges(OriginalCode, OriginalCursor, InsertedText); 250 // Never *remove* lines in response to pressing enter! This annoys users. 251 if (InsertedText == "\n") { 252 Style.MaxEmptyLinesToKeep = 1000; 253 Style.KeepEmptyLinesAtTheStartOfBlocks = true; 254 } 255 256 // Compute the code we want to format: 257 // 1) Start with code after the pre-formatting edits. 258 std::string CodeToFormat = cantFail( 259 tooling::applyAllReplacements(OriginalCode, Incremental.Changes)); 260 unsigned Cursor = Incremental.Changes.getShiftedCodePosition(OriginalCursor); 261 // 2) Truncate code after the last interesting range. 262 unsigned FormatLimit = Cursor; 263 for (tooling::Range &R : Incremental.FormatRanges) 264 FormatLimit = std::max(FormatLimit, R.getOffset() + R.getLength()); 265 CodeToFormat.resize(FormatLimit); 266 // 3) Insert a placeholder for the cursor. 267 CodeToFormat.insert(Cursor, Incremental.CursorPlaceholder); 268 // 4) Append brackets after FormatLimit so the code is well-formed. 269 closeBrackets(CodeToFormat, Style); 270 271 // Determine the ranges to format: 272 std::vector<tooling::Range> RangesToFormat = Incremental.FormatRanges; 273 // Ranges after the cursor need to be adjusted for the placeholder. 274 for (auto &R : RangesToFormat) { 275 if (R.getOffset() > Cursor) 276 R = tooling::Range(R.getOffset() + Incremental.CursorPlaceholder.size(), 277 R.getLength()); 278 } 279 // We also format the cursor. 280 RangesToFormat.push_back( 281 tooling::Range(Cursor, Incremental.CursorPlaceholder.size())); 282 // Also update FormatLimit for the placeholder, we'll use this later. 283 FormatLimit += Incremental.CursorPlaceholder.size(); 284 285 // Run clang-format, and truncate changes at FormatLimit. 286 tooling::Replacements FormattingChanges; 287 format::FormattingAttemptStatus Status; 288 for (const tooling::Replacement &R : format::reformat( 289 Style, CodeToFormat, RangesToFormat, Filename, &Status)) { 290 if (R.getOffset() + R.getLength() <= FormatLimit) // Before limit. 291 cantFail(FormattingChanges.add(R)); 292 else if(R.getOffset() < FormatLimit) { // Overlaps limit. 293 if (R.getReplacementText().empty()) // Deletions are easy to handle. 294 cantFail(FormattingChanges.add(tooling::Replacement(Filename, 295 R.getOffset(), FormatLimit - R.getOffset(), ""))); 296 else 297 // Hopefully won't happen in practice? 298 elog("Incremental clang-format edit overlapping cursor @ {0}!\n{1}", 299 Cursor, CodeToFormat); 300 } 301 } 302 if (!Status.FormatComplete) 303 vlog("Incremental format incomplete at line {0}", Status.Line); 304 305 // Now we are ready to compose the changes relative to OriginalCode. 306 // edits -> insert placeholder -> format -> remove placeholder. 307 // We must express insert/remove as Replacements. 308 tooling::Replacements InsertCursorPlaceholder( 309 tooling::Replacement(Filename, Cursor, 0, Incremental.CursorPlaceholder)); 310 unsigned FormattedCursorStart = 311 FormattingChanges.getShiftedCodePosition(Cursor), 312 FormattedCursorEnd = FormattingChanges.getShiftedCodePosition( 313 Cursor + Incremental.CursorPlaceholder.size()); 314 tooling::Replacements RemoveCursorPlaceholder( 315 tooling::Replacement(Filename, FormattedCursorStart, 316 FormattedCursorEnd - FormattedCursorStart, "")); 317 318 // We can't simply merge() and return: tooling::Replacements will combine 319 // adjacent edits left and right of the cursor. This gives the right source 320 // code, but loses information about where the cursor is! 321 // Fortunately, none of the individual passes lose information, so: 322 // - we use merge() to compute the final Replacements 323 // - we chain getShiftedCodePosition() to compute final cursor position 324 // - we split the final Replacements at the cursor position, so that 325 // each Replacement lies either before or after the cursor. 326 tooling::Replacements Final; 327 unsigned FinalCursor = OriginalCursor; 328 #ifndef NDEBUG 329 std::string FinalCode = OriginalCode; 330 dlog("Initial code: {0}", FinalCode); 331 #endif 332 for (auto Pass : 333 std::vector<std::pair<const char *, const tooling::Replacements *>>{ 334 {"Pre-formatting changes", &Incremental.Changes}, 335 {"Insert placeholder", &InsertCursorPlaceholder}, 336 {"clang-format", &FormattingChanges}, 337 {"Remove placeholder", &RemoveCursorPlaceholder}}) { 338 Final = Final.merge(*Pass.second); 339 FinalCursor = Pass.second->getShiftedCodePosition(FinalCursor); 340 #ifndef NDEBUG 341 FinalCode = 342 cantFail(tooling::applyAllReplacements(FinalCode, *Pass.second)); 343 dlog("After {0}:\n{1}^{2}", Pass.first, 344 StringRef(FinalCode).take_front(FinalCursor), 345 StringRef(FinalCode).drop_front(FinalCursor)); 346 #endif 347 } 348 return split(Final, OriginalCursor, FinalCursor); 349 } 350 351 unsigned 352 transformCursorPosition(unsigned Offset, 353 const std::vector<tooling::Replacement> &Replacements) { 354 unsigned OriginalOffset = Offset; 355 for (const auto &R : Replacements) { 356 if (R.getOffset() + R.getLength() <= OriginalOffset) { 357 // Replacement is before cursor. 358 Offset += R.getReplacementText().size(); 359 Offset -= R.getLength(); 360 } else if (R.getOffset() < OriginalOffset) { 361 // Replacement overlaps cursor. 362 // Preserve position within replacement text, as far as possible. 363 unsigned PositionWithinReplacement = Offset - R.getOffset(); 364 if (PositionWithinReplacement > R.getReplacementText().size()) { 365 Offset += R.getReplacementText().size(); 366 Offset -= PositionWithinReplacement; 367 } 368 } else { 369 // Replacement after cursor. 370 break; // Replacements are sorted, the rest are also after the cursor. 371 } 372 } 373 return Offset; 374 } 375 376 } // namespace clangd 377 } // namespace clang 378