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