| //===- 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); |
| } |