Files
swift-mirror/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp
Chris Lattner 7daaa22d93 Completely reimplement/redesign the AST representation of parameters.
Parameters (to methods, initializers, accessors, subscripts, etc) have always been represented
as Pattern's (of a particular sort), stemming from an early design direction that was abandoned.
Being built on top of patterns leads to patterns being overly complicated (e.g. tuple patterns
have to have varargs and default parameters) and make working on parameter lists complicated
and error prone.  This might have been ok in 2015, but there is no way we can live like this in
2016.

Instead of using Patterns, carve out a new ParameterList and Parameter type to represent all the
parameter specific stuff.  This simplifies many things and allows a lot of simplifications.
Unfortunately, I wasn't able to do this very incrementally, so this is a huge patch.  The good
news is that it erases a ton of code, and the technical debt that went with it.  Ignoring test
suite changes, we have:
   77 files changed, 2359 insertions(+), 3221 deletions(-)

This patch also makes a bunch of wierd things dead, but I'll sweep those out in follow-on
patches.

Fixes <rdar://problem/22846558> No code completions in Foo( when Foo has error type
Fixes <rdar://problem/24026538> Slight regression in generated header, which I filed to go with 3a23d75.

Fixes an overloading bug involving default arguments and curried functions (see the diff to
Constraints/diagnostics.swift, which we now correctly accept).

Fixes cases where problems with parameters would get emitted multiple times, e.g. in the
test/Parse/subscripting.swift testcase.

The source range for ParamDecl now includes its type, which permutes some of the IDE / SourceModel tests
(for the better, I think).

Eliminates the bogus "type annotation missing in pattern" error message when a type isn't
specified for a parameter (see test/decl/func/functions.swift).

This now consistently parenthesizes argument lists in function types, which leads to many diffs in the
SILGen tests among others.

This does break the "sibling indentation" test in SourceKit/CodeFormat/indent-sibling.swift, and
I haven't been able to figure it out.  Given that this is experimental functionality anyway,
I'm just XFAILing the test for now.  i'll look at it separately from this mongo diff.
2015-12-31 19:24:46 -08:00

2930 lines
97 KiB
C++

