1 //===- RewriteBuffer.h - Buffer rewriting interface -----------------------===// 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 9 #include "llvm/ADT/RewriteBuffer.h" 10 #include "llvm/Support/raw_ostream.h" 11 12 using namespace llvm; 13 14 raw_ostream &RewriteBuffer::write(raw_ostream &Stream) const { 15 // Walk RewriteRope chunks efficiently using MoveToNextPiece() instead of the 16 // character iterator. 17 for (RopePieceBTreeIterator I = begin(), E = end(); I != E; 18 I.MoveToNextPiece()) 19 Stream << I.piece(); 20 return Stream; 21 } 22 23 /// Return true if this character is non-new-line whitespace: 24 /// ' ', '\\t', '\\f', '\\v', '\\r'. 25 static inline bool isWhitespaceExceptNL(unsigned char c) { 26 return c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r'; 27 } 28 29 void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size, 30 bool removeLineIfEmpty) { 31 // Nothing to remove, exit early. 32 if (Size == 0) 33 return; 34 35 unsigned RealOffset = getMappedOffset(OrigOffset, true); 36 assert(RealOffset + Size <= Buffer.size() && "Invalid location"); 37 38 // Remove the dead characters. 39 Buffer.erase(RealOffset, Size); 40 41 // Add a delta so that future changes are offset correctly. 42 AddReplaceDelta(OrigOffset, -Size); 43 44 if (removeLineIfEmpty) { 45 // Find the line that the remove occurred and if it is completely empty 46 // remove the line as well. 47 48 iterator curLineStart = begin(); 49 unsigned curLineStartOffs = 0; 50 iterator posI = begin(); 51 for (unsigned i = 0; i != RealOffset; ++i) { 52 if (*posI == '\n') { 53 curLineStart = posI; 54 ++curLineStart; 55 curLineStartOffs = i + 1; 56 } 57 ++posI; 58 } 59 60 unsigned lineSize = 0; 61 posI = curLineStart; 62 while (posI != end() && isWhitespaceExceptNL(*posI)) { 63 ++posI; 64 ++lineSize; 65 } 66 if (posI != end() && *posI == '\n') { 67 Buffer.erase(curLineStartOffs, lineSize + 1 /* + '\n'*/); 68 // FIXME: Here, the offset of the start of the line is supposed to be 69 // expressed in terms of the original input not the "real" rewrite 70 // buffer. How do we compute that reliably? It might be tempting to use 71 // curLineStartOffs + OrigOffset - RealOffset, but that assumes the 72 // difference between the original and real offset is the same at the 73 // removed text and at the start of the line, but that's not true if 74 // edits were previously made earlier on the line. This bug is also 75 // documented by a FIXME on the definition of 76 // clang::Rewriter::RewriteOptions::RemoveLineIfEmpty. A reproducer for 77 // the implementation below is the test RemoveLineIfEmpty in 78 // clang/unittests/Rewrite/RewriteBufferTest.cpp. 79 AddReplaceDelta(curLineStartOffs, -(lineSize + 1 /* + '\n'*/)); 80 } 81 } 82 } 83 84 void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str, 85 bool InsertAfter) { 86 // Nothing to insert, exit early. 87 if (Str.empty()) 88 return; 89 90 unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); 91 Buffer.insert(RealOffset, Str.begin(), Str.end()); 92 93 // Add a delta so that future changes are offset correctly. 94 AddInsertDelta(OrigOffset, Str.size()); 95 } 96 97 /// ReplaceText - This method replaces a range of characters in the input 98 /// buffer with a new string. This is effectively a combined "remove+insert" 99 /// operation. 100 void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, 101 StringRef NewStr) { 102 unsigned RealOffset = getMappedOffset(OrigOffset, true); 103 Buffer.erase(RealOffset, OrigLength); 104 Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); 105 if (OrigLength != NewStr.size()) 106 AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); 107 } 108