xref: /llvm-project/llvm/lib/Support/RewriteBuffer.cpp (revision 0d150db214e2aa13a825b563c7238e1243d61db1)
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