blob: 5f935c328113d417327f318f7029066383f646b7 [file] [log] [blame]
Chris Lattner8bd12b82007-09-15 22:21:22 +00001//===--- Rewriter.cpp - Code rewriting interface --------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file was developed by Chris Lattner and is distributed under
6// the University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines the Rewriter class, which is used for code
11// transformations.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Rewrite/Rewriter.h"
Chris Lattner8a12c272007-10-11 18:38:32 +000016#include "clang/Basic/SourceManager.h"
Chris Lattner8bd12b82007-09-15 22:21:22 +000017using namespace clang;
18
Chris Lattner7c239602007-10-13 00:11:23 +000019/// getMappedOffset - Given an offset into the original SourceBuffer that this
20/// RewriteBuffer is based on, map it into the offset space of the
21/// RewriteBuffer.
22unsigned RewriteBuffer::getMappedOffset(unsigned OrigOffset,
23 bool AfterInserts) const {
24 unsigned ResultOffset = OrigOffset;
25 unsigned DeltaIdx = 0;
26
27 // Move past any deltas that are relevant.
28 // FIXME: binary search.
29 for (; DeltaIdx != Deltas.size() &&
Chris Lattner88d0ed02007-10-13 00:17:04 +000030 Deltas[DeltaIdx].FileLoc < OrigOffset; ++DeltaIdx)
Chris Lattner7c239602007-10-13 00:11:23 +000031 ResultOffset += Deltas[DeltaIdx].Delta;
32
33 if (AfterInserts && DeltaIdx != Deltas.size() &&
34 OrigOffset == Deltas[DeltaIdx].FileLoc)
35 ResultOffset += Deltas[DeltaIdx].Delta;
36 return ResultOffset;
37}
38
39/// AddDelta - When a change is made that shifts around the text buffer, this
40/// method is used to record that info.
41void RewriteBuffer::AddDelta(unsigned OrigOffset, int Change) {
42 assert(Change != 0 && "Not changing anything");
43 unsigned DeltaIdx = 0;
44
45 // Skip over any unrelated deltas.
46 for (; DeltaIdx != Deltas.size() &&
Chris Lattner88d0ed02007-10-13 00:17:04 +000047 Deltas[DeltaIdx].FileLoc < OrigOffset; ++DeltaIdx)
Chris Lattner7c239602007-10-13 00:11:23 +000048 ;
49
50 // If there is no a delta for this offset, insert a new delta record.
51 if (DeltaIdx == Deltas.size() || OrigOffset != Deltas[DeltaIdx].FileLoc) {
52 // If this is a removal, check to see if this can be folded into
53 // a delta at the end of the deletion. For example, if we have:
54 // ABCXDEF (X inserted after C) and delete C, we want to end up with no
55 // delta because X basically replaced C.
56 if (Change < 0 && DeltaIdx != Deltas.size() &&
57 OrigOffset-Change == Deltas[DeltaIdx].FileLoc) {
58 // Adjust the start of the delta to be the start of the deleted region.
59 Deltas[DeltaIdx].FileLoc += Change;
60 Deltas[DeltaIdx].Delta += Change;
61
62 // If the delta becomes a noop, remove it.
63 if (Deltas[DeltaIdx].Delta == 0)
64 Deltas.erase(Deltas.begin()+DeltaIdx);
65 return;
66 }
67
68 // Otherwise, create an entry and return.
69 Deltas.insert(Deltas.begin()+DeltaIdx,
70 SourceDelta::get(OrigOffset, Change));
71 return;
72 }
73
74 // Otherwise, we found a delta record at this offset, adjust it.
75 Deltas[DeltaIdx].Delta += Change;
76
77 // If it is now dead, remove it.
78 if (Deltas[DeltaIdx].Delta)
79 Deltas.erase(Deltas.begin()+DeltaIdx);
80}
81
Chris Lattner8bd12b82007-09-15 22:21:22 +000082
83void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) {
Chris Lattner7c239602007-10-13 00:11:23 +000084 // Nothing to remove, exit early.
85 if (Size == 0) return;
86
87 unsigned RealOffset = getMappedOffset(OrigOffset, true);
88 assert(RealOffset+Size < Buffer.size() && "Invalid location");
89
90 // Remove the dead characters.
91 Buffer.erase(Buffer.begin()+RealOffset, Buffer.begin()+RealOffset+Size);
92
93 // Add a delta so that future changes are offset correctly.
94 AddDelta(OrigOffset, -Size);
Chris Lattner8bd12b82007-09-15 22:21:22 +000095}
96
97void RewriteBuffer::InsertText(unsigned OrigOffset,
98 const char *StrData, unsigned StrLen) {
Chris Lattner03b07102007-10-13 00:21:23 +000099 // Nothing to insert, exit early.
Chris Lattner7c239602007-10-13 00:11:23 +0000100 if (StrLen == 0) return;
Chris Lattner03b07102007-10-13 00:21:23 +0000101
102 unsigned RealOffset = getMappedOffset(OrigOffset, true);
103 assert(RealOffset <= Buffer.size() && "Invalid location");
104
105 // Remove the dead characters.
106 Buffer.insert(Buffer.begin()+RealOffset, StrData, StrData+StrLen);
107
108 // Add a delta so that future changes are offset correctly.
109 AddDelta(OrigOffset, StrLen);
Chris Lattner8bd12b82007-09-15 22:21:22 +0000110}
Chris Lattner8a12c272007-10-11 18:38:32 +0000111
Chris Lattner7c239602007-10-13 00:11:23 +0000112/// ReplaceText - This method replaces a range of characters in the input
113/// buffer with a new string. This is effectively a combined "remove/insert"
114/// operation.
115void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
116 const char *NewStr, unsigned NewLength) {
Chris Lattner57c337d2007-10-13 00:46:29 +0000117 unsigned RealOffset = getMappedOffset(OrigOffset);
118 assert(RealOffset+OrigLength <= Buffer.size() && "Invalid location");
119
120 // Overwrite the common piece.
121 memcpy(&Buffer[RealOffset], NewStr, std::min(OrigLength, NewLength));
Chris Lattner7c239602007-10-13 00:11:23 +0000122
Chris Lattner57c337d2007-10-13 00:46:29 +0000123 // If replacing without shifting around, just overwrite the text.
124 if (OrigLength == NewLength)
Chris Lattner7c239602007-10-13 00:11:23 +0000125 return;
Chris Lattner57c337d2007-10-13 00:46:29 +0000126
127 // If inserting more than existed before, this is like an insertion.
128 if (NewLength > OrigLength) {
129 Buffer.insert(Buffer.begin()+RealOffset+OrigLength,
130 NewStr+OrigLength, NewStr+NewLength);
131 } else {
132 // If insertion less than existed before, this is like a removal.
133 Buffer.erase(Buffer.begin()+RealOffset+NewLength,
134 Buffer.begin()+RealOffset+OrigLength);
Chris Lattner7c239602007-10-13 00:11:23 +0000135 }
Chris Lattner57c337d2007-10-13 00:46:29 +0000136 AddDelta(OrigOffset, NewLength-OrigLength);
Chris Lattner7c239602007-10-13 00:11:23 +0000137}
Chris Lattner8a12c272007-10-11 18:38:32 +0000138
139
140//===----------------------------------------------------------------------===//
141// Rewriter class
142//===----------------------------------------------------------------------===//
143
Chris Lattner7c239602007-10-13 00:11:23 +0000144unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
145 unsigned &FileID) const {
146 std::pair<unsigned,unsigned> V = SourceMgr.getDecomposedFileLoc(Loc);
147 FileID = V.first;
148 return V.second;
149}
150
151
Chris Lattner8a12c272007-10-11 18:38:32 +0000152/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
153///
154RewriteBuffer &Rewriter::getEditBuffer(unsigned FileID) {
155 std::map<unsigned, RewriteBuffer>::iterator I =
156 RewriteBuffers.lower_bound(FileID);
157 if (I != RewriteBuffers.end() && I->first == FileID)
158 return I->second;
159 I = RewriteBuffers.insert(I, std::make_pair(FileID, RewriteBuffer()));
160
161 std::pair<const char*, const char*> MB = SourceMgr.getBufferData(FileID);
162 I->second.Initialize(MB.first, MB.second);
163
164 return I->second;
165}
166
167
168void Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
169 const char *NewStr, unsigned NewLength) {
170 assert(isRewritable(Start) && "Not a rewritable location!");
Chris Lattner7c239602007-10-13 00:11:23 +0000171 unsigned StartFileID;
172 unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
Chris Lattner8a12c272007-10-11 18:38:32 +0000173
Chris Lattner7c239602007-10-13 00:11:23 +0000174 getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength,
175 NewStr, NewLength);
Chris Lattner8a12c272007-10-11 18:38:32 +0000176}