From: Chris Lattner Date: Sat, 13 Oct 2007 00:11:23 +0000 (+0000) Subject: another step forward in rewriter stuff. This still has X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7c239606f3a7a685653a0a7e64459c3f43522666;p=clang another step forward in rewriter stuff. This still has some incredibly subtle details that I'm working on getting right. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@42940 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Driver/RewriteTest.cpp b/Driver/RewriteTest.cpp index 9e32c8c211..a8d658e237 100644 --- a/Driver/RewriteTest.cpp +++ b/Driver/RewriteTest.cpp @@ -85,8 +85,9 @@ RewriteTest::~RewriteTest() { // we are done. if (const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(MainFileID)) { - RewriteBuf = 0; - printf("Changed\n"); + printf("Changed:\n"); + std::string S(RewriteBuf->begin(), RewriteBuf->end()); + printf("%s\n", S.c_str()); } else { printf("No changes\n"); } diff --git a/Rewrite/Rewriter.cpp b/Rewrite/Rewriter.cpp index 19e901242d..01d6842c35 100644 --- a/Rewrite/Rewriter.cpp +++ b/Rewrite/Rewriter.cpp @@ -16,22 +16,121 @@ #include "clang/Basic/SourceManager.h" using namespace clang; +/// getMappedOffset - Given an offset into the original SourceBuffer that this +/// RewriteBuffer is based on, map it into the offset space of the +/// RewriteBuffer. +unsigned RewriteBuffer::getMappedOffset(unsigned OrigOffset, + bool AfterInserts) const { + unsigned ResultOffset = OrigOffset; + unsigned DeltaIdx = 0; + + // Move past any deltas that are relevant. + // FIXME: binary search. + for (; DeltaIdx != Deltas.size() && + OrigOffset < Deltas[DeltaIdx].FileLoc; ++DeltaIdx) + ResultOffset += Deltas[DeltaIdx].Delta; + + if (AfterInserts && DeltaIdx != Deltas.size() && + OrigOffset == Deltas[DeltaIdx].FileLoc) + ResultOffset += Deltas[DeltaIdx].Delta; + return ResultOffset; +} + +/// AddDelta - When a change is made that shifts around the text buffer, this +/// method is used to record that info. +void RewriteBuffer::AddDelta(unsigned OrigOffset, int Change) { + assert(Change != 0 && "Not changing anything"); + unsigned DeltaIdx = 0; + + // Skip over any unrelated deltas. + for (; DeltaIdx != Deltas.size() && + OrigOffset < Deltas[DeltaIdx].FileLoc; ++DeltaIdx) + ; + + // If there is no a delta for this offset, insert a new delta record. + if (DeltaIdx == Deltas.size() || OrigOffset != Deltas[DeltaIdx].FileLoc) { + // If this is a removal, check to see if this can be folded into + // a delta at the end of the deletion. For example, if we have: + // ABCXDEF (X inserted after C) and delete C, we want to end up with no + // delta because X basically replaced C. + if (Change < 0 && DeltaIdx != Deltas.size() && + OrigOffset-Change == Deltas[DeltaIdx].FileLoc) { + // Adjust the start of the delta to be the start of the deleted region. + Deltas[DeltaIdx].FileLoc += Change; + Deltas[DeltaIdx].Delta += Change; + + // If the delta becomes a noop, remove it. + if (Deltas[DeltaIdx].Delta == 0) + Deltas.erase(Deltas.begin()+DeltaIdx); + return; + } + + // Otherwise, create an entry and return. + Deltas.insert(Deltas.begin()+DeltaIdx, + SourceDelta::get(OrigOffset, Change)); + return; + } + + // Otherwise, we found a delta record at this offset, adjust it. + Deltas[DeltaIdx].Delta += Change; + + // If it is now dead, remove it. + if (Deltas[DeltaIdx].Delta) + Deltas.erase(Deltas.begin()+DeltaIdx); +} + void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) { - // FIXME: + // Nothing to remove, exit early. + if (Size == 0) return; + + unsigned RealOffset = getMappedOffset(OrigOffset, true); + assert(RealOffset+Size < Buffer.size() && "Invalid location"); + + // Remove the dead characters. + Buffer.erase(Buffer.begin()+RealOffset, Buffer.begin()+RealOffset+Size); + + // Add a delta so that future changes are offset correctly. + AddDelta(OrigOffset, -Size); } void RewriteBuffer::InsertText(unsigned OrigOffset, const char *StrData, unsigned StrLen) { + if (StrLen == 0) return; // FIXME: } +/// ReplaceText - This method replaces a range of characters in the input +/// buffer with a new string. This is effectively a combined "remove/insert" +/// operation. +void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, + const char *NewStr, unsigned NewLength) { + RemoveText(OrigOffset, OrigLength); + return; + + unsigned MappedOffs = getMappedOffset(OrigOffset); + // TODO: FIXME location. + assert(OrigOffset+OrigLength <= Buffer.size() && "Invalid location"); + if (OrigLength == NewLength) { + // If replacing without shifting around, just overwrite the text. + memcpy(&Buffer[OrigOffset], NewStr, NewLength); + return; + } +} //===----------------------------------------------------------------------===// // Rewriter class //===----------------------------------------------------------------------===// +unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, + unsigned &FileID) const { + std::pair V = SourceMgr.getDecomposedFileLoc(Loc); + FileID = V.first; + return V.second; +} + + /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. /// RewriteBuffer &Rewriter::getEditBuffer(unsigned FileID) { @@ -51,5 +150,9 @@ RewriteBuffer &Rewriter::getEditBuffer(unsigned FileID) { void Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, const char *NewStr, unsigned NewLength) { assert(isRewritable(Start) && "Not a rewritable location!"); + unsigned StartFileID; + unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); + getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, + NewStr, NewLength); } diff --git a/include/clang/Rewrite/Rewriter.h b/include/clang/Rewrite/Rewriter.h index 3cae99ddc6..24c7f1c8f3 100644 --- a/include/clang/Rewrite/Rewriter.h +++ b/include/clang/Rewrite/Rewriter.h @@ -29,6 +29,13 @@ namespace clang { struct SourceDelta { unsigned FileLoc; int Delta; + + static SourceDelta get(unsigned Loc, int D) { + SourceDelta Delta; + Delta.FileLoc = Loc; + Delta.Delta = D; + return Delta; + } }; @@ -51,6 +58,10 @@ class RewriteBuffer { public: + typedef std::vector::const_iterator iterator; + iterator begin() const { return Buffer.begin(); } + iterator end() const { return Buffer.end(); } + private: // Methods only usable by Rewriter. @@ -60,6 +71,18 @@ private: // Methods only usable by Rewriter. Buffer.assign(BufStart, BufEnd); } + /// getMappedOffset - Given an offset into the original SourceBuffer that this + /// RewriteBuffer is based on, map it into the offset space of the + /// RewriteBuffer. If AfterInserts is true and if the OrigOffset indicates a + /// position where text is inserted, the location returned will be after any + /// inserted text at the position. + unsigned getMappedOffset(unsigned OrigOffset, bool AfterInserts = false)const; + + + /// AddDelta - When a change is made that shifts around the text buffer, this + /// method is used to record that info. + void AddDelta(unsigned OrigOffset, int Change); + /// RemoveText - Remove the specified text. void RemoveText(unsigned OrigOffset, unsigned Size); @@ -70,6 +93,13 @@ private: // Methods only usable by Rewriter. /// after the atomic point: i.e. whether the atomic point is moved to after /// the inserted text or not. void InsertText(unsigned OrigOffset, const char *StrData, unsigned StrLen); + + /// ReplaceText - This method replaces a range of characters in the input + /// buffer with a new string. This is effectively a combined "remove/insert" + /// operation. + void ReplaceText(unsigned OrigOffset, unsigned OrigLength, + const char *NewStr, unsigned NewLength); + }; @@ -116,6 +146,8 @@ public: } private: RewriteBuffer &getEditBuffer(unsigned FileID); + unsigned getLocationOffsetAndFileID(SourceLocation Loc, + unsigned &FileID) const; }; } // end namespace clang