blob: f20d429d7380a0b72abb0eeba1291e9f63df9c0a [file] [log] [blame]
//===--- Requests.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 "DictionaryKeys.h"
#include "sourcekitd/CodeCompletionResultsArray.h"
#include "sourcekitd/DocStructureArray.h"
#include "sourcekitd/DocSupportAnnotationArray.h"
#include "sourcekitd/TokenAnnotationsArray.h"
#include "SourceKit/Core/Context.h"
#include "SourceKit/Core/LangSupport.h"
#include "SourceKit/Core/NotificationCenter.h"
#include "SourceKit/Support/Concurrency.h"
#include "SourceKit/Support/Logging.h"
#include "SourceKit/Support/UIdent.h"
#include "SourceKit/SwiftLang/Factory.h"
#include "swift/Demangling/ManglingMacros.h"
#include "swift/Demangling/Demangler.h"
#include "swift/Basic/Mangler.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <mutex>
// FIXME: Portability.
#include <dispatch/dispatch.h>
using namespace sourcekitd;
using namespace SourceKit;
namespace {
class LazySKDUID {
const char *Name;
mutable std::atomic<sourcekitd_uid_t> UID { nullptr };
public:
LazySKDUID(const char *Name) : Name(Name) { }
sourcekitd_uid_t get() const {
sourcekitd_uid_t UIDValue = UID;
if (!UIDValue)
UID = SKDUIDFromUIdent(UIdent(Name));
return UID;
}
operator sourcekitd_uid_t() const {
return get();
}
StringRef str() const {
return StringRef(Name);
}
};
} // anonymous namespace
#define REQUEST(NAME, CONTENT) static LazySKDUID Request##NAME(CONTENT);
#define KIND(NAME, CONTENT) static LazySKDUID Kind##NAME(CONTENT);
#include "SourceKit/Core/ProtocolUIDs.def"
#define REFACTORING(KIND, NAME, ID) static LazySKDUID Kind##Refactoring##KIND("source.refactoring.kind."#ID);
#include "swift/IDE/RefactoringKinds.def"
static void onDocumentUpdateNotification(StringRef DocumentName) {
static UIdent DocumentUpdateNotificationUID(
"source.notification.editor.documentupdate");
ResponseBuilder RespBuilder;
auto Dict = RespBuilder.getDictionary();
Dict.set(KeyNotification, DocumentUpdateNotificationUID);
Dict.set(KeyName, DocumentName);
sourcekitd::postNotification(RespBuilder.createResponse());
}
static SourceKit::Context *GlobalCtx = nullptr;
void sourcekitd::initialize() {
GlobalCtx = new SourceKit::Context(sourcekitd::getRuntimeLibPath(),
SourceKit::createSwiftLangSupport);
GlobalCtx->getNotificationCenter().addDocumentUpdateNotificationReceiver(
onDocumentUpdateNotification);
}
void sourcekitd::shutdown() {
delete GlobalCtx;
GlobalCtx = nullptr;
}
static SourceKit::Context &getGlobalContext() {
assert(GlobalCtx);
return *GlobalCtx;
}
static sourcekitd_response_t demangleNames(ArrayRef<const char *> MangledNames,
bool Simplified);
static sourcekitd_response_t
mangleSimpleClassNames(ArrayRef<std::pair<StringRef, StringRef>> ModuleClassPairs);
static sourcekitd_response_t indexSource(StringRef Filename,
ArrayRef<const char *> Args,
StringRef KnownHash);
static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf,
StringRef ModuleName,
ArrayRef<const char *> Args);
static void reportCursorInfo(const CursorInfoData &Info, ResponseReceiver Rec);
static void reportRangeInfo(const RangeInfo &Info, ResponseReceiver Rec);
static void reportNameInfo(const NameTranslatingInfo &Info, ResponseReceiver Rec);
static void findRelatedIdents(StringRef Filename,
int64_t Offset,
bool CancelOnSubsequentRequest,
ArrayRef<const char *> Args,
ResponseReceiver Rec);
static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf,
int64_t Offset,
ArrayRef<const char *> Args);
static sourcekitd_response_t codeCompleteOpen(StringRef name,
llvm::MemoryBuffer *InputBuf,
int64_t Offset,
Optional<RequestDict> optionsDict,
ArrayRef<const char *> Args);
static sourcekitd_response_t
codeCompleteUpdate(StringRef name, int64_t Offset,
Optional<RequestDict> optionsDict);
static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset);
static sourcekitd_response_t
editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, bool EnableSyntaxMap,
bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly,
ArrayRef<const char *> Args);
static sourcekitd_response_t
editorOpenInterface(StringRef Name, StringRef ModuleName,
Optional<StringRef> Group, ArrayRef<const char *> Args,
bool SynthesizedExtensions,
Optional<StringRef> InterestedUSR);
static sourcekitd_response_t
editorOpenHeaderInterface(StringRef Name, StringRef HeaderName,
ArrayRef<const char *> Args,
bool UsingSwiftArgs,
bool SynthesizedExtensions,
Optional<unsigned> swiftVersion);
static void
editorOpenSwiftSourceInterface(StringRef Name, StringRef SourceName,
ArrayRef<const char *> Args,
ResponseReceiver Rec);
static void
editorOpenSwiftTypeInterface(StringRef TypeUsr, ArrayRef<const char *> Args,
ResponseReceiver Rec);
static sourcekitd_response_t editorExtractTextFromComment(StringRef Source);
static sourcekitd_response_t editorConvertMarkupToXML(StringRef Source);
static sourcekitd_response_t
editorClose(StringRef Name, bool RemoveCache);
static sourcekitd_response_t
editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf, unsigned Offset,
unsigned Length, bool EnableSyntaxMap, bool EnableStructure,
bool EnableDiagnostics, bool SyntacticOnly);
static void
editorApplyFormatOptions(StringRef Name, RequestDict &FmtOptions);
static sourcekitd_response_t
editorFormatText(StringRef Name, unsigned Line, unsigned Length);
static sourcekitd_response_t
editorExpandPlaceholder(StringRef Name, unsigned Offset, unsigned Length);
static sourcekitd_response_t
editorFindUSR(StringRef DocumentName, StringRef USR);
static sourcekitd_response_t
editorFindInterfaceDoc(StringRef ModuleName, ArrayRef<const char *> Args);
static sourcekitd_response_t
editorFindModuleGroups(StringRef ModuleName, ArrayRef<const char *> Args);
static bool
buildRenameLocationsFromDict(RequestDict &Req, bool UseNewName,
std::vector<RenameLocations> &RenameLocations,
llvm::SmallString<64> &Error);
static sourcekitd_response_t
createCategorizedEditsResponse(ArrayRef<CategorizedEdits> AllEdits,
StringRef Error);
static sourcekitd_response_t
syntacticRename(llvm::MemoryBuffer *InputBuf,
ArrayRef<RenameLocations> RenameLocations,
ArrayRef<const char*> Args);
static sourcekitd_response_t
createCategorizedRenameRangesResponse(ArrayRef<CategorizedRenameRanges> Ranges,
StringRef Error);
static sourcekitd_response_t
findRenameRanges(llvm::MemoryBuffer *InputBuf,
ArrayRef<RenameLocations> RenameLocations,
ArrayRef<const char *> Args);
static bool isSemanticEditorDisabled();
static void fillDictionaryForDiagnosticInfo(
ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info);
static void handleRequestImpl(sourcekitd_object_t Req,
ResponseReceiver Receiver);
void sourcekitd::handleRequest(sourcekitd_object_t Req,
ResponseReceiver Receiver) {
LOG_SECTION("handleRequest-before", InfoHighPrio) {
sourcekitd::printRequestObject(Req, Log->getOS());
}
handleRequestImpl(Req, [Receiver](sourcekitd_response_t Resp) {
LOG_SECTION("handleRequest-after", InfoHighPrio) {
// Responses are big, print them out with info medium priority.
if (Logger::isLoggingEnabledForLevel(Logger::Level::InfoMediumPrio))
sourcekitd::printResponse(Resp, Log->getOS());
}
Receiver(Resp);
});
}
static std::unique_ptr<llvm::MemoryBuffer>
getInputBufForRequest(Optional<StringRef> SourceFile,
Optional<StringRef> SourceText,
llvm::SmallString<64> &ErrBuf) {
std::unique_ptr<llvm::MemoryBuffer> InputBuf;
if (SourceText.hasValue()) {
StringRef BufName;
if (SourceFile.hasValue())
BufName = *SourceFile;
else
BufName = "<input>";
InputBuf = llvm::MemoryBuffer::getMemBuffer(*SourceText, BufName);
} else if (SourceFile.hasValue()) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
llvm::MemoryBuffer::getFile(*SourceFile);
if (FileBufOrErr) {
InputBuf = std::move(FileBufOrErr.get());
} else {
llvm::raw_svector_ostream OSErr(ErrBuf);
OSErr << "error opening input file '" << *SourceFile << "' ("
<< FileBufOrErr.getError().message() << ')';
return nullptr;
}
} else {
InputBuf = llvm::MemoryBuffer::getNewMemBuffer(0, "<input>");
}
return InputBuf;
}
static void
handleSemanticRequest(RequestDict Req,
ResponseReceiver Receiver,
sourcekitd_uid_t ReqUID,
Optional<StringRef> SourceFile,
Optional<StringRef> SourceText,
ArrayRef<const char *> Args);
void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) {
RequestDict Req(ReqObj);
sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest);
if (!ReqUID)
return Rec(createErrorRequestInvalid("missing 'key.request' with UID value"));
if (ReqUID == RequestProtocolVersion) {
ResponseBuilder RB;
auto dict = RB.getDictionary();
dict.set(KeyVersionMajor, ProtocolMajorVersion);
dict.set(KeyVersionMinor, static_cast<int64_t>(ProtocolMinorVersion));
return Rec(RB.createResponse());
}
if (ReqUID == RequestCrashWithExit) {
// 'exit' has the same effect as crashing but without the crash log.
::exit(1);
}
if (ReqUID == RequestDemangle) {
SmallVector<const char *, 8> MangledNames;
bool Failed = Req.getStringArray(KeyNames, MangledNames, /*isOptional=*/true);
if (Failed) {
return Rec(createErrorRequestInvalid(
"'key.names' not an array of strings"));
}
int64_t Simplified = false;
Req.getInt64(KeySimplified, Simplified, /*isOptional=*/true);
return Rec(demangleNames(MangledNames, Simplified));
}
if (ReqUID == RequestMangleSimpleClass) {
SmallVector<std::pair<StringRef, StringRef>, 16> ModuleClassPairs;
sourcekitd_response_t err = nullptr;
bool failed = Req.dictionaryArrayApply(KeyNames, [&](RequestDict dict) {
Optional<StringRef> ModuleName = dict.getString(KeyModuleName);
if (!ModuleName.hasValue()) {
err = createErrorRequestInvalid("missing 'key.modulename'");
return true;
}
Optional<StringRef> ClassName = dict.getString(KeyName);
if (!ClassName.hasValue()) {
err = createErrorRequestInvalid("missing 'key.name'");
return true;
}
ModuleClassPairs.push_back(std::make_pair(*ModuleName, *ClassName));
return false;
});
if (failed) {
if (!err)
err = createErrorRequestInvalid("missing 'key.names'");
return Rec(err);
}
return Rec(mangleSimpleClassNames(ModuleClassPairs));
}
// Just accept 'source.request.buildsettings.register' for now, don't do
// anything else.
// FIXME: Heavy WIP here.
if (ReqUID == RequestBuildSettingsRegister) {
return Rec(ResponseBuilder().createResponse());
}
Optional<StringRef> SourceFile = Req.getString(KeySourceFile);
Optional<StringRef> SourceText = Req.getString(KeySourceText);
llvm::SmallString<64> ErrBuf;
SmallVector<const char *, 8> Args;
bool Failed = Req.getStringArray(KeyCompilerArgs, Args, /*isOptional=*/true);
if (Failed) {
return Rec(createErrorRequestInvalid(
"'key.compilerargs' not an array of strings"));
}
if (ReqUID == RequestDocInfo) {
std::unique_ptr<llvm::MemoryBuffer>
InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
StringRef ModuleName;
Optional<StringRef> ModuleNameOpt = Req.getString(KeyModuleName);
if (ModuleNameOpt.hasValue()) ModuleName = *ModuleNameOpt;
return Rec(reportDocInfo(InputBuf.get(), ModuleName, Args));
}
if (ReqUID == RequestEditorOpen) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
std::unique_ptr<llvm::MemoryBuffer>
InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
int64_t EnableSyntaxMap = true;
Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true);
int64_t EnableStructure = true;
Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true);
int64_t EnableDiagnostics = true;
Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true);
int64_t SyntacticOnly = false;
Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true);
return Rec(editorOpen(*Name, InputBuf.get(), EnableSyntaxMap, EnableStructure,
EnableDiagnostics, SyntacticOnly, Args));
}
if (ReqUID == RequestEditorClose) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
// Whether we remove the cached AST from libcache, by default, false.
int64_t RemoveCache = false;
Req.getInt64(KeyRemoveCache, RemoveCache, /*isOptional=*/true);
return Rec(editorClose(*Name, RemoveCache));
}
if (ReqUID == RequestEditorReplaceText) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
std::unique_ptr<llvm::MemoryBuffer>
InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
int64_t Offset = 0;
Req.getInt64(KeyOffset, Offset, /*isOptional=*/true);
int64_t Length = 0;
Req.getInt64(KeyLength, Length, /*isOptional=*/true);
int64_t EnableSyntaxMap = true;
Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true);
int64_t EnableStructure = true;
Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true);
int64_t EnableDiagnostics = true;
Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true);
int64_t SyntacticOnly = false;
Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true);
return Rec(editorReplaceText(*Name, InputBuf.get(), Offset, Length,
EnableSyntaxMap, EnableStructure,
EnableDiagnostics, SyntacticOnly));
}
if (ReqUID == RequestEditorFormatText) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
Optional<RequestDict> FmtOptions = Req.getDictionary(KeyFormatOptions);
if (FmtOptions.hasValue())
editorApplyFormatOptions(*Name, *FmtOptions);
int64_t Line = 0;
Req.getInt64(KeyLine, Line, /*isOptional=*/false);
int64_t Length = 0;
Req.getInt64(KeyLength, Length, /*isOptional=*/true);
return Rec(editorFormatText(*Name, Line, Length));
}
if (ReqUID == RequestEditorExpandPlaceholder) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
int64_t Offset = 0;
Req.getInt64(KeyOffset, Offset, /*isOptional=*/false);
int64_t Length = 0;
Req.getInt64(KeyLength, Length, /*isOptional=*/false);
return Rec(editorExpandPlaceholder(*Name, Offset, Length));
}
if (ReqUID == RequestEditorOpenInterface) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
if (!ModuleName.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.modulename'"));
Optional<StringRef> GroupName = Req.getString(KeyGroupName);
int64_t SynthesizedExtension = false;
Req.getInt64(KeySynthesizedExtension, SynthesizedExtension,
/*isOptional=*/true);
Optional<StringRef> InterestedUSR = Req.getString(KeyInterestedUSR);
return Rec(editorOpenInterface(*Name, *ModuleName, GroupName, Args,
SynthesizedExtension, InterestedUSR));
}
if (ReqUID == RequestEditorOpenHeaderInterface) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
Optional<StringRef> HeaderName = Req.getString(KeyFilePath);
if (!HeaderName.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.filepath'"));
int64_t SynthesizedExtension = false;
Req.getInt64(KeySynthesizedExtension, SynthesizedExtension,
/*isOptional=*/true);
Optional<int64_t> UsingSwiftArgs = Req.getOptionalInt64(KeyUsingSwiftArgs);
Optional<int64_t> swiftVerVal = Req.getOptionalInt64(KeySwiftVersion);
Optional<unsigned> swiftVer;
if (swiftVerVal.hasValue())
swiftVer = *swiftVerVal;
return Rec(editorOpenHeaderInterface(*Name, *HeaderName, Args,
UsingSwiftArgs.getValueOr(false),
SynthesizedExtension, swiftVer));
}
if (ReqUID == RequestEditorOpenSwiftSourceInterface) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
Optional<StringRef> FileName = Req.getString(KeySourceFile);
if (!FileName.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
return editorOpenSwiftSourceInterface(*Name, *FileName, Args, Rec);
}
if (ReqUID == RequestEditorOpenSwiftTypeInterface) {
Optional<StringRef> Usr = Req.getString(KeyUSR);
if (!Usr.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.usr'"));
return editorOpenSwiftTypeInterface(*Usr, Args, Rec);
}
if (ReqUID == RequestEditorExtractTextFromComment) {
Optional<StringRef> Source = Req.getString(KeySourceText);
if (!Source.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.sourcetext'"));
return Rec(editorExtractTextFromComment(Source.getValue()));
}
if (ReqUID == RequestMarkupToXML) {
Optional<StringRef> Source = Req.getString(KeySourceText);
if (!Source.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.sourcetext'"));
return Rec(editorConvertMarkupToXML(Source.getValue()));
}
if (ReqUID == RequestEditorFindUSR) {
Optional<StringRef> Name = Req.getString(KeySourceFile);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
Optional<StringRef> USR = Req.getString(KeyUSR);
if (!USR.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.usr'"));
return Rec(editorFindUSR(*Name, *USR));
}
if (ReqUID == RequestEditorFindInterfaceDoc) {
Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
if (!ModuleName.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.modulename'"));
return Rec(editorFindInterfaceDoc(*ModuleName, Args));
}
if (ReqUID == RequestModuleGroups) {
Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
if (!ModuleName.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.modulename'"));
return Rec(editorFindModuleGroups(*ModuleName, Args));
}
if (ReqUID == RequestSyntacticRename) {
std::unique_ptr<llvm::MemoryBuffer>
InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
std::vector<RenameLocations> RenameLocations;
if (buildRenameLocationsFromDict(Req, true, RenameLocations, ErrBuf))
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
return Rec(syntacticRename(InputBuf.get(), RenameLocations, Args));
}
if (ReqUID == RequestFindRenameRanges) {
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
std::vector<RenameLocations> RenameLocations;
if (buildRenameLocationsFromDict(Req, false, RenameLocations, ErrBuf))
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
return Rec(findRenameRanges(InputBuf.get(), RenameLocations, Args));
}
if (ReqUID == RequestCodeCompleteClose) {
// Unlike opening code completion, this is not a semantic request.
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
int64_t Offset;
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
return Rec(codeCompleteClose(*Name, Offset));
}
if (ReqUID == RequestCodeCompleteCacheOnDisk) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.codeCompleteCacheOnDisk(*Name);
ResponseBuilder b;
return Rec(b.createResponse());
}
if (ReqUID == RequestCodeCompleteSetPopularAPI) {
llvm::SmallVector<const char *, 0> popular;
llvm::SmallVector<const char *, 0> unpopular;
Req.getStringArray(KeyPopular, popular, /*isOptional=*/false);
Req.getStringArray(KeyUnpopular, unpopular, /*isOptional=*/false);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.codeCompleteSetPopularAPI(popular, unpopular);
ResponseBuilder b;
return Rec(b.createResponse());
}
if (ReqUID == RequestCodeCompleteSetCustom) {
SmallVector<CustomCompletionInfo, 16> customCompletions;
sourcekitd_response_t err = nullptr;
bool failed = Req.dictionaryArrayApply(KeyResults, [&](RequestDict dict) {
CustomCompletionInfo CCInfo;
Optional<StringRef> Name = dict.getString(KeyName);
if (!Name.hasValue()) {
err = createErrorRequestInvalid("missing 'key.name'");
return true;
}
CCInfo.Name = *Name;
sourcekitd_uid_t Kind = dict.getUID(KeyKind);
if (!Kind) {
err = createErrorRequestInvalid("missing 'key.kind'");
return true;
}
CCInfo.Kind = Kind;
SmallVector<sourcekitd_uid_t, 3> contexts;
if (dict.getUIDArray(KeyContext, contexts, false)) {
err = createErrorRequestInvalid("missing 'key.context'");
return true;
}
for (auto context : contexts) {
if (context == KindExpr) {
CCInfo.Contexts |= CustomCompletionInfo::Expr;
} else if (context == KindStmt) {
CCInfo.Contexts |= CustomCompletionInfo::Stmt;
} else if (context == KindType) {
CCInfo.Contexts |= CustomCompletionInfo::Type;
} else if (context == KindForEachSequence) {
CCInfo.Contexts |= CustomCompletionInfo::ForEachSequence;
} else {
err = createErrorRequestInvalid("invalid value for 'key.context'");
return true;
}
}
customCompletions.push_back(std::move(CCInfo));
return false;
});
if (failed) {
if (!err)
err = createErrorRequestInvalid("missing 'key.results'");
return Rec(err);
}
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.codeCompleteSetCustom(customCompletions);
return Rec(ResponseBuilder().createResponse());
}
if (!SourceFile.hasValue() && !SourceText.hasValue() &&
ReqUID != RequestCodeCompleteUpdate)
return Rec(createErrorRequestInvalid(
"missing 'key.sourcefile' or 'key.sourcetext'"));
// Requests that need semantic typechecking.
// Typechecking arrays can blow up the stack currently.
// Run them under a malloc'ed stack.
static WorkQueue SemaQueue{ WorkQueue::Dequeuing::Concurrent,
"sourcekit.request.semantic" };
sourcekitd_request_retain(ReqObj);
SemaQueue.dispatch(
[ReqObj, Rec, ReqUID, SourceFile, SourceText, Args] {
RequestDict Req(ReqObj);
handleSemanticRequest(Req, Rec, ReqUID, SourceFile, SourceText, Args);
sourcekitd_request_release(ReqObj);
},
/*isStackDeep=*/true);
}
static void
handleSemanticRequest(RequestDict Req,
ResponseReceiver Rec,
sourcekitd_uid_t ReqUID,
Optional<StringRef> SourceFile,
Optional<StringRef> SourceText,
ArrayRef<const char *> Args) {
llvm::SmallString<64> ErrBuf;
if (ReqUID == RequestCodeComplete) {
std::unique_ptr<llvm::MemoryBuffer>
InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
int64_t Offset;
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
return Rec(codeComplete(InputBuf.get(), Offset, Args));
}
if (ReqUID == RequestCodeCompleteOpen) {
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
int64_t Offset;
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions);
return Rec(codeCompleteOpen(*Name, InputBuf.get(), Offset, options, Args));
}
if (ReqUID == RequestCodeCompleteUpdate) {
Optional<StringRef> Name = Req.getString(KeyName);
if (!Name.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.name'"));
int64_t Offset;
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions);
return Rec(codeCompleteUpdate(*Name, Offset, options));
}
if (!SourceFile.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
if (ReqUID == RequestIndex) {
StringRef Hash;
Optional<StringRef> HashOpt = Req.getString(KeyHash);
if (HashOpt.hasValue()) Hash = *HashOpt;
return Rec(indexSource(*SourceFile, Args, Hash));
}
if (isSemanticEditorDisabled())
return Rec(createErrorRequestFailed("semantic editor is disabled"));
if (ReqUID == RequestCursorInfo) {
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
// For backwards compatibility, the default is 1.
int64_t CancelOnSubsequentRequest = 1;
Req.getInt64(KeyCancelOnSubsequentRequest, CancelOnSubsequentRequest,
/*isOptional=*/true);
int64_t Offset;
if (!Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) {
int64_t Length = 0;
Req.getInt64(KeyLength, Length, /*isOptional=*/true);
int64_t Actionables = false;
Req.getInt64(KeyRetrieveRefactorActions, Actionables, /*isOptional=*/true);
return Lang.getCursorInfo(
*SourceFile, Offset, Length, Actionables, CancelOnSubsequentRequest,
Args, [Rec](const CursorInfoData &Info) { reportCursorInfo(Info, Rec); });
}
if (auto USR = Req.getString(KeyUSR)) {
return Lang.getCursorInfoFromUSR(
*SourceFile, *USR, CancelOnSubsequentRequest, Args,
[Rec](const CursorInfoData &Info) { reportCursorInfo(Info, Rec); });
}
return Rec(createErrorRequestInvalid(
"either 'key.offset' or 'key.usr' is required"));
}
if (ReqUID == RequestRangeInfo) {
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
int64_t Offset;
int64_t Length;
// For backwards compatibility, the default is 1.
int64_t CancelOnSubsequentRequest = 1;
Req.getInt64(KeyCancelOnSubsequentRequest, CancelOnSubsequentRequest,
/*isOptional=*/true);
if (!Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) {
if (!Req.getInt64(KeyLength, Length, /*isOptional=*/false)) {
return Lang.getRangeInfo(*SourceFile, Offset, Length,
CancelOnSubsequentRequest, Args,
[Rec](const RangeInfo &Info) { reportRangeInfo(Info, Rec); });
}
}
return Rec(createErrorRequestInvalid(
"'key.offset' or 'key.length' is required"));
}
if (ReqUID == RequestSemanticRefactoring) {
int64_t Line = 0;
int64_t Column = 0;
int64_t Length = 0;
auto KA = Req.getUID(KeyActionUID);
if (!KA) {
return Rec(createErrorRequestInvalid("'key.actionuid' is required"));
}
SemanticRefactoringInfo Info;
Info.Kind = SemanticRefactoringKind::None;
#define SEMANTIC_REFACTORING(KIND, NAME, ID) \
if (KA == KindRefactoring##KIND) Info.Kind = SemanticRefactoringKind::KIND;
#include "swift/IDE/RefactoringKinds.def"
if (Info.Kind == SemanticRefactoringKind::None)
return Rec(createErrorRequestInvalid("'key.actionuid' isn't recognized"));
if (!Req.getInt64(KeyLine, Line, /*isOptional=*/false)) {
if (!Req.getInt64(KeyColumn, Column, /*isOptional=*/false)) {
Req.getInt64(KeyLength, Length, /*isOptional=*/true);
if (auto N = Req.getString(KeyName))
Info.PreferredName = *N;
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Info.Line = Line;
Info.Column = Column;
Info.Length = Length;
return Lang.semanticRefactoring(*SourceFile, Info, Args,
[Rec](ArrayRef<CategorizedEdits> CategorizedEdits, StringRef Error) {
Rec(createCategorizedEditsResponse(CategorizedEdits, Error));
});
}
}
return Rec(createErrorRequestInvalid("'key.line' or 'key.column' are required"));
}
if (ReqUID == RequestFindLocalRenameRanges) {
int64_t Line = 0, Column = 0, Length = 0;
if (Req.getInt64(KeyLine, Line, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("'key.line' is required"));
if (Req.getInt64(KeyColumn, Column, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("'key.column' is required"));
Req.getInt64(KeyLength, Length, /*isOptional=*/true);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
return Lang.findLocalRenameRanges(
*SourceFile, Line, Column, Length, Args,
[Rec](ArrayRef<CategorizedRenameRanges> Ranges, StringRef Error) {
Rec(createCategorizedRenameRangesResponse(Ranges, Error));
});
}
if (ReqUID == RequestNameTranslation) {
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
int64_t Offset;
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) {
return Rec(createErrorRequestInvalid("'key.offset' is required"));
}
NameTranslatingInfo Input;
auto NK = Req.getUID(KeyNameKind);
if (!NK) {
return Rec(createErrorRequestInvalid("'key.namekind' is required"));
}
static UIdent UIDKindNameSwift(KindNameSwift.str());
static UIdent UIDKindNameObjc(KindNameObjc.str());
if (NK == KindNameSwift.get())
Input.NameKind = UIDKindNameSwift;
else if (NK == KindNameObjc.get())
Input.NameKind = UIDKindNameObjc;
else
return Rec(createErrorRequestInvalid("'key.namekind' is unrecognizable"));
if (auto Base = Req.getString(KeyBaseName)) {
Input.BaseName = Base.getValue();
}
llvm::SmallVector<const char*, 4> ArgParts;
llvm::SmallVector<const char*, 4> Selectors;
Req.getStringArray(KeyArgNames, ArgParts, true);
Req.getStringArray(KeySelectorPieces, Selectors, true);
if (!ArgParts.empty() && !Selectors.empty()) {
return Rec(createErrorRequestInvalid("cannot specify 'key.selectorpieces' "
"and 'key.argnames' at the same time"));
}
std::transform(ArgParts.begin(), ArgParts.end(),
std::back_inserter(Input.ArgNames),
[](const char *C) { return StringRef(C); });
std::transform(Selectors.begin(), Selectors.end(),
std::back_inserter(Input.ArgNames),
[](const char *C) { return StringRef(C); });
return Lang.getNameInfo(*SourceFile, Offset, Input, Args,
[Rec](const NameTranslatingInfo &Info) { reportNameInfo(Info, Rec); });
}
if (ReqUID == RequestRelatedIdents) {
int64_t Offset;
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
// For backwards compatibility, the default is 1.
int64_t CancelOnSubsequentRequest = 1;
Req.getInt64(KeyCancelOnSubsequentRequest, CancelOnSubsequentRequest,
/*isOptional=*/true);
return findRelatedIdents(*SourceFile, Offset, CancelOnSubsequentRequest,
Args, Rec);
}
{
llvm::raw_svector_ostream OSErr(ErrBuf);
OSErr << "unknown request: " << UIdentFromSKDUID(ReqUID).getName();
}
return Rec(createErrorRequestInvalid(ErrBuf.c_str()));
}
//===----------------------------------------------------------------------===//
// Index
//===----------------------------------------------------------------------===//
namespace {
class SKIndexingConsumer : public IndexingConsumer {
struct Entity {
UIdent Kind;
ResponseBuilder::Dictionary Data;
ResponseBuilder::Array Entities;
ResponseBuilder::Array Related;
};
SmallVector<Entity, 6> EntitiesStack;
struct Dependency {
UIdent Kind;
ResponseBuilder::Dictionary Data;
ResponseBuilder::Array Dependencies;
};
SmallVector<Dependency, 6> DependenciesStack;
ResponseBuilder::Dictionary TopDict;
bool Cancelled = false;
public:
std::string ErrorDescription;
explicit SKIndexingConsumer(ResponseBuilder &RespBuilder) {
TopDict = RespBuilder.getDictionary();
// First in stack is the top-level "key.entities" container.
EntitiesStack.push_back(
{ UIdent(),
TopDict,
ResponseBuilder::Array(),
ResponseBuilder::Array() });
DependenciesStack.push_back({UIdent(), TopDict, ResponseBuilder::Array() });
}
~SKIndexingConsumer() override {
assert(Cancelled ||
(EntitiesStack.size() == 1 && DependenciesStack.size() == 1));
(void) Cancelled;
}
void failed(StringRef ErrDescription) override;
bool recordHash(StringRef Hash, bool isKnown) override;
bool startDependency(UIdent Kind,
StringRef Name,
StringRef Path,
bool IsSystem,
StringRef Hash) override;
bool finishDependency(UIdent Kind) override;
bool startSourceEntity(const EntityInfo &Info) override;
bool recordRelatedEntity(const EntityInfo &Info) override;
bool finishSourceEntity(UIdent Kind) override;
};
} // end anonymous namespace
static sourcekitd_response_t indexSource(StringRef Filename,
ArrayRef<const char *> Args,
StringRef KnownHash) {
ResponseBuilder RespBuilder;
SKIndexingConsumer IdxConsumer(RespBuilder);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.indexSource(Filename, IdxConsumer, Args, KnownHash);
if (!IdxConsumer.ErrorDescription.empty())
return createErrorRequestFailed(IdxConsumer.ErrorDescription.c_str());
return RespBuilder.createResponse();
}
void SKIndexingConsumer::failed(StringRef ErrDescription) {
ErrorDescription = ErrDescription;
}
bool SKIndexingConsumer::recordHash(StringRef Hash, bool isKnown) {
assert(!Hash.empty());
TopDict.set(KeyHash, Hash);
if (!isKnown) {
// If the hash is known key.entities should be missing otherwise it should
// exist, even as an empty array, so create it here.
assert(EntitiesStack.size() == 1);
Entity &Top = EntitiesStack.back();
ResponseBuilder::Array &Arr = Top.Entities;
assert(Arr.isNull());
Arr = Top.Data.setArray(KeyEntities);
}
return true;
}
bool SKIndexingConsumer::startDependency(UIdent Kind,
StringRef Name,
StringRef Path,
bool IsSystem,
StringRef Hash) {
Dependency &Parent = DependenciesStack.back();
ResponseBuilder::Array &Arr = Parent.Dependencies;
if (Arr.isNull())
Arr = Parent.Data.setArray(KeyDependencies);
auto Elem = Arr.appendDictionary();
Elem.set(KeyKind, Kind);
Elem.set(KeyName, Name);
Elem.set(KeyFilePath, Path);
if (IsSystem)
Elem.setBool(KeyIsSystem, IsSystem);
if (!Hash.empty())
Elem.set(KeyHash, Hash);
DependenciesStack.push_back({ Kind, Elem, ResponseBuilder::Array() });
return true;
}
bool SKIndexingConsumer::finishDependency(UIdent Kind) {
assert(DependenciesStack.back().Kind == Kind);
DependenciesStack.pop_back();
return true;
}
bool SKIndexingConsumer::startSourceEntity(const EntityInfo &Info) {
Entity &Parent = EntitiesStack.back();
ResponseBuilder::Array &Arr = Parent.Entities;
if (Arr.isNull())
Arr = Parent.Data.setArray(KeyEntities);
auto Elem = Arr.appendDictionary();
Elem.set(KeyKind, Info.Kind);
if (!Info.Name.empty())
Elem.set(KeyName, Info.Name);
if (!Info.USR.empty())
Elem.set(KeyUSR, Info.USR);
if (Info.Line != 0) {
assert(Info.Column != 0);
Elem.set(KeyLine, Info.Line);
Elem.set(KeyColumn, Info.Column);
}
if (!Info.Group.empty())
Elem.set(KeyGroupName, Info.Group);
if (!Info.ReceiverUSR.empty())
Elem.set(KeyReceiverUSR, Info.ReceiverUSR);
if (Info.IsDynamic)
Elem.setBool(KeyIsDynamic, true);
if (Info.IsTestCandidate)
Elem.setBool(KeyIsTestCandidate, true);
if (!Info.Attrs.empty()) {
auto AttrArray = Elem.setArray(KeyAttributes);
for (auto Attr : Info.Attrs) {
auto AttrDict = AttrArray.appendDictionary();
AttrDict.set(KeyAttribute, Attr);
}
}
EntitiesStack.push_back({ Info.Kind, Elem, ResponseBuilder::Array(),
ResponseBuilder::Array()});
return true;
}
bool SKIndexingConsumer::recordRelatedEntity(const EntityInfo &Info) {
assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
Entity &Parent = EntitiesStack.back();
ResponseBuilder::Array &Arr = Parent.Related;
if (Arr.isNull())
Arr = Parent.Data.setArray(KeyRelated);
auto Elem = Arr.appendDictionary();
Elem.set(KeyKind, Info.Kind);
if (!Info.Name.empty())
Elem.set(KeyName, Info.Name);
if (!Info.USR.empty())
Elem.set(KeyUSR, Info.USR);
if (Info.Line != 0) {
assert(Info.Column != 0);
Elem.set(KeyLine, Info.Line);
Elem.set(KeyColumn, Info.Column);
}
return true;
}
bool SKIndexingConsumer::finishSourceEntity(UIdent Kind) {
Entity &CurrEnt = EntitiesStack.back();
assert(CurrEnt.Kind == Kind);
(void) CurrEnt;
EntitiesStack.pop_back();
return true;
}
//===----------------------------------------------------------------------===//
// ReportDocInfo
//===----------------------------------------------------------------------===//
namespace {
class SKDocConsumer : public DocInfoConsumer {
ResponseBuilder &RespBuilder;
struct Entity {
UIdent Kind;
ResponseBuilder::Dictionary Data;
ResponseBuilder::Array Entities;
ResponseBuilder::Array Inherits;
ResponseBuilder::Array Conforms;
ResponseBuilder::Array Attrs;
};
SmallVector<Entity, 6> EntitiesStack;
ResponseBuilder::Dictionary TopDict;
ResponseBuilder::Array Diags;
DocSupportAnnotationArrayBuilder AnnotationsBuilder;
bool Cancelled = false;
void addDocEntityInfoToDict(const DocEntityInfo &Info,
ResponseBuilder::Dictionary Dict);
public:
std::string ErrorDescription;
explicit SKDocConsumer(ResponseBuilder &RespBuilder)
: RespBuilder(RespBuilder) {
TopDict = RespBuilder.getDictionary();
// First in stack is the top-level "key.entities" container.
EntitiesStack.push_back(
{ UIdent(),
TopDict,
ResponseBuilder::Array(),
ResponseBuilder::Array(),
ResponseBuilder::Array(),
ResponseBuilder::Array() });
}
~SKDocConsumer() override {
assert(Cancelled || EntitiesStack.size() == 1);
(void) Cancelled;
}
sourcekitd_response_t createResponse() {
TopDict.setCustomBuffer(KeyAnnotations,
CustomBufferKind::DocSupportAnnotationArray,
AnnotationsBuilder.createBuffer());
return RespBuilder.createResponse();
}
void failed(StringRef ErrDescription) override;
bool handleSourceText(StringRef Text) override;
bool handleAnnotation(const DocEntityInfo &Info) override;
bool startSourceEntity(const DocEntityInfo &Info) override;
bool handleInheritsEntity(const DocEntityInfo &Info) override;
bool handleConformsToEntity(const DocEntityInfo &Info) override;
bool handleExtendsEntity(const DocEntityInfo &Info) override;
bool handleAvailableAttribute(const AvailableAttrInfo &Info) override;
bool finishSourceEntity(UIdent Kind) override;
bool handleDiagnostic(const DiagnosticEntryInfo &Info) override;
};
} // end anonymous namespace
static bool isSwiftPrefixed(StringRef MangledName) {
if (MangledName.size() < 2)
return false;
return MangledName[0] == '_' &&
(MangledName[1] == 'T' || MangledName[1] == MANGLING_PREFIX_STR[1]);
}
static sourcekitd_response_t demangleNames(ArrayRef<const char *> MangledNames,
bool Simplified) {
swift::Demangle::DemangleOptions DemangleOptions;
if (Simplified) {
DemangleOptions =
swift::Demangle::DemangleOptions::SimplifiedUIDemangleOptions();
}
auto getDemangledName = [&](StringRef MangledName) -> std::string {
if (!isSwiftPrefixed(MangledName))
return std::string(); // Not a mangled name
std::string Result = swift::Demangle::demangleSymbolAsString(
MangledName, DemangleOptions);
if (Result == MangledName)
return std::string(); // Not a mangled name
return Result;
};
ResponseBuilder RespBuilder;
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
for (auto MangledName : MangledNames) {
std::string Result = getDemangledName(MangledName);
auto Entry = Arr.appendDictionary();
Entry.set(KeyName, Result.c_str());
}
return RespBuilder.createResponse();
}
static std::string mangleSimpleClass(StringRef moduleName,
StringRef className) {
using namespace swift::Demangle;
Demangler Dem;
auto moduleNode = Dem.createNode(Node::Kind::Module, moduleName);
auto IdNode = Dem.createNode(Node::Kind::Identifier, className);
auto classNode = Dem.createNode(Node::Kind::Class);
auto typeNode = Dem.createNode(Node::Kind::Type);
auto typeManglingNode = Dem.createNode(Node::Kind::TypeMangling);
auto globalNode = Dem.createNode(Node::Kind::Global);
classNode->addChild(moduleNode, Dem);
classNode->addChild(IdNode, Dem);
typeNode->addChild(classNode, Dem);
typeManglingNode->addChild(typeNode, Dem);
globalNode->addChild(typeManglingNode, Dem);
return mangleNode(globalNode);
}
static sourcekitd_response_t
mangleSimpleClassNames(ArrayRef<std::pair<StringRef, StringRef>> ModuleClassPairs) {
ResponseBuilder RespBuilder;
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
for (auto &pair : ModuleClassPairs) {
std::string Result = mangleSimpleClass(pair.first, pair.second);
auto Entry = Arr.appendDictionary();
Entry.set(KeyName, Result.c_str());
}
return RespBuilder.createResponse();
}
static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf,
StringRef ModuleName,
ArrayRef<const char *> Args) {
ResponseBuilder RespBuilder;
SKDocConsumer DocConsumer(RespBuilder);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.getDocInfo(InputBuf, ModuleName, Args, DocConsumer);
if (!DocConsumer.ErrorDescription.empty())
return createErrorRequestFailed(DocConsumer.ErrorDescription.c_str());
return DocConsumer.createResponse();
}
void SKDocConsumer::addDocEntityInfoToDict(const DocEntityInfo &Info,
ResponseBuilder::Dictionary Elem) {
Elem.set(KeyKind, Info.Kind);
if (!Info.Name.empty())
Elem.set(KeyName, Info.Name);
if (!Info.Argument.empty())
Elem.set(KeyKeyword, Info.Argument);
if (!Info.SubModuleName.empty())
Elem.set(KeyModuleName, Info.SubModuleName);
if (!Info.USR.empty())
Elem.set(KeyUSR, Info.USR);
if (!Info.OriginalUSR.empty())
Elem.set(KeyOriginalUSR, Info.OriginalUSR);
if (!Info.ProvideImplementationOfUSR.empty())
Elem.set(KeyDefaultImplementationOf, Info.ProvideImplementationOfUSR);
if (Info.Length > 0) {
Elem.set(KeyOffset, Info.Offset);
Elem.set(KeyLength, Info.Length);
}
if (Info.IsUnavailable)
Elem.set(KeyIsUnavailable, Info.IsUnavailable);
if (Info.IsDeprecated)
Elem.set(KeyIsDeprecated, Info.IsDeprecated);
if (Info.IsOptional)
Elem.set(KeyIsOptional, Info.IsOptional);
if (!Info.DocComment.empty())
Elem.set(KeyDocFullAsXML, Info.DocComment);
if (!Info.FullyAnnotatedDecl.empty())
Elem.set(KeyFullyAnnotatedDecl, Info.FullyAnnotatedDecl);
if (!Info.LocalizationKey.empty())
Elem.set(KeyLocalizationKey, Info.LocalizationKey);
if (!Info.GenericParams.empty()) {
auto GPArray = Elem.setArray(KeyGenericParams);
for (auto &GP : Info.GenericParams) {
auto GPElem = GPArray.appendDictionary();
GPElem.set(KeyName, GP.Name);
if (!GP.Inherits.empty())
GPElem.set(KeyInherits, GP.Inherits);
}
}
// Note that due to protocol extensions, GenericRequirements may be non-empty
// while GenericParams is empty.
if (!Info.GenericRequirements.empty()) {
auto ReqArray = Elem.setArray(KeyGenericRequirements);
for (auto &Req : Info.GenericRequirements) {
auto ReqElem = ReqArray.appendDictionary();
ReqElem.set(KeyDescription, Req);
}
}
}
void SKDocConsumer::failed(StringRef ErrDescription) {
ErrorDescription = ErrDescription;
}
bool SKDocConsumer::handleSourceText(StringRef Text) {
TopDict.set(KeySourceText, Text);
return true;
}
bool SKDocConsumer::handleAnnotation(const DocEntityInfo &Info) {
AnnotationsBuilder.add(Info);
return true;
}
bool SKDocConsumer::startSourceEntity(const DocEntityInfo &Info) {
Entity &Parent = EntitiesStack.back();
ResponseBuilder::Array &Arr = Parent.Entities;
if (Arr.isNull())
Arr = Parent.Data.setArray(KeyEntities);
auto Elem = Arr.appendDictionary();
addDocEntityInfoToDict(Info, Elem);
EntitiesStack.push_back({ Info.Kind, Elem, ResponseBuilder::Array(),
ResponseBuilder::Array(),
ResponseBuilder::Array(),
ResponseBuilder::Array()});
return true;
}
bool SKDocConsumer::handleInheritsEntity(const DocEntityInfo &Info) {
assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
Entity &Parent = EntitiesStack.back();
ResponseBuilder::Array &Arr = Parent.Inherits;
if (Arr.isNull())
Arr = Parent.Data.setArray(KeyInherits);
addDocEntityInfoToDict(Info, Arr.appendDictionary());
return true;
}
bool SKDocConsumer::handleConformsToEntity(const DocEntityInfo &Info) {
assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
Entity &Parent = EntitiesStack.back();
ResponseBuilder::Array &Arr = Parent.Conforms;
if (Arr.isNull())
Arr = Parent.Data.setArray(KeyConforms);
addDocEntityInfoToDict(Info, Arr.appendDictionary());
return true;
}
bool SKDocConsumer::handleExtendsEntity(const DocEntityInfo &Info) {
assert(EntitiesStack.size() > 1 && "Related entity at top-level ?");
Entity &Parent = EntitiesStack.back();
addDocEntityInfoToDict(Info, Parent.Data.setDictionary(KeyExtends));
return true;
}
bool SKDocConsumer::handleAvailableAttribute(const AvailableAttrInfo &Info) {
Entity &Parent = EntitiesStack.back();
ResponseBuilder::Array &Arr = Parent.Attrs;
if (Arr.isNull())
Arr = Parent.Data.setArray(KeyAttributes);
auto Elem = Arr.appendDictionary();
Elem.set(KeyKind, Info.AttrKind);
if (Info.IsUnavailable)
Elem.set(KeyIsUnavailable, Info.IsUnavailable);
if (Info.IsDeprecated)
Elem.set(KeyIsDeprecated, Info.IsDeprecated);
if (Info.Platform.isValid())
Elem.set(KeyPlatform, Info.Platform);
if (!Info.Message.empty())
Elem.set(KeyMessage, Info.Message);
if (Info.Introduced.hasValue())
Elem.set(KeyIntroduced, Info.Introduced.getValue().getAsString());
if (Info.Deprecated.hasValue())
Elem.set(KeyDeprecated, Info.Deprecated.getValue().getAsString());
if (Info.Obsoleted.hasValue())
Elem.set(KeyObsoleted, Info.Obsoleted.getValue().getAsString());
return true;
}
bool SKDocConsumer::finishSourceEntity(UIdent Kind) {
Entity &CurrEnt = EntitiesStack.back();
assert(CurrEnt.Kind == Kind);
(void) CurrEnt;
EntitiesStack.pop_back();
return true;
}
bool SKDocConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info) {
ResponseBuilder::Array &Arr = Diags;
if (Arr.isNull())
Arr = TopDict.setArray(KeyDiagnostics);
auto Elem = Arr.appendDictionary();
UIdent SeverityUID;
static UIdent UIDKindDiagWarning(KindDiagWarning.str());
static UIdent UIDKindDiagError(KindDiagError.str());
switch (Info.Severity) {
case DiagnosticSeverityKind::Warning:
SeverityUID = UIDKindDiagWarning;
break;
case DiagnosticSeverityKind::Error:
SeverityUID = UIDKindDiagError;
break;
}
Elem.set(KeySeverity, SeverityUID);
fillDictionaryForDiagnosticInfo(Elem, Info);
if (!Info.Notes.empty()) {
auto NotesArr = Elem.setArray(KeyDiagnostics);
for (auto &NoteDiag : Info.Notes) {
auto NoteElem = NotesArr.appendDictionary();
NoteElem.set(KeySeverity, KindDiagNote);
fillDictionaryForDiagnosticInfo(NoteElem, NoteDiag);
}
}
return true;
}
//===----------------------------------------------------------------------===//
// ReportCursorInfo
//===----------------------------------------------------------------------===//
static void reportCursorInfo(const CursorInfoData &Info, ResponseReceiver Rec) {
if (Info.IsCancelled)
return Rec(createErrorRequestCancelled());
ResponseBuilder RespBuilder;
if (Info.Kind.isInvalid())
return Rec(RespBuilder.createResponse());
auto Elem = RespBuilder.getDictionary();
Elem.set(KeyKind, Info.Kind);
Elem.set(KeyName, Info.Name);
if (!Info.USR.empty())
Elem.set(KeyUSR, Info.USR);
if (!Info.TypeName.empty())
Elem.set(KeyTypeName, Info.TypeName);
if (!Info.DocComment.empty())
Elem.set(KeyDocFullAsXML, Info.DocComment);
if (!Info.AnnotatedDeclaration.empty())
Elem.set(KeyAnnotatedDecl, Info.AnnotatedDeclaration);
if (!Info.FullyAnnotatedDeclaration.empty())
Elem.set(KeyFullyAnnotatedDecl, Info.FullyAnnotatedDeclaration);
if (!Info.ModuleName.empty())
Elem.set(KeyModuleName, Info.ModuleName);
if (!Info.GroupName.empty())
Elem.set(KeyGroupName, Info.GroupName);
if (!Info.LocalizationKey.empty())
Elem.set(KeyLocalizationKey, Info.LocalizationKey);
if (!Info.ModuleInterfaceName.empty())
Elem.set(KeyModuleInterfaceName, Info.ModuleInterfaceName);
if (Info.DeclarationLoc.hasValue()) {
Elem.set(KeyOffset, Info.DeclarationLoc.getValue().first);
Elem.set(KeyLength, Info.DeclarationLoc.getValue().second);
if (!Info.Filename.empty())
Elem.set(KeyFilePath, Info.Filename);
}
if (!Info.OverrideUSRs.empty()) {
auto Overrides = Elem.setArray(KeyOverrides);
for (auto USR : Info.OverrideUSRs) {
auto Override = Overrides.appendDictionary();
Override.set(KeyUSR, USR);
}
}
if (!Info.ModuleGroupArray.empty()) {
auto Groups = Elem.setArray(KeyModuleGroups);
for (auto Name : Info.ModuleGroupArray) {
auto Entry = Groups.appendDictionary();
Entry.set(KeyGroupName, Name);
}
}
if (!Info.AvailableActions.empty()) {
auto Actions = Elem.setArray(KeyRefactorActions);
for (auto Info : Info.AvailableActions) {
auto Entry = Actions.appendDictionary();
Entry.set(KeyActionUID, Info.Kind);
Entry.set(KeyActionName, Info.KindName);
if (!Info.UnavailableReason.empty())
Entry.set(KeyActionUnavailableReason, Info.UnavailableReason);
}
}
if (Info.ParentNameOffset) {
Elem.set(KeyParentLoc, Info.ParentNameOffset.getValue());
}
if (!Info.AnnotatedRelatedDeclarations.empty()) {
auto RelDecls = Elem.setArray(KeyRelatedDecls);
for (auto AnnotDecl : Info.AnnotatedRelatedDeclarations) {
auto RelDecl = RelDecls.appendDictionary();
RelDecl.set(KeyAnnotatedDecl, AnnotDecl);
}
}
if (Info.IsSystem)
Elem.setBool(KeyIsSystem, true);
if (!Info.TypeInterface.empty())
Elem.set(KeyTypeInterface, Info.TypeInterface);
if (!Info.TypeUSR.empty())
Elem.set(KeyTypeUsr, Info.TypeUSR);
if (!Info.ContainerTypeUSR.empty())
Elem.set(KeyContainerTypeUsr, Info.ContainerTypeUSR);
return Rec(RespBuilder.createResponse());
}
//===----------------------------------------------------------------------===//
// ReportRangeInfo
//===----------------------------------------------------------------------===//
static void reportRangeInfo(const RangeInfo &Info, ResponseReceiver Rec) {
if (Info.IsCancelled)
return Rec(createErrorRequestCancelled());
ResponseBuilder RespBuilder;
auto Elem = RespBuilder.getDictionary();
Elem.set(KeyKind, Info.RangeKind);
Elem.set(KeyTypeName, Info.ExprType);
Elem.set(KeyRangeContent, Info.RangeContent);
Rec(RespBuilder.createResponse());
}
//===----------------------------------------------------------------------===//
// ReportNameInfo
//===----------------------------------------------------------------------===//
static void reportNameInfo(const NameTranslatingInfo &Info, ResponseReceiver Rec) {
if (Info.IsCancelled)
return Rec(createErrorRequestCancelled());
ResponseBuilder RespBuilder;
if (Info.NameKind.isInvalid())
return Rec(RespBuilder.createResponse());
if (Info.BaseName.empty() && Info.ArgNames.empty())
return Rec(RespBuilder.createResponse());
auto Elem = RespBuilder.getDictionary();
Elem.set(KeyNameKind, Info.NameKind);
if (!Info.BaseName.empty()) {
Elem.set(KeyBaseName, Info.BaseName);
}
if (!Info.ArgNames.empty()) {
static UIdent UIDKindNameSwift(KindNameSwift.str());
auto Arr = Elem.setArray(Info.NameKind == UIDKindNameSwift ?
KeyArgNames : KeySelectorPieces);
for (auto N : Info.ArgNames) {
auto NameEle = Arr.appendDictionary();
NameEle.set(KeyName, N);
}
}
if (Info.IsZeroArgSelector) {
Elem.set(KeyIsZeroArgSelector, Info.IsZeroArgSelector);
}
Rec(RespBuilder.createResponse());
}
//===----------------------------------------------------------------------===//
// FindRelatedIdents
//===----------------------------------------------------------------------===//
static void findRelatedIdents(StringRef Filename,
int64_t Offset,
bool CancelOnSubsequentRequest,
ArrayRef<const char *> Args,
ResponseReceiver Rec) {
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.findRelatedIdentifiersInFile(Filename, Offset, CancelOnSubsequentRequest,
Args, [Rec](const RelatedIdentsInfo &Info) {
if (Info.IsCancelled)
return Rec(createErrorRequestCancelled());
ResponseBuilder RespBuilder;
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
for (auto R : Info.Ranges) {
auto Elem = Arr.appendDictionary();
Elem.set(KeyOffset, R.first);
Elem.set(KeyLength, R.second);
}
Rec(RespBuilder.createResponse());
});
}
//===----------------------------------------------------------------------===//
// CodeComplete
//===----------------------------------------------------------------------===//
namespace {
class SKCodeCompletionConsumer : public CodeCompletionConsumer {
ResponseBuilder &RespBuilder;
CodeCompletionResultsArrayBuilder ResultsBuilder;
std::string ErrorDescription;
public:
explicit SKCodeCompletionConsumer(ResponseBuilder &RespBuilder)
: RespBuilder(RespBuilder) {
}
sourcekitd_response_t createResponse() {
if (!ErrorDescription.empty())
return createErrorRequestFailed(ErrorDescription.c_str());
RespBuilder.getDictionary().setCustomBuffer(KeyResults,
CustomBufferKind::CodeCompletionResultsArray,
ResultsBuilder.createBuffer());
return RespBuilder.createResponse();
}
void failed(StringRef ErrDescription) override;
bool handleResult(const CodeCompletionInfo &Info) override;
};
} // end anonymous namespace
static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf,
int64_t Offset,
ArrayRef<const char *> Args) {
ResponseBuilder RespBuilder;
SKCodeCompletionConsumer CCC(RespBuilder);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.codeComplete(InputBuf, Offset, CCC, Args);
return CCC.createResponse();
}
void SKCodeCompletionConsumer::failed(StringRef ErrDescription) {
ErrorDescription = ErrDescription;
}
bool SKCodeCompletionConsumer::handleResult(const CodeCompletionInfo &R) {
Optional<StringRef> ModuleNameOpt;
if (!R.ModuleName.empty())
ModuleNameOpt = R.ModuleName;
Optional<StringRef> DocBriefOpt;
if (!R.DocBrief.empty())
DocBriefOpt = R.DocBrief;
Optional<StringRef> AssocUSRsOpt;
if (!R.AssocUSRs.empty())
AssocUSRsOpt = R.AssocUSRs;
assert(!R.ModuleImportDepth && "not implemented on CompactArray path");
ResultsBuilder.add(R.Kind,
R.Name,
R.Description,
R.SourceText,
R.TypeName,
ModuleNameOpt,
DocBriefOpt,
AssocUSRsOpt,
R.SemanticContext,
R.NotRecommended,
R.NumBytesToErase);
return true;
}
//===----------------------------------------------------------------------===//
// (New) CodeComplete
//===----------------------------------------------------------------------===//
namespace {
class SKGroupedCodeCompletionConsumer : public GroupedCodeCompletionConsumer {
ResponseBuilder &RespBuilder;
ResponseBuilder::Dictionary Response;
SmallVector<ResponseBuilder::Array, 3> GroupContentsStack;
std::string ErrorDescription;
public:
explicit SKGroupedCodeCompletionConsumer(ResponseBuilder &RespBuilder)
: RespBuilder(RespBuilder) {}
sourcekitd_response_t createResponse() {
if (!ErrorDescription.empty())
return createErrorRequestFailed(ErrorDescription.c_str());
assert(GroupContentsStack.empty() && "mismatched start/endGroup");
return RespBuilder.createResponse();
}
void failed(StringRef ErrDescription) override;
bool handleResult(const CodeCompletionInfo &Info) override;
void startGroup(UIdent kind, StringRef name) override;
void endGroup() override;
void setNextRequestStart(unsigned offset) override;
};
class SKOptionsDictionary : public OptionsDictionary {
RequestDict &Options;
public:
explicit SKOptionsDictionary(RequestDict &Options) : Options(Options) {}
bool valueForOption(UIdent Key, unsigned &Val) override {
int64_t result;
if (Options.getInt64(Key, result, false))
return false;
Val = static_cast<unsigned>(result);
return true;
}
bool valueForOption(UIdent Key, bool &Val) override {
int64_t result;
if (Options.getInt64(Key, result, false))
return false;
Val = result ? true : false;
return true;
}
bool valueForOption(UIdent Key, StringRef &Val) override {
Optional<StringRef> value = Options.getString(Key);
if (!value)
return false;
Val = *value;
return true;
}
};
} // end anonymous namespace
static sourcekitd_response_t codeCompleteOpen(StringRef Name,
llvm::MemoryBuffer *InputBuf,
int64_t Offset,
Optional<RequestDict> optionsDict,
ArrayRef<const char *> Args) {
ResponseBuilder RespBuilder;
SKGroupedCodeCompletionConsumer CCC(RespBuilder);
std::unique_ptr<SKOptionsDictionary> options;
std::vector<FilterRule> filterRules;
if (optionsDict) {
options = llvm::make_unique<SKOptionsDictionary>(*optionsDict);
bool failed = false;
optionsDict->dictionaryArrayApply(KeyFilterRules, [&](RequestDict dict) {
FilterRule rule;
auto kind = dict.getUID(KeyKind);
if (kind == KindCodeCompletionEverything) {
rule.kind = FilterRule::Everything;
} else if (kind == KindCodeCompletionModule) {
rule.kind = FilterRule::Module;
} else if (kind == KindCodeCompletionKeyword) {
rule.kind = FilterRule::Keyword;
} else if (kind == KindCodeCompletionLiteral) {
rule.kind = FilterRule::Literal;
} else if (kind == KindCodeCompletionCustom) {
rule.kind = FilterRule::CustomCompletion;
} else if (kind == KindCodeCompletionIdentifier) {
rule.kind = FilterRule::Identifier;
} else if (kind == KindCodeCompletionDescription) {
rule.kind = FilterRule::Description;
} else {
// Warning: unknown
}
int64_t hide;
if (dict.getInt64(KeyHide, hide, false)) {
failed = true;
CCC.failed("filter rule missing required key 'key.hide'");
return true;
}
rule.hide = hide;
switch (rule.kind) {
case FilterRule::Everything:
break;
case FilterRule::Module:
case FilterRule::Identifier: {
SmallVector<const char *, 8> names;
if (dict.getStringArray(KeyNames, names, false)) {
failed = true;
CCC.failed("filter rule missing required key 'key.names'");
return true;
}
rule.names.assign(names.begin(), names.end());
break;
}
case FilterRule::Description: {
SmallVector<const char *, 8> names;
if (dict.getStringArray(KeyNames, names, false)) {
failed = true;
CCC.failed("filter rule missing required key 'key.names'");
return true;
}
rule.names.assign(names.begin(), names.end());
break;
}
case FilterRule::Keyword:
case FilterRule::Literal:
case FilterRule::CustomCompletion: {
SmallVector<sourcekitd_uid_t, 8> uids;
dict.getUIDArray(KeyUIDs, uids, true);
for (auto uid : uids)
rule.uids.push_back(UIdentFromSKDUID(uid));
break;
}
}
filterRules.push_back(std::move(rule));
return false; // continue
});
if (failed)
return CCC.createResponse();
}
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.codeCompleteOpen(Name, InputBuf, Offset, options.get(), filterRules, CCC,
Args);
return CCC.createResponse();
}
static sourcekitd_response_t codeCompleteClose(StringRef Name, int64_t Offset) {
ResponseBuilder RespBuilder;
SKGroupedCodeCompletionConsumer CCC(RespBuilder);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.codeCompleteClose(Name, Offset, CCC);
return CCC.createResponse();
}
static sourcekitd_response_t
codeCompleteUpdate(StringRef name, int64_t offset,
Optional<RequestDict> optionsDict) {
ResponseBuilder RespBuilder;
SKGroupedCodeCompletionConsumer CCC(RespBuilder);
std::unique_ptr<SKOptionsDictionary> options;
if (optionsDict)
options = llvm::make_unique<SKOptionsDictionary>(*optionsDict);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.codeCompleteUpdate(name, offset, options.get(), CCC);
return CCC.createResponse();
}
void SKGroupedCodeCompletionConsumer::failed(StringRef ErrDescription) {
ErrorDescription = ErrDescription;
}
bool SKGroupedCodeCompletionConsumer::handleResult(const CodeCompletionInfo &R) {
assert(!GroupContentsStack.empty() && "missing root group");
auto result = GroupContentsStack.back().appendDictionary();
if (R.CustomKind)
result.set(KeyKind, sourcekitd_uid_t(R.CustomKind));
else
result.set(KeyKind, R.Kind);
result.set(KeyName, R.Name);
result.set(KeyDescription, R.Description);
result.set(KeySourceText, R.SourceText);
result.set(KeyTypeName, R.TypeName);
result.set(KeyContext, R.SemanticContext);
if (!R.ModuleName.empty())
result.set(KeyModuleName, R.ModuleName);
if (!R.DocBrief.empty())
result.set(KeyDocBrief, R.DocBrief);
if (!R.AssocUSRs.empty())
result.set(KeyAssociatedUSRs, R.AssocUSRs);
if (R.ModuleImportDepth)
result.set(KeyModuleImportDepth, *R.ModuleImportDepth);
if (R.NotRecommended)
result.set(KeyNotRecommended, R.NotRecommended);
result.set(KeyNumBytesToErase, R.NumBytesToErase);
if (R.descriptionStructure) {
auto addRange = [](ResponseBuilder::Dictionary dict, UIdent offset,
UIdent length, CodeCompletionInfo::IndexRange range) {
if (!range.empty()) {
dict.set(offset, range.begin);
dict.set(length, range.length());
}
};
auto structure = result.setDictionary(KeySubStructure);
addRange(structure, KeyNameOffset, KeyNameLength,
R.descriptionStructure->baseName);
addRange(structure, KeyBodyOffset, KeyBodyLength,
R.descriptionStructure->parameterRange);
addRange(structure, KeyThrowOffset, KeyThrowLength,
R.descriptionStructure->throwsRange);
if (R.parametersStructure) {
auto params = structure.setArray(KeySubStructure);
for (auto &P : *R.parametersStructure) {
auto param = params.appendDictionary();
addRange(param, KeyNameOffset, KeyNameLength, P.name);
addRange(param, KeyBodyOffset, KeyBodyLength, P.afterColon);
if (P.isLocalName)
param.set(KeyIsLocal, true);
}
}
}
return true;
}
void SKGroupedCodeCompletionConsumer::startGroup(UIdent kind, StringRef name) {
ResponseBuilder::Dictionary group;
if (GroupContentsStack.empty()) {
group = RespBuilder.getDictionary();
Response = group;
} else {
group = GroupContentsStack.back().appendDictionary();
}
group.set(KeyKind, kind);
group.set(KeyName, name);
auto contents = group.setArray(KeyResults);
GroupContentsStack.push_back(contents);
}
void SKGroupedCodeCompletionConsumer::endGroup() {
assert(!GroupContentsStack.empty());
GroupContentsStack.pop_back();
}
void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) {
assert(!Response.isNull());
Response.set(KeyNextRequestStart, offset);
}
//===----------------------------------------------------------------------===//
// Editor
//===----------------------------------------------------------------------===//
namespace {
class SKEditorConsumer : public EditorConsumer {
ResponseReceiver RespReceiver;
ResponseBuilder RespBuilder;
ResponseBuilder::Dictionary Dict;
DocStructureArrayBuilder DocStructure;
TokenAnnotationsArrayBuilder SyntaxMap;
TokenAnnotationsArrayBuilder SemanticAnnotations;
ResponseBuilder::Array Diags;
sourcekitd_response_t Error = nullptr;
bool EnableSyntaxMap;
bool EnableStructure;
bool EnableDiagnostics;
bool SyntacticOnly;
public:
SKEditorConsumer(bool EnableSyntaxMap, bool EnableStructure,
bool EnableDiagnostics, bool SyntacticOnly)
: EnableSyntaxMap(EnableSyntaxMap), EnableStructure(EnableStructure),
EnableDiagnostics(EnableDiagnostics), SyntacticOnly(SyntacticOnly) {
Dict = RespBuilder.getDictionary();
}
SKEditorConsumer(ResponseReceiver RespReceiver, bool EnableSyntaxMap,
bool EnableStructure, bool EnableDiagnostics,
bool SyntacticOnly)
: SKEditorConsumer(EnableSyntaxMap, EnableStructure,
EnableDiagnostics, SyntacticOnly) {
this->RespReceiver = RespReceiver;
}
sourcekitd_response_t createResponse();
bool needsSemanticInfo() override {
return !SyntacticOnly && !isSemanticEditorDisabled();
}
void handleRequestError(const char *Description) override;
bool handleSyntaxMap(unsigned Offset, unsigned Length, UIdent Kind) override;
bool handleSemanticAnnotation(unsigned Offset, unsigned Length, UIdent Kind,
bool isSystem) override;
bool beginDocumentSubStructure(unsigned Offset, unsigned Length, UIdent Kind,
UIdent AccessLevel,
UIdent SetterAccessLevel,
unsigned NameOffset,
unsigned NameLength,
unsigned BodyOffset,
unsigned BodyLength,
unsigned DocOffset,
unsigned DocLength,
StringRef DisplayName,
StringRef TypeName,
StringRef RuntimeName,
StringRef SelectorName,
ArrayRef<StringRef> InheritedTypes,
ArrayRef<std::tuple<UIdent, unsigned, unsigned>> Attrs) override;
bool endDocumentSubStructure() override;
bool handleDocumentSubStructureElement(UIdent Kind,
unsigned Offset,
unsigned Length) override;
bool recordAffectedRange(unsigned Offset, unsigned Length) override;
bool recordAffectedLineRange(unsigned Line, unsigned Length) override;
bool recordFormattedText(StringRef Text) override;
bool setDiagnosticStage(UIdent DiagStage) override;
bool handleDiagnostic(const DiagnosticEntryInfo &Info,
UIdent DiagStage) override;
bool handleSourceText(StringRef Text) override;
void finished() override {
if (RespReceiver)
RespReceiver(createResponse());
}
};
} // end anonymous namespace
static sourcekitd_response_t
editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, bool EnableSyntaxMap,
bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly,
ArrayRef<const char *> Args) {
SKEditorConsumer EditC(EnableSyntaxMap, EnableStructure,
EnableDiagnostics, SyntacticOnly);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorOpen(Name, Buf, EnableSyntaxMap, EditC, Args);
return EditC.createResponse();
}
static sourcekitd_response_t
editorOpenInterface(StringRef Name, StringRef ModuleName,
Optional<StringRef> Group, ArrayRef<const char *> Args,
bool SynthesizedExtensions,
Optional<StringRef> InterestedUSR) {
SKEditorConsumer EditC(/*EnableSyntaxMap=*/true,
/*EnableStructure=*/true,
/*EnableDiagnostics=*/false,
/*SyntacticOnly=*/false);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorOpenInterface(EditC, Name, ModuleName, Group, Args,
SynthesizedExtensions, InterestedUSR);
return EditC.createResponse();
}
/// Getting the interface from a swift source file differs from getting interfaces
/// from headers or modules for its performing asynchronously.
static void
editorOpenSwiftSourceInterface(StringRef Name, StringRef HeaderName,
ArrayRef<const char *> Args,
ResponseReceiver Rec) {
auto EditC = std::make_shared<SKEditorConsumer>(Rec,
/*EnableSyntaxMap=*/true,
/*EnableStructure=*/true,
/*EnableDiagnostics=*/false,
/*SyntacticOnly=*/false);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorOpenSwiftSourceInterface(Name, HeaderName, Args, EditC);
}
static void
editorOpenSwiftTypeInterface(StringRef TypeUsr, ArrayRef<const char *> Args,
ResponseReceiver Rec) {
auto EditC = std::make_shared<SKEditorConsumer>(Rec,
/*EnableSyntaxMap=*/true,
/*EnableStructure=*/true,
/*EnableDiagnostics=*/false,
/*SyntacticOnly=*/false);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorOpenTypeInterface(*EditC, Args, TypeUsr);
}
static sourcekitd_response_t editorExtractTextFromComment(StringRef Source) {
SKEditorConsumer EditC(/*EnableSyntaxMap=*/false,
/*EnableStructure=*/false,
/*EnableDiagnostics=*/false,
/*SyntacticOnly=*/true);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorExtractTextFromComment(Source, EditC);
return EditC.createResponse();
}
static sourcekitd_response_t editorConvertMarkupToXML(StringRef Source) {
SKEditorConsumer EditC(/*EnableSyntaxMap=*/false,
/*EnableStructure=*/false,
/*EnableDiagnostics=*/false,
/*SyntacticOnly=*/true);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorConvertMarkupToXML(Source, EditC);
return EditC.createResponse();
}
static sourcekitd_response_t
editorOpenHeaderInterface(StringRef Name, StringRef HeaderName,
ArrayRef<const char *> Args,
bool UsingSwiftArgs,
bool SynthesizedExtensions,
Optional<unsigned> swiftVersion) {
SKEditorConsumer EditC(/*EnableSyntaxMap=*/true,
/*EnableStructure=*/true,
/*EnableDiagnostics=*/false,
/*SyntacticOnly=*/false);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorOpenHeaderInterface(EditC, Name, HeaderName, Args, UsingSwiftArgs,
SynthesizedExtensions, swiftVersion);
return EditC.createResponse();
}
static sourcekitd_response_t
editorClose(StringRef Name, bool RemoveCache) {
ResponseBuilder RespBuilder;
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorClose(Name, RemoveCache);
return RespBuilder.createResponse();
}
static sourcekitd_response_t
editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf, unsigned Offset,
unsigned Length, bool EnableSyntaxMap, bool EnableStructure,
bool EnableDiagnostics, bool SyntacticOnly) {
SKEditorConsumer EditC(EnableSyntaxMap, EnableStructure,
EnableDiagnostics, SyntacticOnly);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorReplaceText(Name, Buf, Offset, Length, EditC);
return EditC.createResponse();
}
static void
editorApplyFormatOptions(StringRef Name, RequestDict &FmtOptions) {
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
SKOptionsDictionary SKFmtOptions(FmtOptions);
Lang.editorApplyFormatOptions(Name, SKFmtOptions);
}
static sourcekitd_response_t
editorFormatText(StringRef Name, unsigned Line, unsigned Length) {
SKEditorConsumer EditC(false, false, false,
/*SyntacticOnly=*/true);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorFormatText(Name, Line, Length, EditC);
return EditC.createResponse();
}
static sourcekitd_response_t
editorExpandPlaceholder(StringRef Name, unsigned Offset, unsigned Length) {
SKEditorConsumer EditC(false, false, false,
/*SyntacticOnly=*/true);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.editorExpandPlaceholder(Name, Offset, Length, EditC);
return EditC.createResponse();
}
sourcekitd_response_t SKEditorConsumer::createResponse() {
if (Error)
return Error;
if (EnableSyntaxMap) {
Dict.setCustomBuffer(KeySyntaxMap,
CustomBufferKind::TokenAnnotationsArray,
SyntaxMap.createBuffer());
}
if (!SemanticAnnotations.empty()) {
Dict.setCustomBuffer(KeyAnnotations,
CustomBufferKind::TokenAnnotationsArray,
SemanticAnnotations.createBuffer());
}
if (EnableStructure) {
Dict.setCustomBuffer(KeySubStructure, CustomBufferKind::DocStructureArray,
DocStructure.createBuffer());
}
return RespBuilder.createResponse();
}
void SKEditorConsumer::handleRequestError(const char *Description) {
if (!Error) {
Error = createErrorRequestFailed(Description);
}
if (RespReceiver) {
RespReceiver(Error);
RespReceiver = ResponseReceiver();
}
}
bool SKEditorConsumer::handleSyntaxMap(unsigned Offset, unsigned Length,
UIdent Kind) {
if (!EnableSyntaxMap)
return true;
SyntaxMap.add(Kind, Offset, Length, /*IsSystem=*/false);
return true;
}
bool SKEditorConsumer::handleSemanticAnnotation(unsigned Offset,
unsigned Length,
UIdent Kind, bool isSystem) {
assert(Kind.isValid());
SemanticAnnotations.add(Kind, Offset, Length, isSystem);
return true;
}
bool
SKEditorConsumer::beginDocumentSubStructure(unsigned Offset,
unsigned Length, UIdent Kind,
UIdent AccessLevel,
UIdent SetterAccessLevel,
unsigned NameOffset,
unsigned NameLength,
unsigned BodyOffset,
unsigned BodyLength,
unsigned DocOffset,
unsigned DocLength,
StringRef DisplayName,
StringRef TypeName,
StringRef RuntimeName,
StringRef SelectorName,
ArrayRef<StringRef> InheritedTypes,
ArrayRef<std::tuple<UIdent, unsigned, unsigned>> Attrs) {
if (EnableStructure) {
DocStructure.beginSubStructure(
Offset, Length, Kind, AccessLevel, SetterAccessLevel, NameOffset,
NameLength, BodyOffset, BodyLength, DocOffset, DocLength, DisplayName,
TypeName, RuntimeName, SelectorName, InheritedTypes, Attrs);
}
return true;
}
bool SKEditorConsumer::endDocumentSubStructure() {
if (EnableStructure)
DocStructure.endSubStructure();
return true;
}
bool SKEditorConsumer::handleDocumentSubStructureElement(UIdent Kind,
unsigned Offset,
unsigned Length) {
if (EnableStructure)
DocStructure.addElement(Kind, Offset, Length);
return true;
}
bool SKEditorConsumer::recordAffectedRange(unsigned Offset, unsigned Length) {
Dict.set(KeyOffset, Offset);
Dict.set(KeyLength, Length);
return true;
}
bool SKEditorConsumer::recordAffectedLineRange(unsigned Line, unsigned Length) {
Dict.set(KeyLine, Line);
Dict.set(KeyLength, Length);
return true;
}
bool SKEditorConsumer::recordFormattedText(StringRef Text) {
Dict.set(KeySourceText, Text);
return true;
}
static void fillDictionaryForDiagnosticInfo(
ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info) {
Elem.set(KeyDescription, Info.Description);
if (Info.Line != 0) {
Elem.set(KeyLine, Info.Line);
Elem.set(KeyColumn, Info.Column);
} else {
Elem.set(KeyOffset, Info.Offset);
}
if (!Info.Filename.empty())
Elem.set(KeyFilePath, Info.Filename);
if (!Info.Ranges.empty()) {
auto RangesArr = Elem.setArray(KeyRanges);
for (auto R : Info.Ranges) {
auto RangeElem = RangesArr.appendDictionary();
RangeElem.set(KeyOffset, R.first);
RangeElem.set(KeyLength, R.second);
}
}
if (!Info.Fixits.empty()) {
auto FixitsArr = Elem.setArray(KeyFixits);
for (auto F : Info.Fixits) {
auto FixitElem = FixitsArr.appendDictionary();
FixitElem.set(KeyOffset, F.Offset);
FixitElem.set(KeyLength, F.Length);
FixitElem.set(KeySourceText, F.Text);
}
}
}
bool SKEditorConsumer::setDiagnosticStage(UIdent DiagStage) {
Dict.set(KeyDiagnosticStage, DiagStage);
return true;
}
bool SKEditorConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info,
UIdent DiagStage) {
if (!EnableDiagnostics)
return true;
ResponseBuilder::Array &Arr = Diags;
if (Arr.isNull())
Arr = Dict.setArray(KeyDiagnostics);
auto Elem = Arr.appendDictionary();
UIdent SeverityUID;
static UIdent UIDKindDiagWarning(KindDiagWarning.str());
static UIdent UIDKindDiagError(KindDiagError.str());
switch (Info.Severity) {
case DiagnosticSeverityKind::Warning:
SeverityUID = UIDKindDiagWarning;
break;
case DiagnosticSeverityKind::Error:
SeverityUID = UIDKindDiagError;
break;
}
Elem.set(KeySeverity, SeverityUID);
Elem.set(KeyDiagnosticStage, DiagStage);
fillDictionaryForDiagnosticInfo(Elem, Info);
if (!Info.Notes.empty()) {
auto NotesArr = Elem.setArray(KeyDiagnostics);
for (auto &NoteDiag : Info.Notes) {
auto NoteElem = NotesArr.appendDictionary();
NoteElem.set(KeySeverity, KindDiagNote);
fillDictionaryForDiagnosticInfo(NoteElem, NoteDiag);
}
}
return true;
}
bool SKEditorConsumer::handleSourceText(StringRef Text) {
Dict.set(KeySourceText, Text);
return true;
}
static sourcekitd_response_t
editorFindUSR(StringRef DocumentName, StringRef USR) {
ResponseBuilder RespBuilder;
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
llvm::Optional<std::pair<unsigned, unsigned>>
Range = Lang.findUSRRange(DocumentName, USR);
if (!Range) {
// If cannot find the synthesized USR, find the actual USR instead.
Range = Lang.findUSRRange(DocumentName,
USR.split(LangSupport::SynthesizedUSRSeparator).
first);
}
if (Range.hasValue()) {
RespBuilder.getDictionary().set(KeyOffset, Range->first);
RespBuilder.getDictionary().set(KeyLength, Range->second);
}
return RespBuilder.createResponse();
}
static sourcekitd_response_t
editorFindInterfaceDoc(StringRef ModuleName, ArrayRef<const char *> Args) {
ResponseBuilder RespBuilder;
sourcekitd_response_t Resp;
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.findInterfaceDocument(ModuleName, Args,
[&](const InterfaceDocInfo &Info) {
if (!Info.Error.empty()) {
SmallString<128> Err(Info.Error);
Resp = createErrorRequestFailed(Err.c_str());
return;
}
auto Elem = RespBuilder.getDictionary();
if (!Info.ModuleInterfaceName.empty())
Elem.set(KeyModuleInterfaceName, Info.ModuleInterfaceName);
if (!Info.CompilerArgs.empty())
Elem.set(KeyCompilerArgs, Info.CompilerArgs);
Resp = RespBuilder.createResponse();
});
return Resp;
}
static sourcekitd_response_t
editorFindModuleGroups(StringRef ModuleName, ArrayRef<const char *> Args) {
ResponseBuilder RespBuilder;
sourcekitd_response_t Resp;
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.findModuleGroups(ModuleName, Args,
[&](ArrayRef<StringRef> Groups, StringRef Error) {
if (!Error.empty()) {
Resp = createErrorRequestFailed(Error.str().c_str());
return;
}
auto Dict = RespBuilder.getDictionary();
auto Arr = Dict.setArray(KeyModuleGroups);
for (auto G : Groups) {
auto Entry = Arr.appendDictionary();
Entry.set(KeyGroupName, G);
}
Resp = RespBuilder.createResponse();
});
return Resp;
}
static bool
buildRenameLocationsFromDict(RequestDict &Req, bool UseNewName,
std::vector<RenameLocations> &RenameLocations,
llvm::SmallString<64> &Error) {
bool Failed = Req.dictionaryArrayApply(KeyRenameLocations,
[&](RequestDict RenameLocation) {
int64_t IsFunctionLike = false;
if (RenameLocation.getInt64(KeyIsFunctionLike, IsFunctionLike, false)) {
Error = "missing key.is_function_like";
return true;
}
int64_t IsNonProtocolType = false;
if (RenameLocation.getInt64(KeyIsNonProtocolType, IsNonProtocolType, false)) {
Error = "missing key.is_non_protocol_type";
return true;
}
Optional<StringRef> OldName = RenameLocation.getString(KeyName);
if (!OldName.hasValue()) {
Error = "missing key.name";
return true;
}
Optional<StringRef> NewName;
if (UseNewName) {
NewName = RenameLocation.getString(KeyNewName);
if (!NewName.hasValue()) {
Error = "missing key.newname";
return true;
}
}
RenameLocations.push_back({*OldName,
UseNewName ? *NewName : "",
static_cast<bool>(IsFunctionLike),
static_cast<bool>(IsNonProtocolType),
{}});
auto &LineCols = RenameLocations.back().LineColumnLocs;
bool Failed = RenameLocation.dictionaryArrayApply(KeyLocations,
[&](RequestDict LineAndCol) {
int64_t Line = 0;
int64_t Column = 0;
if (LineAndCol.getInt64(KeyLine, Line, false)) {
Error = "missing key.line";
return true;
}
if (LineAndCol.getInt64(KeyColumn, Column, false)) {
Error = "missing key.column";
return true;
}
sourcekitd_uid_t NameType = LineAndCol.getUID(KeyNameType);
if (!NameType) {
Error = "missing key.nametype";
return true;
}
RenameType RenameType = RenameType::Unknown;
if (NameType == KindDefinition) {
RenameType = RenameType::Definition;
} else if (NameType == KindReference) {
RenameType = RenameType::Reference;
} else if (NameType == KindCall) {
RenameType = RenameType::Call;
} else if (NameType != KindUnknown) {
Error = "invalid value for 'key.nametype'";
return true;
}
LineCols.push_back({static_cast<unsigned>(Line),
static_cast<unsigned>(Column), RenameType});
return false;
});
if (Failed && Error.empty()) {
Error = "invalid key.locations";
}
return Failed;
});
if (Failed && Error.empty()) {
Error = "invalid key.renamelocations";
}
return Failed;
}
static sourcekitd_response_t
createCategorizedEditsResponse(ArrayRef<CategorizedEdits> AllEdits,
StringRef Error) {
if (!Error.empty()) {
return createErrorRequestFailed(Error.str().c_str());
}
ResponseBuilder RespBuilder;
auto Dict = RespBuilder.getDictionary();
auto Arr = Dict.setArray(KeyCategorizedEdits);
for (auto &TheEdit : AllEdits) {
auto Entry = Arr.appendDictionary();
Entry.set(KeyCategory, TheEdit.Category);
auto Edits = Entry.setArray(KeyEdits);
for(auto E: TheEdit.Edits) {
auto Edit = Edits.appendDictionary();
Edit.set(KeyLine, E.StartLine);
Edit.set(KeyColumn, E.StartColumn);
Edit.set(KeyEndLine, E.EndLine);
Edit.set(KeyEndColumn, E.EndColumn);
Edit.set(KeyText, E.NewText);
if (!E.RegionsWithNote.empty()) {
auto Notes = Edit.setArray(KeyRangesWorthNote);
for (auto R : E.RegionsWithNote) {
auto N = Notes.appendDictionary();
N.set(KeyKind, R.Kind);
N.set(KeyLine, R.StartLine);
N.set(KeyColumn, R.StartColumn);
N.set(KeyEndLine, R.EndLine);
N.set(KeyEndColumn, R.EndColumn);
if (R.ArgIndex)
N.set(KeyArgIndex, *R.ArgIndex);
}
}
}
}
return RespBuilder.createResponse();
}
static sourcekitd_response_t
syntacticRename(llvm::MemoryBuffer *InputBuf,
ArrayRef<RenameLocations> RenameLocations,
ArrayRef<const char*> Args) {
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
sourcekitd_response_t Result;
Lang.syntacticRename(InputBuf, RenameLocations, Args,
[&](ArrayRef<CategorizedEdits> AllEdits, StringRef Error) {
Result = createCategorizedEditsResponse(AllEdits, Error);
});
return Result;
}
static sourcekitd_response_t
createCategorizedRenameRangesResponse(ArrayRef<CategorizedRenameRanges> Ranges,
StringRef Error) {
if (!Error.empty()) {
return createErrorRequestFailed(Error.str().c_str());
}
ResponseBuilder RespBuilder;
auto Dict = RespBuilder.getDictionary();
auto Arr = Dict.setArray(KeyCategorizedRanges);
for (const auto &CategorizedRange : Ranges) {
auto Entry = Arr.appendDictionary();
Entry.set(KeyCategory, CategorizedRange.Category);
auto Ranges = Entry.setArray(KeyRanges);
for (const auto &R : CategorizedRange.Ranges) {
auto Range = Ranges.appendDictionary();
Range.set(KeyLine, R.StartLine);
Range.set(KeyColumn, R.StartColumn);
Range.set(KeyEndLine, R.EndLine);
Range.set(KeyEndColumn, R.EndColumn);
Range.set(KeyKind, R.Kind);
if (R.ArgIndex) {
Range.set(KeyArgIndex, *R.ArgIndex);
}
}
}
return RespBuilder.createResponse();
}
static sourcekitd_response_t
findRenameRanges(llvm::MemoryBuffer *InputBuf,
ArrayRef<RenameLocations> RenameLocations,
ArrayRef<const char *> Args) {
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
sourcekitd_response_t Result;
Lang.findRenameRanges(
InputBuf, RenameLocations, Args,
[&](ArrayRef<CategorizedRenameRanges> Ranges, StringRef Error) {
Result = createCategorizedRenameRangesResponse(Ranges, Error);
});
return Result;
}
static bool isSemanticEditorDisabled() {
enum class SemaInfoToggle : char {
None, Disable, Enable
};
static SemaInfoToggle Toggle = SemaInfoToggle::None;
if (Toggle == SemaInfoToggle::None) {
static std::once_flag flag;
std::call_once(flag, []() {
Toggle = SemaInfoToggle::Enable;
const char *EnvOpt = ::getenv("SOURCEKIT_DELAY_SEMA_EDITOR");
if (!EnvOpt) {
return;
}
unsigned Seconds;
if (StringRef(EnvOpt).getAsInteger(10, Seconds))
return;
// A crash occurred previously. Disable semantic info in the editor for
// the given amount, to avoid repeated crashers.
LOG_WARN_FUNC("delaying semantic editor for " << Seconds << " seconds");
Toggle = SemaInfoToggle::Disable;
dispatch_time_t When = dispatch_time(DISPATCH_TIME_NOW,
NSEC_PER_SEC * Seconds);
dispatch_after(When, dispatch_get_main_queue(), ^{
Toggle = SemaInfoToggle::Enable;
});
});
}
assert(Toggle != SemaInfoToggle::None);
return Toggle == SemaInfoToggle::Disable;
}