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, AfterInserts: true); |
36 | assert(RealOffset + Size <= Buffer.size() && "Invalid location" ); |
37 | |
38 | // Remove the dead characters. |
39 | Buffer.erase(Offset: RealOffset, NumBytes: Size); |
40 | |
41 | // Add a delta so that future changes are offset correctly. |
42 | AddReplaceDelta(OrigOffset, Change: -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(c: *posI)) { |
63 | ++posI; |
64 | ++lineSize; |
65 | } |
66 | if (posI != end() && *posI == '\n') { |
67 | Buffer.erase(Offset: curLineStartOffs, NumBytes: 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(OrigOffset: curLineStartOffs, Change: -(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, AfterInserts: InsertAfter); |
91 | Buffer.insert(Offset: RealOffset, Start: Str.begin(), End: Str.end()); |
92 | |
93 | // Add a delta so that future changes are offset correctly. |
94 | AddInsertDelta(OrigOffset, Change: 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, AfterInserts: true); |
103 | Buffer.erase(Offset: RealOffset, NumBytes: OrigLength); |
104 | Buffer.insert(Offset: RealOffset, Start: NewStr.begin(), End: NewStr.end()); |
105 | if (OrigLength != NewStr.size()) |
106 | AddReplaceDelta(OrigOffset, Change: NewStr.size() - OrigLength); |
107 | } |
108 | |