Replace the -code-completion-dump option with 

  -code-completion-at=filename:line:column

which performs code completion at the specified location by truncating
the file at that position and enabling code completion. This approach
makes it possible to run multiple tests from a single test file, and
gives a more natural command-line interface.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82571 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp
index d1c4709..962cb4c4 100644
--- a/lib/Basic/SourceManager.cpp
+++ b/lib/Basic/SourceManager.cpp
@@ -41,9 +41,10 @@
 /// getSize - Returns the size of the content encapsulated by this ContentCache.
 ///  This can be the size of the source file or the size of an arbitrary
 ///  scratch buffer.  If the ContentCache encapsulates a source file, that
-///  file is not lazily brought in from disk to satisfy this query.
+///  file is not lazily brought in from disk to satisfy this query unless it
+///  needs to be truncated due to a truncateAt() call.
 unsigned ContentCache::getSize() const {
-  return Entry ? Entry->getSize() : Buffer->getBufferSize();
+  return Buffer ? Buffer->getBufferSize() : Entry->getSize();
 }
 
 const llvm::MemoryBuffer *ContentCache::getBuffer() const {
@@ -52,10 +53,54 @@
     // FIXME: Should we support a way to not have to do this check over
     //   and over if we cannot open the file?
     Buffer = MemoryBuffer::getFile(Entry->getName(), 0, Entry->getSize());
+    if (isTruncated())
+      const_cast<ContentCache *>(this)->truncateAt(TruncateAtLine, 
+                                                   TruncateAtColumn);
   }
   return Buffer;
 }
 
+void ContentCache::truncateAt(unsigned Line, unsigned Column) {
+  TruncateAtLine = Line;
+  TruncateAtColumn = Column;
+  
+  if (!isTruncated() || !Buffer)
+    return;
+  
+  // Find the byte position of the truncation point.
+  const char *Position = Buffer->getBufferStart();
+  for (unsigned Line = 1; Line < TruncateAtLine; ++Line) {
+    for (; *Position; ++Position) {
+      if (*Position != '\r' && *Position != '\n')
+        continue;
+      
+      // Eat \r\n or \n\r as a single line.
+      if ((Position[1] == '\r' || Position[1] == '\n') &&
+          Position[0] != Position[1])
+        ++Position;
+      ++Position;
+      break;
+    }
+  }
+  
+  for (unsigned Column = 1; Column < TruncateAtColumn; ++Column, ++Position) {
+    if (!*Position)
+      break;
+    
+    if (*Position == '\t')
+      Column += 7;
+  }
+  
+  // Truncate the buffer.
+  if (Position != Buffer->getBufferEnd()) {
+    MemoryBuffer *TruncatedBuffer 
+      = MemoryBuffer::getMemBufferCopy(Buffer->getBufferStart(), Position, 
+                                       Buffer->getBufferIdentifier());
+    delete Buffer;
+    Buffer = TruncatedBuffer;
+  }
+}
+
 unsigned LineTableInfo::getLineTableFilenameID(const char *Ptr, unsigned Len) {
   // Look up the filename in the string table, returning the pre-existing value
   // if it exists.
@@ -287,6 +332,16 @@
   EntryAlign = std::max(8U, EntryAlign);
   Entry = ContentCacheAlloc.Allocate<ContentCache>(1, EntryAlign);
   new (Entry) ContentCache(FileEnt);
+  
+  if (FileEnt == TruncateFile) {
+    // If we had queued up a file truncation request, perform the truncation
+    // now.
+    Entry->truncateAt(TruncateAtLine, TruncateAtColumn);
+    TruncateFile = 0;
+    TruncateAtLine = 0;
+    TruncateAtColumn = 0;
+  }
+  
   return Entry;
 }
 
@@ -1058,6 +1113,28 @@
   }
 }
 
+void SourceManager::truncateFileAt(const FileEntry *Entry, unsigned Line, 
+                                   unsigned Column) {
+  llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*>::iterator FI
+     = FileInfos.find(Entry);
+  if (FI != FileInfos.end()) {
+    FI->second->truncateAt(Line, Column);
+    return;
+  }
+  
+  // We cannot perform the truncation until we actually see the file, so
+  // save the truncation information.
+  assert(TruncateFile == 0 && "Can't queue up multiple file truncations!");
+  TruncateFile = Entry;
+  TruncateAtLine = Line;
+  TruncateAtColumn = Column;
+}
+
+/// \brief Determine whether this file was truncated.
+bool SourceManager::isTruncatedFile(FileID FID) const {
+  return getSLocEntry(FID).getFile().getContentCache()->isTruncated();
+}
+
 /// PrintStats - Print statistics to stderr.
 ///
 void SourceManager::PrintStats() const {