static FileContent getFileContentFromSnap(ImmutableTextSnapshotRef Snap, bool IsPrimary, StringRef FilePath) { auto Buf = llvm::MemoryBuffer::getMemBufferCopy( Snap->getBuffer()->getText(), FilePath); return FileContent(Snap, FilePath, std::move(Buf), IsPrimary, Snap->getStamp()); }
/// Tries to remap the location from a previous snapshot to the latest one. static llvm::Optional<std::pair<unsigned, unsigned>> tryRemappingLocToLatestSnapshot(SwiftLangSupport &Lang, std::pair<unsigned, unsigned> Range, StringRef Filename, ArrayRef<ImmutableTextSnapshotRef> PreviousASTSnaps) { ImmutableTextSnapshotRef LatestSnap; if (auto EditorDoc = Lang.getEditorDocuments().findByPath(Filename)) LatestSnap = EditorDoc->getLatestSnapshot(); if (!LatestSnap) return Range; for (auto &PrevSnap : PreviousASTSnaps) { if (PrevSnap->isFromSameBuffer(LatestSnap)) { if (PrevSnap->getStamp() == LatestSnap->getStamp()) return Range; auto OptBegin = mapOffsetToNewerSnapshot(Range.first, PrevSnap, LatestSnap); if (!OptBegin.hasValue()) return None; auto OptEnd = mapOffsetToNewerSnapshot(Range.first+Range.second, PrevSnap, LatestSnap); if (!OptEnd.hasValue()) return None; return std::make_pair(*OptBegin, *OptEnd-*OptBegin); } } return Range; }
bool canUseASTWithSnapshots( ArrayRef<ImmutableTextSnapshotRef> Snapshots) override { if (!TryExistingAST) { LOG_INFO_FUNC(High, "will resolve using up-to-date AST"); return false; } // If there is an existing AST and the offset can be mapped back to the // document snapshot that was used to create it, then use that AST. // The downside is that we may return stale information, but we get the // benefit of increased responsiveness, since the request will not be // blocked waiting on the AST to be fully typechecked. ImmutableTextSnapshotRef InputSnap; if (auto EditorDoc = Lang.getEditorDocuments().findByPath(InputFile)) InputSnap = EditorDoc->getLatestSnapshot(); if (!InputSnap) return false; auto mappedBackOffset = [&]()->llvm::Optional<unsigned> { for (auto &Snap : Snapshots) { if (Snap->isFromSameBuffer(InputSnap)) { if (Snap->getStamp() == InputSnap->getStamp()) return Offset; auto OptOffset = mapOffsetToOlderSnapshot(Offset, InputSnap, Snap); if (!OptOffset.hasValue()) return None; // Check that the new and old offset still point to the same token. StringRef NewTok = getSourceToken(Offset, InputSnap); if (NewTok.empty()) return None; if (NewTok == getSourceToken(OptOffset.getValue(), Snap)) return OptOffset; return None; } } return None; }; auto OldOffsetOpt = mappedBackOffset(); if (OldOffsetOpt.hasValue()) { Offset = *OldOffsetOpt; PreviousASTSnaps.append(Snapshots.begin(), Snapshots.end()); LOG_INFO_FUNC(High, "will try existing AST"); return true; } LOG_INFO_FUNC(High, "will resolve using up-to-date AST"); return false; }
static llvm::Optional<unsigned> mapOffsetToOlderSnapshot(unsigned Offset, ImmutableTextSnapshotRef NewSnap, ImmutableTextSnapshotRef OldSnap) { SmallVector<ReplaceImmutableTextUpdateRef, 16> Updates; OldSnap->foreachReplaceUntil(NewSnap, [&](ReplaceImmutableTextUpdateRef Upd)->bool { Updates.push_back(Upd); return true; }); // Walk the updates backwards and "undo" them. for (auto I = Updates.rbegin(), E = Updates.rend(); I != E; ++I) { auto Upd = *I; if (Upd->getByteOffset() <= Offset && Offset < Upd->getByteOffset() + Upd->getText().size()) return None; // Offset is part of newly inserted text. if (Upd->getByteOffset() <= Offset) { Offset += Upd->getLength(); // "bring back" what was removed. Offset -= Upd->getText().size(); // "remove" what was added. } } return Offset; }
static StringRef getSourceToken(unsigned Offset, ImmutableTextSnapshotRef Snap) { auto MemBuf = Snap->getBuffer()->getInternalBuffer(); SourceManager SM; auto MemBufRef = llvm::MemoryBuffer::getMemBuffer(MemBuf->getBuffer(), MemBuf->getBufferIdentifier()); auto BufId = SM.addNewSourceBuffer(std::move(MemBufRef)); SourceLoc Loc = SM.getLocForOffset(BufId, Offset); // Use fake language options; language options only affect validity // and the exact token produced. LangOptions FakeLangOpts; Lexer L(FakeLangOpts, SM, BufId, nullptr, /*InSILMode=*/ false, CommentRetentionMode::ReturnAsTokens); return L.getTokenAt(Loc).getText(); }
static llvm::Optional<unsigned> mapOffsetToNewerSnapshot(unsigned Offset, ImmutableTextSnapshotRef OldSnap, ImmutableTextSnapshotRef NewSnap) { bool Completed = OldSnap->foreachReplaceUntil(NewSnap, [&](ReplaceImmutableTextUpdateRef Upd)->bool { if (Upd->getByteOffset() <= Offset && Offset < Upd->getByteOffset() + Upd->getLength()) return false; // Offset is part of removed text. if (Upd->getByteOffset() <= Offset) { Offset += Upd->getText().size(); Offset -= Upd->getLength(); } return true; }); if (Completed) return Offset; return None; }