//===--- SwiftEditor.cpp --------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://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/AST.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/DiagnosticsClangImporter.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/CodeCompletion.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IDE/SyntaxModel.h"
#include "swift/IDE/SourceEntityWalker.h"
#include "swift/Subsystems.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 Text,
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;
SKInfo.Description = Text;
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 Column;
unsigned Length:24;
swift::ide::SyntaxNodeKind Kind:8;
SwiftSyntaxToken(unsigned Column, unsigned Length,
swift::ide::SyntaxNodeKind Kind)
:Column(Column), Length(Length), Kind(Kind) { }
};
class SwiftSyntaxMap {
typedef std::vector<SwiftSyntaxToken> SwiftSyntaxLineMap;
std::vector<SwiftSyntaxLineMap> Lines;
public:
bool matchesFirstTokenOnLine(unsigned Line,
const SwiftSyntaxToken &Token) const {
assert(Line > 0);
if (Lines.size() < Line)
return false;
unsigned LineOffset = Line - 1;
const SwiftSyntaxLineMap &LineMap = Lines[LineOffset];
if (LineMap.empty())
return false;
const SwiftSyntaxToken &Tok = LineMap.front();
if (Tok.Column == Token.Column && Tok.Length == Token.Length
&& Tok.Kind == Token.Kind) {
return true;
}
return false;
}
void addTokenForLine(unsigned Line, const SwiftSyntaxToken &Token) {
assert(Line > 0);
if (Lines.size() < Line) {
Lines.resize(Line);
}
unsigned LineOffset = Line - 1;
SwiftSyntaxLineMap &LineMap = Lines[LineOffset];
// FIXME: Assert this token is after the last one
LineMap.push_back(Token);
}
void mergeTokenForLine(unsigned Line, const SwiftSyntaxToken &Token) {
assert(Line > 0);
if (Lines.size() < Line) {
Lines.resize(Line);
}
unsigned LineOffset = Line - 1;
SwiftSyntaxLineMap &LineMap = Lines[LineOffset];
if (!LineMap.empty()) {
auto &LastTok = LineMap.back();
mergeSplitRanges(LastTok.Column, LastTok.Length,
Token.Column, Token.Length,
[&](unsigned BeforeOff, unsigned BeforeLen,
unsigned AfterOff, unsigned AfterLen) {
auto LastKind = LastTok.Kind;
LineMap.pop_back();
if (BeforeLen)
LineMap.emplace_back(BeforeOff, BeforeLen, LastKind);
LineMap.push_back(Token);
if (AfterLen)
LineMap.emplace_back(AfterOff, AfterLen, LastKind);
});
}
else {
// Not overlapping, just add the new token to the end
LineMap.push_back(Token);
}
}
void clearLineRange(unsigned StartLine, unsigned Length) {
assert(StartLine > 0);
unsigned LineOffset = StartLine - 1;
for (unsigned Line = LineOffset; Line < LineOffset + Length
&& Line < Lines.size(); ++Line) {
Lines[Line].clear();
}
}
void removeLineRange(unsigned StartLine, unsigned Length) {
assert(StartLine > 0 && Length > 0);
if (StartLine < Lines.size()) {
unsigned EndLine = StartLine + Length - 1;
// Delete all syntax map data from start line through end line
Lines.erase(Lines.begin() + StartLine - 1,
EndLine >= Lines.size() ? Lines.end()
: Lines.begin() + EndLine);
}
}
void insertLineRange(unsigned StartLine, unsigned Length) {
Lines.insert(StartLine <= Lines.size() ? Lines.begin() + StartLine - 1
: Lines.end(),
Length, SwiftSyntaxLineMap());
}
void reset() {
Lines.clear();
}
};
struct EditorConsumerSyntaxMapEntry {
unsigned Offset;
unsigned Length;
UIdent Kind;
EditorConsumerSyntaxMapEntry(unsigned Offset, unsigned Length, UIdent Kind)
:Offset(Offset), Length(Length), Kind(Kind) { }
};
class SwiftEditorLineRange {
unsigned StartLine;
unsigned Length;
public:
SwiftEditorLineRange()
:StartLine(0), Length(0) { }
SwiftEditorLineRange(unsigned StartLine, unsigned Length)
:StartLine(StartLine), Length(Length) { }
SwiftEditorLineRange(const SwiftEditorLineRange &Other)
:StartLine(Other.StartLine), Length(Other.Length) { }
bool isValid() const {
return Length != 0;
}
unsigned startLine() const {
return StartLine;
}
unsigned endLine() const {
return isValid() ? StartLine + Length - 1 : 0;
}
unsigned lineCount() const {
return Length;
}
void setRange(unsigned NewStartLine, unsigned NewLength) {
StartLine = NewStartLine;
Length = NewLength;
}
void extendToIncludeLine(unsigned Line) {
if (!isValid()) {
StartLine = Line;
Length = 1;
}
else if (Line >= StartLine + Length) {
Length = Line - StartLine + 1;
}
}
};
typedef std::pair<unsigned, unsigned> SwiftEditorCharRange;
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;
bool IsRef : 1;
bool IsSystem : 1;
SwiftSemanticToken(CodeCompletionDeclKind Kind,
unsigned ByteOffset, unsigned Length,
bool IsRef, bool IsSystem)
: ByteOffset(ByteOffset), Length(Length), Kind(Kind),
IsRef(IsRef), IsSystem(IsSystem) { }
UIdent getUIdentForKind() const {
return SwiftLangSupport::getUIDForCodeCompletionDeclKind(Kind, IsRef);
}
};
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,
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);
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,
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);
}
static bool
adjustDiagnosticRanges(SmallVectorImpl<std::pair<unsigned, unsigned>> &Ranges,
unsigned ByteOffset, unsigned RemoveLen, int Delta) {
for (auto &Range : Ranges) {
unsigned RangeBegin = Range.first;
unsigned RangeEnd = Range.first + Range.second;
unsigned RemoveEnd = ByteOffset + RemoveLen;
// If it intersects with the remove range, ignore the whole diagnostic.
if (!(RangeEnd < ByteOffset || RangeBegin > RemoveEnd))
return true; // Ignore.
if (RangeBegin > RemoveEnd)
Range.first += Delta;
}
return false;
}
static bool
adjustDiagnosticFixits(SmallVectorImpl<DiagnosticEntryInfo::Fixit> &Fixits,
unsigned ByteOffset, unsigned RemoveLen, int Delta) {
for (auto &Fixit : Fixits) {
unsigned FixitBegin = Fixit.Offset;
unsigned FixitEnd = Fixit.Offset + Fixit.Length;
unsigned RemoveEnd = ByteOffset + RemoveLen;
// If it intersects with the remove range, ignore the whole diagnostic.
if (!(FixitEnd < ByteOffset || FixitBegin > RemoveEnd))
return true; // Ignore.
if (FixitBegin > FixitEnd)
Fixit.Offset += Delta;
}
return false;
}
static bool
adjustDiagnosticBase(DiagnosticEntryInfoBase &Diag,
unsigned ByteOffset, unsigned RemoveLen, int Delta) {
if (Diag.Offset >= ByteOffset && Diag.Offset < ByteOffset+RemoveLen)
return true; // Ignore.
bool Ignore = adjustDiagnosticRanges(Diag.Ranges, ByteOffset, RemoveLen, Delta);
if (Ignore)
return true;
Ignore = adjustDiagnosticFixits(Diag.Fixits, ByteOffset, RemoveLen, Delta);
if (Ignore)
return true;
if (Diag.Offset > ByteOffset)
Diag.Offset += Delta;
return false;
}
static bool
adjustDiagnostic(DiagnosticEntryInfo &Diag, StringRef Filename,
unsigned ByteOffset, unsigned RemoveLen, int Delta) {
for (auto &Note : Diag.Notes) {
if (Filename != Note.Filename)
continue;
bool Ignore = adjustDiagnosticBase(Note, ByteOffset, RemoveLen, Delta);
if (Ignore)
return true;
}
return adjustDiagnosticBase(Diag, ByteOffset, RemoveLen, Delta);
}
static std::vector<DiagnosticEntryInfo>
adjustDiagnostics(std::vector<DiagnosticEntryInfo> Diags, StringRef Filename,
unsigned ByteOffset, unsigned RemoveLen, int Delta) {
std::vector<DiagnosticEntryInfo> NewDiags;
NewDiags.reserve(Diags.size());
for (auto &Diag : Diags) {
bool Ignore = adjustDiagnostic(Diag, Filename, ByteOffset, RemoveLen, Delta);
if (!Ignore) {
NewDiags.push_back(std::move(Diag));
}
}
return NewDiags;
}
std::vector<DiagnosticEntryInfo>
SwiftDocumentSemanticInfo::getSemanticDiagnostics(
ImmutableTextSnapshotRef NewSnapshot,
ArrayRef<DiagnosticEntryInfo> ParserDiags) {
llvm::sys::ScopedLock L(Mtx);
if (SemaDiags.empty())
return SemaDiags;
assert(DiagSnapshot && "If we have diagnostics, we must have snapshot!");
if (!DiagSnapshot->precedesOrSame(NewSnapshot)) {
// It may happen that other thread has already updated the diagnostics to
// the version *after* NewSnapshot. This can happen in at least two cases:
// (a) two or more editor.open or editor.replacetext queries are being
// processed concurrently (not valid, but possible call pattern)
// (b) while editor.replacetext processing is running, a concurrent
// thread executes getBuffer()/getBufferForSnapshot() on the same
// Snapshot/EditableBuffer (thus creating a new ImmutableTextBuffer)
// and updates DiagSnapshot/SemaDiags
assert(NewSnapshot->precedesOrSame(DiagSnapshot));
// Since we cannot "adjust back" diagnostics, we just return an empty set.
// FIXME: add handling of the case#b above
return {};
}
SmallVector<unsigned, 16> ParserDiagLines;
for (auto Diag : ParserDiags)
ParserDiagLines.push_back(Diag.Line);
std::sort(ParserDiagLines.begin(), ParserDiagLines.end());
auto hasParserDiagAtLine = [&](unsigned Line) {
return std::binary_search(ParserDiagLines.begin(), ParserDiagLines.end(),
Line);
};
// Adjust the position of the diagnostics.
DiagSnapshot->foreachReplaceUntil(NewSnapshot,
[&](ReplaceImmutableTextUpdateRef Upd) -> bool {
if (SemaDiags.empty())
return false;
unsigned ByteOffset = Upd->getByteOffset();
unsigned RemoveLen = Upd->getLength();
unsigned InsertLen = Upd->getText().size();
int Delta = InsertLen - RemoveLen;
SemaDiags = adjustDiagnostics(std::move(SemaDiags), Filename,
ByteOffset, RemoveLen, Delta);
return true;
});
if (!SemaDiags.empty()) {
auto ImmBuf = NewSnapshot->getBuffer();
for (auto &Diag : SemaDiags) {
std::tie(Diag.Line, Diag.Column) = ImmBuf->getLineAndColumn(Diag.Offset);
}
// If there is a parser diagnostic in a line, ignore diagnostics in the same
// line that we got from the semantic pass.
// Note that the semantic pass also includes parser diagnostics so this
// avoids duplicates.
SemaDiags.erase(std::remove_if(SemaDiags.begin(), SemaDiags.end(),
[&](const DiagnosticEntryInfo &Diag) -> bool {
return hasParserDiagAtLine(Diag.Line);
}),
SemaDiags.end());
}
DiagSnapshot = NewSnapshot;
return SemaDiags;
}
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, Type T) override {
if (isa<VarDecl>(D) && D->hasName() && D->getName().str() == "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, Type());
}
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::CodeFormatOptions {
bool UseTabs = false;
unsigned IndentWidth = 4;
unsigned TabWidth = 4;
};
struct SwiftEditorDocument::Implementation {
SwiftLangSupport &LangSupport;
const std::string FilePath;
EditableTextBufferRef EditableBuffer;
SwiftSyntaxMap SyntaxMap;
SwiftEditorLineRange EditedLineRange;
SwiftEditorCharRange AffectedRange;
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)
: LangSupport(LangSupport), FilePath(FilePath) {
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 getAccessibilityUID(Accessibility Access) {
static UIdent AccessPublic("source.lang.swift.accessibility.public");
static UIdent AccessInternal("source.lang.swift.accessibility.internal");
static UIdent AccessPrivate("source.lang.swift.accessibility.private");
switch (Access) {
case Accessibility::Private:
return AccessPrivate;
case Accessibility::Internal:
return AccessInternal;
case Accessibility::Public:
return AccessPublic;
}
}
static Accessibility inferDefaultAccessibility(const ExtensionDecl *ED) {
if (ED->hasDefaultAccessibility())
return ED->getDefaultAccessibility();
if (auto *AA = ED->getAttrs().getAttribute<AccessibilityAttr>())
return AA->getAccess();
// Assume "internal", which is the most common thing anyway.
return Accessibility::Internal;
}
/// If typechecking was performed we use the computed accessibility, otherwise
/// we fallback to inferring accessibility syntactically. This may not be as
/// accurate but it's only until we have typechecked the AST.
static Accessibility inferAccessibility(const ValueDecl *D) {
assert(D);
if (D->hasAccessibility())
return D->getFormalAccess();
// Check if the decl has an explicit accessibility attribute.
if (auto *AA = D->getAttrs().getAttribute<AccessibilityAttr>())
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 Accessibility::Private;
case DeclContextKind::Module:
case DeclContextKind::FileUnit:
return Accessibility::Internal;
case DeclContextKind::NominalTypeDecl: {
auto Nominal = cast<NominalTypeDecl>(DC);
Accessibility Access = inferAccessibility(Nominal);
if (!isa<ProtocolDecl>(Nominal))
Access = std::min(Access, Accessibility::Internal);
return Access;
}
case DeclContextKind::ExtensionDecl:
return inferDefaultAccessibility(cast<ExtensionDecl>(DC));
}
}
static Optional<Accessibility>
inferSetterAccessibility(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<SetterAccessibilityAttr>())
return AA->getAccess();
else
return inferAccessibility(D);
}
std::vector<UIdent> UIDsFromDeclAttributes(const DeclAttributes &Attrs) {
std::vector<UIdent> AttrUIDs;
#define ATTR(X) \
if (Attrs.has(AK_##X)) { \
static UIdent Attr_##X("source.decl.attribute."#X); \
AttrUIDs.push_back(Attr_##X); \
}
#include "swift/AST/Attr.def"
for (auto Attr : Attrs) {
// Check special-case names first.
switch (Attr->getKind()) {
case DAK_IBAction: {
static UIdent Attr_IBAction("source.decl.attribute.ibaction");
AttrUIDs.push_back(Attr_IBAction);
continue;
}
case DAK_IBOutlet: {
static UIdent Attr_IBOutlet("source.decl.attribute.iboutlet");
AttrUIDs.push_back(Attr_IBOutlet);
continue;
}
case DAK_IBDesignable: {
static UIdent Attr_IBDesignable("source.decl.attribute.ibdesignable");
AttrUIDs.push_back(Attr_IBDesignable);
continue;
}
case DAK_IBInspectable: {
static UIdent Attr_IBInspectable("source.decl.attribute.ibinspectable");
AttrUIDs.push_back(Attr_IBInspectable);
continue;
}
case DAK_ObjC: {
static UIdent Attr_Objc("source.decl.attribute.objc");
static UIdent Attr_ObjcNamed("source.decl.attribute.objc.name");
if (cast<ObjCAttr>(Attr)->hasName()) {
AttrUIDs.push_back(Attr_ObjcNamed);
} else {
AttrUIDs.push_back(Attr_Objc);
}
continue;
}
// We handle accessibility explicitly.
case DAK_Accessibility:
case DAK_SetterAccessibility:
continue;
default:
break;
}
switch (Attr->getKind()) {
case DAK_Count:
break;
#define DECL_ATTR(X, CLASS, ...)\
case DAK_##CLASS: {\
static UIdent Attr_##X("source.decl.attribute."#X); \
AttrUIDs.push_back(Attr_##X); \
break;\
}
#include "swift/AST/Attr.def"
}
}
return AttrUIDs;
}
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;
}
UIdent Kind = SwiftLangSupport::getUIDForSyntaxStructureKind(Node.Kind);
UIdent AccessLevel;
UIdent SetterAccessLevel;
if (Node.Kind != SyntaxStructureKind::Parameter) {
if (auto *VD = dyn_cast_or_null<ValueDecl>(Node.Dcl)) {
AccessLevel = getAccessibilityUID(inferAccessibility(VD));
}
if (auto *ASD = dyn_cast_or_null<AbstractStorageDecl>(Node.Dcl)) {
Optional<Accessibility> SetAccess = inferSetterAccessibility(ASD);
if (SetAccess.hasValue()) {
SetterAccessLevel = getAccessibilityUID(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<UIdent> Attrs = UIDsFromDeclAttributes(Node.Attrs);
Consumer.beginDocumentSubStructure(StartOffset, EndOffset - StartOffset,
Kind, AccessLevel, SetterAccessLevel,
NameStart, NameEnd - NameStart,
BodyOffset, BodyEnd - BodyOffset,
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)
return StringRef();
if (!isa<ClassDecl>(D) && !isa<ProtocolDecl>(D))
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,
StringRef(),
StringRef(), StringRef(),
StringRef(),
{}, {});
return true;
}
bool walkToNodePost(SyntaxNode Node) override {
if (Node.Kind != SyntaxNodeKind::CommentMarker)
return true;
Consumer.endDocumentSubStructure();
return true;
}
};
class SwiftEditorSyntaxWalker: public ide::SyntaxModelWalker {
SwiftSyntaxMap &SyntaxMap;
SwiftEditorLineRange EditedLineRange;
SwiftEditorCharRange &AffectedRange;
SourceManager &SrcManager;
EditorConsumer &Consumer;
unsigned BufferID;
SwiftDocumentStructureWalker DocStructureWalker;
std::vector<EditorConsumerSyntaxMapEntry> ConsumerSyntaxMap;
unsigned NestingLevel = 0;
public:
SwiftEditorSyntaxWalker(SwiftSyntaxMap &SyntaxMap,
SwiftEditorLineRange EditedLineRange,
SwiftEditorCharRange &AffectedRange,
SourceManager &SrcManager, EditorConsumer &Consumer,
unsigned BufferID)
: SyntaxMap(SyntaxMap), EditedLineRange(EditedLineRange),
AffectedRange(AffectedRange), SrcManager(SrcManager), Consumer(Consumer),
BufferID(BufferID),
DocStructureWalker(SrcManager, BufferID, Consumer) { }
bool walkToNodePre(SyntaxNode Node) override {
if (Node.Kind == SyntaxNodeKind::CommentMarker)
return DocStructureWalker.walkToNodePre(Node);
++NestingLevel;
SourceLoc StartLoc = Node.Range.getStart();
auto StartLineAndColumn = SrcManager.getLineAndColumn(StartLoc);
auto EndLineAndColumn = SrcManager.getLineAndColumn(Node.Range.getEnd());
unsigned StartLine = StartLineAndColumn.first;
unsigned EndLine = EndLineAndColumn.second > 1 ? EndLineAndColumn.first
: EndLineAndColumn.first - 1;
unsigned Offset = SrcManager.getByteDistance(
SrcManager.getLocForBufferStart(BufferID), StartLoc);
// Note that the length can span multiple lines.
unsigned Length = Node.Range.getByteLength();
SwiftSyntaxToken Token(StartLineAndColumn.second, Length,
Node.Kind);
if (EditedLineRange.isValid()) {
if (StartLine < EditedLineRange.startLine()) {
if (EndLine < EditedLineRange.startLine()) {
// We're entirely before the edited range, no update needed.
return true;
}
// This token starts before the edited range, but doesn't end before it,
// we need to adjust edited line range and clear the affected syntax map
// line range.
unsigned AdjLineCount = EditedLineRange.startLine() - StartLine;
EditedLineRange.setRange(StartLine, AdjLineCount
+ EditedLineRange.lineCount());
SyntaxMap.clearLineRange(StartLine, AdjLineCount);
// Also adjust the affected char range accordingly.
unsigned AdjCharCount = AffectedRange.first - Offset;
AffectedRange.first -= AdjCharCount;
AffectedRange.second += AdjCharCount;
}
else if (Offset > AffectedRange.first + AffectedRange.second) {
// We're passed the affected range and already synced up, just return.
return true;
}
else if (StartLine > EditedLineRange.endLine()) {
// We're after the edited line range, let's test if we're synced up.
if (SyntaxMap.matchesFirstTokenOnLine(StartLine, Token)) {
// We're synced up, mark the affected range and return.
AffectedRange.second =
Offset - (StartLineAndColumn.second - 1) - AffectedRange.first;
return true;
}
// We're not synced up, continue replacing syntax map data on this line.
SyntaxMap.clearLineRange(StartLine, 1);
EditedLineRange.extendToIncludeLine(StartLine);
}
if (EndLine > StartLine) {
// The token spans multiple lines, make sure to replace syntax map data
// for affected lines.
EditedLineRange.extendToIncludeLine(EndLine);
unsigned LineCount = EndLine - StartLine + 1;
SyntaxMap.clearLineRange(StartLine, LineCount);
}
}
// Add the syntax map token.
if (NestingLevel > 1)
SyntaxMap.mergeTokenForLine(StartLine, Token);
else
SyntaxMap.addTokenForLine(StartLine, Token);
// Add consumer entry.
unsigned ByteOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(),
BufferID);
UIdent Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind);
if (NestingLevel > 1) {
assert(!ConsumerSyntaxMap.empty());
auto &Last = ConsumerSyntaxMap.back();
mergeSplitRanges(Last.Offset, Last.Length, ByteOffset, Length,
[&](unsigned BeforeOff, unsigned BeforeLen,
unsigned AfterOff, unsigned AfterLen) {
auto LastKind = Last.Kind;
ConsumerSyntaxMap.pop_back();
if (BeforeLen)
ConsumerSyntaxMap.emplace_back(BeforeOff, BeforeLen, LastKind);
ConsumerSyntaxMap.emplace_back(ByteOffset, Length, Kind);
if (AfterLen)
ConsumerSyntaxMap.emplace_back(AfterOff, AfterLen, LastKind);
});
}
else
ConsumerSyntaxMap.emplace_back(ByteOffset, Length, Kind);
return true;
}
bool walkToNodePost(SyntaxNode Node) override {
if (Node.Kind == SyntaxNodeKind::CommentMarker)
return DocStructureWalker.walkToNodePost(Node);
if (--NestingLevel == 0) {
// We've unwound to the top level, so inform the consumer and drain
// the consumer syntax map queue.
for (auto &Entry: ConsumerSyntaxMap)
Consumer.handleSyntaxMap(Entry.Offset, Entry.Length, Entry.Kind);
ConsumerSyntaxMap.clear();
}
return true;
}
bool walkToSubStructurePre(SyntaxStructureNode Node) override {
return DocStructureWalker.walkToSubStructurePre(Node);
}
bool walkToSubStructurePost(SyntaxStructureNode Node) override {
return DocStructureWalker.walkToSubStructurePost(Node);
}
};
typedef llvm::SmallString<64> StringBuilder;
static SourceLoc getVarDeclInitEnd(VarDecl *VD) {
return VD->getBracesRange().isValid() ? VD->getBracesRange().End :
VD->getParentInitializer() &&
VD->getParentInitializer()->getEndLoc().isValid() ?
VD->getParentInitializer()->getEndLoc() :
SourceLoc();
}
class FormatContext
{
SourceManager &SM;
std::vector<swift::ASTWalker::ParentTy>& Stack;
std::vector<swift::ASTWalker::ParentTy>::reverse_iterator Cursor;
swift::ASTWalker::ParentTy Start;
swift::ASTWalker::ParentTy End;
bool InDocCommentBlock;
bool InCommentLine;
SourceLoc SiblingLoc;
public:
FormatContext(SourceManager &SM,
std::vector<swift::ASTWalker::ParentTy>& Stack,
swift::ASTWalker::ParentTy Start = swift::ASTWalker::ParentTy(),
swift::ASTWalker::ParentTy End = swift::ASTWalker::ParentTy(),
bool InDocCommentBlock = false,
bool InCommentLine = false,
SourceLoc SiblingLoc = SourceLoc())
:SM(SM), Stack(Stack), Cursor(Stack.rbegin()), Start(Start), End(End),
InDocCommentBlock(InDocCommentBlock), InCommentLine(InCommentLine),
SiblingLoc(SiblingLoc) { }
FormatContext parent() {
assert(Cursor != Stack.rend());
FormatContext Parent(*this);
++Parent.Cursor;
return Parent;
}
bool IsInDocCommentBlock() {
return InDocCommentBlock;
}
bool IsInCommentLine() {
return InCommentLine;
}
void padToSiblingColumn(StringBuilder &Builder) {
assert(SiblingLoc.isValid() && "No sibling to align with.");
CharSourceRange Range(SM, Lexer::getLocForStartOfLine(SM, SiblingLoc),
SiblingLoc);
for (auto C : Range.str()) {
Builder.append(1, C == '\t' ? C : ' ');
}
}
bool HasSibling() {
return SiblingLoc.isValid();
}
std::pair<unsigned, unsigned> lineAndColumn() {
if (Cursor == Stack.rend())
return std::make_pair(0, 0);
if (Stmt *S = Cursor->getAsStmt()) {
SourceLoc SL = S->getStartLoc();
return SM.getLineAndColumn(SL);
}
if (Decl *D = Cursor->getAsDecl()) {
SourceLoc SL = D->getStartLoc();
// FIXME: put the attributes into forward source order so we don't need
// to iterate through them.
for (auto *Attr : D->getAttrs()) {
SourceLoc AttrLoc = Attr->getRangeWithAt().Start;
if (AttrLoc.isValid() && SM.isBeforeInBuffer(AttrLoc, SL))
SL = AttrLoc;
}
return SM.getLineAndColumn(SL);
}
if (Expr *E = Cursor->getAsExpr()) {
SourceLoc SL = E->getStartLoc();
return SM.getLineAndColumn(SL);
}
return std::make_pair(0, 0);
}
template <class T>
bool isStmtContext() {
if (Cursor == Stack.rend())
return false;
Stmt *ContextStmt = Cursor->getAsStmt();
return ContextStmt && isa<T>(ContextStmt);
}
bool isBraceContext() {
return isStmtContext<BraceStmt>();
}
bool isImplicitBraceContext() {
// If we're directly at the top, it's implicit.
if (Cursor == Stack.rend())
return true;
if (!isBraceContext())
return false;
auto Parent = parent();
// If the parent is directly at the top, it's implicit.
if (Parent.Cursor == Stack.rend())
return true;
// If we're within a case body, it's implicit.
// For example:
// case ...:
// case body is implicitly wrapped in a brace statement
if (Parent.isCaseContext())
return true;
return false;
}
bool isCaseContext() {
return isStmtContext<CaseStmt>();
}
bool isSwitchContext() {
return isStmtContext<SwitchStmt>();
}
std::pair<unsigned, unsigned> indentLineAndColumn() {
if (Cursor == Stack.rend())
return std::make_pair(0, 0);
// Get the line and indent position for this context.
auto LineAndColumn = lineAndColumn();
auto SavedCursor = Cursor;
// Walk up the context stack to find the topmost applicable context.
while (++Cursor != Stack.rend()) {
auto ParentLineAndColumn = lineAndColumn();
if (ParentLineAndColumn.second == 0)
break;
if (ParentLineAndColumn.first != LineAndColumn.first) {
// The start line is not the same, see if this is at the 'else' clause.
if (IfStmt *If = dyn_cast_or_null<IfStmt>(Cursor->getAsStmt())) {
SourceLoc ElseLoc = If->getElseLoc();
// If we're at 'else', take the indent of 'if' and continue.
if (ElseLoc.isValid() &&
LineAndColumn.first == SM.getLineAndColumn(ElseLoc).first) {
LineAndColumn = ParentLineAndColumn;
continue;
}
// If we are at conditions, take the indent of 'if' and continue.
for (auto Cond : If->getCond()) {
if (LineAndColumn.first == SM.getLineNumber(Cond.getEndLoc())) {
LineAndColumn = ParentLineAndColumn;
continue;
}
}
}
// No extra indentation level for getters without explicit names.
// e.g.
// public var someValue: Int {
// return 0; <- No indentation added because of the getter.
// }
if (auto VD = dyn_cast_or_null<VarDecl>(Cursor->getAsDecl())) {
if (auto Getter = VD->getGetter()) {
if (Getter->getAccessorKeywordLoc().isInvalid()) {
LineAndColumn = ParentLineAndColumn;
continue;
}
}
}
// Align with Func start instead of with param decls.
if (auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(Cursor->getAsDecl())) {
if (LineAndColumn.first <= SM.getLineNumber(FD->getSignatureSourceRange().End)) {
LineAndColumn = ParentLineAndColumn;
continue;
}
}
// Break out if the line is no longer the same.
break;
}
LineAndColumn.second = ParentLineAndColumn.second;
}
Cursor = SavedCursor;
return LineAndColumn;
}
bool shouldAddIndentForLine(unsigned Line) {
if (Cursor == Stack.rend())
return false;
// Handle switch / case, indent unless at a case label.
if (CaseStmt *Case = dyn_cast_or_null<CaseStmt>(Cursor->getAsStmt())) {
auto LabelItems = Case->getCaseLabelItems();
SourceLoc Loc;
if (!LabelItems.empty())
Loc = LabelItems.back().getPattern()->getLoc();
if (Loc.isValid())
return Line > SM.getLineAndColumn(Loc).first;
return true;
}
if (isSwitchContext()) {
// If we're at the start of a case label, don't add indent.
// For example:
// switch ... {
// case xyz: <-- No indent here, should be at same level as switch.
Stmt *AtStmtStart = Start.getAsStmt();
if (AtStmtStart && isa<CaseStmt>(AtStmtStart))
return false;
// If we're at the open brace of the switch, don't add an indent.
// For example:
// switch ...
// { <-- No indent here, open brace should be at same level as switch.
auto *S = cast<SwitchStmt>(Cursor->getAsStmt());
if (SM.getLineAndColumn(S->getLBraceLoc()).first == Line)
return false;
if(IsInCommentLine()) {
for (auto Case : S->getCases()) {
// switch ...
// {
// // case comment <-- No indent here.
// case 0:
if (SM.getLineAndColumn(Case->swift::Stmt::getStartLoc()).first == Line + 1)
return false;
}
}
}
// If we're within an implicit brace context, don't add indent.
if (isImplicitBraceContext())
return false;
// If we're at the open brace of a no-name getter, don't add an indent.
// For example:
// public var someValue: Int
// { <- We add no indentation here.
// return 0
// }
if (auto FD = dyn_cast_or_null<FuncDecl>(Start.getAsDecl())) {
if(FD->isGetter() && FD->getAccessorKeywordLoc().isInvalid()) {
if(SM.getLineNumber(FD->getBody()->getLBraceLoc()) == Line)
return false;
}
}
// If we're at the beginning of a brace on a separate line in the context
// of anything other than BraceStmt, don't add an indent.
// For example:
// func foo()
// { <-- No indent here, open brace should be at same level as func.
Stmt *AtStmtStart = Start.getAsStmt();
if (AtStmtStart && isa<BraceStmt>(AtStmtStart) && !isBraceContext())
return false;
// If we're at the end of a brace on a separate line in the context
// of anything other than BraceStmt, don't add an indent.
// For example:
if (Stmt *AtStmtEnd = End.getAsStmt()) {
if (!isBraceContext()) {
// func foo() {
// } <-- No indent here, close brace should be at same level as func.
if (isa<BraceStmt>(AtStmtEnd))
return false;
// do {
// }
// catch {
// } <-- No indent here, close brace should be at same level as do.
// catch {
// }
if (isa<CatchStmt>(AtStmtEnd))
return false;
}
}
// If we're at the open brace of a NominalTypeDecl or ExtensionDecl,
// don't add an indent.
// For example:
// class Foo
// { <-- No indent here, open brace should be at same level as class.
auto *NTD = dyn_cast_or_null<NominalTypeDecl>(Cursor->getAsDecl());
if (NTD && SM.getLineAndColumn(NTD->getBraces().Start).first == Line)
return false;
auto *ETD = dyn_cast_or_null<ExtensionDecl>(Cursor->getAsDecl());
if (ETD && SM.getLineAndColumn(ETD->getBraces().Start).first == Line)
return false;
// If we are at the start of a trailing closure, do not add indentation.
// For example:
// foo(1)
// { <-- No indent here.
auto *TE = dyn_cast_or_null<TupleExpr>(Cursor->getAsExpr());
if (TE && TE->hasTrailingClosure() &&
SM.getLineNumber(TE->getElements().back()->getStartLoc()) == Line) {
return false;
}
// If we're in an IfStmt and at the 'else', don't add an indent.
IfStmt *If = dyn_cast_or_null<IfStmt>(Cursor->getAsStmt());
if (If && If->getElseLoc().isValid() &&
SM.getLineAndColumn(If->getElseLoc()).first == Line)
return false;
// If we're in a DoCatchStmt and at a 'catch', don't add an indent.
if (auto *DoCatchS = dyn_cast_or_null<DoCatchStmt>(Cursor->getAsStmt())) {
for (CatchStmt *CatchS : DoCatchS->getCatches()) {
SourceLoc Loc = CatchS->getCatchLoc();
if (Loc.isValid() && SM.getLineAndColumn(Loc).first == Line)
return false;
}
}
// If we're at the end of a closure, paren or tuple expr, and the context
// is a paren/tuple expr ending with that sub expression, and it ends on the
// same line, don't add an indent.
// For example:
// foo(x, {
// }) <-- No indent here, the paren expr for the call ends on the same line.
Expr *AtExprEnd = End.getAsExpr();
if (AtExprEnd && (isa<ClosureExpr>(AtExprEnd) ||
isa<ParenExpr>(AtExprEnd) ||
isa<TupleExpr>(AtExprEnd))) {
if (auto *Paren = dyn_cast_or_null<ParenExpr>(Cursor->getAsExpr())) {
auto *SubExpr = Paren->getSubExpr();
if (SubExpr && SubExpr == AtExprEnd &&
SM.getLineAndColumn(Paren->getEndLoc()).first == Line)
return false;
}
else if (auto *Tuple = dyn_cast_or_null<TupleExpr>(Cursor->getAsExpr())) {
auto SubExprs = Tuple->getElements();
if (!SubExprs.empty() && SubExprs.back() == AtExprEnd &&
SM.getLineAndColumn(Tuple->getEndLoc()).first == Line) {
return false;
}
} else if (auto *VD = dyn_cast_or_null<VarDecl>(Cursor->getAsDecl())) {
SourceLoc Loc = getVarDeclInitEnd(VD);
if (Loc.isValid() && SM.getLineNumber(Loc) == Line) {
return false;
}
}
}
// Indent another level from the outer context by default.
return true;
}
};
class FormatWalker: public ide::SourceEntityWalker {
typedef std::vector<Token>::iterator TokenIt;
class SiblingCollector {
SourceLoc FoundSibling;
SourceManager &SM;
std::vector<Token> &Tokens;
SourceLoc &TargetLoc;
TokenIt TI;
bool isImmediateAfterSeparator(SourceLoc End, tok Separator) {
auto BeforeE = [&]() {
return TI != Tokens.end() &&
!SM.isBeforeInBuffer(End, TI->getLoc());
};
if (!BeforeE())
return false;
for (; BeforeE(); TI ++);
if (TI == Tokens.end() || TI->getKind() != Separator)
return false;
auto SeparatorLoc = TI->getLoc();
TI ++;
if (TI == Tokens.end())
return false;
auto NextLoc = TI->getLoc();
return SM.isBeforeInBuffer(SeparatorLoc, TargetLoc) &&
!SM.isBeforeInBuffer(NextLoc, TargetLoc);
}
public:
SiblingCollector(SourceManager &SM, std::vector<Token> &Tokens,
SourceLoc &TargetLoc) : SM(SM), Tokens(Tokens),
TargetLoc(TargetLoc), TI(Tokens.begin()) {}
void collect(ASTNode Node) {
if (FoundSibling.isValid())
return;
SourceLoc PrevLoc;
auto FindAlignLoc = [&](SourceLoc Loc) {
if (PrevLoc.isValid() &&
SM.getLineNumber(PrevLoc) == SM.getLineNumber(Loc))
return PrevLoc;
return PrevLoc = Loc;
};
auto addPair = [&](SourceLoc EndLoc, SourceLoc AlignLoc, tok Separator) {
if (isImmediateAfterSeparator(EndLoc, Separator))
FoundSibling = AlignLoc;
};
if (auto AE = dyn_cast_or_null<ApplyExpr>(Node.dyn_cast<Expr *>())) {
collect(AE->getArg());
return;
}
if (auto PE = dyn_cast_or_null<ParenExpr>(Node.dyn_cast<Expr *>())) {
if (auto Sub = PE->getSubExpr()) {
addPair(Sub->getEndLoc(), FindAlignLoc(Sub->getStartLoc()),
tok::comma);
}
}
// Tuple elements are siblings.
if (auto TE = dyn_cast_or_null<TupleExpr>(Node.dyn_cast<Expr *>())) {
// Trailing closures are not considered siblings to other args.
unsigned EndAdjust = TE->hasTrailingClosure() ? 1 : 0;
for (unsigned I = 0, N = TE->getNumElements() - EndAdjust; I < N; I ++) {
addPair(TE->getElement(I)->getEndLoc(),
FindAlignLoc(TE->getElement(I)->getStartLoc()), tok::comma);
}
}
if (auto AFD = dyn_cast_or_null<AbstractFunctionDecl>(Node.dyn_cast<Decl*>())) {
// Generic type params are siblings to align.
if (auto GPL = AFD->getGenericParams()) {
const auto Params = GPL->getParams();
for (unsigned I = 0, N = Params.size(); I < N; I ++) {
addPair(Params[I]->getEndLoc(), FindAlignLoc(Params[I]->getStartLoc()),
tok::comma);
}
}
// Function parameters are siblings.
for (auto P : AFD->getParameterLists()) {
for (auto &param : *P) {
addPair(param.getEndLoc(),
FindAlignLoc(param.getStartLoc()), tok::comma);
}
}
}
// Array/Dictionary elements are siblings to align with each other.
if (auto AE = dyn_cast_or_null<CollectionExpr>(Node.dyn_cast<Expr *>())) {
for (unsigned I = 0, N = AE->getNumElements(); I < N; I ++) {
addPair(AE->getElement(I)->getEndLoc(),
FindAlignLoc(AE->getElement(I)->getStartLoc()), tok::comma);
}
}
};
SourceLoc findSibling() {
return FoundSibling;
}
};
SourceFile &SF;
SourceManager &SM;
SourceLoc TargetLocation;
std::vector<swift::ASTWalker::ParentTy> Stack;
swift::ASTWalker::ParentTy AtStart;
swift::ASTWalker::ParentTy AtEnd;
bool InDocCommentBlock = false;
bool InCommentLine = false;
std::vector<Token> Tokens;
LangOptions Options;
TokenIt CurrentTokIt;
unsigned TargetLine;
SiblingCollector SCollector;
/// Sometimes, target is a part of "parent", for instance, "#else" is a part
/// of an ifconfigstmt, so that ifconfigstmt is not really the parent of "#else".
bool isTargetPartOf(swift::ASTWalker::ParentTy Parent) {
if(auto Conf = dyn_cast_or_null<IfConfigStmt>(Parent.getAsStmt())) {
for (auto Clause : Conf->getClauses()) {
if (Clause.Loc == TargetLocation)
return true;
}
} else if (auto Call = dyn_cast_or_null<CallExpr>(Parent.getAsExpr())) {
if(auto Clo = dyn_cast<ClosureExpr>(Call->getFn())) {
if (Clo->getBody()->getLBraceLoc() == TargetLocation ||
Clo->getBody()->getRBraceLoc() == TargetLocation) {
return true;
}
}
}
return false;
}
template <class T>
bool HandlePre(T* Node, SourceLoc Start, SourceLoc End) {
scanForComments(Start);
SCollector.collect(Node);
if (SM.isBeforeInBuffer(TargetLocation, Start))
return false; // Target is before start of Node, skip it.
if (SM.isBeforeInBuffer(End, TargetLocation))
return false; // Target is after end of Node, skip it.
if (TargetLocation == Start) {
// Target is right at the start of Node, mark it.
AtStart = Node;
return false;
}
if (TargetLocation == End) {
// Target is right at the end of Node, mark it.
AtEnd = Node;
return false;
}
// Target is within Node and Node is really the parent of Target, take it.
if (!isTargetPartOf(Node))
Stack.push_back(Node);
return true;
}
void scanForComments(SourceLoc Loc) {
if (InDocCommentBlock || InCommentLine)
return;
for (auto InValid = Loc.isInvalid(); CurrentTokIt != Tokens.end() &&
(InValid || SM.isBeforeInBuffer(CurrentTokIt->getLoc(), Loc));
CurrentTokIt ++) {
if (CurrentTokIt->getKind() == tok::comment) {
auto StartLine = SM.getLineNumber(CurrentTokIt->getRange().getStart());
auto EndLine = SM.getLineNumber(CurrentTokIt->getRange().getEnd());
auto TokenStr = CurrentTokIt->getRange().str();
InDocCommentBlock |= TargetLine > StartLine && TargetLine <= EndLine &&
TokenStr.startswith("/*");
InCommentLine |= StartLine == TargetLine && TokenStr.startswith("//");
}
}
}
template <typename T>
bool HandlePost(T* Node) {
if (SM.isBeforeInBuffer(TargetLocation, Node->getStartLoc()))
return false; // Target is before start of Node, terminate walking.
return true;
}
public:
explicit FormatWalker(SourceFile &SF, SourceManager &SM)
:SF(SF), SM(SM),
Tokens(tokenize(Options, SM, SF.getBufferID().getValue())),
CurrentTokIt(Tokens.begin()),
SCollector(SM, Tokens, TargetLocation) {}
FormatContext walkToLocation(SourceLoc Loc) {
Stack.clear();
TargetLocation = Loc;
TargetLine = SM.getLineNumber(TargetLocation);
AtStart = AtEnd = swift::ASTWalker::ParentTy();
walk(SF);
scanForComments(SourceLoc());
return FormatContext(SM, Stack, AtStart, AtEnd, InDocCommentBlock,
InCommentLine, SCollector.findSibling());
}
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
SourceLoc Start = D->getStartLoc();
SourceLoc End = D->getEndLoc();
if (auto *VD = dyn_cast<VarDecl>(D)) {
// We'll treat properties with accessors as spanning the braces as well.
// This will ensure we can do indentation inside the braces.
auto Loc = getVarDeclInitEnd(VD);
End = Loc.isValid() ? Loc : End;
}
return HandlePre(D, Start, End);
}
bool walkToDeclPost(Decl *D) override {
return HandlePost(D);
}
bool walkToStmtPre(Stmt *S) override {
return HandlePre(S, S->getStartLoc(), S->getEndLoc());
}
bool walkToStmtPost(Stmt *S) override {
return HandlePost(S);
}
bool walkToExprPre(Expr *E) override {
return HandlePre(E, E->getStartLoc(), E->getEndLoc());
}
bool walkToExprPost(Expr *E) override {
return HandlePost(E);
}
bool shouldWalkInactiveConfigRegion() override {
return true;
}
};
class CodeFormatter {
SwiftEditorDocument &Doc;
EditorConsumer &Consumer;
public:
CodeFormatter(SwiftEditorDocument &Doc, EditorConsumer& Consumer)
:Doc(Doc), Consumer(Consumer) { }
SwiftEditorLineRange indent(unsigned LineIndex, FormatContext &FC) {
auto &FmtOptions = Doc.getFormatOptions();
// If having sibling locs to align with, respect siblings.
if (FC.HasSibling()) {
StringRef Line = Doc.getTrimmedTextForLine(LineIndex);
StringBuilder Builder;
FC.padToSiblingColumn(Builder);
Builder.append(Line);
Consumer.recordFormattedText(Builder.str().str());
return SwiftEditorLineRange(LineIndex, 1);
}
// Take the current indent position of the outer context, then add another
// indent level if expected.
auto LineAndColumn = FC.indentLineAndColumn();
size_t ExpandedIndent = Doc.getExpandedIndentForLine(LineAndColumn.first);
auto AddIndentFunc = [&] () {
auto Width = FmtOptions.UseTabs ? FmtOptions.TabWidth
: FmtOptions.IndentWidth;
// Increment indent.
ExpandedIndent += Width;
// Normalize indent to align on proper column indent width.
ExpandedIndent -= ExpandedIndent % Width;
};
if (LineAndColumn.second > 0 && FC.shouldAddIndentForLine(LineIndex))
AddIndentFunc();
if (FC.IsInDocCommentBlock()) {
// Inside doc comment block, the indent is one space, e.g.
// /**
// * <---Indent to align with the first star.
// */
ExpandedIndent += 1;
}
// Reformat the specified line with the calculated indent.
StringRef Line = Doc.getTrimmedTextForLine(LineIndex);
std::string IndentedLine;
if (FmtOptions.UseTabs)
IndentedLine.assign(ExpandedIndent / FmtOptions.TabWidth, '\t');
else
IndentedLine.assign(ExpandedIndent, ' ');
IndentedLine.append(Line);
Consumer.recordFormattedText(IndentedLine);
// Return affected line range, which can later be more than one line.
return SwiftEditorLineRange(LineIndex, 1);
}
};
class PlaceholderExpansionScanner {
public:
struct Param {
CharSourceRange NameRange;
CharSourceRange TypeRange;
Param(CharSourceRange NameRange, CharSourceRange TypeRange)
:NameRange(NameRange), TypeRange(TypeRange) { }
};
private:
SourceManager &SM;
std::vector<Param> Params;
CharSourceRange ReturnTypeRange;
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 };
}
};
bool scanClosureType(SourceFile &SF, SourceLoc PlaceholderLoc) {
Params.clear();
ReturnTypeRange = CharSourceRange();
PlaceholderFinder Finder(PlaceholderLoc, PHE);
SF.walk(Finder);
if (!PHE || !PHE->getTypeForExpansion())
return false;
class ClosureTypeWalker: public ASTWalker {
public:
PlaceholderExpansionScanner &S;
bool FoundFunctionTypeRepr = false;
explicit ClosureTypeWalker(PlaceholderExpansionScanner &S)
:S(S) { }
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 *ArgTR : TTR->getElements()) {
CharSourceRange NR;
CharSourceRange TR;
auto *NTR = dyn_cast<NamedTypeRepr>(ArgTR);
if (NTR && NTR->hasName()) {
NR = CharSourceRange(NTR->getNameLoc(),
NTR->getName().getLength());
ArgTR = NTR->getTypeRepr();
}
SourceLoc SRE = Lexer::getLocForEndOfToken(S.SM,
ArgTR->getEndLoc());
TR = CharSourceRange(S.SM, ArgTR->getStartLoc(), SRE);
S.Params.emplace_back(NR, TR);
}
} else if (FTR->getArgsTypeRepr()) {
CharSourceRange TR;
TR = CharSourceRange(S.SM, FTR->getArgsTypeRepr()->getStartLoc(),
Lexer::getLocForEndOfToken(S.SM,
FTR->getArgsTypeRepr()->getEndLoc()));
S.Params.emplace_back(CharSourceRange(), TR);
}
if (auto *RTR = FTR->getResultTypeRepr()) {
SourceLoc SRE = Lexer::getLocForEndOfToken(S.SM, RTR->getEndLoc());
S.ReturnTypeRange = CharSourceRange(S.SM, RTR->getStartLoc(), SRE);
}
}
return !FoundFunctionTypeRepr;
}
bool walkToTypeReprPost(TypeRepr *T) override {
// If we just visited the FunctionTypeRepr, end traversal.
return !FoundFunctionTypeRepr;
}
} PW(*this);
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 ide::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 && !isa<BraceStmt>(S))
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);
}
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)) {
if (!TE->getElements().empty())
UseTrailingClosure =
TE->getElements().back()->getStartLoc() == PlaceholderStartLoc;
}
}
Callback(Args, UseTrailingClosure, Params, ReturnTypeRange);
return true;
}
};
} // anonymous namespace
SwiftEditorDocument::SwiftEditorDocument(StringRef FilePath,
SwiftLangSupport &LangSupport)
:Impl(*new Implementation(FilePath, LangSupport)) { }
SwiftEditorDocument::~SwiftEditorDocument()
{
delete &Impl;
}
ImmutableTextSnapshotRef SwiftEditorDocument::initializeText(
llvm::MemoryBuffer *Buf, ArrayRef<const char *> Args) {
llvm::sys::ScopedLock L(Impl.AccessMtx);
Impl.EditableBuffer =
new EditableTextBuffer(Impl.FilePath, Buf->getBuffer());
Impl.SyntaxMap.reset();
Impl.EditedLineRange.setRange(0,0);
Impl.AffectedRange = std::make_pair(0, Buf->getBufferSize());
Impl.SemanticInfo =
new SwiftDocumentSemanticInfo(Impl.FilePath, Impl.LangSupport);
Impl.SemanticInfo->setCompilerArgs(Args);
return Impl.EditableBuffer->getSnapshot();
}
ImmutableTextSnapshotRef SwiftEditorDocument::replaceText(
unsigned int Offset, unsigned int Length, llvm::MemoryBuffer *Buf,
bool ProvideSemanticInfo) {
llvm::sys::ScopedLock L(Impl.AccessMtx);
llvm::StringRef Str = Buf->getBuffer();
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();
}
}
}
}
}
SourceManager &SrcManager = Impl.SyntaxInfo->getSourceManager();
unsigned BufID = Impl.SyntaxInfo->getBufferID();
SourceLoc StartLoc = SrcManager.getLocForBufferStart(BufID).getAdvancedLoc(
Offset);
unsigned StartLine = SrcManager.getLineAndColumn(StartLoc).first;
unsigned EndLine = SrcManager.getLineAndColumn(
StartLoc.getAdvancedLoc(Length)).first;
// Delete all syntax map data from start line through end line.
unsigned OldLineCount = EndLine - StartLine + 1;
Impl.SyntaxMap.removeLineRange(StartLine, OldLineCount);
// Insert empty syntax map data for replaced lines.
unsigned NewLineCount = Str.count('\n') + 1;
Impl.SyntaxMap.insertLineRange(StartLine, NewLineCount);
// Update the edited line range.
Impl.EditedLineRange.setRange(StartLine, NewLineCount);
ImmutableTextBufferRef ImmBuf = Snapshot->getBuffer();
// The affected range starts from the previous newline.
if (Offset > 0) {
auto AffectedRangeOffset = ImmBuf->getText().rfind('\n', Offset);
Impl.AffectedRange.first =
AffectedRangeOffset != StringRef::npos ? AffectedRangeOffset + 1 : 0;
}
else
Impl.AffectedRange.first = 0;
Impl.AffectedRange.second = ImmBuf->getText().size() - Impl.AffectedRange.first;
return Snapshot;
}
void SwiftEditorDocument::updateSemaInfo() {
if (auto SemaInfo = 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());
SwiftEditorSyntaxWalker SyntaxWalker(Impl.SyntaxMap,
Impl.EditedLineRange,
Impl.AffectedRange,
Impl.SyntaxInfo->getSourceManager(),
Consumer,
Impl.SyntaxInfo->getBufferID());
ModelContext.walk(SyntaxWalker);
Consumer.recordAffectedRange(Impl.AffectedRange.first,
Impl.AffectedRange.second);
}
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;
std::vector<DiagnosticEntryInfo> SemaDiags;
// FIXME: Parser diagnostics should be filtered out of the semantic ones,
// Then just merge the semantic ones with the current parse ones.
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.IsSystem;
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 (!SemaDiags.empty() || !SemaToks.empty()) {
Consumer.setDiagnosticStage(SemaDiagStage);
} else {
Consumer.setDiagnosticStage(ParseDiagStage);
}
for (auto &Diag : Impl.ParserDiagnostics)
Consumer.handleDiagnostic(Diag, ParseDiagStage);
for (auto &Diag : SemaDiags)
Consumer.handleDiagnostic(Diag, SemaDiagStage);
}
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
SwiftEditorDocument::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);
}
FormatWalker walker(SF, SM);
size_t Offset = getTrimmedLineOffset(Line);
SourceLoc Loc = SM.getLocForBufferStart(BufID).getAdvancedLoc(Offset);
FormatContext FC = walker.walkToLocation(Loc);
CodeFormatter CF(*this, Consumer);
SwiftEditorLineRange LineRange = CF.indent(Line, FC);
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<#code#>\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;
});
}
size_t SwiftEditorDocument::getLineOffset(unsigned LineIndex) {
StringRef Text = Impl.EditableBuffer->getBuffer()->getText();
// FIXME: We should have a cached line map in EditableTextBuffer, for now
// we just do the slow naive thing here.
size_t LineOffset = 0;
unsigned CurrentLine = 0;
while (LineOffset < Text.size() && ++CurrentLine < LineIndex) {
LineOffset = Text.find_first_of("\r\n", LineOffset);
if (LineOffset != std::string::npos) {
++LineOffset;
if (LineOffset < Text.size() &&
Text[LineOffset - 1] == '\r' && Text[LineOffset] == '\n')
++LineOffset;
}
}
if (LineOffset == std::string::npos)
LineOffset = 0;
return LineOffset;
}
size_t SwiftEditorDocument::getTrimmedLineOffset(unsigned LineIndex) {
size_t LineOffset = getLineOffset(LineIndex);
// Skip leading whitespace.
StringRef Text = Impl.EditableBuffer->getBuffer()->getText();
size_t FirstNonWSOnLine = Text.find_first_not_of(" \t\v\f", LineOffset);
if (FirstNonWSOnLine != std::string::npos)
LineOffset = FirstNonWSOnLine;
return LineOffset;
}
size_t SwiftEditorDocument::getExpandedIndentForLine(unsigned LineIndex) {
size_t LineOffset = getLineOffset(LineIndex);
// Tab-expand all leading whitespace
StringRef Text = Impl.EditableBuffer->getBuffer()->getText();
size_t FirstNonWSOnLine = Text.find_first_not_of(" \t\v\f", LineOffset);
size_t Indent = 0;
while (LineOffset < Text.size() && LineOffset < FirstNonWSOnLine) {
if (Text[LineOffset++] == '\t')
Indent += Impl.FormatOptions.TabWidth;
else
Indent += 1;
}
return Indent;
}
StringRef SwiftEditorDocument::getTrimmedTextForLine(unsigned LineIndex) {
StringRef Text = Impl.EditableBuffer->getBuffer()->getText();
size_t LineOffset = getTrimmedLineOffset(LineIndex);
size_t LineEnd = Text.find_first_of("\r\n", LineOffset);
return Text.slice(LineOffset, LineEnd);
}
ImmutableTextSnapshotRef SwiftEditorDocument::getLatestSnapshot() const {
return Impl.EditableBuffer->getSnapshot();
}
void SwiftEditorDocument::reportDocumentStructure(swift::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));
}
//============================================================================//
// 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);
}