//===- RewriteBuffer.h - Buffer rewriting interface -----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/RewriteBuffer.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; raw_ostream &RewriteBuffer::write(raw_ostream &Stream) const { // Walk RewriteRope chunks efficiently using MoveToNextPiece() instead of the // character iterator. for (RopePieceBTreeIterator I = begin(), E = end(); I != E; I.MoveToNextPiece()) Stream << I.piece(); return Stream; } /// Return true if this character is non-new-line whitespace: /// ' ', '\\t', '\\f', '\\v', '\\r'. static inline bool isWhitespaceExceptNL(unsigned char c) { return c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r'; } void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size, bool removeLineIfEmpty) { // Nothing to remove, exit early. if (Size == 0) return; unsigned RealOffset = getMappedOffset(OrigOffset, true); assert(RealOffset + Size <= Buffer.size() && "Invalid location"); // Remove the dead characters. Buffer.erase(RealOffset, Size); // Add a delta so that future changes are offset correctly. AddReplaceDelta(OrigOffset, -Size); if (removeLineIfEmpty) { // Find the line that the remove occurred and if it is completely empty // remove the line as well. iterator curLineStart = begin(); unsigned curLineStartOffs = 0; iterator posI = begin(); for (unsigned i = 0; i != RealOffset; ++i) { if (*posI == '\n') { curLineStart = posI; ++curLineStart; curLineStartOffs = i + 1; } ++posI; } unsigned lineSize = 0; posI = curLineStart; while (posI != end() && isWhitespaceExceptNL(*posI)) { ++posI; ++lineSize; } if (posI != end() && *posI == '\n') { Buffer.erase(curLineStartOffs, lineSize + 1 /* + '\n'*/); // FIXME: Here, the offset of the start of the line is supposed to be // expressed in terms of the original input not the "real" rewrite // buffer. How do we compute that reliably? It might be tempting to use // curLineStartOffs + OrigOffset - RealOffset, but that assumes the // difference between the original and real offset is the same at the // removed text and at the start of the line, but that's not true if // edits were previously made earlier on the line. This bug is also // documented by a FIXME on the definition of // clang::Rewriter::RewriteOptions::RemoveLineIfEmpty. A reproducer for // the implementation below is the test RemoveLineIfEmpty in // clang/unittests/Rewrite/RewriteBufferTest.cpp. AddReplaceDelta(curLineStartOffs, -(lineSize + 1 /* + '\n'*/)); } } } void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str, bool InsertAfter) { // Nothing to insert, exit early. if (Str.empty()) return; unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); Buffer.insert(RealOffset, Str.begin(), Str.end()); // Add a delta so that future changes are offset correctly. AddInsertDelta(OrigOffset, Str.size()); } /// ReplaceText - This method replaces a range of characters in the input /// buffer with a new string. This is effectively a combined "remove+insert" /// operation. void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, StringRef NewStr) { unsigned RealOffset = getMappedOffset(OrigOffset, true); Buffer.erase(RealOffset, OrigLength); Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); if (OrigLength != NewStr.size()) AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); }