mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
It’s easier to understand the code if we don’t have these two nearly, but not quite, identical types.
4250 lines
152 KiB
C++
4250 lines
152 KiB
C++
//===--- 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 "sourcekitd/Service.h"
|
|
#include "sourcekitd/CodeCompletionResultsArray.h"
|
|
#include "sourcekitd/DictionaryKeys.h"
|
|
#include "sourcekitd/DocStructureArray.h"
|
|
#include "sourcekitd/DocSupportAnnotationArray.h"
|
|
#include "sourcekitd/TokenAnnotationsArray.h"
|
|
#include "sourcekitd/ExpressionTypeArray.h"
|
|
#include "sourcekitd/VariableTypeArray.h"
|
|
|
|
#include "SourceKit/Core/Context.h"
|
|
#include "SourceKit/Core/LangSupport.h"
|
|
#include "SourceKit/Core/NotificationCenter.h"
|
|
#include "SourceKit/Support/CancellationToken.h"
|
|
#include "SourceKit/Support/Concurrency.h"
|
|
#include "SourceKit/Support/Logging.h"
|
|
#include "SourceKit/Support/Statistic.h"
|
|
#include "SourceKit/Support/Tracing.h"
|
|
#include "SourceKit/Support/UIdent.h"
|
|
#include "SourceKit/SwiftLang/Factory.h"
|
|
|
|
#include "swift/Basic/InitializeSwiftModules.h"
|
|
#include "swift/Basic/LLVMInitialize.h"
|
|
#include "swift/Basic/Statistic.h"
|
|
#include "swift/Basic/Version.h"
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/PrettyStackTrace.h"
|
|
#include "llvm/Support/VirtualFileSystem.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);
|
|
}
|
|
};
|
|
|
|
struct SKEditorConsumerOptions {
|
|
bool EnableSyntaxMap = false;
|
|
bool EnableStructure = false;
|
|
bool EnableDiagnostics = false;
|
|
bool SyntacticOnly = false;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
static Optional<UIdent> getUIDForOperationKind(trace::OperationKind OpKind);
|
|
static void fillDiagnosticInfo(ResponseBuilder::Dictionary ParentElem,
|
|
ArrayRef<DiagnosticEntryInfo> Diags,
|
|
Optional<UIdent> DiagStage);
|
|
|
|
#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/Refactoring/RefactoringKinds.def"
|
|
|
|
static SourceKit::Context *GlobalCtx = nullptr;
|
|
|
|
// NOTE: if we had a connection context, these stats should move into it.
|
|
static Statistic numRequests(UIdentFromSKDUID(KindStatNumRequests),
|
|
"# of requests (total)");
|
|
static Statistic numSemaRequests(UIdentFromSKDUID(KindStatNumSemaRequests),
|
|
"# of semantic requests");
|
|
|
|
void sourcekitd::initializeService(
|
|
llvm::StringRef swiftExecutablePath, StringRef runtimeLibPath,
|
|
StringRef diagnosticDocumentationPath,
|
|
std::function<void(sourcekitd_response_t)> postNotification) {
|
|
INITIALIZE_LLVM();
|
|
initializeSwiftModules();
|
|
llvm::EnablePrettyStackTrace();
|
|
GlobalCtx = new SourceKit::Context(swiftExecutablePath, runtimeLibPath,
|
|
diagnosticDocumentationPath,
|
|
SourceKit::createSwiftLangSupport);
|
|
auto noteCenter = GlobalCtx->getNotificationCenter();
|
|
|
|
noteCenter->addDocumentUpdateNotificationReceiver([postNotification](StringRef DocumentName) {
|
|
static UIdent DocumentUpdateNotificationUID(
|
|
"source.notification.editor.documentupdate");
|
|
ResponseBuilder RespBuilder;
|
|
auto Dict = RespBuilder.getDictionary();
|
|
Dict.set(KeyNotification, DocumentUpdateNotificationUID);
|
|
Dict.set(KeyName, DocumentName);
|
|
postNotification(RespBuilder.createResponse());
|
|
});
|
|
|
|
noteCenter->addTestNotificationReceiver([postNotification] {
|
|
static UIdent TestNotification("source.notification.test");
|
|
ResponseBuilder RespBuilder;
|
|
auto Dict = RespBuilder.getDictionary();
|
|
Dict.set(KeyNotification, TestNotification);
|
|
postNotification(RespBuilder.createResponse());
|
|
});
|
|
|
|
noteCenter->addSemaEnabledNotificationReceiver([postNotification] {
|
|
static UIdent SemaEnabledNotificationUID(
|
|
"source.notification.sema_enabled");
|
|
ResponseBuilder RespBuilder;
|
|
auto Dict = RespBuilder.getDictionary();
|
|
Dict.set(KeyNotification, SemaEnabledNotificationUID);
|
|
postNotification(RespBuilder.createResponse());
|
|
});
|
|
|
|
noteCenter->addCompileWillStartNotificationReceiver([postNotification](uint64_t OpId, trace::OperationKind OpKind, const trace::SwiftInvocation &Inv){
|
|
static UIdent CompileWillStartUID("source.notification.compile-will-start");
|
|
ResponseBuilder RespBuilder;
|
|
auto Dict = RespBuilder.getDictionary();
|
|
Dict.set(KeyNotification, CompileWillStartUID);
|
|
Dict.set(KeyCompileID, std::to_string(OpId));
|
|
Dict.set(KeyFilePath, Inv.Args.PrimaryFile);
|
|
if (auto OperationUID = getUIDForOperationKind(OpKind))
|
|
Dict.set(KeyCompileOperation, OperationUID.value());
|
|
Dict.set(KeyCompilerArgsString, Inv.Args.Arguments);
|
|
postNotification(RespBuilder.createResponse());
|
|
});
|
|
|
|
noteCenter->addCompileDidFinishNotificationReceiver([postNotification](uint64_t OpId, trace::OperationKind OpKind, ArrayRef<DiagnosticEntryInfo> Diagnostics){
|
|
static UIdent CompileDidFinishUID("source.notification.compile-did-finish");
|
|
ResponseBuilder RespBuilder;
|
|
auto Dict = RespBuilder.getDictionary();
|
|
Dict.set(KeyNotification, CompileDidFinishUID);
|
|
Dict.set(KeyCompileID, std::to_string(OpId));
|
|
if (auto OperationUID = getUIDForOperationKind(OpKind))
|
|
Dict.set(KeyCompileOperation, OperationUID.value());
|
|
|
|
fillDiagnosticInfo(Dict, Diagnostics, /*DiagStage*/ None);
|
|
postNotification(RespBuilder.createResponse());
|
|
});
|
|
}
|
|
|
|
void sourcekitd::shutdownService() {
|
|
delete GlobalCtx;
|
|
GlobalCtx = nullptr;
|
|
}
|
|
|
|
static SourceKit::Context &getGlobalContext() {
|
|
assert(GlobalCtx);
|
|
return *GlobalCtx;
|
|
}
|
|
|
|
static sourcekitd_response_t indexSource(StringRef Filename,
|
|
ArrayRef<const char *> Args);
|
|
|
|
static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf,
|
|
StringRef ModuleName,
|
|
ArrayRef<const char *> Args);
|
|
|
|
static void reportCursorInfo(const RequestResult<CursorInfoData> &Result, ResponseReceiver Rec);
|
|
|
|
static void reportDiagnostics(const RequestResult<DiagnosticsResult> &Result,
|
|
ResponseReceiver Rec);
|
|
|
|
static void
|
|
reportSemanticTokens(const RequestResult<SemanticTokensResult> &Result,
|
|
ResponseReceiver Rec);
|
|
|
|
static void reportExpressionTypeInfo(const RequestResult<ExpressionTypesInFile> &Result,
|
|
ResponseReceiver Rec);
|
|
|
|
static void
|
|
reportVariableTypeInfo(const RequestResult<VariableTypesInFile> &Result,
|
|
ResponseReceiver Rec);
|
|
|
|
static void reportRangeInfo(const RequestResult<RangeInfo> &Result, ResponseReceiver Rec);
|
|
|
|
static void reportNameInfo(const RequestResult<NameTranslatingInfo> &Result, ResponseReceiver Rec);
|
|
|
|
static void findRelatedIdents(StringRef PrimaryFilePath,
|
|
StringRef InputBufferName, int64_t Offset,
|
|
bool CancelOnSubsequentRequest,
|
|
ArrayRef<const char *> Args,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec);
|
|
|
|
static void findActiveRegions(StringRef PrimaryFilePath,
|
|
StringRef InputBufferName,
|
|
ArrayRef<const char *> Args,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec);
|
|
|
|
static sourcekitd_response_t
|
|
codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken);
|
|
|
|
static sourcekitd_response_t
|
|
codeCompleteOpen(StringRef name, llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken);
|
|
|
|
static sourcekitd_response_t
|
|
codeCompleteUpdate(StringRef name, int64_t Offset,
|
|
Optional<RequestDict> optionsDict,
|
|
SourceKitCancellationToken CancellationToken);
|
|
|
|
static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset);
|
|
|
|
static sourcekitd_response_t
|
|
typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken);
|
|
|
|
static sourcekitd_response_t conformingMethodList(
|
|
llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
ArrayRef<const char *> ExpectedTypes, Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken);
|
|
|
|
static sourcekitd_response_t
|
|
editorOpen(StringRef Name, llvm::MemoryBuffer *Buf,
|
|
SKEditorConsumerOptions Opts, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions);
|
|
|
|
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,
|
|
StringRef swiftVersion);
|
|
|
|
static void editorOpenSwiftSourceInterface(
|
|
StringRef Name, StringRef SourceName, ArrayRef<const char *> Args,
|
|
SourceKitCancellationToken CancellationToken, 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, SKEditorConsumerOptions Opts);
|
|
|
|
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(const RequestDict &Req,
|
|
std::vector<RenameLoc> &RenameLocations,
|
|
llvm::SmallString<64> &Error);
|
|
|
|
static sourcekitd_response_t
|
|
createCategorizedEditsResponse(
|
|
const RequestResult<ArrayRef<CategorizedEdits>> &Result);
|
|
|
|
static sourcekitd_response_t createCategorizedRenameRangesResponse(
|
|
const CancellableResult<std::vector<CategorizedRenameRanges>> &Result);
|
|
|
|
static sourcekitd_response_t
|
|
findRenameRanges(llvm::MemoryBuffer *InputBuf,
|
|
ArrayRef<RenameLoc> RenameLocations,
|
|
ArrayRef<const char *> Args);
|
|
|
|
static bool isSemanticEditorDisabled();
|
|
static void enableCompileNotifications(bool value);
|
|
|
|
namespace {
|
|
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;
|
|
}
|
|
|
|
bool forEach(UIdent key, llvm::function_ref<bool(OptionsDictionary &)> applier) override {
|
|
return Options.dictionaryArrayApply(key, [=](RequestDict dict) {
|
|
SKOptionsDictionary skDict(dict);
|
|
return applier(skDict);
|
|
});
|
|
}
|
|
};
|
|
} // anonymous namespace
|
|
|
|
static void handleRequestImpl(sourcekitd_object_t Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Receiver);
|
|
|
|
void sourcekitd::handleRequest(sourcekitd_object_t Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Receiver) {
|
|
LOG_SECTION("handleRequest-before", InfoHighPrio) {
|
|
sourcekitd::printRequestObject(Req, Log->getOS());
|
|
}
|
|
|
|
handleRequestImpl(
|
|
Req, CancellationToken,
|
|
[Receiver, CancellationToken](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());
|
|
}
|
|
|
|
sourcekitd::disposeCancellationToken(CancellationToken);
|
|
|
|
Receiver(Resp);
|
|
});
|
|
}
|
|
|
|
void sourcekitd::cancelRequest(SourceKitCancellationToken CancellationToken) {
|
|
getGlobalContext().getRequestTracker()->cancel(CancellationToken);
|
|
}
|
|
|
|
void sourcekitd::disposeCancellationToken(
|
|
SourceKitCancellationToken CancellationToken) {
|
|
getGlobalContext().getRequestTracker()->stopTracking(CancellationToken);
|
|
}
|
|
|
|
bool sourcekitd::requestIsBarrier(sourcekitd_object_t ReqObj) {
|
|
RequestDict Req(ReqObj);
|
|
sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest);
|
|
return ReqUID == RequestEditorOpen || ReqUID == RequestEditorReplaceText ||
|
|
ReqUID == RequestEditorClose;
|
|
}
|
|
|
|
bool sourcekitd::requestIsEnableBarriers(sourcekitd_object_t ReqObj) {
|
|
RequestDict Req(ReqObj);
|
|
sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest);
|
|
return ReqUID == RequestEnableRequestBarriers;
|
|
}
|
|
|
|
void sourcekitd::sendBarriersEnabledResponse(ResponseReceiver Receiver) {
|
|
ResponseBuilder RespBuilder;
|
|
auto Elem = RespBuilder.getDictionary();
|
|
Elem.setBool(KeyBarriersEnabled, true);
|
|
Receiver(RespBuilder.createResponse());
|
|
}
|
|
|
|
static std::unique_ptr<llvm::MemoryBuffer> getInputBufForRequest(
|
|
Optional<StringRef> SourceFile, Optional<StringRef> SourceText,
|
|
const Optional<VFSOptions> &vfsOptions, llvm::SmallString<64> &ErrBuf) {
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf;
|
|
|
|
if (SourceText.has_value()) {
|
|
StringRef BufName;
|
|
if (SourceFile.has_value())
|
|
BufName = *SourceFile;
|
|
else
|
|
BufName = "<input>";
|
|
InputBuf = llvm::MemoryBuffer::getMemBuffer(*SourceText, BufName);
|
|
|
|
} else if (vfsOptions.has_value() && SourceFile.has_value()) {
|
|
ErrBuf = "using 'key.sourcefile' to read source text from the filesystem "
|
|
"is not supported when using 'key.vfs.name'";
|
|
return nullptr;
|
|
|
|
} else if (SourceFile.has_value()) {
|
|
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::WritableMemoryBuffer::getNewMemBuffer(0, "<input>");
|
|
}
|
|
|
|
return InputBuf;
|
|
}
|
|
|
|
/// Get the input buffer from 'key.sourcefile' or 'key.sourcetext' value in
|
|
/// \p Req . If buffer cannot be retrieved for some reason, Reply an error to
|
|
/// \c Rec and returns \c nullptr .
|
|
static std::unique_ptr<llvm::MemoryBuffer>
|
|
getInputBufForRequestOrEmitError(const RequestDict &Req,
|
|
const Optional<VFSOptions> &vfsOptions,
|
|
ResponseReceiver Rec) {
|
|
Optional<StringRef> SourceFile = Req.getString(KeySourceFile);
|
|
Optional<StringRef> SourceText = Req.getString(KeySourceText);
|
|
SmallString<64> ErrBuf;
|
|
auto buf = getInputBufForRequest(SourceFile, SourceText, vfsOptions, ErrBuf);
|
|
if (!buf) {
|
|
Rec(createErrorRequestFailed(ErrBuf));
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/// Retrieves `key.primary_file` value as a string, or `key.sourcefile` if
|
|
/// missing. If both are missing, reply with an error and return \c None.
|
|
///
|
|
/// The "primary file" is the file to mark as `-primary-file` when building
|
|
/// the corresponding AST for this request. The "input file" is the file to
|
|
/// resolve any offset or line/column in. These were (prior to
|
|
/// \c GeneratedSourceInfo) always the same, but the input file is now able to
|
|
/// be a generated buffer name (where that buffer is created during the AST
|
|
/// build).
|
|
static Optional<StringRef>
|
|
getPrimaryFilePathForRequestOrEmitError(const RequestDict &Req,
|
|
ResponseReceiver Rec) {
|
|
Optional<StringRef> PrimaryFilePath = Req.getString(KeyPrimaryFile);
|
|
if (!PrimaryFilePath) {
|
|
// Fallback to the old key.sourcefile
|
|
PrimaryFilePath = Req.getString(KeySourceFile);
|
|
if (!PrimaryFilePath) {
|
|
Rec(createErrorRequestInvalid(
|
|
"missing 'key.primary_file' and 'key.sourcefile'"));
|
|
}
|
|
}
|
|
return PrimaryFilePath;
|
|
}
|
|
|
|
/// Retrieves `key.sourcefile` only if it is different to `key.primary_file`.
|
|
/// Returns an empty string if it's the same or if it was missing. Callers are
|
|
/// expected to pull the primary file from the AST if the input file is
|
|
/// empty.
|
|
///
|
|
/// See \c getPrimaryFilePathForRequestOrEmitError for an explanation of primary
|
|
/// vs input file.
|
|
static StringRef getInputBufferNameForRequest(const RequestDict &Req,
|
|
ResponseReceiver Rec) {
|
|
Optional<StringRef> PrimaryFilePath = Req.getString(KeyPrimaryFile);
|
|
Optional<StringRef> InputBufferName = Req.getString(KeySourceFile);
|
|
if (!PrimaryFilePath || PrimaryFilePath == InputBufferName)
|
|
return "";
|
|
return InputBufferName.value_or("");
|
|
}
|
|
|
|
/// Get compiler arguments from 'key.compilerargs' in \p Req . If the key is
|
|
/// missing, reply an error to \c Rec and returns \c true .
|
|
static bool
|
|
getCompilerArgumentsForRequestOrEmitError(const RequestDict &Req,
|
|
SmallVectorImpl<const char *> &Args,
|
|
ResponseReceiver Rec) {
|
|
bool Failed = Req.getStringArray(KeyCompilerArgs, Args, /*isOptional=*/true);
|
|
if (Failed) {
|
|
Rec(createErrorRequestInvalid(
|
|
"'key.compilerargs' not an array of strings"));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Read optional VFSOptions from a request dictionary. The request dictionary
|
|
/// *must* outlive the resulting VFSOptions.
|
|
/// \returns true on failure and sets \p error.
|
|
static Optional<VFSOptions> getVFSOptions(const RequestDict &Req) {
|
|
auto name = Req.getString(KeyVFSName);
|
|
if (!name)
|
|
return None;
|
|
|
|
std::unique_ptr<OptionsDictionary> options;
|
|
if (auto dict = Req.getDictionary(KeyVFSOptions)) {
|
|
options = std::make_unique<SKOptionsDictionary>(*dict);
|
|
}
|
|
|
|
return VFSOptions{name->str(), std::move(options)};
|
|
}
|
|
|
|
static bool checkVFSNotSupported(const RequestDict &Req, ResponseReceiver Rec) {
|
|
if (Req.getString(KeyVFSName)) {
|
|
Rec(createErrorRequestInvalid("This request does not support custom filesystems"));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void handleSemanticRequest(const RequestDict &Req, ResponseReceiver Rec,
|
|
std::function<void()> Fn) {
|
|
if (isSemanticEditorDisabled())
|
|
return Rec(createErrorRequestFailed("semantic editor is disabled"));
|
|
|
|
// Typechecking arrays can blow up the stack currently.
|
|
// Run them under a malloc'ed stack.
|
|
|
|
static WorkQueue SemaQueue{WorkQueue::Dequeuing::Concurrent,
|
|
"sourcekit.request.semantic"};
|
|
++numSemaRequests;
|
|
SemaQueue.dispatch([Fn] { Fn(); }, /*isStackDeep=*/true);
|
|
}
|
|
|
|
static void
|
|
handleRequestGlobalConfiguration(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
auto Config = getGlobalContext().getGlobalConfiguration();
|
|
ResponseBuilder RB;
|
|
auto dict = RB.getDictionary();
|
|
|
|
Optional<unsigned> CompletionMaxASTContextReuseCount = swift::transform(
|
|
Req.getOptionalInt64(KeyCompletionMaxASTContextReuseCount),
|
|
[](int64_t v) -> unsigned { return v; });
|
|
Optional<unsigned> CompletionCheckDependencyInterval = swift::transform(
|
|
Req.getOptionalInt64(KeyCompletionCheckDependencyInterval),
|
|
[](int64_t v) -> unsigned { return v; });
|
|
|
|
GlobalConfig::Settings UpdatedConfig =
|
|
Config->update(CompletionMaxASTContextReuseCount,
|
|
CompletionCheckDependencyInterval);
|
|
|
|
getGlobalContext().getSwiftLangSupport().globalConfigurationUpdated(Config);
|
|
|
|
dict.set(KeyCompletionMaxASTContextReuseCount,
|
|
UpdatedConfig.IDEInspectionOpts.MaxASTContextReuseCount);
|
|
dict.set(KeyCompletionCheckDependencyInterval,
|
|
UpdatedConfig.IDEInspectionOpts.CheckDependencyInterval);
|
|
|
|
return Rec(RB.createResponse());
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestProtocolVersion(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
ResponseBuilder RB;
|
|
auto dict = RB.getDictionary();
|
|
dict.set(KeyVersionMajor, ProtocolMajorVersion);
|
|
dict.set(KeyVersionMinor, static_cast<int64_t>(ProtocolMinorVersion));
|
|
return Rec(RB.createResponse());
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestCompilerVersion(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
ResponseBuilder RB;
|
|
auto dict = RB.getDictionary();
|
|
auto thisVersion = swift::version::Version::getCurrentLanguageVersion();
|
|
dict.set(KeyVersionMajor, static_cast<int64_t>(thisVersion[0]));
|
|
dict.set(KeyVersionMinor, static_cast<int64_t>(thisVersion[1]));
|
|
if (thisVersion.size() > 2)
|
|
dict.set(KeyVersionPatch, static_cast<int64_t>(thisVersion[2]));
|
|
else
|
|
dict.set(KeyVersionPatch, static_cast<int64_t>(0));
|
|
return Rec(RB.createResponse());
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestCrashWithExit(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
// 'exit' has the same effect as crashing but without the crash log.
|
|
::exit(1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestTestNotification(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
getGlobalContext().getNotificationCenter()->postTestNotification();
|
|
return Rec(ResponseBuilder().createResponse());
|
|
}
|
|
}
|
|
|
|
static void handleRequestDemangle(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
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);
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.demangleNames(MangledNames, Simplified, [Rec](auto result) {
|
|
if (result.isError())
|
|
return Rec(createErrorRequestFailed(result.getError()));
|
|
if (result.isCancelled())
|
|
return Rec(createErrorRequestFailed(result.getError()));
|
|
ResponseBuilder RespBuilder;
|
|
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
|
|
for (auto demangldedName : result.value()) {
|
|
auto Entry = Arr.appendDictionary();
|
|
Entry.set(KeyName, demangldedName.c_str());
|
|
}
|
|
Rec(RespBuilder.createResponse());
|
|
});
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestMangleSimpleClass(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
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.has_value()) {
|
|
err = createErrorRequestInvalid("missing 'key.modulename'");
|
|
return true;
|
|
}
|
|
Optional<StringRef> ClassName = dict.getString(KeyName);
|
|
if (!ClassName.has_value()) {
|
|
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);
|
|
}
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.mangleSimpleClassNames(ModuleClassPairs, [Rec](auto result) {
|
|
if (result.isError())
|
|
return Rec(createErrorRequestFailed(result.getError()));
|
|
if (result.isCancelled())
|
|
return Rec(createErrorRequestFailed(result.getError()));
|
|
ResponseBuilder RespBuilder;
|
|
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
|
|
for (auto &mangledName : result.value()) {
|
|
auto Entry = Arr.appendDictionary();
|
|
Entry.set(KeyName, mangledName.c_str());
|
|
}
|
|
Rec(RespBuilder.createResponse());
|
|
});
|
|
}
|
|
}
|
|
|
|
static void handleRequestEnableCompileNotifications(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
int64_t value = true;
|
|
if (Req.getInt64(KeyValue, value, /*isOptional=*/false)) {
|
|
return Rec(createErrorRequestInvalid("missing 'key.value'"));
|
|
}
|
|
enableCompileNotifications(value);
|
|
return Rec(ResponseBuilder().createResponse());
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestBuildSettingsRegister(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
// Just accept 'source.request.buildsettings.register' for now, don't do
|
|
// anything else.
|
|
// FIXME: Heavy WIP here.
|
|
{ return Rec(ResponseBuilder().createResponse()); }
|
|
}
|
|
|
|
static void
|
|
handleRequestDependencyUpdated(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
getGlobalContext().getSwiftLangSupport().dependencyUpdated();
|
|
return Rec(ResponseBuilder().createResponse());
|
|
}
|
|
}
|
|
|
|
static void handleRequestDocInfo(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
StringRef ModuleName;
|
|
Optional<StringRef> ModuleNameOpt = Req.getString(KeyModuleName);
|
|
if (ModuleNameOpt.has_value()) ModuleName = *ModuleNameOpt;
|
|
return Rec(reportDocInfo(InputBuf.get(), ModuleName, Args));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestEditorOpen(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
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);
|
|
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.EnableSyntaxMap = EnableSyntaxMap;
|
|
Opts.EnableStructure = EnableStructure;
|
|
Opts.EnableDiagnostics = EnableDiagnostics;
|
|
Opts.SyntacticOnly = SyntacticOnly;
|
|
return Rec(editorOpen(*Name, InputBuf.get(), Opts, Args, std::move(vfsOptions)));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestEditorClose(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
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));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestEditorReplaceText(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
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);
|
|
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.EnableSyntaxMap = EnableSyntaxMap;
|
|
Opts.EnableStructure = EnableStructure;
|
|
Opts.EnableDiagnostics = EnableDiagnostics;
|
|
Opts.SyntacticOnly = SyntacticOnly;
|
|
|
|
return Rec(editorReplaceText(*Name, InputBuf.get(), Offset, Length, Opts));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestEditorFormatText(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
Optional<RequestDict> FmtOptions = Req.getDictionary(KeyFormatOptions);
|
|
if (FmtOptions.has_value())
|
|
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));
|
|
}
|
|
}
|
|
|
|
static void handleRequestEditorExpandPlaceholder(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
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));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestEditorOpenInterface(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
|
|
if (!ModuleName.has_value())
|
|
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));
|
|
}
|
|
}
|
|
|
|
static void handleRequestEditorOpenHeaderInterface(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
Optional<StringRef> HeaderName = Req.getString(KeyFilePath);
|
|
if (!HeaderName.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.filepath'"));
|
|
int64_t SynthesizedExtension = false;
|
|
Req.getInt64(KeySynthesizedExtension, SynthesizedExtension,
|
|
/*isOptional=*/true);
|
|
Optional<int64_t> UsingSwiftArgs = Req.getOptionalInt64(KeyUsingSwiftArgs);
|
|
std::string swiftVer;
|
|
Optional<StringRef> swiftVerValStr = Req.getString(KeySwiftVersion);
|
|
if (swiftVerValStr.has_value()) {
|
|
swiftVer = swiftVerValStr.value().str();
|
|
} else {
|
|
Optional<int64_t> swiftVerVal = Req.getOptionalInt64(KeySwiftVersion);
|
|
if (swiftVerVal.has_value())
|
|
swiftVer = std::to_string(*swiftVerVal);
|
|
}
|
|
return Rec(editorOpenHeaderInterface(*Name, *HeaderName, Args,
|
|
UsingSwiftArgs.value_or(false),
|
|
SynthesizedExtension, swiftVer));
|
|
}
|
|
}
|
|
|
|
static void handleRequestEditorOpenSwiftSourceInterface(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
Optional<StringRef> FileName = Req.getString(KeySourceFile);
|
|
if (!FileName.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
|
|
return editorOpenSwiftSourceInterface(*Name, *FileName, Args,
|
|
CancellationToken, Rec);
|
|
}
|
|
}
|
|
|
|
static void handleRequestEditorOpenSwiftTypeInterface(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> Usr = Req.getString(KeyUSR);
|
|
if (!Usr.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.usr'"));
|
|
return editorOpenSwiftTypeInterface(*Usr, Args, Rec);
|
|
}
|
|
}
|
|
|
|
static void handleRequestEditorExtractTextFromComment(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Source = Req.getString(KeySourceText);
|
|
if (!Source.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.sourcetext'"));
|
|
return Rec(editorExtractTextFromComment(Source.value()));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestMarkupToXML(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Source = Req.getString(KeySourceText);
|
|
if (!Source.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.sourcetext'"));
|
|
return Rec(editorConvertMarkupToXML(Source.value()));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestEditorFindUSR(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Name = Req.getString(KeySourceFile);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
|
|
Optional<StringRef> USR = Req.getString(KeyUSR);
|
|
if (!USR.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.usr'"));
|
|
return Rec(editorFindUSR(*Name, *USR));
|
|
}
|
|
}
|
|
|
|
static void handleRequestEditorFindInterfaceDoc(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
|
|
if (!ModuleName.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.modulename'"));
|
|
return Rec(editorFindInterfaceDoc(*ModuleName, Args));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestModuleGroups(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> ModuleName = Req.getString(KeyModuleName);
|
|
if (!ModuleName.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.modulename'"));
|
|
return Rec(editorFindModuleGroups(*ModuleName, Args));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestFindRenameRanges(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
|
|
SmallString<64> ErrBuf;
|
|
std::vector<RenameLoc> RenameLocations;
|
|
if (buildRenameLocationsFromDict(Req, RenameLocations, ErrBuf))
|
|
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
|
|
return Rec(findRenameRanges(InputBuf.get(), RenameLocations, Args));
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestCodeCompleteClose(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
// Unlike opening code completion, this is not a semantic request.
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
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));
|
|
}
|
|
}
|
|
|
|
static void handleRequestCodeCompleteCacheOnDisk(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.codeCompleteCacheOnDisk(*Name);
|
|
ResponseBuilder b;
|
|
return Rec(b.createResponse());
|
|
}
|
|
}
|
|
|
|
static void handleRequestCodeCompleteSetPopularAPI(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
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());
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestCodeCompleteSetCustom(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
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.has_value()) {
|
|
err = createErrorRequestInvalid("missing 'key.name'");
|
|
return true;
|
|
}
|
|
CCInfo.Name = (*Name).str();
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestStatistics(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.getStatistics([Rec](ArrayRef<Statistic *> stats) {
|
|
ResponseBuilder builder;
|
|
auto results = builder.getDictionary().setArray(KeyResults);
|
|
auto addStat = [&results](Statistic *stat) {
|
|
auto dict = results.appendDictionary();
|
|
dict.set(KeyKind, stat->name);
|
|
dict.set(KeyDescription, stat->description);
|
|
dict.set(KeyValue, stat->value);
|
|
};
|
|
|
|
Statistic instructionCount(
|
|
UIdentFromSKDUID(KindStatInstructionCount),
|
|
"# of instructions executed since the SourceKit process was started");
|
|
instructionCount.value.store(swift::getInstructionsExecuted());
|
|
addStat(&instructionCount);
|
|
addStat(&numRequests);
|
|
addStat(&numSemaRequests);
|
|
std::for_each(stats.begin(), stats.end(), addStat);
|
|
|
|
Rec(builder.createResponse());
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void handleRequestCompile(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.performCompile(
|
|
*Name, Args, std::move(vfsOptions), CancellationToken,
|
|
[Rec](const RequestResult<CompilationResult> &result) {
|
|
if (result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (result.isError())
|
|
return Rec(createErrorRequestFailed(result.getError()));
|
|
|
|
const CompilationResult &info = result.value();
|
|
|
|
ResponseBuilder builder;
|
|
|
|
builder.getDictionary().set(KeyValue, info.ResultStatus);
|
|
fillDiagnosticInfo(builder.getDictionary(), info.Diagnostics,
|
|
/*DiagStage*/ None);
|
|
Rec(builder.createResponse());
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestCompileClose(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
{
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
return Rec(createErrorRequestInvalid("missing 'key.name'"));
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.closeCompile(*Name);
|
|
|
|
return Rec(ResponseBuilder().createResponse());
|
|
}
|
|
}
|
|
|
|
static void
|
|
handleRequestCodeComplete(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
int64_t Offset;
|
|
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
|
|
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
|
|
Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions);
|
|
return Rec(codeComplete(InputBuf.get(), Offset, options, Args,
|
|
std::move(vfsOptions), CancellationToken));
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestCodeCompleteOpen(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
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,
|
|
std::move(vfsOptions), CancellationToken));
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestCodeCompleteUpdate(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<StringRef> Name = Req.getString(KeyName);
|
|
if (!Name.has_value())
|
|
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, CancellationToken));
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestTypeContextInfo(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
int64_t Offset;
|
|
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
|
|
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
|
|
Optional<RequestDict> options =
|
|
Req.getDictionary(KeyTypeContextInfoOptions);
|
|
return Rec(typeContextInfo(InputBuf.get(), Offset, options, Args,
|
|
std::move(vfsOptions), CancellationToken));
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestConformingMethodList(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
|
|
getInputBufForRequestOrEmitError(Req, vfsOptions, Rec);
|
|
if (!InputBuf)
|
|
return;
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
int64_t Offset;
|
|
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
|
|
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
|
|
SmallVector<const char *, 8> ExpectedTypeNames;
|
|
if (Req.getStringArray(KeyExpectedTypes, ExpectedTypeNames, true))
|
|
return Rec(createErrorRequestInvalid("invalid 'key.expectedtypes'"));
|
|
Optional<RequestDict> options =
|
|
Req.getDictionary(KeyConformingMethodListOptions);
|
|
return Rec(conformingMethodList(InputBuf.get(), Offset, options, Args,
|
|
ExpectedTypeNames, std::move(vfsOptions),
|
|
CancellationToken));
|
|
});
|
|
}
|
|
|
|
static void handleRequestIndex(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, Rec]() {
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
return Rec(indexSource(*PrimaryFilePath, Args));
|
|
});
|
|
}
|
|
|
|
static void handleRequestIndexToStore(
|
|
const RequestDict &Req, SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, Rec, CancellationToken]() {
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
IndexStoreOptions Opts;
|
|
if (auto IndexStorePath = Req.getString(KeyIndexStorePath))
|
|
Opts.IndexStorePath = IndexStorePath->str();
|
|
else
|
|
return Rec(createErrorRequestInvalid("'key.index_store_path' is required"));
|
|
|
|
if (auto IndexUnitOutputPath = Req.getString(KeyIndexUnitOutputPath))
|
|
Opts.IndexUnitOutputPath = IndexUnitOutputPath->str();
|
|
else
|
|
return Rec(createErrorRequestInvalid("'key.index_unit_output_path' is required"));
|
|
|
|
SmallVector<const char *, 0> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.indexToStore(*PrimaryFilePath, Args, std::move(Opts),
|
|
CancellationToken,
|
|
[Rec](const RequestResult<IndexStoreInfo> &Result) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
ResponseBuilder RespBuilder;
|
|
return Rec(RespBuilder.createResponse());
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
static void
|
|
handleRequestCursorInfo(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
// 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);
|
|
int64_t SymbolGraph = false;
|
|
Req.getInt64(KeyRetrieveSymbolGraph, SymbolGraph, /*isOptional=*/true);
|
|
return Lang.getCursorInfo(
|
|
*PrimaryFilePath, InputBufferName, Offset, Length, Actionables,
|
|
SymbolGraph, CancelOnSubsequentRequest, Args, std::move(vfsOptions),
|
|
CancellationToken,
|
|
[Rec](const RequestResult<CursorInfoData> &Result) {
|
|
reportCursorInfo(Result, Rec);
|
|
});
|
|
}
|
|
if (auto USR = Req.getString(KeyUSR)) {
|
|
return Lang.getCursorInfoFromUSR(
|
|
*PrimaryFilePath, InputBufferName, *USR, CancelOnSubsequentRequest,
|
|
Args, std::move(vfsOptions), CancellationToken,
|
|
[Rec](const RequestResult<CursorInfoData> &Result) {
|
|
reportCursorInfo(Result, Rec);
|
|
});
|
|
}
|
|
|
|
return Rec(createErrorRequestInvalid(
|
|
"either 'key.offset' or 'key.usr' is required"));
|
|
});
|
|
}
|
|
|
|
static void handleRequestRangeInfo(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
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(*PrimaryFilePath, InputBufferName, Offset,
|
|
Length, CancelOnSubsequentRequest, Args,
|
|
CancellationToken,
|
|
[Rec](const RequestResult<RangeInfo> &Result) {
|
|
reportRangeInfo(Result, Rec);
|
|
});
|
|
}
|
|
}
|
|
|
|
return Rec(createErrorRequestInvalid(
|
|
"'key.offset' or 'key.length' is required"));
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestSemanticRefactoring(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
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/Refactoring/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.InputBufferName = InputBufferName;
|
|
Info.Line = Line;
|
|
Info.Column = Column;
|
|
Info.Length = Length;
|
|
return Lang.semanticRefactoring(
|
|
*PrimaryFilePath, Info, Args, CancellationToken,
|
|
[Rec](const RequestResult<ArrayRef<CategorizedEdits>> &Result) {
|
|
Rec(createCategorizedEditsResponse(Result));
|
|
});
|
|
}
|
|
}
|
|
return Rec(createErrorRequestInvalid("'key.line' or 'key.column' are required"));
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestCollectExpressionType(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
SmallVector<const char *, 8> ExpectedProtocols;
|
|
if (Req.getStringArray(KeyExpectedTypes, ExpectedProtocols, true))
|
|
return Rec(createErrorRequestInvalid("invalid 'key.expectedtypes'"));
|
|
int64_t FullyQualified = false;
|
|
Req.getInt64(KeyFullyQualified, FullyQualified, /*isOptional=*/true);
|
|
int64_t CanonicalTy = false;
|
|
Req.getInt64(KeyCanonicalizeType, CanonicalTy, /*isOptional=*/true);
|
|
return Lang.collectExpressionTypes(
|
|
*PrimaryFilePath, InputBufferName, Args, ExpectedProtocols,
|
|
FullyQualified, CanonicalTy, CancellationToken,
|
|
[Rec](const RequestResult<ExpressionTypesInFile> &Result) {
|
|
reportExpressionTypeInfo(Result, Rec);
|
|
});
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestCollectVariableType(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
Optional<unsigned> Offset =
|
|
swift::transform(Req.getOptionalInt64(KeyOffset),
|
|
[](int64_t v) -> unsigned { return v; });
|
|
Optional<unsigned> Length =
|
|
swift::transform(Req.getOptionalInt64(KeyLength),
|
|
[](int64_t v) -> unsigned { return v; });
|
|
int64_t FullyQualified = false;
|
|
Req.getInt64(KeyFullyQualified, FullyQualified, /*isOptional=*/true);
|
|
return Lang.collectVariableTypes(
|
|
*PrimaryFilePath, InputBufferName, Args, Offset, Length, FullyQualified,
|
|
CancellationToken,
|
|
[Rec](const RequestResult<VariableTypesInFile> &Result) {
|
|
reportVariableTypeInfo(Result, Rec);
|
|
});
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestFindLocalRenameRanges(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
auto PrimaryFilePath = getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
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(
|
|
*PrimaryFilePath, Line, Column, Length, Args, CancellationToken,
|
|
[Rec](const CancellableResult<std::vector<CategorizedRenameRanges>>
|
|
&Result) {
|
|
Rec(createCategorizedRenameRangesResponse(Result));
|
|
});
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestNameTranslation(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
auto PrimaryFilePath = getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
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)) {
|
|
if (Input.NameKind == UIDKindNameSwift) {
|
|
Input.BaseName = Base.value().trim('`');
|
|
} else {
|
|
Input.BaseName = Base.value();
|
|
}
|
|
}
|
|
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"));
|
|
}
|
|
llvm::transform(ArgParts, std::back_inserter(Input.ArgNames),
|
|
[](const char *C) { return StringRef(C).trim('`'); });
|
|
llvm::transform(Selectors, std::back_inserter(Input.ArgNames),
|
|
[](const char *C) { return StringRef(C); });
|
|
return Lang.getNameInfo(
|
|
*PrimaryFilePath, "", Offset, Input, Args, CancellationToken,
|
|
[Rec](const RequestResult<NameTranslatingInfo> &Result) {
|
|
reportNameInfo(Result, Rec);
|
|
});
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestRelatedIdents(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
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(*PrimaryFilePath, InputBufferName, Offset,
|
|
CancelOnSubsequentRequest, Args, CancellationToken,
|
|
Rec);
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestActiveRegions(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
if (checkVFSNotSupported(Req, Rec))
|
|
return;
|
|
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<StringRef> PrimaryFilePath =
|
|
getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
return findActiveRegions(*PrimaryFilePath, InputBufferName, Args,
|
|
CancellationToken, Rec);
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestDiagnostics(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
auto PrimaryFilePath = getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.getDiagnostics(*PrimaryFilePath, Args, std::move(vfsOptions),
|
|
CancellationToken,
|
|
[Rec](const RequestResult<DiagnosticsResult> &Result) {
|
|
reportDiagnostics(Result, Rec);
|
|
});
|
|
return;
|
|
});
|
|
}
|
|
|
|
static void
|
|
handleRequestSemanticTokens(const RequestDict &Req,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(Req);
|
|
auto PrimaryFilePath = getPrimaryFilePathForRequestOrEmitError(Req, Rec);
|
|
if (!PrimaryFilePath)
|
|
return;
|
|
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
|
|
|
|
SmallVector<const char *, 8> Args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
|
return;
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.getSemanticTokens(
|
|
*PrimaryFilePath, InputBufferName, Args, std::move(vfsOptions),
|
|
CancellationToken,
|
|
[Rec](const RequestResult<SemanticTokensResult> &Result) {
|
|
reportSemanticTokens(Result, Rec);
|
|
});
|
|
return;
|
|
});
|
|
}
|
|
|
|
/// Expand macros in the specified source file syntactically.
|
|
///
|
|
/// Request would look like:
|
|
/// {
|
|
/// key.compilerargs: []
|
|
/// key.sourcefile: <file name>
|
|
/// key.sourcetext: <source text> (optional)
|
|
/// key.expansions: [<expansion specifier>...]
|
|
/// }
|
|
/// 'compilerargs' is used for plugin search paths.
|
|
/// 'expansion specifier' is
|
|
/// {
|
|
/// key.offset: <offset>
|
|
/// key.modulename: <plugin module name>
|
|
/// key.typename: <macro typename>
|
|
/// key.macro_roles: [<macro role UID>...]
|
|
/// }
|
|
///
|
|
/// Sends the results as a 'CategorizedEdits'.
|
|
/// Note that, unlike refactoring, each edit doesn't have 'key.buffer_name'.
|
|
/// FIXME: Support nested expansion.
|
|
static void handleRequestSyntacticMacroExpansion(
|
|
const RequestDict &req, SourceKitCancellationToken cancellationToken,
|
|
ResponseReceiver rec) {
|
|
|
|
Optional<VFSOptions> vfsOptions = getVFSOptions(req);
|
|
std::unique_ptr<llvm::MemoryBuffer> inputBuf =
|
|
getInputBufForRequestOrEmitError(req, vfsOptions, rec);
|
|
if (!inputBuf)
|
|
return;
|
|
|
|
SmallVector<const char *, 16> args;
|
|
if (getCompilerArgumentsForRequestOrEmitError(req, args, rec))
|
|
return;
|
|
|
|
// key.expansions: [
|
|
// { key.offset: 42,
|
|
// key.macro_roles: [source.lang.swift.macrorole.conformance,
|
|
// source.lang.swift.macrorole.member],
|
|
// key.modulename: "MyMacroImpl",
|
|
// key.typename: "StringifyMacro"},
|
|
// { key.offset: 132,
|
|
// key.sourceText: "foo(bar, baz)",
|
|
// key.macro_roles: [source.lang.swift.macrorole.conformance,
|
|
// source.lang.swift.macrorole.member],
|
|
// key.expandedmacro_replacements: [
|
|
// {key.offset: 4, key.length: 3, key.argindex: 0},
|
|
// {key.offset: 9, key.length: 3, key.argindex: 1}]}
|
|
// ]
|
|
std::vector<MacroExpansionInfo> expansions;
|
|
bool failed = req.dictionaryArrayApply(KeyExpansions, [&](RequestDict dict) {
|
|
// offset.
|
|
int64_t offset;
|
|
dict.getInt64(KeyOffset, offset, false);
|
|
|
|
// macro roles.
|
|
SmallVector<sourcekitd_uid_t, 1> macroRoleUIDs;
|
|
if (dict.getUIDArray(KeyMacroRoles, macroRoleUIDs, false)) {
|
|
rec(createErrorRequestInvalid(
|
|
"missing 'key.macro_roles' for expansion specifier"));
|
|
return true;
|
|
}
|
|
MacroRoles macroRoles;
|
|
for (auto uid : macroRoleUIDs) {
|
|
#define MACRO_ROLE(Name, Description) \
|
|
if (uid == KindMacroRole##Name) \
|
|
macroRoles |= MacroRole::Name;
|
|
#include "swift/Basic/MacroRoles.def"
|
|
}
|
|
|
|
// definition.
|
|
if (auto moduleName = dict.getString(KeyModuleName)) {
|
|
auto typeName = dict.getString(KeyTypeName);
|
|
if (!typeName) {
|
|
rec(createErrorRequestInvalid(
|
|
"missing 'key.typename' for external macro definition"));
|
|
return true;
|
|
}
|
|
MacroExpansionInfo::ExternalMacroReference definition(moduleName->str(),
|
|
typeName->str());
|
|
expansions.emplace_back(offset, macroRoles, definition);
|
|
} else if (auto expandedText = dict.getString(KeySourceText)) {
|
|
MacroExpansionInfo::ExpandedMacroDefinition definition(*expandedText);
|
|
bool failed = dict.dictionaryArrayApply(
|
|
KeyExpandedMacroReplacements, [&](RequestDict dict) {
|
|
int64_t offset, length, paramIndex;
|
|
bool failed = false;
|
|
failed |= dict.getInt64(KeyOffset, offset, false);
|
|
failed |= dict.getInt64(KeyLength, length, false);
|
|
failed |= dict.getInt64(KeyArgIndex, paramIndex, false);
|
|
if (failed) {
|
|
rec(createErrorRequestInvalid(
|
|
"macro replacement should have key.offset, key.length, and "
|
|
"key.argindex"));
|
|
return true;
|
|
}
|
|
definition.replacements.emplace_back(
|
|
RawCharSourceRange{unsigned(offset), unsigned(length)},
|
|
paramIndex);
|
|
return false;
|
|
});
|
|
if (failed)
|
|
return true;
|
|
expansions.emplace_back(offset, macroRoles, definition);
|
|
}
|
|
return false;
|
|
});
|
|
if (failed)
|
|
return;
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.expandMacroSyntactically(
|
|
inputBuf.get(), args, expansions,
|
|
[&](const auto &Result) { rec(createCategorizedEditsResponse(Result)); });
|
|
}
|
|
|
|
void handleRequestImpl(sourcekitd_object_t ReqObj,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
++numRequests;
|
|
|
|
RequestDict Req(ReqObj);
|
|
|
|
if (auto SimulateLongRequestDuration =
|
|
Req.getOptionalInt64(KeySimulateLongRequest)) {
|
|
if (!getGlobalContext().getSlowRequestSimulator()->simulateLongRequest(
|
|
*SimulateLongRequestDuration, CancellationToken)) {
|
|
Rec(createErrorRequestCancelled());
|
|
return;
|
|
}
|
|
}
|
|
|
|
sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest);
|
|
if (!ReqUID)
|
|
return Rec(
|
|
createErrorRequestInvalid("missing 'key.request' with UID value"));
|
|
|
|
#define HANDLE_REQUEST(Kind, IMPL_FUNC) \
|
|
if (ReqUID == Kind) { \
|
|
return IMPL_FUNC(Req, CancellationToken, Rec); \
|
|
}
|
|
|
|
HANDLE_REQUEST(RequestGlobalConfiguration, handleRequestGlobalConfiguration)
|
|
HANDLE_REQUEST(RequestProtocolVersion, handleRequestProtocolVersion)
|
|
HANDLE_REQUEST(RequestCompilerVersion, handleRequestCompilerVersion)
|
|
HANDLE_REQUEST(RequestCrashWithExit, handleRequestCrashWithExit)
|
|
HANDLE_REQUEST(RequestTestNotification, handleRequestTestNotification)
|
|
HANDLE_REQUEST(RequestStatistics, handleRequestStatistics)
|
|
HANDLE_REQUEST(RequestDemangle, handleRequestDemangle)
|
|
HANDLE_REQUEST(RequestMangleSimpleClass, handleRequestMangleSimpleClass)
|
|
|
|
HANDLE_REQUEST(RequestEnableCompileNotifications,
|
|
handleRequestEnableCompileNotifications)
|
|
|
|
HANDLE_REQUEST(RequestBuildSettingsRegister,
|
|
handleRequestBuildSettingsRegister)
|
|
HANDLE_REQUEST(RequestDependencyUpdated, handleRequestDependencyUpdated)
|
|
|
|
HANDLE_REQUEST(RequestDocInfo, handleRequestDocInfo)
|
|
|
|
HANDLE_REQUEST(RequestEditorOpen, handleRequestEditorOpen)
|
|
HANDLE_REQUEST(RequestEditorClose, handleRequestEditorClose)
|
|
HANDLE_REQUEST(RequestEditorReplaceText, handleRequestEditorReplaceText)
|
|
HANDLE_REQUEST(RequestEditorFormatText, handleRequestEditorFormatText)
|
|
HANDLE_REQUEST(RequestEditorExpandPlaceholder,
|
|
handleRequestEditorExpandPlaceholder)
|
|
HANDLE_REQUEST(RequestEditorOpenInterface, handleRequestEditorOpenInterface)
|
|
HANDLE_REQUEST(RequestEditorOpenHeaderInterface,
|
|
handleRequestEditorOpenHeaderInterface)
|
|
HANDLE_REQUEST(RequestEditorOpenSwiftSourceInterface,
|
|
handleRequestEditorOpenSwiftSourceInterface)
|
|
HANDLE_REQUEST(RequestEditorOpenSwiftTypeInterface,
|
|
handleRequestEditorOpenSwiftTypeInterface)
|
|
HANDLE_REQUEST(RequestEditorExtractTextFromComment,
|
|
handleRequestEditorExtractTextFromComment)
|
|
HANDLE_REQUEST(RequestMarkupToXML, handleRequestMarkupToXML)
|
|
HANDLE_REQUEST(RequestEditorFindUSR, handleRequestEditorFindUSR)
|
|
HANDLE_REQUEST(RequestEditorFindInterfaceDoc,
|
|
handleRequestEditorFindInterfaceDoc)
|
|
HANDLE_REQUEST(RequestModuleGroups, handleRequestModuleGroups)
|
|
|
|
HANDLE_REQUEST(RequestFindRenameRanges, handleRequestFindRenameRanges)
|
|
|
|
HANDLE_REQUEST(RequestCodeCompleteClose, handleRequestCodeCompleteClose)
|
|
HANDLE_REQUEST(RequestCodeCompleteCacheOnDisk,
|
|
handleRequestCodeCompleteCacheOnDisk)
|
|
HANDLE_REQUEST(RequestCodeCompleteSetPopularAPI,
|
|
handleRequestCodeCompleteSetPopularAPI)
|
|
HANDLE_REQUEST(RequestCodeCompleteSetCustom,
|
|
handleRequestCodeCompleteSetCustom)
|
|
|
|
HANDLE_REQUEST(RequestCompile, handleRequestCompile)
|
|
HANDLE_REQUEST(RequestCompileClose, handleRequestCompileClose)
|
|
|
|
// Requests that need semantic typechecking.
|
|
// NOTE: checking semantic functionalities enabled is a handler's
|
|
// responsibility.
|
|
|
|
HANDLE_REQUEST(RequestCodeComplete, handleRequestCodeComplete)
|
|
HANDLE_REQUEST(RequestCodeCompleteOpen, handleRequestCodeCompleteOpen)
|
|
HANDLE_REQUEST(RequestCodeCompleteUpdate, handleRequestCodeCompleteUpdate)
|
|
HANDLE_REQUEST(RequestTypeContextInfo, handleRequestTypeContextInfo)
|
|
HANDLE_REQUEST(RequestConformingMethodList, handleRequestConformingMethodList)
|
|
|
|
HANDLE_REQUEST(RequestIndex, handleRequestIndex)
|
|
HANDLE_REQUEST(RequestIndexToStore, handleRequestIndexToStore)
|
|
HANDLE_REQUEST(RequestCursorInfo, handleRequestCursorInfo)
|
|
HANDLE_REQUEST(RequestRangeInfo, handleRequestRangeInfo)
|
|
HANDLE_REQUEST(RequestSemanticRefactoring, handleRequestSemanticRefactoring)
|
|
|
|
HANDLE_REQUEST(RequestCollectExpressionType,
|
|
handleRequestCollectExpressionType)
|
|
HANDLE_REQUEST(RequestCollectVariableType, handleRequestCollectVariableType)
|
|
HANDLE_REQUEST(RequestFindLocalRenameRanges,
|
|
handleRequestFindLocalRenameRanges)
|
|
HANDLE_REQUEST(RequestNameTranslation, handleRequestNameTranslation)
|
|
HANDLE_REQUEST(RequestRelatedIdents, handleRequestRelatedIdents)
|
|
HANDLE_REQUEST(RequestActiveRegions, handleRequestActiveRegions)
|
|
HANDLE_REQUEST(RequestDiagnostics, handleRequestDiagnostics)
|
|
HANDLE_REQUEST(RequestSemanticTokens, handleRequestSemanticTokens)
|
|
HANDLE_REQUEST(RequestSyntacticMacroExpansion,
|
|
handleRequestSyntacticMacroExpansion)
|
|
|
|
{
|
|
SmallString<64> ErrBuf;
|
|
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 startDependency(UIdent Kind,
|
|
StringRef Name,
|
|
StringRef Path,
|
|
bool IsSystem) 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) {
|
|
ResponseBuilder RespBuilder;
|
|
SKIndexingConsumer IdxConsumer(RespBuilder);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.indexSource(Filename, IdxConsumer, Args);
|
|
|
|
if (!IdxConsumer.ErrorDescription.empty())
|
|
return createErrorRequestFailed(IdxConsumer.ErrorDescription.c_str());
|
|
|
|
return RespBuilder.createResponse();
|
|
}
|
|
|
|
void SKIndexingConsumer::failed(StringRef ErrDescription) {
|
|
ErrorDescription = ErrDescription.str();
|
|
}
|
|
|
|
bool SKIndexingConsumer::startDependency(UIdent Kind,
|
|
StringRef Name,
|
|
StringRef Path,
|
|
bool IsSystem) {
|
|
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);
|
|
|
|
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.IsImplicit)
|
|
Elem.setBool(KeyIsImplicit, 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);
|
|
}
|
|
}
|
|
|
|
if (Info.EffectiveAccess)
|
|
Elem.set(KeyEffectiveAccess, Info.EffectiveAccess.value());
|
|
|
|
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, 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 handleDiagnostics(ArrayRef<DiagnosticEntryInfo> Diags) override;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
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.IsAsync)
|
|
Elem.set(KeyIsAsync, Info.IsAsync);
|
|
if (!Info.DocComment.empty())
|
|
Elem.set(KeyDocFullAsXML, Info.DocComment);
|
|
if (!Info.FullyAnnotatedDecl.empty())
|
|
Elem.set(KeyFullyAnnotatedDecl, Info.FullyAnnotatedDecl);
|
|
if (!Info.FullyAnnotatedGenericSig.empty())
|
|
Elem.set(KeyFullyAnnotatedGenericSignature, Info.FullyAnnotatedGenericSig);
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (!Info.RequiredBystanders.empty())
|
|
Elem.set(KeyRequiredBystanders, Info.RequiredBystanders);
|
|
}
|
|
|
|
void SKDocConsumer::failed(StringRef ErrDescription) {
|
|
ErrorDescription = ErrDescription.str();
|
|
}
|
|
|
|
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.has_value())
|
|
Elem.set(KeyIntroduced, Info.Introduced.value().getAsString());
|
|
if (Info.Deprecated.has_value())
|
|
Elem.set(KeyDeprecated, Info.Deprecated.value().getAsString());
|
|
if (Info.Obsoleted.has_value())
|
|
Elem.set(KeyObsoleted, Info.Obsoleted.value().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::handleDiagnostics(ArrayRef<DiagnosticEntryInfo> Diags) {
|
|
if (Diags.empty())
|
|
return true;
|
|
|
|
fillDiagnosticInfo(TopDict, Diags, /*DiagStage*/ None);
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ReportCursorInfo
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void addCursorSymbolInfo(const CursorSymbolInfo &Symbol,
|
|
ResponseBuilder::Dictionary &Elem) {
|
|
Elem.set(KeyKind, Symbol.Kind);
|
|
if (Symbol.DeclarationLang.isValid())
|
|
Elem.set(KeyDeclarationLang, Symbol.DeclarationLang);
|
|
Elem.set(KeyName, Symbol.Name);
|
|
if (!Symbol.USR.empty())
|
|
Elem.set(KeyUSR, Symbol.USR);
|
|
if (!Symbol.TypeName.empty())
|
|
Elem.set(KeyTypeName, Symbol.TypeName);
|
|
if (!Symbol.TypeUSR.empty())
|
|
Elem.set(KeyTypeUsr, Symbol.TypeUSR);
|
|
if (!Symbol.ContainerTypeUSR.empty())
|
|
Elem.set(KeyContainerTypeUsr, Symbol.ContainerTypeUSR);
|
|
if (!Symbol.DocComment.empty())
|
|
Elem.set(KeyDocFullAsXML, Symbol.DocComment);
|
|
if (!Symbol.GroupName.empty())
|
|
Elem.set(KeyGroupName, Symbol.GroupName);
|
|
if (!Symbol.LocalizationKey.empty())
|
|
Elem.set(KeyLocalizationKey, Symbol.LocalizationKey);
|
|
if (!Symbol.AnnotatedDeclaration.empty())
|
|
Elem.set(KeyAnnotatedDecl, Symbol.AnnotatedDeclaration);
|
|
if (!Symbol.FullyAnnotatedDeclaration.empty())
|
|
Elem.set(KeyFullyAnnotatedDecl, Symbol.FullyAnnotatedDeclaration);
|
|
if (!Symbol.SymbolGraph.empty())
|
|
Elem.set(KeySymbolGraph, Symbol.SymbolGraph);
|
|
if (!Symbol.ModuleName.empty())
|
|
Elem.set(KeyModuleName, Symbol.ModuleName);
|
|
if (!Symbol.ModuleInterfaceName.empty())
|
|
Elem.set(KeyModuleInterfaceName, Symbol.ModuleInterfaceName);
|
|
if (!Symbol.Location.Filename.empty()) {
|
|
Elem.set(KeyFilePath, Symbol.Location.Filename);
|
|
Elem.set(KeyOffset, Symbol.Location.Offset);
|
|
Elem.set(KeyLength, Symbol.Location.Length);
|
|
Elem.set(KeyLine, Symbol.Location.Line);
|
|
Elem.set(KeyColumn, Symbol.Location.Column);
|
|
}
|
|
|
|
if (!Symbol.OverrideUSRs.empty()) {
|
|
auto Overrides = Elem.setArray(KeyOverrides);
|
|
for (auto USR : Symbol.OverrideUSRs) {
|
|
auto Override = Overrides.appendDictionary();
|
|
Override.set(KeyUSR, USR);
|
|
}
|
|
}
|
|
|
|
if (!Symbol.AnnotatedRelatedDeclarations.empty()) {
|
|
auto RelDecls = Elem.setArray(KeyRelatedDecls);
|
|
for (auto AnnotDecl : Symbol.AnnotatedRelatedDeclarations) {
|
|
auto RelDecl = RelDecls.appendDictionary();
|
|
RelDecl.set(KeyAnnotatedDecl, AnnotDecl);
|
|
}
|
|
}
|
|
|
|
if (!Symbol.ModuleGroupArray.empty()) {
|
|
auto Groups = Elem.setArray(KeyModuleGroups);
|
|
for (auto Name : Symbol.ModuleGroupArray) {
|
|
auto Entry = Groups.appendDictionary();
|
|
Entry.set(KeyGroupName, Name);
|
|
}
|
|
}
|
|
|
|
if (!Symbol.ParentContexts.empty()) {
|
|
auto Parents = Elem.setArray(KeyParentContexts);
|
|
for (const auto &ParentTy : Symbol.ParentContexts) {
|
|
auto Parent = Parents.appendDictionary();
|
|
Parent.set(KeyName, ParentTy.Title);
|
|
Parent.set(KeyKind, ParentTy.KindName);
|
|
Parent.set(KeyUSR, ParentTy.USR);
|
|
}
|
|
}
|
|
|
|
if (!Symbol.ReferencedSymbols.empty()) {
|
|
auto Refs = Elem.setArray(KeyReferencedSymbols);
|
|
for (const auto &Ref: Symbol.ReferencedSymbols) {
|
|
auto Symbol = Refs.appendDictionary();
|
|
Symbol.set(KeyUSR, Ref.USR);
|
|
Symbol.set(KeyAccessLevel, Ref.AccessLevel);
|
|
Symbol.set(KeyFilePath, Ref.FilePath);
|
|
Symbol.set(KeyModuleName, Ref.ModuleName);
|
|
Symbol.set(KeyDeclarationLang, Ref.DeclarationLang);
|
|
Symbol.setBool(KeyIsSystem, Ref.IsSystem);
|
|
Symbol.setBool(KeyIsSPI, Ref.IsSPI);
|
|
|
|
auto Parents = Symbol.setArray(KeyParentContexts);
|
|
for (const auto &ParentTy : Ref.ParentContexts) {
|
|
auto Parent = Parents.appendDictionary();
|
|
Parent.set(KeyName, ParentTy.Title);
|
|
Parent.set(KeyKind, ParentTy.KindName);
|
|
Parent.set(KeyUSR, ParentTy.USR);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Symbol.ReceiverUSRs.empty()) {
|
|
auto Receivers = Elem.setArray(KeyReceivers);
|
|
for (auto USR : Symbol.ReceiverUSRs) {
|
|
auto Receiver = Receivers.appendDictionary();
|
|
Receiver.set(KeyUSR, USR);
|
|
}
|
|
}
|
|
|
|
if (Symbol.IsSystem)
|
|
Elem.setBool(KeyIsSystem, true);
|
|
if (Symbol.IsDynamic)
|
|
Elem.setBool(KeyIsDynamic, true);
|
|
if (Symbol.IsSynthesized)
|
|
Elem.setBool(KeyIsSynthesized, true);
|
|
|
|
if (Symbol.ParentNameOffset)
|
|
Elem.set(KeyParentLoc, Symbol.ParentNameOffset.value());
|
|
}
|
|
|
|
static void reportCursorInfo(const RequestResult<CursorInfoData> &Result,
|
|
ResponseReceiver Rec) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
const CursorInfoData &Info = Result.value();
|
|
|
|
ResponseBuilder RespBuilder;
|
|
auto Elem = RespBuilder.getDictionary();
|
|
if (!Info.InternalDiagnostic.empty()) {
|
|
Elem.set(KeyInternalDiagnostic, Info.InternalDiagnostic);
|
|
return Rec(RespBuilder.createResponse());
|
|
}
|
|
|
|
if (!Info.Symbols.empty()) {
|
|
addCursorSymbolInfo(Info.Symbols[0], Elem);
|
|
if (Info.Symbols.size() > 1) {
|
|
auto SecondarySymbols = Elem.setArray(KeySecondarySymbols);
|
|
for (auto Secondary : makeArrayRef(Info.Symbols).drop_front()) {
|
|
auto SecondaryElem = SecondarySymbols.appendDictionary();
|
|
addCursorSymbolInfo(Secondary, SecondaryElem);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Info.AvailableActions.empty()) {
|
|
// Clients rely on a kind being set to determine whether the cursor
|
|
// has any results or not. Add one if there's no symbols, ie. only actions
|
|
// were requested (even though it's meaningless).
|
|
if (Info.Symbols.empty())
|
|
Elem.set(KeyKind, KindRefModule);
|
|
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);
|
|
}
|
|
}
|
|
|
|
Elem.setBool(KeyReusingASTContext, Info.DidReuseAST);
|
|
|
|
return Rec(RespBuilder.createResponse());
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ReportDiagnostics
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void reportDiagnostics(const RequestResult<DiagnosticsResult> &Result,
|
|
ResponseReceiver Rec) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
auto &DiagResults = Result.value();
|
|
|
|
ResponseBuilder RespBuilder;
|
|
auto Dict = RespBuilder.getDictionary();
|
|
fillDiagnosticInfo(Dict, DiagResults, /*DiagStage*/ None);
|
|
Rec(RespBuilder.createResponse());
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ReportSemanticTokens
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void
|
|
reportSemanticTokens(const RequestResult<SemanticTokensResult> &Result,
|
|
ResponseReceiver Rec) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
ResponseBuilder RespBuilder;
|
|
auto Dict = RespBuilder.getDictionary();
|
|
TokenAnnotationsArrayBuilder SemanticAnnotations;
|
|
for (auto SemaTok : Result.value()) {
|
|
UIdent Kind = SemaTok.getUIdentForKind();
|
|
if (Kind.isValid()) {
|
|
SemanticAnnotations.add(Kind, SemaTok.ByteOffset, SemaTok.Length,
|
|
SemaTok.getIsSystem());
|
|
}
|
|
}
|
|
Dict.setCustomBuffer(KeySemanticTokens, SemanticAnnotations.createBuffer());
|
|
Rec(RespBuilder.createResponse());
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ReportRangeInfo
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void reportRangeInfo(const RequestResult<RangeInfo> &Result,
|
|
ResponseReceiver Rec) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
const RangeInfo &Info = Result.value();
|
|
|
|
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 RequestResult<NameTranslatingInfo> &Result,
|
|
ResponseReceiver Rec) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
const NameTranslatingInfo &Info = Result.value();
|
|
|
|
ResponseBuilder RespBuilder;
|
|
if (!Info.InternalDiagnostic.empty()) {
|
|
auto Elem = RespBuilder.getDictionary();
|
|
Elem.set(KeyInternalDiagnostic, Info.InternalDiagnostic);
|
|
return Rec(RespBuilder.createResponse());
|
|
}
|
|
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());
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ReportExpressionTypeInfo
|
|
//===----------------------------------------------------------------------===//
|
|
static void reportExpressionTypeInfo(const RequestResult<ExpressionTypesInFile> &Result,
|
|
ResponseReceiver Rec) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
const ExpressionTypesInFile &Info = Result.value();
|
|
|
|
ResponseBuilder Builder;
|
|
auto Dict = Builder.getDictionary();
|
|
ExpressionTypeArrayBuilder ArrBuilder(Info.TypeBuffer);
|
|
for (auto &R: Info.Results) {
|
|
ArrBuilder.add(R);
|
|
}
|
|
Dict.setCustomBuffer(KeyExpressionTypeList, ArrBuilder.createBuffer());
|
|
Rec(Builder.createResponse());
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ReportVariableTypeInfo
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void
|
|
reportVariableTypeInfo(const RequestResult<VariableTypesInFile> &Result,
|
|
ResponseReceiver Rec) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
const VariableTypesInFile &Info = Result.value();
|
|
|
|
ResponseBuilder Builder;
|
|
auto Dict = Builder.getDictionary();
|
|
VariableTypeArrayBuilder ArrBuilder(Info.TypeBuffer);
|
|
for (auto &R : Info.Results) {
|
|
ArrBuilder.add(R);
|
|
}
|
|
Dict.setCustomBuffer(KeyVariableTypeList, ArrBuilder.createBuffer());
|
|
Rec(Builder.createResponse());
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// FindRelatedIdents
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void findRelatedIdents(StringRef PrimaryFilePath,
|
|
StringRef InputBufferName, int64_t Offset,
|
|
bool CancelOnSubsequentRequest,
|
|
ArrayRef<const char *> Args,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.findRelatedIdentifiersInFile(
|
|
PrimaryFilePath, InputBufferName, Offset, CancelOnSubsequentRequest, Args,
|
|
CancellationToken,
|
|
[Rec](const RequestResult<ArrayRef<RelatedIdentInfo>> &Result) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
const ArrayRef<RelatedIdentInfo> &Info = Result.value();
|
|
|
|
ResponseBuilder RespBuilder;
|
|
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
|
|
for (auto R : Info) {
|
|
auto Elem = Arr.appendDictionary();
|
|
Elem.set(KeyOffset, R.Offset);
|
|
Elem.set(KeyLength, R.Length);
|
|
}
|
|
|
|
Rec(RespBuilder.createResponse());
|
|
});
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// FindActiveRegions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void findActiveRegions(StringRef PrimaryFilePath,
|
|
StringRef InputBufferName,
|
|
ArrayRef<const char *> Args,
|
|
SourceKitCancellationToken CancellationToken,
|
|
ResponseReceiver Rec) {
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
|
|
Lang.findActiveRegionsInFile(
|
|
PrimaryFilePath, InputBufferName, Args, CancellationToken,
|
|
[Rec](const RequestResult<ActiveRegionsInfo> &Result) {
|
|
if (Result.isCancelled())
|
|
return Rec(createErrorRequestCancelled());
|
|
if (Result.isError())
|
|
return Rec(createErrorRequestFailed(Result.getError()));
|
|
|
|
const ActiveRegionsInfo &Info = Result.value();
|
|
|
|
ResponseBuilder RespBuilder;
|
|
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
|
|
for (auto Config : Info.Configs) {
|
|
auto Elem = Arr.appendDictionary();
|
|
Elem.set(KeyOffset, Config.Offset);
|
|
if (Config.IsActive)
|
|
Elem.setBool(KeyIsActive, true);
|
|
}
|
|
|
|
Rec(RespBuilder.createResponse());
|
|
});
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// CodeComplete
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
class SKCodeCompletionConsumer : public CodeCompletionConsumer {
|
|
ResponseBuilder &RespBuilder;
|
|
CodeCompletionResultsArrayBuilder ResultsBuilder;
|
|
|
|
std::string ErrorDescription;
|
|
bool WasCancelled = false;
|
|
|
|
public:
|
|
explicit SKCodeCompletionConsumer(ResponseBuilder &RespBuilder)
|
|
: RespBuilder(RespBuilder) {
|
|
}
|
|
|
|
sourcekitd_response_t createResponse() {
|
|
if (WasCancelled) {
|
|
return createErrorRequestCancelled();
|
|
} else if (!ErrorDescription.empty()) {
|
|
return createErrorRequestFailed(ErrorDescription.c_str());
|
|
} else {
|
|
RespBuilder.getDictionary().setCustomBuffer(
|
|
KeyResults, ResultsBuilder.createBuffer());
|
|
return RespBuilder.createResponse();
|
|
}
|
|
}
|
|
|
|
|
|
void failed(StringRef ErrDescription) override;
|
|
void cancelled() override;
|
|
|
|
void setCompletionKind(UIdent kind) override;
|
|
void setReusingASTContext(bool flag) override;
|
|
void setAnnotatedTypename(bool flag) override;
|
|
bool handleResult(const CodeCompletionInfo &Info) override;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static sourcekitd_response_t
|
|
codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken) {
|
|
ResponseBuilder RespBuilder;
|
|
SKCodeCompletionConsumer CCC(RespBuilder);
|
|
|
|
std::unique_ptr<SKOptionsDictionary> options;
|
|
if (optionsDict)
|
|
options = std::make_unique<SKOptionsDictionary>(*optionsDict);
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.codeComplete(InputBuf, Offset, options.get(), CCC, Args,
|
|
std::move(vfsOptions), CancellationToken);
|
|
return CCC.createResponse();
|
|
}
|
|
|
|
void SKCodeCompletionConsumer::failed(StringRef ErrDescription) {
|
|
ErrorDescription = ErrDescription.str();
|
|
}
|
|
|
|
void SKCodeCompletionConsumer::cancelled() { WasCancelled = true; }
|
|
|
|
void SKCodeCompletionConsumer::setCompletionKind(UIdent kind) {
|
|
assert(kind.isValid());
|
|
RespBuilder.getDictionary().set(KeyKind, kind);
|
|
}
|
|
|
|
void SKCodeCompletionConsumer::setReusingASTContext(bool flag) {
|
|
if (flag)
|
|
RespBuilder.getDictionary().setBool(KeyReusingASTContext, flag);
|
|
}
|
|
|
|
void SKCodeCompletionConsumer::setAnnotatedTypename(bool flag) {
|
|
if (flag)
|
|
RespBuilder.getDictionary().setBool(KeyAnnotatedTypename, flag);
|
|
}
|
|
|
|
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.TypeRelation,
|
|
R.NotRecommended,
|
|
R.IsSystem,
|
|
R.NumBytesToErase);
|
|
return true;
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// (New) CodeComplete
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
class SKGroupedCodeCompletionConsumer : public GroupedCodeCompletionConsumer {
|
|
ResponseBuilder &RespBuilder;
|
|
ResponseBuilder::Dictionary Response;
|
|
SmallVector<ResponseBuilder::Array, 3> GroupContentsStack;
|
|
std::string ErrorDescription;
|
|
bool WasCancelled = false;
|
|
|
|
public:
|
|
explicit SKGroupedCodeCompletionConsumer(ResponseBuilder &RespBuilder)
|
|
: RespBuilder(RespBuilder) {}
|
|
|
|
sourcekitd_response_t createResponse() {
|
|
if (WasCancelled) {
|
|
return createErrorRequestCancelled();
|
|
} else if (!ErrorDescription.empty()) {
|
|
return createErrorRequestFailed(ErrorDescription.c_str());
|
|
} else {
|
|
assert(GroupContentsStack.empty() && "mismatched start/endGroup");
|
|
return RespBuilder.createResponse();
|
|
}
|
|
}
|
|
|
|
void failed(StringRef ErrDescription) override;
|
|
void cancelled() override;
|
|
bool handleResult(const CodeCompletionInfo &Info) override;
|
|
void startGroup(UIdent kind, StringRef name) override;
|
|
void endGroup() override;
|
|
void setNextRequestStart(unsigned offset) override;
|
|
void setReusingASTContext(bool flag) override;
|
|
void setAnnotatedTypename(bool flag) override;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static sourcekitd_response_t
|
|
codeCompleteOpen(StringRef Name, llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken) {
|
|
ResponseBuilder RespBuilder;
|
|
SKGroupedCodeCompletionConsumer CCC(RespBuilder);
|
|
std::unique_ptr<SKOptionsDictionary> options;
|
|
std::vector<FilterRule> filterRules;
|
|
if (optionsDict) {
|
|
options = std::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:
|
|
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, std::move(vfsOptions), CancellationToken);
|
|
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,
|
|
SourceKitCancellationToken CancellationToken) {
|
|
ResponseBuilder RespBuilder;
|
|
SKGroupedCodeCompletionConsumer CCC(RespBuilder);
|
|
std::unique_ptr<SKOptionsDictionary> options;
|
|
if (optionsDict)
|
|
options = std::make_unique<SKOptionsDictionary>(*optionsDict);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.codeCompleteUpdate(name, offset, options.get(), CancellationToken, CCC);
|
|
return CCC.createResponse();
|
|
}
|
|
|
|
void SKGroupedCodeCompletionConsumer::failed(StringRef ErrDescription) {
|
|
ErrorDescription = ErrDescription.str();
|
|
}
|
|
|
|
void SKGroupedCodeCompletionConsumer::cancelled() { WasCancelled = true; }
|
|
|
|
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);
|
|
if (R.IsSystem)
|
|
result.set(KeyIsSystem, R.IsSystem);
|
|
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);
|
|
|
|
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);
|
|
}
|
|
void SKGroupedCodeCompletionConsumer::setReusingASTContext(bool flag) {
|
|
if (flag)
|
|
RespBuilder.getDictionary().setBool(KeyReusingASTContext, flag);
|
|
}
|
|
void SKGroupedCodeCompletionConsumer::setAnnotatedTypename(bool flag) {
|
|
if (flag)
|
|
RespBuilder.getDictionary().setBool(KeyAnnotatedTypename, flag);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Type Context Info
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static sourcekitd_response_t
|
|
typeContextInfo(llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken) {
|
|
ResponseBuilder RespBuilder;
|
|
|
|
class Consumer : public TypeContextInfoConsumer {
|
|
ResponseBuilder RespBuilder;
|
|
ResponseBuilder::Array SKResults;
|
|
Optional<std::string> ErrorDescription;
|
|
bool WasCancelled = false;
|
|
|
|
public:
|
|
Consumer(ResponseBuilder Builder)
|
|
: RespBuilder(Builder),
|
|
SKResults(Builder.getDictionary().setArray(KeyResults)) {}
|
|
|
|
void handleResult(const TypeContextInfoItem &Item) override {
|
|
auto SKElem = SKResults.appendDictionary();
|
|
SKElem.set(KeyTypeName, Item.TypeName);
|
|
SKElem.set(KeyTypeUsr, Item.TypeUSR);
|
|
auto members = SKElem.setArray(KeyImplicitMembers);
|
|
for (auto member : Item.ImplicitMembers) {
|
|
auto memberElem = members.appendDictionary();
|
|
memberElem.set(KeyName, member.Name);
|
|
memberElem.set(KeyDescription, member.Description);
|
|
memberElem.set(KeySourceText, member.SourceText);
|
|
if (!member.DocBrief.empty())
|
|
memberElem.set(KeyDocBrief, member.DocBrief);
|
|
}
|
|
}
|
|
|
|
void setReusingASTContext(bool flag) override {
|
|
if (flag)
|
|
RespBuilder.getDictionary().setBool(KeyReusingASTContext, flag);
|
|
}
|
|
|
|
void failed(StringRef ErrDescription) override {
|
|
ErrorDescription = ErrDescription.str();
|
|
}
|
|
|
|
void cancelled() override { WasCancelled = true; }
|
|
|
|
bool wasCancelled() const { return WasCancelled; }
|
|
bool isError() const { return ErrorDescription.has_value(); }
|
|
const char *getErrorDescription() const {
|
|
return ErrorDescription->c_str();
|
|
}
|
|
} Consumer(RespBuilder);
|
|
|
|
std::unique_ptr<SKOptionsDictionary> options;
|
|
if (optionsDict)
|
|
options = std::make_unique<SKOptionsDictionary>(*optionsDict);
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.getExpressionContextInfo(InputBuf, Offset, options.get(), Args,
|
|
CancellationToken, Consumer,
|
|
std::move(vfsOptions));
|
|
|
|
if (Consumer.wasCancelled()) {
|
|
return createErrorRequestCancelled();
|
|
} else if (Consumer.isError()) {
|
|
return createErrorRequestFailed(Consumer.getErrorDescription());
|
|
} else {
|
|
return RespBuilder.createResponse();
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Conforming Method List
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static sourcekitd_response_t conformingMethodList(
|
|
llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
|
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
|
ArrayRef<const char *> ExpectedTypes, Optional<VFSOptions> vfsOptions,
|
|
SourceKitCancellationToken CancellationToken) {
|
|
ResponseBuilder RespBuilder;
|
|
|
|
class Consumer : public ConformingMethodListConsumer {
|
|
ResponseBuilder::Dictionary SKResult;
|
|
Optional<std::string> ErrorDescription;
|
|
bool WasCancelled = false;
|
|
|
|
public:
|
|
Consumer(ResponseBuilder Builder) : SKResult(Builder.getDictionary()) {}
|
|
|
|
void handleResult(const ConformingMethodListResult &Result) override {
|
|
SKResult.set(KeyTypeName, Result.TypeName);
|
|
SKResult.set(KeyTypeUsr, Result.TypeUSR);
|
|
auto members = SKResult.setArray(KeyMembers);
|
|
for (auto member : Result.Members) {
|
|
auto memberElem = members.appendDictionary();
|
|
memberElem.set(KeyName, member.Name);
|
|
memberElem.set(KeyTypeName, member.TypeName);
|
|
memberElem.set(KeyTypeUsr, member.TypeUSR);
|
|
memberElem.set(KeyDescription, member.Description);
|
|
memberElem.set(KeySourceText, member.SourceText);
|
|
if (!member.DocBrief.empty())
|
|
memberElem.set(KeyDocBrief, member.DocBrief);
|
|
}
|
|
}
|
|
|
|
void setReusingASTContext(bool flag) override {
|
|
if (flag)
|
|
SKResult.setBool(KeyReusingASTContext, flag);
|
|
}
|
|
|
|
void failed(StringRef ErrDescription) override {
|
|
ErrorDescription = ErrDescription.str();
|
|
}
|
|
|
|
void cancelled() override { WasCancelled = true; }
|
|
|
|
bool wasCancelled() const { return WasCancelled; }
|
|
bool isError() const { return ErrorDescription.has_value(); }
|
|
const char *getErrorDescription() const {
|
|
return ErrorDescription->c_str();
|
|
}
|
|
} Consumer(RespBuilder);
|
|
|
|
std::unique_ptr<SKOptionsDictionary> options;
|
|
if (optionsDict)
|
|
options = std::make_unique<SKOptionsDictionary>(*optionsDict);
|
|
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.getConformingMethodList(InputBuf, Offset, options.get(), Args,
|
|
ExpectedTypes, CancellationToken, Consumer,
|
|
std::move(vfsOptions));
|
|
|
|
if (Consumer.wasCancelled()) {
|
|
return createErrorRequestCancelled();
|
|
} else if (Consumer.isError()) {
|
|
return createErrorRequestFailed(Consumer.getErrorDescription());
|
|
} else {
|
|
return RespBuilder.createResponse();
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Editor
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
class SKEditorConsumer : public EditorConsumer {
|
|
ResponseReceiver RespReceiver;
|
|
ResponseBuilder RespBuilder;
|
|
|
|
public:
|
|
ResponseBuilder::Dictionary Dict;
|
|
DocStructureArrayBuilder DocStructure;
|
|
TokenAnnotationsArrayBuilder SyntaxMap;
|
|
TokenAnnotationsArrayBuilder SemanticAnnotations;
|
|
|
|
sourcekitd_response_t Error = nullptr;
|
|
|
|
SKEditorConsumerOptions Opts;
|
|
|
|
public:
|
|
SKEditorConsumer(SKEditorConsumerOptions Opts) : Opts(Opts) {
|
|
Dict = RespBuilder.getDictionary();
|
|
}
|
|
|
|
SKEditorConsumer(ResponseReceiver RespReceiver, SKEditorConsumerOptions Opts)
|
|
: SKEditorConsumer(Opts) {
|
|
this->RespReceiver = RespReceiver;
|
|
}
|
|
|
|
sourcekitd_response_t createResponse();
|
|
|
|
bool needsSemanticInfo() override {
|
|
return !Opts.SyntacticOnly && !isSemanticEditorDisabled();
|
|
}
|
|
|
|
void handleRequestError(const char *Description) override;
|
|
|
|
bool syntaxMapEnabled() override { return Opts.EnableSyntaxMap; }
|
|
|
|
void handleSyntaxMap(unsigned Offset, unsigned Length, UIdent Kind) override;
|
|
|
|
void handleSemanticAnnotation(unsigned Offset, unsigned Length, UIdent Kind,
|
|
bool isSystem) override;
|
|
|
|
bool documentStructureEnabled() override { return Opts.EnableStructure; }
|
|
|
|
void 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;
|
|
|
|
void endDocumentSubStructure() override;
|
|
|
|
void handleDocumentSubStructureElement(UIdent Kind, unsigned Offset,
|
|
unsigned Length) override;
|
|
|
|
void recordAffectedRange(unsigned Offset, unsigned Length) override;
|
|
|
|
void recordAffectedLineRange(unsigned Line, unsigned Length) override;
|
|
|
|
void recordFormattedText(StringRef Text) override;
|
|
|
|
bool diagnosticsEnabled() override { return Opts.EnableDiagnostics; }
|
|
|
|
void handleDiagnostics(ArrayRef<DiagnosticEntryInfo> Diags,
|
|
UIdent DiagStage) override;
|
|
|
|
void handleSourceText(StringRef Text) override;
|
|
|
|
void finished() override {
|
|
if (RespReceiver)
|
|
RespReceiver(createResponse());
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static sourcekitd_response_t
|
|
editorOpen(StringRef Name, llvm::MemoryBuffer *Buf,
|
|
SKEditorConsumerOptions Opts, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions) {
|
|
SKEditorConsumer EditC(Opts);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.editorOpen(Name, Buf, EditC, Args, std::move(vfsOptions));
|
|
return EditC.createResponse();
|
|
}
|
|
|
|
static sourcekitd_response_t
|
|
editorOpenInterface(StringRef Name, StringRef ModuleName,
|
|
Optional<StringRef> Group, ArrayRef<const char *> Args,
|
|
bool SynthesizedExtensions,
|
|
Optional<StringRef> InterestedUSR) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.EnableSyntaxMap = true;
|
|
Opts.EnableStructure = true;
|
|
SKEditorConsumer EditC(Opts);
|
|
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,
|
|
SourceKitCancellationToken CancellationToken, ResponseReceiver Rec) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.EnableSyntaxMap = true;
|
|
Opts.EnableStructure = true;
|
|
auto EditC = std::make_shared<SKEditorConsumer>(Rec, Opts);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.editorOpenSwiftSourceInterface(Name, HeaderName, Args, CancellationToken,
|
|
EditC);
|
|
}
|
|
|
|
static void
|
|
editorOpenSwiftTypeInterface(StringRef TypeUsr, ArrayRef<const char *> Args,
|
|
ResponseReceiver Rec) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.EnableSyntaxMap = true;
|
|
Opts.EnableStructure = true;
|
|
auto EditC = std::make_shared<SKEditorConsumer>(Rec, Opts);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.editorOpenTypeInterface(*EditC, Args, TypeUsr);
|
|
}
|
|
|
|
static sourcekitd_response_t editorExtractTextFromComment(StringRef Source) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.SyntacticOnly = true;
|
|
SKEditorConsumer EditC(Opts);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.editorExtractTextFromComment(Source, EditC);
|
|
return EditC.createResponse();
|
|
}
|
|
|
|
static sourcekitd_response_t editorConvertMarkupToXML(StringRef Source) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.SyntacticOnly = true;
|
|
SKEditorConsumer EditC(Opts);
|
|
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,
|
|
StringRef swiftVersion) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.EnableSyntaxMap = true;
|
|
Opts.EnableStructure = true;
|
|
SKEditorConsumer EditC(Opts);
|
|
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, SKEditorConsumerOptions Opts) {
|
|
SKEditorConsumer EditC(Opts);
|
|
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) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.SyntacticOnly = true;
|
|
SKEditorConsumer EditC(Opts);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.editorFormatText(Name, Line, Length, EditC);
|
|
return EditC.createResponse();
|
|
}
|
|
|
|
static sourcekitd_response_t
|
|
editorExpandPlaceholder(StringRef Name, unsigned Offset, unsigned Length) {
|
|
SKEditorConsumerOptions Opts;
|
|
Opts.SyntacticOnly = true;
|
|
SKEditorConsumer EditC(Opts);
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
Lang.editorExpandPlaceholder(Name, Offset, Length, EditC);
|
|
return EditC.createResponse();
|
|
}
|
|
|
|
sourcekitd_response_t SKEditorConsumer::createResponse() {
|
|
if (Error)
|
|
return Error;
|
|
|
|
if (Opts.EnableSyntaxMap) {
|
|
Dict.setCustomBuffer(KeySyntaxMap, SyntaxMap.createBuffer());
|
|
}
|
|
if (!SemanticAnnotations.empty()) {
|
|
Dict.setCustomBuffer(KeyAnnotations, SemanticAnnotations.createBuffer());
|
|
}
|
|
if (Opts.EnableStructure) {
|
|
Dict.setCustomBuffer(KeySubStructure, DocStructure.createBuffer());
|
|
}
|
|
|
|
|
|
return RespBuilder.createResponse();
|
|
}
|
|
|
|
void SKEditorConsumer::handleRequestError(const char *Description) {
|
|
if (!Error) {
|
|
Error = createErrorRequestFailed(Description);
|
|
}
|
|
if (RespReceiver) {
|
|
RespReceiver(Error);
|
|
RespReceiver = ResponseReceiver();
|
|
}
|
|
}
|
|
|
|
void SKEditorConsumer::handleSyntaxMap(unsigned Offset, unsigned Length,
|
|
UIdent Kind) {
|
|
if (!Opts.EnableSyntaxMap)
|
|
return;
|
|
|
|
SyntaxMap.add(Kind, Offset, Length, /*IsSystem=*/false);
|
|
}
|
|
|
|
void SKEditorConsumer::handleSemanticAnnotation(unsigned Offset,
|
|
unsigned Length, UIdent Kind,
|
|
bool isSystem) {
|
|
assert(Kind.isValid());
|
|
SemanticAnnotations.add(Kind, Offset, Length, isSystem);
|
|
}
|
|
|
|
void
|
|
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 (Opts.EnableStructure) {
|
|
DocStructure.beginSubStructure(
|
|
Offset, Length, Kind, AccessLevel, SetterAccessLevel, NameOffset,
|
|
NameLength, BodyOffset, BodyLength, DocOffset, DocLength, DisplayName,
|
|
TypeName, RuntimeName, SelectorName, InheritedTypes, Attrs);
|
|
}
|
|
}
|
|
|
|
void SKEditorConsumer::endDocumentSubStructure() {
|
|
if (Opts.EnableStructure)
|
|
DocStructure.endSubStructure();
|
|
}
|
|
|
|
void SKEditorConsumer::handleDocumentSubStructureElement(UIdent Kind,
|
|
unsigned Offset,
|
|
unsigned Length) {
|
|
if (Opts.EnableStructure)
|
|
DocStructure.addElement(Kind, Offset, Length);
|
|
}
|
|
|
|
void SKEditorConsumer::recordAffectedRange(unsigned Offset, unsigned Length) {
|
|
Dict.set(KeyOffset, Offset);
|
|
Dict.set(KeyLength, Length);
|
|
}
|
|
|
|
void SKEditorConsumer::recordAffectedLineRange(unsigned Line, unsigned Length) {
|
|
Dict.set(KeyLine, Line);
|
|
Dict.set(KeyLength, Length);
|
|
}
|
|
|
|
void SKEditorConsumer::recordFormattedText(StringRef Text) {
|
|
Dict.set(KeySourceText, Text);
|
|
}
|
|
|
|
static void fillDictionaryForRange(ResponseBuilder::Dictionary Elem,
|
|
const RawCharSourceRange &R) {
|
|
Elem.set(KeyOffset, R.Offset);
|
|
Elem.set(KeyLength, R.Length);
|
|
}
|
|
|
|
namespace {
|
|
/// Buffer state that we need to persist when writing out diagnostics.
|
|
struct GeneratedBuffersState {
|
|
/// The root dictionary.
|
|
ResponseBuilder::Dictionary ParentElem;
|
|
|
|
/// The generated buffer array.
|
|
ResponseBuilder::Array GeneratedBuffersElem;
|
|
|
|
/// The buffer names that have had their info written out.
|
|
llvm::StringSet<> BufferInfosWritten;
|
|
|
|
GeneratedBuffersState(ResponseBuilder::Dictionary ParentElem)
|
|
: ParentElem(ParentElem) {}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static void writeBufferInfoIfNeeded(const BufferInfo *Info,
|
|
GeneratedBuffersState &GeneratedBuffers);
|
|
|
|
static void
|
|
fillDictionaryForGeneratedBufferInfo(ResponseBuilder::Dictionary Elem,
|
|
const BufferInfo *Info,
|
|
GeneratedBuffersState &GeneratedBuffers) {
|
|
Elem.set(KeyBufferName, Info->BufferName);
|
|
|
|
if (auto &Contents = Info->Contents)
|
|
Elem.set(KeyBufferText, *Contents);
|
|
|
|
if (auto &OriginalLoc = Info->OrigLocation) {
|
|
// Write out the original buffer info if needed.
|
|
auto *OrigBufferInfo = OriginalLoc->OrigBufferInfo.get();
|
|
writeBufferInfoIfNeeded(OrigBufferInfo, GeneratedBuffers);
|
|
|
|
auto OrigLocElem = Elem.setDictionary(KeyOriginalLocation);
|
|
OrigLocElem.set(KeyBufferName, OrigBufferInfo->BufferName);
|
|
fillDictionaryForRange(OrigLocElem, OriginalLoc->Range);
|
|
}
|
|
}
|
|
|
|
/// If the given buffer info is for a generated buffer, and has not yet been
|
|
/// written to the generated buffer info array, write it out.
|
|
static void writeBufferInfoIfNeeded(const BufferInfo *Info,
|
|
GeneratedBuffersState &GeneratedBuffers) {
|
|
// No contents or original location, nothing interesting to write out.
|
|
if (!Info->Contents && !Info->OrigLocation)
|
|
return;
|
|
|
|
if (!GeneratedBuffers.BufferInfosWritten.insert(Info->BufferName).second)
|
|
return;
|
|
|
|
// Add the generated_buffers key to the parent dictionary if we don't already
|
|
// have it.
|
|
auto &GeneratedBuffersElem = GeneratedBuffers.GeneratedBuffersElem;
|
|
if (GeneratedBuffersElem.isNull()) {
|
|
GeneratedBuffersElem =
|
|
GeneratedBuffers.ParentElem.setArray(KeyGeneratedBuffers);
|
|
}
|
|
|
|
// Append the new element.
|
|
auto Elt = GeneratedBuffers.GeneratedBuffersElem.appendDictionary();
|
|
fillDictionaryForGeneratedBufferInfo(Elt, Info, GeneratedBuffers);
|
|
}
|
|
|
|
static void
|
|
fillDictionaryForDiagnosticInfoBase(ResponseBuilder::Dictionary Elem,
|
|
const DiagnosticEntryInfoBase &Info,
|
|
GeneratedBuffersState &GeneratedBuffers) {
|
|
|
|
if (!Info.ID.empty())
|
|
Elem.set(KeyID, Info.ID);
|
|
|
|
if (!Info.Categories.empty()) {
|
|
SmallVector<SourceKit::UIdent, 1> CategoryUIDs;
|
|
|
|
static UIdent UIDKindDiagDeprecation(KindDiagDeprecation.str());
|
|
static UIdent UIDKindDiagNoUsage(KindDiagNoUsage.str());
|
|
|
|
for (auto C : Info.Categories) {
|
|
switch (C) {
|
|
case DiagnosticCategory::Deprecation:
|
|
CategoryUIDs.push_back(UIDKindDiagDeprecation);
|
|
break;
|
|
case DiagnosticCategory::NoUsage:
|
|
CategoryUIDs.push_back(UIDKindDiagNoUsage);
|
|
break;
|
|
}
|
|
}
|
|
Elem.set(KeyCategories, CategoryUIDs);
|
|
}
|
|
|
|
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);
|
|
}
|
|
auto *BufferInfo = Info.FileInfo.get();
|
|
if (!BufferInfo->BufferName.empty())
|
|
Elem.set(KeyFilePath, Info.FileInfo->BufferName);
|
|
|
|
writeBufferInfoIfNeeded(BufferInfo, GeneratedBuffers);
|
|
|
|
if (!Info.EducationalNotePaths.empty())
|
|
Elem.set(KeyEducationalNotePaths, Info.EducationalNotePaths);
|
|
|
|
if (!Info.Ranges.empty()) {
|
|
auto RangesArr = Elem.setArray(KeyRanges);
|
|
for (auto R : Info.Ranges)
|
|
fillDictionaryForRange(RangesArr.appendDictionary(), R);
|
|
}
|
|
|
|
if (!Info.Fixits.empty()) {
|
|
auto FixitsArr = Elem.setArray(KeyFixits);
|
|
for (auto F : Info.Fixits) {
|
|
auto FixitElem = FixitsArr.appendDictionary();
|
|
fillDictionaryForRange(FixitElem, F.Range);
|
|
FixitElem.set(KeySourceText, F.Text);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
fillDictionaryForDiagnosticInfo(ResponseBuilder::Dictionary Elem,
|
|
const DiagnosticEntryInfo &Info,
|
|
GeneratedBuffersState &GeneratedBuffers) {
|
|
|
|
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);
|
|
fillDictionaryForDiagnosticInfoBase(Elem, Info, GeneratedBuffers);
|
|
|
|
if (!Info.Notes.empty()) {
|
|
auto NotesArr = Elem.setArray(KeyDiagnostics);
|
|
for (auto &NoteDiag : Info.Notes) {
|
|
auto NoteElem = NotesArr.appendDictionary();
|
|
NoteElem.set(KeySeverity, KindDiagNote);
|
|
fillDictionaryForDiagnosticInfoBase(NoteElem, NoteDiag, GeneratedBuffers);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Fill in diagnostic info for the 'diagnostics' key in the given \p
|
|
/// ParentElem.
|
|
static void fillDiagnosticInfo(ResponseBuilder::Dictionary ParentElem,
|
|
ArrayRef<DiagnosticEntryInfo> Diags,
|
|
Optional<UIdent> DiagStage) {
|
|
// Add the key, and bail if the diags are empty.
|
|
auto DiagsElem = ParentElem.setArray(KeyDiagnostics);
|
|
if (Diags.empty())
|
|
return;
|
|
|
|
GeneratedBuffersState State(ParentElem);
|
|
for (auto &Diag : Diags) {
|
|
auto Elem = DiagsElem.appendDictionary();
|
|
if (DiagStage)
|
|
Elem.set(KeyDiagnosticStage, *DiagStage);
|
|
|
|
fillDictionaryForDiagnosticInfo(Elem, Diag, State);
|
|
}
|
|
}
|
|
|
|
void SKEditorConsumer::handleDiagnostics(ArrayRef<DiagnosticEntryInfo> Diags,
|
|
UIdent DiagStage) {
|
|
// TODO: Setting the stage here matches the old behavior, but should we
|
|
// consider moving until after the below if check? Do we even need this key if
|
|
// each individual diagnostic has its stage set? Or should we remove those
|
|
// keys?
|
|
Dict.set(KeyDiagnosticStage, DiagStage);
|
|
|
|
if (!Opts.EnableDiagnostics || Diags.empty())
|
|
return;
|
|
|
|
fillDiagnosticInfo(Dict, Diags, DiagStage);
|
|
}
|
|
|
|
void SKEditorConsumer::handleSourceText(StringRef Text) {
|
|
Dict.set(KeySourceText, Text);
|
|
}
|
|
|
|
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.has_value()) {
|
|
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 RequestResult<InterfaceDocInfo> &Result) {
|
|
if (Result.isCancelled()) {
|
|
Resp = createErrorRequestCancelled();
|
|
return;
|
|
}
|
|
if (Result.isError()) {
|
|
Resp = createErrorRequestFailed(Result.getError());
|
|
return;
|
|
}
|
|
|
|
const InterfaceDocInfo &Info = Result.value();
|
|
|
|
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,
|
|
[&](const RequestResult<ArrayRef<StringRef>> &Result) {
|
|
if (Result.isCancelled()) {
|
|
Resp = createErrorRequestCancelled();
|
|
return;
|
|
}
|
|
if (Result.isError()) {
|
|
Resp = createErrorRequestFailed(Result.getError());
|
|
return;
|
|
}
|
|
|
|
ArrayRef<StringRef> Groups = Result.value();
|
|
|
|
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(const RequestDict &Req,
|
|
std::vector<RenameLoc> &RenameLocations,
|
|
llvm::SmallString<64> &Error) {
|
|
bool Failed = Req.dictionaryArrayApply(KeyRenameLocations,
|
|
[&](RequestDict RenameLocation) {
|
|
Optional<StringRef> OldName = RenameLocation.getString(KeyName);
|
|
if (!OldName.has_value()) {
|
|
Error = "missing key.name";
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
using swift::ide::RenameLocUsage;
|
|
RenameLocUsage RenameType = RenameLocUsage::Unknown;
|
|
if (NameType == KindDefinition) {
|
|
RenameType = RenameLocUsage::Definition;
|
|
} else if (NameType == KindReference) {
|
|
RenameType = RenameLocUsage::Reference;
|
|
} else if (NameType == KindCall) {
|
|
RenameType = RenameLocUsage::Call;
|
|
} else if (NameType != KindUnknown) {
|
|
Error = "invalid value for 'key.nametype'";
|
|
return true;
|
|
}
|
|
RenameLocations.emplace_back(
|
|
static_cast<unsigned>(Line), static_cast<unsigned>(Column),
|
|
RenameType, *OldName);
|
|
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(const RequestResult<ArrayRef<CategorizedEdits>> &Result) {
|
|
if (Result.isCancelled())
|
|
return createErrorRequestCancelled();
|
|
if (Result.isError())
|
|
return createErrorRequestFailed(Result.getError());
|
|
|
|
const ArrayRef<CategorizedEdits> &AllEdits = Result.value();
|
|
|
|
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();
|
|
if (!E.Path.empty()) {
|
|
Edit.set(KeyFilePath, E.Path);
|
|
}
|
|
Edit.set(KeyLine, E.StartLine);
|
|
Edit.set(KeyColumn, E.StartColumn);
|
|
Edit.set(KeyEndLine, E.EndLine);
|
|
Edit.set(KeyEndColumn, E.EndColumn);
|
|
if (!E.BufferName.empty()) {
|
|
Edit.set(KeyBufferName, E.BufferName);
|
|
}
|
|
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 createCategorizedRenameRangesResponse(
|
|
const CancellableResult<std::vector<CategorizedRenameRanges>> &Result) {
|
|
using swift::ide::CancellableResultKind;
|
|
switch (Result.getKind()) {
|
|
case CancellableResultKind::Cancelled:
|
|
return createErrorRequestCancelled();
|
|
case CancellableResultKind::Failure:
|
|
return createErrorRequestFailed(Result.getError());
|
|
case CancellableResultKind::Success:
|
|
break; // Handle below
|
|
}
|
|
|
|
const std::vector<CategorizedRenameRanges> &Ranges = Result.getResult();
|
|
|
|
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<RenameLoc> RenameLocations,
|
|
ArrayRef<const char *> Args) {
|
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
|
CancellableResult<std::vector<CategorizedRenameRanges>> ReqResult =
|
|
Lang.findRenameRanges(InputBuf, RenameLocations, Args);
|
|
return createCategorizedRenameRangesResponse(ReqResult);
|
|
}
|
|
|
|
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;
|
|
getGlobalContext()
|
|
.getNotificationCenter()
|
|
->postSemaEnabledNotification();
|
|
});
|
|
});
|
|
}
|
|
|
|
assert(Toggle != SemaInfoToggle::None);
|
|
return Toggle == SemaInfoToggle::Disable;
|
|
}
|
|
|
|
namespace {
|
|
class CompileTrackingConsumer final : public trace::TraceConsumer {
|
|
public:
|
|
void operationStarted(uint64_t OpId, trace::OperationKind OpKind,
|
|
const trace::SwiftInvocation &Inv,
|
|
const trace::StringPairs &OpArgs) override;
|
|
void operationFinished(uint64_t OpId, trace::OperationKind OpKind,
|
|
ArrayRef<DiagnosticEntryInfo> Diagnostics) override;
|
|
swift::OptionSet<trace::OperationKind> desiredOperations() override {
|
|
return swift::OptionSet<trace::OperationKind>() |
|
|
trace::OperationKind::PerformSema |
|
|
trace::OperationKind::CodeCompletion;
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static Optional<UIdent> getUIDForOperationKind(trace::OperationKind OpKind) {
|
|
static UIdent CompileOperationIndexSource("source.compile.operation.index-source");
|
|
static UIdent CompileOperationCodeCompletion("source.compile.operation.code-completion");
|
|
switch (OpKind) {
|
|
case trace::OperationKind::PerformSema:
|
|
return None;
|
|
case trace::OperationKind::IndexSource:
|
|
return CompileOperationIndexSource;
|
|
case trace::OperationKind::CodeCompletion:
|
|
return CompileOperationCodeCompletion;
|
|
default:
|
|
llvm_unreachable("Unknown operation kind");
|
|
}
|
|
}
|
|
|
|
void CompileTrackingConsumer::operationStarted(
|
|
uint64_t OpId, trace::OperationKind OpKind,
|
|
const trace::SwiftInvocation &Inv, const trace::StringPairs &OpArgs) {
|
|
if (desiredOperations().contains(OpKind))
|
|
getGlobalContext()
|
|
.getNotificationCenter()
|
|
->postCompileWillStartNotification(OpId, OpKind, Inv);
|
|
}
|
|
|
|
void CompileTrackingConsumer::operationFinished(
|
|
uint64_t OpId, trace::OperationKind OpKind,
|
|
ArrayRef<DiagnosticEntryInfo> Diagnostics) {
|
|
if (desiredOperations().contains(OpKind))
|
|
getGlobalContext()
|
|
.getNotificationCenter()
|
|
->postCompileDidFinishNotification(OpId, OpKind, Diagnostics);
|
|
}
|
|
|
|
static void enableCompileNotifications(bool value) {
|
|
static std::atomic<bool> status{false};
|
|
if (status.exchange(value) == value) {
|
|
return; // Unchanged.
|
|
}
|
|
|
|
static CompileTrackingConsumer compileConsumer;
|
|
if (value) {
|
|
trace::registerConsumer(&compileConsumer);
|
|
} else {
|
|
trace::unregisterConsumer(&compileConsumer);
|
|
}
|
|
}
|