blob: 41c5c6ff3ff29ed163147475e99befa4eb8d6766 [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 Lattner03b07102007-10-13 00:21:23 +0000117 InsertText(OrigOffset, NewStr, NewLength);
Chris Lattner7c239602007-10-13 00:11:23 +0000118 return;
119
120 unsigned MappedOffs = getMappedOffset(OrigOffset);
121 // TODO: FIXME location.
122 assert(OrigOffset+OrigLength <= Buffer.size() && "Invalid location");
123 if (OrigLength == NewLength) {
124 // If replacing without shifting around, just overwrite the text.
125 memcpy(&Buffer[OrigOffset], NewStr, NewLength);
126 return;
127 }
128}
Chris Lattner8a12c272007-10-11 18:38:32 +0000129
130
131//===----------------------------------------------------------------------===//
132// Rewriter class
133//===----------------------------------------------------------------------===//
134
Chris Lattner7c239602007-10-13 00:11:23 +0000135unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
136 unsigned &FileID) const {
137 std::pair<unsigned,unsigned> V = SourceMgr.getDecomposedFileLoc(Loc);
138 FileID = V.first;
139 return V.second;
140}
141
142
Chris Lattner8a12c272007-10-11 18:38:32 +0000143/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
144///
145RewriteBuffer &Rewriter::getEditBuffer(unsigned FileID) {
146 std::map<unsigned, RewriteBuffer>::iterator I =
147 RewriteBuffers.lower_bound(FileID);
148 if (I != RewriteBuffers.end() && I->first == FileID)
149 return I->second;
150 I = RewriteBuffers.insert(I, std::make_pair(FileID, RewriteBuffer()));
151
152 std::pair<const char*, const char*> MB = SourceMgr.getBufferData(FileID);
153 I->second.Initialize(MB.first, MB.second);
154
155 return I->second;
156}
157
158
159void Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
160 const char *NewStr, unsigned NewLength) {
161 assert(isRewritable(Start) && "Not a rewritable location!");
Chris Lattner7c239602007-10-13 00:11:23 +0000162 unsigned StartFileID;
163 unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
Chris Lattner8a12c272007-10-11 18:38:32 +0000164
Chris Lattner7c239602007-10-13 00:11:23 +0000165 getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength,
166 NewStr, NewLength);
Chris Lattner8a12c272007-10-11 18:38:32 +0000167}