blob: 94d30e9f83f56a6fd8dc941b874f5d947c7402cc [file] [log] [blame]
//===--- SwiftEditor.cpp --------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "SwiftASTManager.h"
#include "SwiftEditorDiagConsumer.h"
#include "SwiftLangSupport.h"
#include "SourceKit/Core/Context.h"
#include "SourceKit/Core/NotificationCenter.h"
#include "SourceKit/Support/ImmutableTextBuffer.h"
#include "SourceKit/Support/Logging.h"
#include "SourceKit/Support/Tracing.h"
#include "SourceKit/Support/UIdent.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/SourceEntityWalker.h"
#include "swift/AST/DiagnosticsClangImporter.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Demangling/ManglingUtils.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/CodeCompletion.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IDE/Formatting.h"
#include "swift/IDE/SyntaxModel.h"
#include "swift/Subsystems.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mutex.h"
using namespace SourceKit;
using namespace swift;
using namespace ide;
void EditorDiagConsumer::handleDiagnostic(
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) {
if (Kind == DiagnosticKind::Error) {
HadAnyError = true;
}
// Filter out lexer errors for placeholders.
if (Info.ID == diag::lex_editor_placeholder.ID)
return;
if (Loc.isInvalid()) {
if (Kind == DiagnosticKind::Error)
HadInvalidLocError = true;
clearLastDiag();
return;
}
bool IsNote = (Kind == DiagnosticKind::Note);
if (IsNote && !haveLastDiag())
// Is this possible?
return;
DiagnosticEntryInfo SKInfo;
// Actually substitute the diagnostic arguments into the diagnostic text.
llvm::SmallString<256> Text;
{
llvm::raw_svector_ostream Out(Text);
DiagnosticEngine::formatDiagnosticText(Out, FormatString, FormatArgs);
}
SKInfo.Description = Text.str();
unsigned BufferID = SM.findBufferContainingLoc(Loc);
if (!isInputBufferID(BufferID)) {
if (Info.ID == diag::error_from_clang.ID ||
Info.ID == diag::warning_from_clang.ID ||
Info.ID == diag::note_from_clang.ID) {
// Handle it as other diagnostics.
} else {
if (!IsNote) {
LOG_WARN_FUNC("got swift diagnostic not pointing at input file, "
"buffer name: " << SM.getIdentifierForBuffer(BufferID));
return;
}
// FIXME: This is a note pointing to a synthesized declaration buffer for
// a declaration coming from a module.
// We should include the Decl* in the DiagnosticInfo and have a way for
// Xcode to handle this "points-at-a-decl-from-module" location.
//
// For now instead of ignoring it, pick up the declaration name from the
// buffer identifier and append it to the diagnostic message.
auto &LastDiag = getLastDiag();
SKInfo.Description += " (";
SKInfo.Description += SM.getIdentifierForBuffer(BufferID);
SKInfo.Description += ")";
SKInfo.Offset = LastDiag.Offset;
SKInfo.Line = LastDiag.Line;
SKInfo.Column = LastDiag.Column;
SKInfo.Filename = LastDiag.Filename;
LastDiag.Notes.push_back(std::move(SKInfo));
return;
}
}
SKInfo.Offset = SM.getLocOffsetInBuffer(Loc, BufferID);
std::tie(SKInfo.Line, SKInfo.Column) = SM.getLineAndColumn(Loc, BufferID);
SKInfo.Filename = SM.getIdentifierForBuffer(BufferID);
for (auto R : Info.Ranges) {
if (R.isInvalid() || SM.findBufferContainingLoc(R.getStart()) != BufferID)
continue;
unsigned Offset = SM.getLocOffsetInBuffer(R.getStart(), BufferID);
unsigned Length = R.getByteLength();
SKInfo.Ranges.push_back({ Offset, Length });
}
for (auto F : Info.FixIts) {
if (F.getRange().isInvalid() ||
SM.findBufferContainingLoc(F.getRange().getStart()) != BufferID)
continue;
unsigned Offset = SM.getLocOffsetInBuffer(F.getRange().getStart(),
BufferID);
unsigned Length = F.getRange().getByteLength();
SKInfo.Fixits.push_back({ Offset, Length, F.getText() });
}
if (IsNote) {
getLastDiag().Notes.push_back(std::move(SKInfo));
return;
}
DiagnosticsTy &Diagnostics = BufferDiagnostics[BufferID];
switch (Kind) {
case DiagnosticKind::Error:
SKInfo.Severity = DiagnosticSeverityKind::Error;
break;
case DiagnosticKind::Warning:
SKInfo.Severity = DiagnosticSeverityKind::Warning;
break;
case DiagnosticKind::Note:
llvm_unreachable("already covered");
}
if (Diagnostics.empty() || Diagnostics.back().Offset <= SKInfo.Offset) {
Diagnostics.push_back(std::move(SKInfo));
LastDiagBufferID = BufferID;
LastDiagIndex = Diagnostics.size() - 1;
return;
}
// Keep the diagnostics array in source order.
auto Pos = std::lower_bound(Diagnostics.begin(), Diagnostics.end(), SKInfo.Offset,
[&](const DiagnosticEntryInfo &LHS, unsigned Offset) -> bool {
return LHS.Offset < Offset;
});
LastDiagBufferID = BufferID;
LastDiagIndex = Pos - Diagnostics.begin();
Diagnostics.insert(Pos, std::move(SKInfo));
}
SwiftEditorDocumentRef
SwiftEditorDocumentFileMap::getByUnresolvedName(StringRef FilePath) {
SwiftEditorDocumentRef EditorDoc;
Queue.dispatchSync([&]{
auto It = Docs.find(FilePath);
if (It != Docs.end())
EditorDoc = It->second.DocRef;
});
return EditorDoc;
}
SwiftEditorDocumentRef
SwiftEditorDocumentFileMap::findByPath(StringRef FilePath) {
SwiftEditorDocumentRef EditorDoc;
std::string ResolvedPath = SwiftLangSupport::resolvePathSymlinks(FilePath);
Queue.dispatchSync([&]{
for (auto &Entry : Docs) {
if (Entry.getKey() == FilePath ||
Entry.getValue().ResolvedPath == ResolvedPath) {
EditorDoc = Entry.getValue().DocRef;
break;
}
}
});
return EditorDoc;
}
bool SwiftEditorDocumentFileMap::getOrUpdate(
StringRef FilePath, SwiftLangSupport &LangSupport,
SwiftEditorDocumentRef &EditorDoc) {
bool found = false;
std::string ResolvedPath = SwiftLangSupport::resolvePathSymlinks(FilePath);
Queue.dispatchBarrierSync([&]{
DocInfo &Doc = Docs[FilePath];
if (!Doc.DocRef) {
Doc.DocRef = EditorDoc;
Doc.ResolvedPath = ResolvedPath;
} else {
EditorDoc = Doc.DocRef;
found = true;
}
});
return found;
}
SwiftEditorDocumentRef SwiftEditorDocumentFileMap::remove(StringRef FilePath) {
SwiftEditorDocumentRef Removed;
Queue.dispatchBarrierSync([&]{
auto I = Docs.find(FilePath);
if (I != Docs.end()) {
Removed = I->second.DocRef;
Docs.erase(I);
}
});
return Removed;
}
namespace {
/// Merges two overlapping ranges and splits the first range into two
/// ranges before and after the overlapping range.
void mergeSplitRanges(unsigned Off1, unsigned Len1, unsigned Off2, unsigned Len2,
std::function<void(unsigned BeforeOff, unsigned BeforeLen,
unsigned AfterOff,
unsigned AfterLen)> applier) {
unsigned End1 = Off1 + Len1;
unsigned End2 = Off2 + Len2;
if (End1 > Off2) {
// Overlapping. Split into before and after ranges.
unsigned BeforeOff = Off1;
unsigned BeforeLen = Off2 > Off1 ? Off2 - Off1 : 0;
unsigned AfterOff = End2;
unsigned AfterLen = End1 > End2 ? End1 - End2 : 0;
applier(BeforeOff, BeforeLen, AfterOff, AfterLen);
}
else {
// Not overlapping.
applier(Off1, Len1, 0, 0);
}
}
struct SwiftSyntaxToken {
unsigned Offset;
unsigned Length:24;
SyntaxNodeKind Kind:8;
static SwiftSyntaxToken createInvalid() {
return {0, 0, SyntaxNodeKind::AttributeBuiltin};
}
SwiftSyntaxToken(unsigned Offset, unsigned Length, SyntaxNodeKind Kind)
: Offset(Offset), Length(Length), Kind(Kind) {}
unsigned endOffset() const { return Offset + Length; }
bool isInvalid() const { return Length == 0; }
bool operator==(const SwiftSyntaxToken &Other) const {
return Offset == Other.Offset && Length == Other.Length &&
Kind == Other.Kind;
}
bool operator!=(const SwiftSyntaxToken &Other) const {
return Offset != Other.Offset || Length != Other.Length ||
Kind != Other.Kind;
}
};
struct SwiftEditorCharRange {
unsigned Offset;
unsigned EndOffset;
SwiftEditorCharRange(unsigned Offset, unsigned EndOffset) :
Offset(Offset), EndOffset(EndOffset) {}
SwiftEditorCharRange(SwiftSyntaxToken Token) :
Offset(Token.Offset), EndOffset(Token.endOffset()) {}
size_t length() const { return EndOffset - Offset; }
bool isEmpty() const { return Offset == EndOffset; }
bool intersects(const SwiftSyntaxToken &Token) const {
return this->Offset < (Token.endOffset()) && this->EndOffset > Token.Offset;
}
void extendToInclude(const SwiftEditorCharRange &Range) {
if (Range.Offset < Offset)
Offset = Range.Offset;
if (Range.EndOffset > EndOffset)
EndOffset = Range.EndOffset;
}
void extendToInclude(unsigned OtherOffset) {
extendToInclude({OtherOffset, OtherOffset});
}
};
/// Finds and represents the first mismatching tokens in two syntax maps,
/// ignoring invalidated tokens.
template <class Iter>
struct TokenMismatch {
/// The begin and end iterators of the previous syntax map
Iter PrevTok, PrevEnd;
/// The begin and end iterators of the current syntax map
Iter CurrTok, CurrEnd;
TokenMismatch(Iter CurrTok, Iter CurrEnd, Iter PrevTok, Iter PrevEnd) :
PrevTok(PrevTok), PrevEnd(PrevEnd), CurrTok(CurrTok), CurrEnd(CurrEnd) {
skipInvalid();
while(advance());
}
/// Returns true if a mismatch was found
bool foundMismatch() const {
return CurrTok != CurrEnd || PrevTok != PrevEnd;
}
/// Returns the smallest start offset of the mismatched token ranges
unsigned mismatchStart() const {
assert(foundMismatch());
if (CurrTok != CurrEnd) {
if (PrevTok != PrevEnd)
return std::min(CurrTok->Offset, PrevTok->Offset);
return CurrTok->Offset;
}
return PrevTok->Offset;
}
/// Returns the largest end offset of the mismatched token ranges
unsigned mismatchEnd() const {
assert(foundMismatch());
if (CurrTok != CurrEnd) {
if (PrevTok != PrevEnd)
return std::max(CurrTok->endOffset(), PrevTok->endOffset());
return CurrTok->endOffset();
}
return PrevTok->endOffset();
}
private:
void skipInvalid() {
while (PrevTok != PrevEnd && PrevTok->isInvalid())
++PrevTok;
}
bool advance() {
if (CurrTok == CurrEnd || PrevTok == PrevEnd || *CurrTok != *PrevTok)
return false;
++CurrTok;
++PrevTok;
skipInvalid();
return true;
}
};
/// Represents a the syntax highlighted token ranges in a source file
struct SwiftSyntaxMap {
std::vector<SwiftSyntaxToken> Tokens;
explicit SwiftSyntaxMap(unsigned Capacity = 0) {
if (Capacity)
Tokens.reserve(Capacity);
}
void addToken(const SwiftSyntaxToken &Token) {
assert(Tokens.empty() || Token.Offset >= Tokens.back().Offset);
Tokens.push_back(Token);
}
/// Merge this nested token into the last token that was added
void mergeToken(const SwiftSyntaxToken &Token) {
if (Tokens.empty()) {
Tokens.push_back(Token);
return;
}
auto &LastTok = Tokens.back();
assert(LastTok.Offset <= Token.Offset);
mergeSplitRanges(LastTok.Offset, LastTok.Length, Token.Offset, Token.Length,
[&](unsigned BeforeOff, unsigned BeforeLen,
unsigned AfterOff, unsigned AfterLen) {
auto LastKind = LastTok.Kind;
Tokens.pop_back();
if (BeforeLen)
Tokens.emplace_back(BeforeOff, BeforeLen, LastKind);
Tokens.push_back(Token);
if (AfterLen)
Tokens.emplace_back(AfterOff, AfterLen, LastKind);
});
}
/// Adjusts the token offsets and lengths in this syntax map to account for
/// replacing \p Len bytes at the given \p Offset with \p NewLen bytes. Tokens
/// before the replacement stay the same, tokens after it are shifted, and
/// tokens that intersect it are 'removed' (really just marked invalid).
/// Clients are expected to match this behavior.
///
/// Returns the union of the replaced range and the token ranges it
/// intersected, or nothing if no tokens were intersected.
llvm::Optional<SwiftEditorCharRange>
adjustForReplacement(unsigned Offset, unsigned Len, unsigned NewLen) {
unsigned ReplacedStart = Offset;
unsigned ReplacedEnd = Offset + Len;
bool TokenIntersected = false;
SwiftEditorCharRange Affected = { /*Offset=*/ReplacedStart,
/*EndOffset=*/ReplacedEnd};
// Adjust the tokens
auto Token = Tokens.begin();
while (Token != Tokens.end() && Token->endOffset() <= ReplacedStart) {
// Completely before the replaced range – no change needed
++Token;
}
while (Token != Tokens.end() && Token->Offset < ReplacedEnd) {
// Intersecting the replaced range – extend Affected and invalidate
TokenIntersected = true;
Affected.extendToInclude(*Token);
*Token = SwiftSyntaxToken::createInvalid();
++Token;
}
while (Token != Tokens.end()) {
// Completely after the replaced range - shift to account for NewLen
if (NewLen >= Len)
Token->Offset += NewLen - Len;
else
Token->Offset -= Len - NewLen;
++Token;
}
// If the replaced range didn't intersect with any existing tokens, there's
// no need to report an affected range
if (!TokenIntersected)
return None;
// Update the end of the affected range to account for NewLen
if (NewLen >= Len) {
Affected.EndOffset += NewLen - Len;
} else {
Affected.EndOffset -= Len - NewLen;
}
return Affected;
}
/// Passes each token in this SwiftSyntaxMap to the given \p Consumer
void forEach(EditorConsumer &Consumer) {
for (auto &Token: Tokens) {
auto Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Token.Kind);
Consumer.handleSyntaxMap(Token.Offset, Token.Length, Kind);
}
}
/// Finds the delta between the given SwiftSyntaxMap, \p Prev, and this one.
/// It passes each token not in \p Prev to the given \p Consumer and, if
/// needed, also expands or sets the given \p Affected range to cover all
/// non-matching tokens in the two lists.
///
/// Returns true if this SwiftSyntaxMap is different to \p Prev.
bool forEachChanged(const SwiftSyntaxMap &Prev,
llvm::Optional<SwiftEditorCharRange> &Affected,
EditorConsumer &Consumer) const {
typedef std::vector<SwiftSyntaxToken>::const_iterator ForwardIt;
typedef std::vector<SwiftSyntaxToken>::const_reverse_iterator ReverseIt;
// Find the first pair of tokens that don't match
TokenMismatch<ForwardIt>
Forward(Tokens.begin(), Tokens.end(), Prev.Tokens.begin(), Prev.Tokens.end());
// Exit early if there was no mismatch
if (!Forward.foundMismatch())
return Affected && !Affected->isEmpty();
// Find the last pair of tokens that don't match
TokenMismatch<ReverseIt>
Backward(Tokens.rbegin(), Tokens.rend(), Prev.Tokens.rbegin(), Prev.Tokens.rend());
assert(Backward.foundMismatch());
// Set or extend the affected range to include the mismatched range
SwiftEditorCharRange
MismatchRange = {Forward.mismatchStart(),Backward.mismatchEnd()};
if (!Affected) {
Affected = MismatchRange;
} else {
Affected->extendToInclude(MismatchRange);
}
// Report all tokens in the affected range to the EditorConsumer
auto From = Forward.CurrTok;
auto To = Backward.CurrTok;
while (From != Tokens.begin() && (From-1)->Offset >= Affected->Offset)
--From;
while (To != Tokens.rbegin() && (To-1)->endOffset() <= Affected->EndOffset)
--To;
for (; From < To.base(); ++From) {
auto Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(From->Kind);
Consumer.handleSyntaxMap(From->Offset, From->Length, Kind);
}
return true;
}
};
struct EditorConsumerSyntaxMapEntry {
unsigned Offset;
unsigned Length;
UIdent Kind;
EditorConsumerSyntaxMapEntry(unsigned Offset, unsigned Length, UIdent Kind)
:Offset(Offset), Length(Length), Kind(Kind) { }
};
struct SwiftSemanticToken {
unsigned ByteOffset;
unsigned Length : 24;
// The code-completion kinds are a good match for the semantic kinds we want.
// FIXME: Maybe rename CodeCompletionDeclKind to a more general concept ?
CodeCompletionDeclKind Kind : 6;
unsigned IsRef : 1;
unsigned IsSystem : 1;
SwiftSemanticToken(CodeCompletionDeclKind Kind,
unsigned ByteOffset, unsigned Length,
bool IsRef, bool IsSystem)
: ByteOffset(ByteOffset), Length(Length), Kind(Kind),
IsRef(IsRef), IsSystem(IsSystem) { }
bool getIsRef() const { return static_cast<bool>(IsRef); }
bool getIsSystem() const { return static_cast<bool>(IsSystem); }
UIdent getUIdentForKind() const {
return SwiftLangSupport::getUIDForCodeCompletionDeclKind(Kind, getIsRef());
}
};
static_assert(sizeof(SwiftSemanticToken) == 8, "Too big");
class SwiftDocumentSemanticInfo :
public ThreadSafeRefCountedBase<SwiftDocumentSemanticInfo> {
const std::string Filename;
SwiftASTManager &ASTMgr;
NotificationCenter &NotificationCtr;
ThreadSafeRefCntPtr<SwiftInvocation> InvokRef;
std::string CompilerArgsError;
uint64_t ASTGeneration = 0;
ImmutableTextSnapshotRef TokSnapshot;
std::vector<SwiftSemanticToken> SemaToks;
ImmutableTextSnapshotRef DiagSnapshot;
std::vector<DiagnosticEntryInfo> SemaDiags;
mutable llvm::sys::Mutex Mtx;
public:
SwiftDocumentSemanticInfo(StringRef Filename, SwiftLangSupport &LangSupport)
: Filename(Filename),
ASTMgr(LangSupport.getASTManager()),
NotificationCtr(LangSupport.getContext().getNotificationCenter()) {}
SwiftInvocationRef getInvocation() const {
return InvokRef;
}
uint64_t getASTGeneration() const;
void setCompilerArgs(ArrayRef<const char *> Args) {
InvokRef = ASTMgr.getInvocation(Args, Filename, CompilerArgsError);
}
void readSemanticInfo(ImmutableTextSnapshotRef NewSnapshot,
std::vector<SwiftSemanticToken> &Tokens,
Optional<std::vector<DiagnosticEntryInfo>> &Diags,
ArrayRef<DiagnosticEntryInfo> ParserDiags);
void processLatestSnapshotAsync(EditableTextBufferRef EditableBuffer);
void updateSemanticInfo(std::vector<SwiftSemanticToken> Toks,
std::vector<DiagnosticEntryInfo> Diags,
ImmutableTextSnapshotRef Snapshot,
uint64_t ASTGeneration);
void removeCachedAST() {
if (InvokRef)
ASTMgr.removeCachedAST(InvokRef);
}
private:
std::vector<SwiftSemanticToken> takeSemanticTokens(
ImmutableTextSnapshotRef NewSnapshot);
Optional<std::vector<DiagnosticEntryInfo>> getSemanticDiagnostics(
ImmutableTextSnapshotRef NewSnapshot,
ArrayRef<DiagnosticEntryInfo> ParserDiags);
};
class SwiftDocumentSyntaxInfo {
SourceManager SM;
EditorDiagConsumer DiagConsumer;
std::unique_ptr<ParserUnit> Parser;
unsigned BufferID;
std::vector<std::string> Args;
std::string PrimaryFile;
public:
SwiftDocumentSyntaxInfo(const CompilerInvocation &CompInv,
ImmutableTextSnapshotRef Snapshot,
std::vector<std::string> &Args,
StringRef FilePath)
: Args(Args), PrimaryFile(FilePath) {
std::unique_ptr<llvm::MemoryBuffer> BufCopy =
llvm::MemoryBuffer::getMemBufferCopy(
Snapshot->getBuffer()->getText(), FilePath);
BufferID = SM.addNewSourceBuffer(std::move(BufCopy));
SM.setHashbangBufferID(BufferID);
DiagConsumer.setInputBufferIDs(BufferID);
Parser.reset(
new ParserUnit(SM, BufferID,
CompInv.getLangOptions(),
CompInv.getModuleName())
);
Parser->getDiagnosticEngine().addConsumer(DiagConsumer);
}
void initArgsAndPrimaryFile(trace::SwiftInvocation &Info) {
Info.Args.PrimaryFile = PrimaryFile;
Info.Args.Args = Args;
}
void parse() {
auto &P = Parser->getParser();
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation Info;
initArgsAndPrimaryFile(Info);
auto Text = SM.getLLVMSourceMgr().getMemoryBuffer(BufferID)->getBuffer();
Info.Files.push_back(std::make_pair(PrimaryFile, Text));
TracedOp.start(trace::OperationKind::SimpleParse, Info);
}
bool Done = false;
while (!Done) {
P.parseTopLevel();
Done = P.Tok.is(tok::eof);
}
}
SourceFile &getSourceFile() {
return Parser->getSourceFile();
}
unsigned getBufferID() {
return BufferID;
}
const LangOptions &getLangOptions() {
return Parser->getLangOptions();
}
SourceManager &getSourceManager() {
return SM;
}
ArrayRef<DiagnosticEntryInfo> getDiagnostics() {
return DiagConsumer.getDiagnosticsForBuffer(BufferID);
}
};
} // anonymous namespace
uint64_t SwiftDocumentSemanticInfo::getASTGeneration() const {
llvm::sys::ScopedLock L(Mtx);
return ASTGeneration;
}
void SwiftDocumentSemanticInfo::readSemanticInfo(
ImmutableTextSnapshotRef NewSnapshot,
std::vector<SwiftSemanticToken> &Tokens,
Optional<std::vector<DiagnosticEntryInfo>> &Diags,
ArrayRef<DiagnosticEntryInfo> ParserDiags) {
llvm::sys::ScopedLock L(Mtx);
Tokens = takeSemanticTokens(NewSnapshot);
Diags = getSemanticDiagnostics(NewSnapshot, ParserDiags);
}
std::vector<SwiftSemanticToken>
SwiftDocumentSemanticInfo::takeSemanticTokens(
ImmutableTextSnapshotRef NewSnapshot) {
llvm::sys::ScopedLock L(Mtx);
if (SemaToks.empty())
return {};
// Adjust the position of the tokens.
TokSnapshot->foreachReplaceUntil(NewSnapshot,
[&](ReplaceImmutableTextUpdateRef Upd) -> bool {
if (SemaToks.empty())
return false;
auto ReplaceBegin = std::lower_bound(SemaToks.begin(), SemaToks.end(),
Upd->getByteOffset(),
[&](const SwiftSemanticToken &Tok, unsigned StartOffset) -> bool {
return Tok.ByteOffset+Tok.Length < StartOffset;
});
std::vector<SwiftSemanticToken>::iterator ReplaceEnd;
if (Upd->getLength() == 0) {
ReplaceEnd = ReplaceBegin;
} else {
ReplaceEnd = std::upper_bound(ReplaceBegin, SemaToks.end(),
Upd->getByteOffset() + Upd->getLength(),
[&](unsigned EndOffset, const SwiftSemanticToken &Tok) -> bool {
return EndOffset < Tok.ByteOffset;
});
}
unsigned InsertLen = Upd->getText().size();
int Delta = InsertLen - Upd->getLength();
if (Delta != 0) {
for (std::vector<SwiftSemanticToken>::iterator
I = ReplaceEnd, E = SemaToks.end(); I != E; ++I)
I->ByteOffset += Delta;
}
SemaToks.erase(ReplaceBegin, ReplaceEnd);
return true;
});
return std::move(SemaToks);
}
Optional<std::vector<DiagnosticEntryInfo>>
SwiftDocumentSemanticInfo::getSemanticDiagnostics(
ImmutableTextSnapshotRef NewSnapshot,
ArrayRef<DiagnosticEntryInfo> ParserDiags) {
std::vector<DiagnosticEntryInfo> curSemaDiags;
{
llvm::sys::ScopedLock L(Mtx);
if (!DiagSnapshot || DiagSnapshot->getStamp() != NewSnapshot->getStamp()) {
// The semantic diagnostics are out-of-date, ignore them.
return llvm::None;
}
curSemaDiags = SemaDiags;
}
// Diagnostics from the AST and diagnostics from the parser are based on the
// same source text snapshot. But diagnostics from the AST may have excluded
// the parser diagnostics due to a fatal error, e.g. if the source has a
// 'so such module' error, which will suppress other diagnostics.
// We don't want to turn off the suppression to avoid a flood of diagnostics
// when a module import fails, but we also don't want to lose the parser
// diagnostics in such a case, so merge the parser diagnostics with the sema
// ones.
auto orderDiagnosticEntryInfos = [](const DiagnosticEntryInfo &LHS,
const DiagnosticEntryInfo &RHS) -> bool {
if (LHS.Filename != RHS.Filename)
return LHS.Filename < RHS.Filename;
if (LHS.Offset != RHS.Offset)
return LHS.Offset < RHS.Offset;
return LHS.Description < RHS.Description;
};
std::vector<DiagnosticEntryInfo> sortedParserDiags;
sortedParserDiags.reserve(ParserDiags.size());
sortedParserDiags.insert(sortedParserDiags.end(), ParserDiags.begin(),
ParserDiags.end());
std::stable_sort(sortedParserDiags.begin(), sortedParserDiags.end(),
orderDiagnosticEntryInfos);
std::vector<DiagnosticEntryInfo> finalDiags;
finalDiags.reserve(sortedParserDiags.size()+curSemaDiags.size());
// Add sema diagnostics unless it is an existing parser diagnostic.
// Note that we want to merge and eliminate diagnostics from the 'sema' set
// that also show up in the 'parser' set, but we don't want to remove
// duplicate diagnostics from within the same set (e.g. duplicates existing in
// the 'sema' set). We want to report the diagnostics as the compiler reported
// them, even if there's some duplicate one. This is why we don't just do a
// simple append/sort/keep-uniques step.
for (const auto &curDE : curSemaDiags) {
bool existsAsParserDiag = std::binary_search(sortedParserDiags.begin(),
sortedParserDiags.end(),
curDE, orderDiagnosticEntryInfos);
if (!existsAsParserDiag) {
finalDiags.push_back(curDE);
}
}
finalDiags.insert(finalDiags.end(),
sortedParserDiags.begin(), sortedParserDiags.end());
std::stable_sort(finalDiags.begin(), finalDiags.end(),
orderDiagnosticEntryInfos);
return finalDiags;
}
void SwiftDocumentSemanticInfo::updateSemanticInfo(
std::vector<SwiftSemanticToken> Toks,
std::vector<DiagnosticEntryInfo> Diags,
ImmutableTextSnapshotRef Snapshot,
uint64_t ASTGeneration) {
{
llvm::sys::ScopedLock L(Mtx);
if (ASTGeneration > this->ASTGeneration) {
SemaToks = std::move(Toks);
SemaDiags = std::move(Diags);
TokSnapshot = DiagSnapshot = std::move(Snapshot);
this->ASTGeneration = ASTGeneration;
}
}
LOG_INFO_FUNC(High, "posted document update notification for: " << Filename);
NotificationCtr.postDocumentUpdateNotification(Filename);
}
namespace {
class SemanticAnnotator : public SourceEntityWalker {
SourceManager &SM;
unsigned BufferID;
public:
std::vector<SwiftSemanticToken> SemaToks;
SemanticAnnotator(SourceManager &SM, unsigned BufferID)
: SM(SM), BufferID(BufferID) {}
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
ReferenceMetaData Data) override {
if (isa<VarDecl>(D) && D->hasName() &&
D->getFullName() == D->getASTContext().Id_self)
return true;
// Do not annotate references to unavailable decls.
if (AvailableAttr::isUnavailable(D))
return true;
if (CtorTyRef)
D = CtorTyRef;
annotate(D, /*IsRef=*/true, Range);
return true;
}
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
bool IsOpenBracket) override {
// We should treat both open and close brackets equally
return visitDeclReference(D, Range, nullptr, nullptr, Type(),
ReferenceMetaData(SemaReferenceKind::SubscriptRef, None));
}
void annotate(const Decl *D, bool IsRef, CharSourceRange Range) {
unsigned ByteOffset = SM.getLocOffsetInBuffer(Range.getStart(), BufferID);
unsigned Length = Range.getByteLength();
auto Kind = CodeCompletionResult::getCodeCompletionDeclKind(D);
bool IsSystem = D->getModuleContext()->isSystemModule();
SemaToks.emplace_back(Kind, ByteOffset, Length, IsRef, IsSystem);
}
};
} // anonymous namespace
namespace {
class AnnotAndDiagASTConsumer : public SwiftASTConsumer {
EditableTextBufferRef EditableBuffer;
RefPtr<SwiftDocumentSemanticInfo> SemaInfoRef;
public:
std::vector<SwiftSemanticToken> SemaToks;
AnnotAndDiagASTConsumer(EditableTextBufferRef EditableBuffer,
RefPtr<SwiftDocumentSemanticInfo> SemaInfoRef)
: EditableBuffer(std::move(EditableBuffer)),
SemaInfoRef(std::move(SemaInfoRef)) { }
void failed(StringRef Error) override {
LOG_WARN_FUNC("sema annotations failed: " << Error);
}
void handlePrimaryAST(ASTUnitRef AstUnit) override {
auto Generation = AstUnit->getGeneration();
auto &CompIns = AstUnit->getCompilerInstance();
auto &Consumer = AstUnit->getEditorDiagConsumer();
assert(Generation);
if (Generation < SemaInfoRef->getASTGeneration()) {
// It may happen that this request was waiting in async queue for
// too long so another thread has already updated this sema with
// ast generation bigger than ASTGeneration
return;
}
ImmutableTextSnapshotRef DocSnapshot;
for (auto &Snap : AstUnit->getSnapshots()) {
if (Snap->getEditableBuffer() == EditableBuffer) {
DocSnapshot = Snap;
break;
}
}
if (!DocSnapshot) {
LOG_WARN_FUNC("did not find document snapshot when handling the AST");
return;
}
if (Generation == SemaInfoRef->getASTGeneration()) {
// Save time if we already know we processed this AST version.
if (DocSnapshot->getStamp() != EditableBuffer->getSnapshot()->getStamp()){
// Handle edits that occurred after we processed the AST.
SemaInfoRef->processLatestSnapshotAsync(EditableBuffer);
}
return;
}
if (!AstUnit->getPrimarySourceFile().getBufferID().hasValue()) {
LOG_WARN_FUNC("Primary SourceFile is expected to have a BufferID");
return;
}
unsigned BufferID = AstUnit->getPrimarySourceFile().getBufferID().getValue();
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation SwiftArgs;
SemaInfoRef->getInvocation()->raw(SwiftArgs.Args.Args,
SwiftArgs.Args.PrimaryFile);
trace::initTraceFiles(SwiftArgs, CompIns);
TracedOp.start(trace::OperationKind::AnnotAndDiag, SwiftArgs);
}
SemanticAnnotator Annotator(CompIns.getSourceMgr(), BufferID);
Annotator.walk(AstUnit->getPrimarySourceFile());
SemaToks = std::move(Annotator.SemaToks);
TracedOp.finish();
SemaInfoRef->
updateSemanticInfo(std::move(SemaToks),
std::move(Consumer.getDiagnosticsForBuffer(BufferID)),
DocSnapshot,
Generation);
if (DocSnapshot->getStamp() != EditableBuffer->getSnapshot()->getStamp()) {
// Handle edits that occurred after we processed the AST.
SemaInfoRef->processLatestSnapshotAsync(EditableBuffer);
}
}
};
} // anonymous namespace
void SwiftDocumentSemanticInfo::processLatestSnapshotAsync(
EditableTextBufferRef EditableBuffer) {
SwiftInvocationRef Invok = InvokRef;
if (!Invok)
return;
RefPtr<SwiftDocumentSemanticInfo> SemaInfoRef = this;
auto Consumer = std::make_shared<AnnotAndDiagASTConsumer>(EditableBuffer,
SemaInfoRef);
// Semantic annotation queries for a particular document should cancel
// previously queued queries for the same document. Each document has a
// SwiftDocumentSemanticInfo pointer so use that for the token.
const void *OncePerASTToken = SemaInfoRef.get();
ASTMgr.processASTAsync(Invok, std::move(Consumer), OncePerASTToken);
}
struct SwiftEditorDocument::Implementation {
SwiftLangSupport &LangSupport;
const std::string FilePath;
EditableTextBufferRef EditableBuffer;
/// The list of syntax highlighted token offsets and ranges in the document
SwiftSyntaxMap SyntaxMap;
/// The minimal range of syntax highlighted tokens affected by the last edit
llvm::Optional<SwiftEditorCharRange> AffectedRange;
/// Whether the last operation was an edit rather than a document open
bool Edited;
std::vector<DiagnosticEntryInfo> ParserDiagnostics;
RefPtr<SwiftDocumentSemanticInfo> SemanticInfo;
CodeFormatOptions FormatOptions;
std::shared_ptr<SwiftDocumentSyntaxInfo> SyntaxInfo;
std::shared_ptr<SwiftDocumentSyntaxInfo> getSyntaxInfo() {
llvm::sys::ScopedLock L(AccessMtx);
return SyntaxInfo;
}
llvm::sys::Mutex AccessMtx;
Implementation(StringRef FilePath, SwiftLangSupport &LangSupport,
CodeFormatOptions options)
: LangSupport(LangSupport), FilePath(FilePath), FormatOptions(options) {
SemanticInfo = new SwiftDocumentSemanticInfo(FilePath, LangSupport);
}
void buildSwiftInv(trace::SwiftInvocation &Inv);
};
void SwiftEditorDocument::Implementation::buildSwiftInv(
trace::SwiftInvocation &Inv) {
if (SemanticInfo->getInvocation()) {
std::string PrimaryFile; // Ignored, FilePath will be used
SemanticInfo->getInvocation()->raw(Inv.Args.Args, PrimaryFile);
}
Inv.Args.PrimaryFile = FilePath;
auto &SM = SyntaxInfo->getSourceManager();
auto ID = SyntaxInfo->getBufferID();
auto Text = SM.getLLVMSourceMgr().getMemoryBuffer(ID)->getBuffer();
Inv.Files.push_back(std::make_pair(FilePath, Text));
}
namespace {
static UIdent getAccessLevelUID(AccessLevel Access) {
static UIdent AccessOpen("source.lang.swift.accessibility.open");
static UIdent AccessPublic("source.lang.swift.accessibility.public");
static UIdent AccessInternal("source.lang.swift.accessibility.internal");
static UIdent AccessFilePrivate("source.lang.swift.accessibility.fileprivate");
static UIdent AccessPrivate("source.lang.swift.accessibility.private");
switch (Access) {
case AccessLevel::Private:
return AccessPrivate;
case AccessLevel::FilePrivate:
return AccessFilePrivate;
case AccessLevel::Internal:
return AccessInternal;
case AccessLevel::Public:
return AccessPublic;
case AccessLevel::Open:
return AccessOpen;
}
llvm_unreachable("Unhandled access level in switch.");
}
static Optional<AccessLevel> getAccessLevelStrictly(const ExtensionDecl *ED) {
if (ED->hasDefaultAccessLevel())
return ED->getDefaultAccessLevel();
// Check if the decl has an explicit access control attribute.
if (auto *AA = ED->getAttrs().getAttribute<AccessControlAttr>())
return AA->getAccess();
return None;
}
static AccessLevel inferDefaultAccessLevel(const ExtensionDecl *ED) {
if (auto StrictAccess = getAccessLevelStrictly(ED))
return StrictAccess.getValue();
// Assume "internal", which is the most common thing anyway.
return AccessLevel::Internal;
}
/// If typechecking was performed we use the computed access level, otherwise
/// we fallback to inferring access syntactically. This may not be as
/// accurate but it's only until we have typechecked the AST.
static AccessLevel inferAccessLevel(const ValueDecl *D) {
assert(D);
if (D->hasAccess())
return D->getFormalAccess();
// Check if the decl has an explicit access control attribute.
if (auto *AA = D->getAttrs().getAttribute<AccessControlAttr>())
return AA->getAccess();
DeclContext *DC = D->getDeclContext();
switch (DC->getContextKind()) {
case DeclContextKind::SerializedLocal:
case DeclContextKind::AbstractClosureExpr:
case DeclContextKind::Initializer:
case DeclContextKind::TopLevelCodeDecl:
case DeclContextKind::AbstractFunctionDecl:
case DeclContextKind::SubscriptDecl:
return AccessLevel::Private;
case DeclContextKind::Module:
case DeclContextKind::FileUnit:
return AccessLevel::Internal;
case DeclContextKind::GenericTypeDecl: {
auto Nominal = cast<GenericTypeDecl>(DC);
AccessLevel Access = inferAccessLevel(Nominal);
if (!isa<ProtocolDecl>(Nominal))
Access = std::min(Access, AccessLevel::Internal);
return Access;
}
case DeclContextKind::ExtensionDecl:
return inferDefaultAccessLevel(cast<ExtensionDecl>(DC));
}
llvm_unreachable("Unhandled DeclContextKind in switch.");
}
static Optional<AccessLevel>
inferSetterAccessLevel(const AbstractStorageDecl *D) {
if (auto *VD = dyn_cast<VarDecl>(D)) {
if (VD->isLet())
return None;
}
if (D->getGetter() && !D->getSetter())
return None;
// FIXME: Have the parser detect as read-only the syntactic form of generated
// interfaces, which is "var foo : Int { get }"
if (auto *AA = D->getAttrs().getAttribute<SetterAccessAttr>())
return AA->getAccess();
else
return inferAccessLevel(D);
}
class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker {
SourceManager &SrcManager;
EditorConsumer &Consumer;
unsigned BufferID;
public:
SwiftDocumentStructureWalker(SourceManager &SrcManager,
unsigned BufferID,
EditorConsumer &Consumer)
: SrcManager(SrcManager), Consumer(Consumer), BufferID(BufferID) { }
bool walkToSubStructurePre(SyntaxStructureNode Node) override {
unsigned StartOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(),
BufferID);
unsigned EndOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(),
BufferID);
unsigned NameStart;
unsigned NameEnd;
if (Node.NameRange.isValid()) {
NameStart = SrcManager.getLocOffsetInBuffer(Node.NameRange.getStart(),
BufferID);
NameEnd = SrcManager.getLocOffsetInBuffer(Node.NameRange.getEnd(),
BufferID);
}
else {
NameStart = NameEnd = 0;
}
unsigned BodyOffset;
unsigned BodyEnd;
if (Node.BodyRange.isValid()) {
BodyOffset = SrcManager.getLocOffsetInBuffer(Node.BodyRange.getStart(),
BufferID);
BodyEnd = SrcManager.getLocOffsetInBuffer(Node.BodyRange.getEnd(),
BufferID);
}
else {
BodyOffset = BodyEnd = 0;
}
unsigned DocOffset = 0;
unsigned DocEnd = 0;
if (Node.DocRange.isValid()) {
DocOffset = SrcManager.getLocOffsetInBuffer(Node.DocRange.getStart(),
BufferID);
DocEnd = SrcManager.getLocOffsetInBuffer(Node.DocRange.getEnd(),
BufferID);
}
UIdent Kind = SwiftLangSupport::getUIDForSyntaxStructureKind(Node.Kind);
UIdent AccessLevel;
UIdent SetterAccessLevel;
if (Node.Kind != SyntaxStructureKind::Parameter &&
Node.Kind != SyntaxStructureKind::LocalVariable) {
if (auto *VD = dyn_cast_or_null<ValueDecl>(Node.Dcl)) {
AccessLevel = getAccessLevelUID(inferAccessLevel(VD));
} else if (auto *ED = dyn_cast_or_null<ExtensionDecl>(Node.Dcl)) {
if (auto StrictAccess = getAccessLevelStrictly(ED))
AccessLevel = getAccessLevelUID(StrictAccess.getValue());
}
if (auto *ASD = dyn_cast_or_null<AbstractStorageDecl>(Node.Dcl)) {
Optional<swift::AccessLevel> SetAccess = inferSetterAccessLevel(ASD);
if (SetAccess.hasValue()) {
SetterAccessLevel = getAccessLevelUID(SetAccess.getValue());
}
}
}
SmallVector<StringRef, 4> InheritedNames;
if (!Node.InheritedTypeRanges.empty()) {
for (auto &TR : Node.InheritedTypeRanges) {
InheritedNames.push_back(SrcManager.extractText(TR));
}
}
StringRef TypeName;
if (Node.TypeRange.isValid()) {
TypeName = SrcManager.extractText(Node.TypeRange);
}
SmallString<64> DisplayNameBuf;
StringRef DisplayName;
if (auto ValueD = dyn_cast_or_null<ValueDecl>(Node.Dcl)) {
llvm::raw_svector_ostream OS(DisplayNameBuf);
if (!SwiftLangSupport::printDisplayName(ValueD, OS))
DisplayName = OS.str();
}
else if (Node.NameRange.isValid()) {
DisplayName = SrcManager.extractText(Node.NameRange);
}
SmallString<64> RuntimeNameBuf;
StringRef RuntimeName = getObjCRuntimeName(Node.Dcl, RuntimeNameBuf);
SmallString<64> SelectorNameBuf;
StringRef SelectorName = getObjCSelectorName(Node.Dcl, SelectorNameBuf);
std::vector<std::tuple<UIdent, unsigned, unsigned>> Attrs;
for (auto Attr : Node.Attrs) {
if (auto AttrUID = SwiftLangSupport::getUIDForDeclAttribute(Attr)) {
unsigned AttrOffset = 0;
unsigned AttrEnd = 0;
auto AttrRange = Attr->getRangeWithAt();
if (AttrRange.isValid()) {
auto CharRange = Lexer::getCharSourceRangeFromSourceRange(SrcManager,
AttrRange);
AttrOffset = SrcManager.getLocOffsetInBuffer(CharRange.getStart(),
BufferID);
AttrEnd = SrcManager.getLocOffsetInBuffer(CharRange.getEnd(),
BufferID);
}
auto AttrTuple = std::make_tuple(AttrUID.getValue(), AttrOffset,
AttrEnd - AttrOffset);
Attrs.push_back(AttrTuple);
}
}
Consumer.beginDocumentSubStructure(StartOffset, EndOffset - StartOffset,
Kind, AccessLevel, SetterAccessLevel,
NameStart, NameEnd - NameStart,
BodyOffset, BodyEnd - BodyOffset,
DocOffset, DocEnd - DocOffset,
DisplayName,
TypeName, RuntimeName,
SelectorName,
InheritedNames, Attrs);
for (const auto &Elem : Node.Elements) {
if (Elem.Range.isInvalid())
continue;
UIdent Kind = SwiftLangSupport::getUIDForSyntaxStructureElementKind(Elem.Kind);
unsigned Offset = SrcManager.getLocOffsetInBuffer(Elem.Range.getStart(),
BufferID);
unsigned Length = Elem.Range.getByteLength();
Consumer.handleDocumentSubStructureElement(Kind, Offset, Length);
}
return true;
}
StringRef getObjCRuntimeName(const Decl *D, SmallString<64> &Buf) {
if (!D || D->isInvalid())
return StringRef();
if (!isa<ClassDecl>(D) && !isa<ProtocolDecl>(D))
return StringRef();
auto *VD = cast<ValueDecl>(D);
if (!VD->hasName())
return StringRef();
auto ident = VD->getBaseName().getIdentifier().str();
if (ident.empty() || Mangle::isDigit(ident.front()))
return StringRef();
// We don't support getting the runtime name for nested classes.
// This would require typechecking or at least name lookup, if the nested
// class is in an extension.
if (!D->getDeclContext()->isModuleScopeContext())
return StringRef();
if (auto ClassD = dyn_cast<ClassDecl>(D)) {
// We don't vend the runtime name for generic classes for now.
if (ClassD->getGenericParams())
return StringRef();
return ClassD->getObjCRuntimeName(Buf);
}
return cast<ProtocolDecl>(D)->getObjCRuntimeName(Buf);
}
StringRef getObjCSelectorName(const Decl *D, SmallString<64> &Buf) {
if (auto FuncD = dyn_cast_or_null<AbstractFunctionDecl>(D)) {
// We only vend the selector name for @IBAction methods.
if (FuncD->getAttrs().hasAttribute<IBActionAttr>())
return FuncD->getObjCSelector().getString(Buf);
}
return StringRef();
}
bool walkToSubStructurePost(SyntaxStructureNode Node) override {
Consumer.endDocumentSubStructure();
return true;
}
bool walkToNodePre(SyntaxNode Node) override {
if (Node.Kind != SyntaxNodeKind::CommentMarker)
return false;
unsigned StartOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(),
BufferID);
unsigned EndOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(),
BufferID);
UIdent Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind);
Consumer.beginDocumentSubStructure(StartOffset, EndOffset - StartOffset,
Kind, UIdent(), UIdent(), 0, 0,
0, 0, 0, 0,
StringRef(),
StringRef(), StringRef(),
StringRef(),
{}, {});
return true;
}
bool walkToNodePost(SyntaxNode Node) override {
if (Node.Kind != SyntaxNodeKind::CommentMarker)
return true;
Consumer.endDocumentSubStructure();
return true;
}
};
/// Walks the syntax model to populate a given SwiftSyntaxMap with the token
/// ranges to highlight and pass document structure information to the given
/// EditorConsumer.
class SwiftEditorSyntaxWalker: public ide::SyntaxModelWalker {
/// The syntax map to populate
SwiftSyntaxMap &SyntaxMap;
SourceManager &SrcManager;
unsigned BufferID;
SwiftDocumentStructureWalker DocStructureWalker;
/// The current token nesting level (e.g. for a field in a doc comment)
unsigned NestingLevel = 0;
public:
SwiftEditorSyntaxWalker(SwiftSyntaxMap &SyntaxMap,
SourceManager &SrcManager, EditorConsumer &Consumer,
unsigned BufferID)
: SyntaxMap(SyntaxMap), SrcManager(SrcManager), BufferID(BufferID),
DocStructureWalker(SrcManager, BufferID, Consumer) { }
bool walkToNodePre(SyntaxNode Node) override {
if (Node.Kind == SyntaxNodeKind::CommentMarker)
return DocStructureWalker.walkToNodePre(Node);
++NestingLevel;
auto End = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), BufferID),
Start = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), BufferID);
if (NestingLevel > 1) {
// We're nested inside the previously reported token - merge
SyntaxMap.mergeToken({Start, End - Start, Node.Kind});
} else {
// We're a top-level token, add it after the previous one
SyntaxMap.addToken({Start, End - Start, Node.Kind});
}
return true;
}
bool walkToNodePost(SyntaxNode Node) override {
if (Node.Kind == SyntaxNodeKind::CommentMarker)
return DocStructureWalker.walkToNodePost(Node);
--NestingLevel;
return true;
}
bool walkToSubStructurePre(SyntaxStructureNode Node) override {
return DocStructureWalker.walkToSubStructurePre(Node);
}
bool walkToSubStructurePost(SyntaxStructureNode Node) override {
return DocStructureWalker.walkToSubStructurePost(Node);
}
};
class PlaceholderExpansionScanner {
public:
struct Param {
CharSourceRange NameRange;
CharSourceRange TypeRange;
Param(CharSourceRange NameRange, CharSourceRange TypeRange)
:NameRange(NameRange), TypeRange(TypeRange) { }
};
private:
struct ClosureInfo {
std::vector<Param> Params;
CharSourceRange ReturnTypeRange;
};
SourceManager &SM;
ClosureInfo TargetClosureInfo;
EditorPlaceholderExpr *PHE = nullptr;
class PlaceholderFinder: public ASTWalker {
SourceLoc PlaceholderLoc;
EditorPlaceholderExpr *&Found;
public:
PlaceholderFinder(SourceLoc PlaceholderLoc,
EditorPlaceholderExpr *&Found)
: PlaceholderLoc(PlaceholderLoc), Found(Found) {
}
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
if (isa<EditorPlaceholderExpr>(E) && E->getStartLoc() == PlaceholderLoc) {
Found = cast<EditorPlaceholderExpr>(E);
return { false, nullptr };
}
return { true, E };
}
};
class ClosureTypeWalker: public ASTWalker {
SourceManager &SM;
ClosureInfo &Info;
public:
bool FoundFunctionTypeRepr = false;
explicit ClosureTypeWalker(SourceManager &SM, ClosureInfo &Info) : SM(SM),
Info(Info) { }
bool walkToTypeReprPre(TypeRepr *T) override {
if (auto *FTR = dyn_cast<FunctionTypeRepr>(T)) {
FoundFunctionTypeRepr = true;
if (auto *TTR = dyn_cast_or_null<TupleTypeRepr>(FTR->getArgsTypeRepr())) {
for (auto &ArgElt : TTR->getElements()) {
CharSourceRange NR;
CharSourceRange TR;
auto name = ArgElt.Name;
if (!name.empty()) {
NR = CharSourceRange(ArgElt.NameLoc,
name.getLength());
}
SourceLoc SRE = Lexer::getLocForEndOfToken(SM,
ArgElt.Type->getEndLoc());
TR = CharSourceRange(SM, ArgElt.Type->getStartLoc(), SRE);
Info.Params.emplace_back(NR, TR);
}
} else if (FTR->getArgsTypeRepr()) {
CharSourceRange TR;
TR = CharSourceRange(SM, FTR->getArgsTypeRepr()->getStartLoc(),
Lexer::getLocForEndOfToken(SM,
FTR->getArgsTypeRepr()->getEndLoc()));
Info.Params.emplace_back(CharSourceRange(), TR);
}
if (auto *RTR = FTR->getResultTypeRepr()) {
SourceLoc SRE = Lexer::getLocForEndOfToken(SM, RTR->getEndLoc());
Info.ReturnTypeRange = CharSourceRange(SM, RTR->getStartLoc(), SRE);
}
}
return !FoundFunctionTypeRepr;
}
bool walkToTypeReprPost(TypeRepr *T) override {
// If we just visited the FunctionTypeRepr, end traversal.
return !FoundFunctionTypeRepr;
}
};
bool containClosure(Expr *E) {
if (E->getStartLoc().isInvalid())
return false;
EditorPlaceholderExpr *Found = nullptr;
ClosureInfo Info;
ClosureTypeWalker ClosureWalker(SM, Info);
PlaceholderFinder Finder(E->getStartLoc(), Found);
E->walk(Finder);
if (Found) {
if (auto TR = Found->getTypeLoc().getTypeRepr()) {
TR->walk(ClosureWalker);
return ClosureWalker.FoundFunctionTypeRepr;
}
}
E->walk(ClosureWalker);
return ClosureWalker.FoundFunctionTypeRepr;
}
bool scanClosureType(SourceFile &SF, SourceLoc PlaceholderLoc) {
TargetClosureInfo.Params.clear();
TargetClosureInfo.ReturnTypeRange = CharSourceRange();
PlaceholderFinder Finder(PlaceholderLoc, PHE);
SF.walk(Finder);
if (!PHE || !PHE->getTypeForExpansion())
return false;
ClosureTypeWalker PW(SM, TargetClosureInfo);
PHE->getTypeForExpansion()->walk(PW);
return PW.FoundFunctionTypeRepr;
}
/// Finds the enclosing CallExpr, and indicates whether it should be further
/// considered a candidate for application of trailing closure.
/// For example, if the CallExpr is enclosed in another expression or statement
/// such as "outer(inner(<#closure#>))", or "if inner(<#closure#>)", then trailing
/// closure should not be applied to the inner call.
std::pair<CallExpr *, bool> enclosingCallExpr(SourceFile &SF, SourceLoc SL) {
class CallExprFinder : public SourceEntityWalker {
public:
const SourceManager &SM;
SourceLoc TargetLoc;
CallExpr *EnclosingCall;
Expr *OuterExpr;
Stmt *OuterStmt;
explicit CallExprFinder(const SourceManager &SM)
:SM(SM) { }
bool walkToExprPre(Expr *E) override {
auto SR = E->getSourceRange();
if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) {
if (auto *CE = dyn_cast<CallExpr>(E)) {
if (EnclosingCall)
OuterExpr = EnclosingCall;
EnclosingCall = CE;
}
else if (!EnclosingCall)
OuterExpr = E;
}
return true;
}
bool walkToExprPost(Expr *E) override {
if (E->getStartLoc() == TargetLoc)
return false; // found what we needed to find, stop walking.
return true;
}
bool walkToStmtPre(Stmt *S) override {
auto SR = S->getSourceRange();
if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) {
if (!EnclosingCall) {
if (isa<BraceStmt>(S))
// In case OuterStmt is already set, we should clear it to nullptr.
OuterStmt = nullptr;
else
OuterStmt = S;
}
}
return true;
}
CallExpr *findEnclosingCall(SourceFile &SF, SourceLoc SL) {
EnclosingCall = nullptr;
OuterExpr = nullptr;
OuterStmt = nullptr;
TargetLoc = SL;
walk(SF);
return EnclosingCall;
}
};
CallExprFinder CEFinder(SM);
auto *CE = CEFinder.findEnclosingCall(SF, SL);
if (!CE)
return std::make_pair(CE, false);
if (CEFinder.OuterExpr)
return std::make_pair(CE, false);
if (CEFinder.OuterStmt)
return std::make_pair(CE, false);
return std::make_pair(CE, true);
}
bool shouldUseTrailingClosureInTuple(TupleExpr *TE,
SourceLoc PlaceHolderStartLoc) {
if (!TE->getElements().empty()) {
for (unsigned I = 0, N = TE->getNumElements(); I < N; ++ I) {
bool IsLast = I == N - 1;
Expr *E = TE->getElement(I);
if (IsLast) {
return E->getStartLoc() == PlaceHolderStartLoc;
} else if (containClosure(E)) {
return false;
}
}
}
return false;
}
public:
explicit PlaceholderExpansionScanner(SourceManager &SM) : SM(SM) { }
/// Retrieves the parameter list, return type and context info for
/// a typed completion placeholder in a function call.
/// For example: foo.bar(aaa, <#T##(Int, Int) -> Bool#>).
bool scan(SourceFile &SF, unsigned BufID, unsigned Offset,
unsigned Length, std::function<void(Expr *Args,
bool UseTrailingClosure,
ArrayRef<Param>,
CharSourceRange)> Callback,
std::function<bool(EditorPlaceholderExpr*)> NonClosureCallback) {
SourceLoc PlaceholderStartLoc = SM.getLocForOffset(BufID, Offset);
// See if the placeholder is encapsulated with an EditorPlaceholderExpr
// and retrieve parameter and return type ranges.
if (!scanClosureType(SF, PlaceholderStartLoc)) {
return NonClosureCallback(PHE);
}
// Now we need to see if we can suggest trailing closure expansion,
// and if the call parens can be removed in that case.
// We'll first find the enclosing CallExpr, and then do further analysis.
bool UseTrailingClosure = false;
std::pair<CallExpr*, bool> ECE = enclosingCallExpr(SF, PlaceholderStartLoc);
Expr *Args = ECE.first ? ECE.first->getArg() : nullptr;
if (Args && ECE.second) {
if (isa<ParenExpr>(Args)) {
UseTrailingClosure = true;
} else if (auto *TE = dyn_cast<TupleExpr>(Args)) {
UseTrailingClosure = shouldUseTrailingClosureInTuple(TE,
PlaceholderStartLoc);
}
}
Callback(Args, UseTrailingClosure, TargetClosureInfo.Params,
TargetClosureInfo.ReturnTypeRange);
return true;
}
};
} // anonymous namespace
SwiftEditorDocument::SwiftEditorDocument(StringRef FilePath,
SwiftLangSupport &LangSupport, CodeFormatOptions Options)
:Impl(*new Implementation(FilePath, LangSupport, Options)) { }
SwiftEditorDocument::~SwiftEditorDocument()
{
delete &Impl;
}
ImmutableTextSnapshotRef SwiftEditorDocument::initializeText(
llvm::MemoryBuffer *Buf, ArrayRef<const char *> Args) {
llvm::sys::ScopedLock L(Impl.AccessMtx);
Impl.Edited = false;
Impl.EditableBuffer =
new EditableTextBuffer(Impl.FilePath, Buf->getBuffer());
// Reset the syntax map data and affected range
Impl.SyntaxMap.Tokens.clear();
Impl.AffectedRange = {0, static_cast<unsigned>(Buf->getBufferSize())};
Impl.SemanticInfo =
new SwiftDocumentSemanticInfo(Impl.FilePath, Impl.LangSupport);
Impl.SemanticInfo->setCompilerArgs(Args);
return Impl.EditableBuffer->getSnapshot();
}
ImmutableTextSnapshotRef SwiftEditorDocument::replaceText(
unsigned Offset, unsigned Length, llvm::MemoryBuffer *Buf,
bool ProvideSemanticInfo) {
llvm::sys::ScopedLock L(Impl.AccessMtx);
Impl.Edited = true;
llvm::StringRef Str = Buf->getBuffer();
// Update the buffer itself
ImmutableTextSnapshotRef Snapshot =
Impl.EditableBuffer->replace(Offset, Length, Str);
if (ProvideSemanticInfo) {
// If this is not a no-op, update semantic info.
if (Length != 0 || Buf->getBufferSize() != 0) {
updateSemaInfo();
if (auto Invok = Impl.SemanticInfo->getInvocation()) {
// Update semantic info for open editor documents of the same module.
// FIXME: Detect edits that don't affect other files, e.g. whitespace,
// comments, inside a function body, etc.
CompilerInvocation CI;
Invok->applyTo(CI);
auto &EditorDocs = Impl.LangSupport.getEditorDocuments();
for (auto &Input : CI.getInputFilenames()) {
if (auto EditorDoc = EditorDocs.findByPath(Input)) {
if (EditorDoc.get() != this)
EditorDoc->updateSemaInfo();
}
}
}
}
}
// Update the old syntax map offsets to account for the replaced range.
// Also set the initial AffectedRange to cover any tokens that
// the replaced range intersected. This allows for clients that split
// multi-line tokens at line boundaries, and ensure all parts of these tokens
// will be cleared.
Impl.AffectedRange = Impl.SyntaxMap.adjustForReplacement(Offset, Length, Str.size());
return Snapshot;
}
void SwiftEditorDocument::updateSemaInfo() {
if (Impl.SemanticInfo) {
Impl.SemanticInfo->processLatestSnapshotAsync(Impl.EditableBuffer);
}
}
void SwiftEditorDocument::parse(ImmutableTextSnapshotRef Snapshot,
SwiftLangSupport &Lang) {
llvm::sys::ScopedLock L(Impl.AccessMtx);
assert(Impl.SemanticInfo && "Impl.SemanticInfo must be set");
std::vector<std::string> Args;
std::string PrimaryFile; // Ignored, Impl.FilePath will be used
CompilerInvocation CompInv;
if (Impl.SemanticInfo->getInvocation()) {
Impl.SemanticInfo->getInvocation()->applyTo(CompInv);
Impl.SemanticInfo->getInvocation()->raw(Args, PrimaryFile);
} else {
ArrayRef<const char *> Args;
std::string Error;
// Ignore possible error(s)
Lang.getASTManager().
initCompilerInvocation(CompInv, Args, StringRef(), Error);
}
// Access to Impl.SyntaxInfo is guarded by Impl.AccessMtx
Impl.SyntaxInfo.reset(
new SwiftDocumentSyntaxInfo(CompInv, Snapshot, Args, Impl.FilePath));
Impl.SyntaxInfo->parse();
}
void SwiftEditorDocument::readSyntaxInfo(EditorConsumer &Consumer) {
llvm::sys::ScopedLock L(Impl.AccessMtx);
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation Info;
Impl.buildSwiftInv(Info);
TracedOp.start(trace::OperationKind::ReadSyntaxInfo, Info);
}
Impl.ParserDiagnostics = Impl.SyntaxInfo->getDiagnostics();
ide::SyntaxModelContext ModelContext(Impl.SyntaxInfo->getSourceFile());
SwiftSyntaxMap NewMap = SwiftSyntaxMap(Impl.SyntaxMap.Tokens.size() + 16);
SwiftEditorSyntaxWalker SyntaxWalker(NewMap,
Impl.SyntaxInfo->getSourceManager(),
Consumer,
Impl.SyntaxInfo->getBufferID());
ModelContext.walk(SyntaxWalker);
bool SawChanges = true;
if (Impl.Edited) {
// We're ansering an edit request. Report all highlighted token ranges not
// in the previous syntax map to the Consumer and extend the AffectedRange
// to contain all added/removed token ranges.
SawChanges = NewMap.forEachChanged(Impl.SyntaxMap, Impl.AffectedRange,
Consumer);
} else {
// The is an open/initialise. Report all highlighted token ranges to the
// Consumer.
NewMap.forEach(Consumer);
}
Impl.SyntaxMap = std::move(NewMap);
// Recording an affected length of 0 still results in the client updating its
// copy of the syntax map (by clearning all tokens on the line of the affected
// offset). We need to not record it at all to signal a no-op.
if (SawChanges)
Consumer.recordAffectedRange(Impl.AffectedRange->Offset,
Impl.AffectedRange->length());
}
void SwiftEditorDocument::readSemanticInfo(ImmutableTextSnapshotRef Snapshot,
EditorConsumer& Consumer) {
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation Info;
Impl.buildSwiftInv(Info);
TracedOp.start(trace::OperationKind::ReadSemanticInfo, Info);
}
std::vector<SwiftSemanticToken> SemaToks;
Optional<std::vector<DiagnosticEntryInfo>> SemaDiags;
Impl.SemanticInfo->readSemanticInfo(Snapshot, SemaToks, SemaDiags,
Impl.ParserDiagnostics);
for (auto SemaTok : SemaToks) {
unsigned Offset = SemaTok.ByteOffset;
unsigned Length = SemaTok.Length;
UIdent Kind = SemaTok.getUIdentForKind();
bool IsSystem = SemaTok.getIsSystem();
if (Kind.isValid())
if (!Consumer.handleSemanticAnnotation(Offset, Length, Kind, IsSystem))
break;
}
static UIdent SemaDiagStage("source.diagnostic.stage.swift.sema");
static UIdent ParseDiagStage("source.diagnostic.stage.swift.parse");
// If there's no value returned for diagnostics it means they are out-of-date
// (based on a different snapshot).
if (SemaDiags.hasValue()) {
Consumer.setDiagnosticStage(SemaDiagStage);
for (auto &Diag : SemaDiags.getValue())
Consumer.handleDiagnostic(Diag, SemaDiagStage);
} else {
Consumer.setDiagnosticStage(ParseDiagStage);
for (auto &Diag : Impl.ParserDiagnostics)
Consumer.handleDiagnostic(Diag, ParseDiagStage);
}
}
void SwiftEditorDocument::removeCachedAST() {
Impl.SemanticInfo->removeCachedAST();
}
void SwiftEditorDocument::applyFormatOptions(OptionsDictionary &FmtOptions) {
static UIdent KeyUseTabs("key.editor.format.usetabs");
static UIdent KeyIndentWidth("key.editor.format.indentwidth");
static UIdent KeyTabWidth("key.editor.format.tabwidth");
FmtOptions.valueForOption(KeyUseTabs, Impl.FormatOptions.UseTabs);
FmtOptions.valueForOption(KeyIndentWidth, Impl.FormatOptions.IndentWidth);
FmtOptions.valueForOption(KeyTabWidth, Impl.FormatOptions.TabWidth);
}
const CodeFormatOptions &SwiftEditorDocument::getFormatOptions() {
return Impl.FormatOptions;
}
void SwiftEditorDocument::formatText(unsigned Line, unsigned Length,
EditorConsumer &Consumer) {
auto SyntaxInfo = Impl.getSyntaxInfo();
SourceFile &SF = SyntaxInfo->getSourceFile();
SourceManager &SM = SyntaxInfo->getSourceManager();
unsigned BufID = SyntaxInfo->getBufferID();
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation SwiftArgs;
// Compiler arguments do not matter
auto Buf = SM.getLLVMSourceMgr().getMemoryBuffer(BufID);
SwiftArgs.Args.PrimaryFile = Buf->getBufferIdentifier();
SwiftArgs.addFile(SwiftArgs.Args.PrimaryFile, Buf->getBuffer());
trace::StringPairs OpArgs = {
std::make_pair("Line", std::to_string(Line)),
std::make_pair("Length", std::to_string(Length)),
std::make_pair("IndentWidth",
std::to_string(Impl.FormatOptions.IndentWidth)),
std::make_pair("TabWidth",
std::to_string(Impl.FormatOptions.TabWidth)),
std::make_pair("UseTabs",
std::to_string(Impl.FormatOptions.UseTabs))};
TracedOp.start(trace::OperationKind::FormatText, SwiftArgs, OpArgs);
}
LineRange inputRange = LineRange(Line, Length);
CodeFormatOptions Options = getFormatOptions();
auto indented = reformat(inputRange, Options, SM, SF);
LineRange LineRange = indented.first;
StringRef ModifiedText = indented.second;
Consumer.recordFormattedText(ModifiedText);
Consumer.recordAffectedLineRange(LineRange.startLine(), LineRange.lineCount());
}
bool isReturningVoid(SourceManager &SM, CharSourceRange Range) {
if (Range.isInvalid())
return false;
StringRef Text = SM.extractText(Range);
return "()" == Text || "Void" == Text;
}
void SwiftEditorDocument::expandPlaceholder(unsigned Offset, unsigned Length,
EditorConsumer &Consumer) {
auto SyntaxInfo = Impl.getSyntaxInfo();
SourceManager &SM = SyntaxInfo->getSourceManager();
unsigned BufID = SyntaxInfo->getBufferID();
const unsigned PlaceholderStartLen = 2;
const unsigned PlaceholderEndLen = 2;
if (Length < (PlaceholderStartLen + PlaceholderEndLen)) {
Consumer.handleRequestError("Invalid Length parameter");
return;
}
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation SwiftArgs;
SyntaxInfo->initArgsAndPrimaryFile(SwiftArgs);
auto Buf = SM.getLLVMSourceMgr().getMemoryBuffer(BufID);
SwiftArgs.addFile(Buf->getBufferIdentifier(), Buf->getBuffer());
trace::StringPairs OpArgs = {
std::make_pair("Offset", std::to_string(Offset)),
std::make_pair("Length", std::to_string(Length))};
TracedOp.start(trace::OperationKind::ExpandPlaceholder, SwiftArgs, OpArgs);
}
PlaceholderExpansionScanner Scanner(SM);
SourceFile &SF = SyntaxInfo->getSourceFile();
Scanner.scan(SF, BufID, Offset, Length,
[&](Expr *Args,
bool UseTrailingClosure,
ArrayRef<PlaceholderExpansionScanner::Param> ClosureParams,
CharSourceRange ClosureReturnTypeRange) {
unsigned EffectiveOffset = Offset;
unsigned EffectiveLength = Length;
llvm::SmallString<128> ExpansionStr;
{
llvm::raw_svector_ostream OS(ExpansionStr);
if (UseTrailingClosure) {
assert(Args);
if (isa<ParenExpr>(Args)) {
// There appears to be no other parameters in this call, so we'll
// expand replacement for trailing closure and cover call parens.
// For example:
// foo.bar(<#closure#>) turns into foo.bar <#closure#>.
EffectiveOffset = SM.getLocOffsetInBuffer(Args->getStartLoc(), BufID);
OS << " ";
} else {
auto *TupleE = cast<TupleExpr>(Args);
auto Elems = TupleE->getElements();
assert(!Elems.empty());
if (Elems.size() == 1) {
EffectiveOffset = SM.getLocOffsetInBuffer(Args->getStartLoc(), BufID);
OS << " ";
} else {
// Expand replacement range for trailing closure.
// For example:
// foo.bar(a, <#closure#>) turns into foo.bar(a) <#closure#>.
// If the preceding token in the call is the leading parameter
// separator, we'll expand replacement to cover that.
assert(Elems.size() > 1);
SourceLoc BeforeLoc = Lexer::getLocForEndOfToken(SM,
Elems[Elems.size()-2]->getEndLoc());
EffectiveOffset = SM.getLocOffsetInBuffer(BeforeLoc, BufID);
OS << ") ";
}
}
unsigned End = SM.getLocOffsetInBuffer(Args->getEndLoc(), BufID);
EffectiveLength = (End + 1) - EffectiveOffset;
}
OS << "{ ";
bool ReturningVoid = isReturningVoid(SM, ClosureReturnTypeRange);
bool HasSignature = !ClosureParams.empty() ||
(ClosureReturnTypeRange.isValid() && !ReturningVoid);
bool FirstParam = true;
if (HasSignature)
OS << "(";
for (auto &Param: ClosureParams) {
if (!FirstParam)
OS << ", ";
FirstParam = false;
if (Param.NameRange.isValid()) {
// If we have a parameter name, just output the name as is and skip
// the type. For example:
// <#(arg1: Int, arg2: Int)#> turns into (arg1, arg2).
OS << SM.extractText(Param.NameRange);
}
else {
// If we only have the parameter type, output the type as a
// placeholder. For example:
// <#(Int, Int)#> turns into (<#Int#>, <#Int#>).
OS << "<#";
OS << SM.extractText(Param.TypeRange);
OS << "#>";
}
}
if (HasSignature)
OS << ") ";
if (ClosureReturnTypeRange.isValid()) {
auto ReturnTypeText = SM.extractText(ClosureReturnTypeRange);
// We need return type if it is not Void.
if (!ReturningVoid) {
OS << "-> ";
OS << ReturnTypeText << " ";
}
}
if (HasSignature)
OS << "in";
OS << "\n" << getCodePlaceholder() << "\n";
OS << "}";
}
Consumer.handleSourceText(ExpansionStr);
Consumer.recordAffectedRange(EffectiveOffset, EffectiveLength);
}, [&](EditorPlaceholderExpr *PHE) {
if (!PHE)
return false;
if (auto Ty = PHE->getTypeForExpansion()) {
std::string S;
llvm::raw_string_ostream OS(S);
Ty->print(OS);
Consumer.handleSourceText(OS.str());
Consumer.recordAffectedRange(Offset, Length);
return true;
}
return false;
});
}
ImmutableTextSnapshotRef SwiftEditorDocument::getLatestSnapshot() const {
return Impl.EditableBuffer->getSnapshot();
}
void SwiftEditorDocument::reportDocumentStructure(SourceFile &SrcFile,
EditorConsumer &Consumer) {
ide::SyntaxModelContext ModelContext(SrcFile);
SwiftDocumentStructureWalker Walker(SrcFile.getASTContext().SourceMgr,
*SrcFile.getBufferID(),
Consumer);
ModelContext.walk(Walker);
}
//===----------------------------------------------------------------------===//
// EditorOpen
//===----------------------------------------------------------------------===//
void SwiftLangSupport::editorOpen(StringRef Name, llvm::MemoryBuffer *Buf,
bool EnableSyntaxMap,
EditorConsumer &Consumer,
ArrayRef<const char *> Args) {
ImmutableTextSnapshotRef Snapshot = nullptr;
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
if (!EditorDoc) {
EditorDoc = new SwiftEditorDocument(Name, *this);
Snapshot = EditorDoc->initializeText(Buf, Args);
EditorDoc->parse(Snapshot, *this);
if (EditorDocuments.getOrUpdate(Name, *this, EditorDoc)) {
// Document already exists, re-initialize it. This should only happen
// if we get OPEN request while the previous document is not closed.
LOG_WARN_FUNC("Document already exists in editorOpen(..): " << Name);
Snapshot = nullptr;
}
}
if (!Snapshot) {
Snapshot = EditorDoc->initializeText(Buf, Args);
EditorDoc->parse(Snapshot, *this);
}
if (Consumer.needsSemanticInfo()) {
EditorDoc->updateSemaInfo();
}
EditorDoc->readSyntaxInfo(Consumer);
EditorDoc->readSemanticInfo(Snapshot, Consumer);
}
//===----------------------------------------------------------------------===//
// EditorClose
//===----------------------------------------------------------------------===//
void SwiftLangSupport::editorClose(StringRef Name, bool RemoveCache) {
auto Removed = EditorDocuments.remove(Name);
if (!Removed)
IFaceGenContexts.remove(Name);
if (Removed && RemoveCache)
Removed->removeCachedAST();
// FIXME: Report error if Name did not apply to anything ?
}
//===----------------------------------------------------------------------===//
// EditorReplaceText
//===----------------------------------------------------------------------===//
void SwiftLangSupport::editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf,
unsigned Offset, unsigned Length,
EditorConsumer &Consumer) {
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
if (!EditorDoc) {
Consumer.handleRequestError("No associated Editor Document");
return;
}
ImmutableTextSnapshotRef Snapshot;
if (Length != 0 || Buf->getBufferSize() != 0) {
Snapshot = EditorDoc->replaceText(Offset, Length, Buf,
Consumer.needsSemanticInfo());
assert(Snapshot);
EditorDoc->parse(Snapshot, *this);
EditorDoc->readSyntaxInfo(Consumer);
} else {
Snapshot = EditorDoc->getLatestSnapshot();
}
EditorDoc->readSemanticInfo(Snapshot, Consumer);
}
//===----------------------------------------------------------------------===//
// EditorFormatText
//===----------------------------------------------------------------------===//
void SwiftLangSupport::editorApplyFormatOptions(StringRef Name,
OptionsDictionary &FmtOptions) {
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
if (EditorDoc)
EditorDoc->applyFormatOptions(FmtOptions);
}
void SwiftLangSupport::editorFormatText(StringRef Name, unsigned Line,
unsigned Length,
EditorConsumer &Consumer) {
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
if (!EditorDoc) {
Consumer.handleRequestError("No associated Editor Document");
return;
}
EditorDoc->formatText(Line, Length, Consumer);
}
void SwiftLangSupport::editorExtractTextFromComment(StringRef Source,
EditorConsumer &Consumer) {
Consumer.handleSourceText(extractPlainTextFromComment(Source));
}
void SwiftLangSupport::editorConvertMarkupToXML(StringRef Source,
EditorConsumer &Consumer) {
std::string Result;
llvm::raw_string_ostream OS(Result);
if (convertMarkupToXML(Source, OS)) {
Consumer.handleRequestError("Conversion failed.");
return;
}
Consumer.handleSourceText(Result);
}
//===----------------------------------------------------------------------===//
// EditorExpandPlaceholder
//===----------------------------------------------------------------------===//
void SwiftLangSupport::editorExpandPlaceholder(StringRef Name, unsigned Offset,
unsigned Length,
EditorConsumer &Consumer) {
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
if (!EditorDoc) {
Consumer.handleRequestError("No associated Editor Document");
return;
}
EditorDoc->expandPlaceholder(Offset, Length, Consumer);
}