| //===--- Requests.cpp -----------------------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "DictionaryKeys.h" |
| #include "sourcekitd/CodeCompletionResultsArray.h" |
| #include "sourcekitd/DocStructureArray.h" |
| #include "sourcekitd/DocSupportAnnotationArray.h" |
| #include "sourcekitd/TokenAnnotationsArray.h" |
| #include "sourcekitd/ExpressionTypeArray.h" |
| |
| #include "SourceKit/Core/Context.h" |
| #include "SourceKit/Core/LangSupport.h" |
| #include "SourceKit/Core/NotificationCenter.h" |
| #include "SourceKit/Support/Concurrency.h" |
| #include "SourceKit/Support/Logging.h" |
| #include "SourceKit/Support/Statistic.h" |
| #include "SourceKit/Support/Tracing.h" |
| #include "SourceKit/Support/UIdent.h" |
| #include "SourceKit/SwiftLang/Factory.h" |
| |
| #include "swift/Basic/ExponentialGrowthAppendingBinaryByteStream.h" |
| #include "swift/Basic/Mangler.h" |
| #include "swift/Basic/Version.h" |
| #include "swift/Demangling/Demangler.h" |
| #include "swift/Demangling/ManglingMacros.h" |
| #include "swift/Syntax/Serialization/SyntaxSerialization.h" |
| #include "swift/Syntax/SyntaxNodes.h" |
| |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/Support/BinaryByteStream.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/NativeFormatting.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; |
| SyntaxTreeTransferMode SyntaxTransferMode = SyntaxTreeTransferMode::Off; |
| SyntaxTreeSerializationFormat SyntaxSerializationFormat = |
| SyntaxTreeSerializationFormat::JSON; |
| bool SyntacticOnly = false; |
| }; |
| |
| } // anonymous namespace |
| |
| static Optional<UIdent> getUIDForOperationKind(trace::OperationKind OpKind); |
| static void fillDictionaryForDiagnosticInfo(ResponseBuilder::Dictionary Elem, |
| const DiagnosticEntryInfo &Info); |
| |
| #define REQUEST(NAME, CONTENT) static LazySKDUID Request##NAME(CONTENT); |
| #define KIND(NAME, CONTENT) static LazySKDUID Kind##NAME(CONTENT); |
| #include "SourceKit/Core/ProtocolUIDs.def" |
| |
| #define REFACTORING(KIND, NAME, ID) static LazySKDUID Kind##Refactoring##KIND("source.refactoring.kind."#ID); |
| #include "swift/IDE/RefactoringKinds.def" |
| |
| static SourceKit::Context *GlobalCtx = nullptr; |
| |
| void sourcekitd::initializeService( |
| StringRef runtimeLibPath, StringRef diagnosticDocumentationPath, |
| std::function<void(sourcekitd_response_t)> postNotification) { |
| llvm::EnablePrettyStackTrace(); |
| GlobalCtx = |
| new SourceKit::Context(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.getValue()); |
| 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.getValue()); |
| auto DiagArray = Dict.setArray(KeyDiagnostics); |
| for (const auto &DiagInfo : Diagnostics) |
| fillDictionaryForDiagnosticInfo(DiagArray.appendDictionary(), DiagInfo); |
| postNotification(RespBuilder.createResponse()); |
| }); |
| } |
| |
| void sourcekitd::shutdownService() { |
| delete GlobalCtx; |
| GlobalCtx = nullptr; |
| } |
| |
| static SourceKit::Context &getGlobalContext() { |
| assert(GlobalCtx); |
| return *GlobalCtx; |
| } |
| |
| static sourcekitd_response_t demangleNames(ArrayRef<const char *> MangledNames, |
| bool Simplified); |
| |
| static sourcekitd_response_t |
| mangleSimpleClassNames(ArrayRef<std::pair<StringRef, StringRef>> ModuleClassPairs); |
| |
| static sourcekitd_response_t indexSource(StringRef Filename, |
| ArrayRef<const char *> Args); |
| |
| 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 reportExpressionTypeInfo(const RequestResult<ExpressionTypesInFile> &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 Filename, |
| int64_t Offset, |
| bool CancelOnSubsequentRequest, |
| ArrayRef<const char *> Args, |
| ResponseReceiver Rec); |
| |
| static sourcekitd_response_t |
| codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, |
| Optional<RequestDict> optionsDict, |
| ArrayRef<const char *> Args, Optional<VFSOptions> vfsOptions); |
| |
| static sourcekitd_response_t codeCompleteOpen(StringRef name, |
| llvm::MemoryBuffer *InputBuf, |
| int64_t Offset, |
| Optional<RequestDict> optionsDict, |
| ArrayRef<const char *> Args, |
| Optional<VFSOptions> vfsOptions); |
| |
| static sourcekitd_response_t |
| codeCompleteUpdate(StringRef name, int64_t Offset, |
| Optional<RequestDict> optionsDict); |
| |
| static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset); |
| |
| static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf, |
| int64_t Offset, |
| Optional<RequestDict> optionsDict, |
| ArrayRef<const char *> Args, |
| Optional<VFSOptions> vfsOptions); |
| |
| 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); |
| |
| 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, |
| 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(RequestDict &Req, bool UseNewName, |
| std::vector<RenameLocations> &RenameLocations, |
| llvm::SmallString<64> &Error); |
| |
| static sourcekitd_response_t |
| createCategorizedEditsResponse( |
| const RequestResult<ArrayRef<CategorizedEdits>> &Result); |
| |
| static sourcekitd_response_t |
| syntacticRename(llvm::MemoryBuffer *InputBuf, |
| ArrayRef<RenameLocations> RenameLocations, |
| ArrayRef<const char*> Args); |
| |
| static sourcekitd_response_t |
| createCategorizedRenameRangesResponse( |
| const RequestResult<ArrayRef<CategorizedRenameRanges>> &Result); |
| |
| static sourcekitd_response_t |
| findRenameRanges(llvm::MemoryBuffer *InputBuf, |
| ArrayRef<RenameLocations> RenameLocations, |
| ArrayRef<const char *> Args); |
| |
| static bool isSemanticEditorDisabled(); |
| static void enableCompileNotifications(bool value); |
| |
| static SyntaxTreeTransferMode syntaxTransferModeFromUID(sourcekitd_uid_t UID) { |
| if (UID == nullptr) { |
| // Default is no syntax tree |
| return SyntaxTreeTransferMode::Off; |
| } else if (UID == KindSyntaxTreeOff) { |
| return SyntaxTreeTransferMode::Off; |
| } else if (UID == KindSyntaxTreeIncremental) { |
| return SyntaxTreeTransferMode::Incremental; |
| } else if (UID == KindSyntaxTreeFull) { |
| return SyntaxTreeTransferMode::Full; |
| } else { |
| llvm_unreachable("Unexpected syntax tree transfer mode"); |
| } |
| } |
| |
| static llvm::Optional<SyntaxTreeSerializationFormat> |
| syntaxSerializationFormatFromUID(sourcekitd_uid_t UID) { |
| if (UID == nullptr) { |
| // Default is JSON |
| return SyntaxTreeSerializationFormat::JSON; |
| } else if (UID == KindSyntaxTreeSerializationJSON) { |
| return SyntaxTreeSerializationFormat::JSON; |
| } else if (UID == KindSyntaxTreeSerializationByteTree) { |
| return SyntaxTreeSerializationFormat::ByteTree; |
| } else { |
| return llvm::None; |
| } |
| } |
| |
| 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, |
| ResponseReceiver Receiver); |
| |
| void sourcekitd::handleRequest(sourcekitd_object_t Req, |
| ResponseReceiver Receiver) { |
| LOG_SECTION("handleRequest-before", InfoHighPrio) { |
| sourcekitd::printRequestObject(Req, Log->getOS()); |
| } |
| |
| handleRequestImpl(Req, [Receiver](sourcekitd_response_t Resp) { |
| LOG_SECTION("handleRequest-after", InfoHighPrio) { |
| // Responses are big, print them out with info medium priority. |
| if (Logger::isLoggingEnabledForLevel(Logger::Level::InfoMediumPrio)) |
| sourcekitd::printResponse(Resp, Log->getOS()); |
| } |
| |
| Receiver(Resp); |
| }); |
| } |
| |
| static std::unique_ptr<llvm::MemoryBuffer> getInputBufForRequest( |
| Optional<StringRef> SourceFile, Optional<StringRef> SourceText, |
| const Optional<VFSOptions> &vfsOptions, llvm::SmallString<64> &ErrBuf) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf; |
| |
| if (SourceText.hasValue()) { |
| StringRef BufName; |
| if (SourceFile.hasValue()) |
| BufName = *SourceFile; |
| else |
| BufName = "<input>"; |
| InputBuf = llvm::MemoryBuffer::getMemBuffer(*SourceText, BufName); |
| |
| } else if (vfsOptions.hasValue() && SourceFile.hasValue()) { |
| ErrBuf = "using 'key.sourcefile' to read source text from the filesystem " |
| "is not supported when using 'key.vfs.name'"; |
| return nullptr; |
| |
| } else if (SourceFile.hasValue()) { |
| llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr = |
| llvm::MemoryBuffer::getFile(*SourceFile); |
| if (FileBufOrErr) { |
| InputBuf = std::move(FileBufOrErr.get()); |
| } else { |
| llvm::raw_svector_ostream OSErr(ErrBuf); |
| OSErr << "error opening input file '" << *SourceFile << "' (" |
| << FileBufOrErr.getError().message() << ')'; |
| return nullptr; |
| } |
| } else { |
| InputBuf = llvm::WritableMemoryBuffer::getNewMemBuffer(0, "<input>"); |
| } |
| |
| return InputBuf; |
| } |
| |
| /// 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(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 void handleSemanticRequest( |
| RequestDict Req, ResponseReceiver Receiver, sourcekitd_uid_t ReqUID, |
| Optional<StringRef> SourceFile, Optional<StringRef> SourceText, |
| ArrayRef<const char *> Args, Optional<VFSOptions> vfsOptions); |
| |
| void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) { |
| // 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"); |
| ++numRequests; |
| |
| RequestDict Req(ReqObj); |
| sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest); |
| if (!ReqUID) |
| return Rec(createErrorRequestInvalid("missing 'key.request' with UID value")); |
| |
| if (ReqUID == RequestGlobalConfiguration) { |
| auto Config = getGlobalContext().getGlobalConfiguration(); |
| ResponseBuilder RB; |
| auto dict = RB.getDictionary(); |
| |
| Optional<bool> OptimizeForIDE = |
| Req.getOptionalInt64(KeyOptimizeForIDE) |
| .map([](int64_t v) -> bool { return v; }); |
| Optional<unsigned> CompletionMaxASTContextReuseCount = |
| Req.getOptionalInt64(KeyCompletionMaxASTContextReuseCount) |
| .map([](int64_t v) -> unsigned { return v; }); |
| Optional<unsigned> CompletionCheckDependencyInterval = |
| Req.getOptionalInt64(KeyCompletionCheckDependencyInterval) |
| .map([](int64_t v) -> unsigned { return v; }); |
| |
| GlobalConfig::Settings UpdatedConfig = |
| Config->update(OptimizeForIDE, CompletionMaxASTContextReuseCount, |
| CompletionCheckDependencyInterval); |
| |
| getGlobalContext().getSwiftLangSupport().globalConfigurationUpdated(Config); |
| |
| dict.set(KeyOptimizeForIDE, UpdatedConfig.OptimizeForIDE); |
| dict.set(KeyCompletionMaxASTContextReuseCount, |
| UpdatedConfig.CompletionOpts.MaxASTContextReuseCount); |
| dict.set(KeyCompletionCheckDependencyInterval, |
| UpdatedConfig.CompletionOpts.CheckDependencyInterval); |
| |
| return Rec(RB.createResponse()); |
| } |
| if (ReqUID == RequestProtocolVersion) { |
| ResponseBuilder RB; |
| auto dict = RB.getDictionary(); |
| dict.set(KeyVersionMajor, ProtocolMajorVersion); |
| dict.set(KeyVersionMinor, static_cast<int64_t>(ProtocolMinorVersion)); |
| return Rec(RB.createResponse()); |
| } |
| |
| if (ReqUID == RequestCompilerVersion) { |
| 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()); |
| } |
| |
| if (ReqUID == RequestCrashWithExit) { |
| // 'exit' has the same effect as crashing but without the crash log. |
| ::exit(1); |
| } |
| |
| if (ReqUID == RequestTestNotification) { |
| getGlobalContext().getNotificationCenter()->postTestNotification(); |
| return Rec(ResponseBuilder().createResponse()); |
| } |
| |
| if (ReqUID == RequestDemangle) { |
| SmallVector<const char *, 8> MangledNames; |
| bool Failed = Req.getStringArray(KeyNames, MangledNames, /*isOptional=*/true); |
| if (Failed) { |
| return Rec(createErrorRequestInvalid( |
| "'key.names' not an array of strings")); |
| } |
| int64_t Simplified = false; |
| Req.getInt64(KeySimplified, Simplified, /*isOptional=*/true); |
| return Rec(demangleNames(MangledNames, Simplified)); |
| } |
| |
| if (ReqUID == RequestMangleSimpleClass) { |
| SmallVector<std::pair<StringRef, StringRef>, 16> ModuleClassPairs; |
| sourcekitd_response_t err = nullptr; |
| bool failed = Req.dictionaryArrayApply(KeyNames, [&](RequestDict dict) { |
| Optional<StringRef> ModuleName = dict.getString(KeyModuleName); |
| if (!ModuleName.hasValue()) { |
| err = createErrorRequestInvalid("missing 'key.modulename'"); |
| return true; |
| } |
| Optional<StringRef> ClassName = dict.getString(KeyName); |
| if (!ClassName.hasValue()) { |
| err = createErrorRequestInvalid("missing 'key.name'"); |
| return true; |
| } |
| ModuleClassPairs.push_back(std::make_pair(*ModuleName, *ClassName)); |
| return false; |
| }); |
| |
| if (failed) { |
| if (!err) |
| err = createErrorRequestInvalid("missing 'key.names'"); |
| return Rec(err); |
| } |
| |
| return Rec(mangleSimpleClassNames(ModuleClassPairs)); |
| } |
| |
| if (ReqUID == RequestEnableCompileNotifications) { |
| int64_t value = true; |
| if (Req.getInt64(KeyValue, value, /*isOptional=*/false)) { |
| return Rec(createErrorRequestInvalid("missing 'key.value'")); |
| } |
| enableCompileNotifications(value); |
| return Rec(ResponseBuilder().createResponse()); |
| } |
| |
| // Just accept 'source.request.buildsettings.register' for now, don't do |
| // anything else. |
| // FIXME: Heavy WIP here. |
| if (ReqUID == RequestBuildSettingsRegister) { |
| return Rec(ResponseBuilder().createResponse()); |
| } |
| |
| Optional<StringRef> SourceFile = Req.getString(KeySourceFile); |
| Optional<StringRef> SourceText = Req.getString(KeySourceText); |
| |
| llvm::SmallString<64> ErrBuf; |
| |
| Optional<VFSOptions> vfsOptions; |
| |
| if (Optional<StringRef> VFSName = Req.getString(KeyVFSName)) { |
| if (ReqUID != RequestEditorOpen && ReqUID != RequestCodeComplete && |
| ReqUID != RequestCodeCompleteOpen && ReqUID != RequestCursorInfo) { |
| return Rec(createErrorRequestInvalid( |
| "This request does not support custom filesystems")); |
| } |
| |
| vfsOptions = getVFSOptions(Req); |
| } |
| |
| SmallVector<const char *, 8> Args; |
| bool Failed = Req.getStringArray(KeyCompilerArgs, Args, /*isOptional=*/true); |
| if (Failed) { |
| return Rec(createErrorRequestInvalid( |
| "'key.compilerargs' not an array of strings")); |
| } |
| |
| if (ReqUID == RequestDocInfo) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = getInputBufForRequest( |
| SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| StringRef ModuleName; |
| Optional<StringRef> ModuleNameOpt = Req.getString(KeyModuleName); |
| if (ModuleNameOpt.hasValue()) ModuleName = *ModuleNameOpt; |
| return Rec(reportDocInfo(InputBuf.get(), ModuleName, Args)); |
| } |
| |
| if (ReqUID == RequestEditorOpen) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = |
| getInputBufForRequest(SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| int64_t EnableSyntaxMap = true; |
| Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true); |
| int64_t EnableStructure = true; |
| Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true); |
| int64_t EnableDiagnostics = true; |
| Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true); |
| auto TransferModeUID = Req.getUID(KeySyntaxTreeTransferMode); |
| auto SerializationFormatUID = Req.getUID(KeySyntaxTreeSerializationFormat); |
| int64_t SyntacticOnly = false; |
| Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true); |
| |
| SKEditorConsumerOptions Opts; |
| Opts.EnableSyntaxMap = EnableSyntaxMap; |
| Opts.EnableStructure = EnableStructure; |
| Opts.EnableDiagnostics = EnableDiagnostics; |
| Opts.SyntaxTransferMode = syntaxTransferModeFromUID(TransferModeUID); |
| auto SyntaxSerializationFormat = |
| syntaxSerializationFormatFromUID(SerializationFormatUID); |
| if (!SyntaxSerializationFormat) |
| return Rec(createErrorRequestFailed("Invalid serialization format")); |
| Opts.SyntaxSerializationFormat = SyntaxSerializationFormat.getValue(); |
| Opts.SyntacticOnly = SyntacticOnly; |
| return Rec(editorOpen(*Name, InputBuf.get(), Opts, Args, std::move(vfsOptions))); |
| } |
| if (ReqUID == RequestEditorClose) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| |
| // Whether we remove the cached AST from libcache, by default, false. |
| int64_t RemoveCache = false; |
| Req.getInt64(KeyRemoveCache, RemoveCache, /*isOptional=*/true); |
| return Rec(editorClose(*Name, RemoveCache)); |
| } |
| if (ReqUID == RequestEditorReplaceText) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = getInputBufForRequest( |
| SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| int64_t Offset = 0; |
| Req.getInt64(KeyOffset, Offset, /*isOptional=*/true); |
| int64_t Length = 0; |
| Req.getInt64(KeyLength, Length, /*isOptional=*/true); |
| int64_t EnableSyntaxMap = true; |
| Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true); |
| int64_t EnableStructure = true; |
| Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true); |
| int64_t EnableDiagnostics = true; |
| Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true); |
| int64_t SyntacticOnly = false; |
| Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true); |
| auto TransferModeUID = Req.getUID(KeySyntaxTreeTransferMode); |
| auto SerializationFormatUID = Req.getUID(KeySyntaxTreeSerializationFormat); |
| |
| SKEditorConsumerOptions Opts; |
| Opts.EnableSyntaxMap = EnableSyntaxMap; |
| Opts.EnableStructure = EnableStructure; |
| Opts.EnableDiagnostics = EnableDiagnostics; |
| Opts.SyntaxTransferMode = syntaxTransferModeFromUID(TransferModeUID); |
| auto SyntaxSerializationFormat = |
| syntaxSerializationFormatFromUID(SerializationFormatUID); |
| if (!SyntaxSerializationFormat) |
| return Rec(createErrorRequestFailed("Invalid serialization format")); |
| Opts.SyntaxSerializationFormat = SyntaxSerializationFormat.getValue(); |
| Opts.SyntacticOnly = SyntacticOnly; |
| |
| return Rec(editorReplaceText(*Name, InputBuf.get(), Offset, Length, Opts)); |
| } |
| if (ReqUID == RequestEditorFormatText) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| Optional<RequestDict> FmtOptions = Req.getDictionary(KeyFormatOptions); |
| if (FmtOptions.hasValue()) |
| editorApplyFormatOptions(*Name, *FmtOptions); |
| int64_t Line = 0; |
| Req.getInt64(KeyLine, Line, /*isOptional=*/false); |
| int64_t Length = 0; |
| Req.getInt64(KeyLength, Length, /*isOptional=*/true); |
| return Rec(editorFormatText(*Name, Line, Length)); |
| } |
| if (ReqUID == RequestEditorExpandPlaceholder) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| int64_t Offset = 0; |
| Req.getInt64(KeyOffset, Offset, /*isOptional=*/false); |
| int64_t Length = 0; |
| Req.getInt64(KeyLength, Length, /*isOptional=*/false); |
| return Rec(editorExpandPlaceholder(*Name, Offset, Length)); |
| } |
| |
| if (ReqUID == RequestEditorOpenInterface) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| Optional<StringRef> ModuleName = Req.getString(KeyModuleName); |
| if (!ModuleName.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.modulename'")); |
| Optional<StringRef> GroupName = Req.getString(KeyGroupName); |
| int64_t SynthesizedExtension = false; |
| Req.getInt64(KeySynthesizedExtension, SynthesizedExtension, |
| /*isOptional=*/true); |
| Optional<StringRef> InterestedUSR = Req.getString(KeyInterestedUSR); |
| return Rec(editorOpenInterface(*Name, *ModuleName, GroupName, Args, |
| SynthesizedExtension, InterestedUSR)); |
| } |
| |
| if (ReqUID == RequestEditorOpenHeaderInterface) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| Optional<StringRef> HeaderName = Req.getString(KeyFilePath); |
| if (!HeaderName.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.filepath'")); |
| int64_t SynthesizedExtension = false; |
| Req.getInt64(KeySynthesizedExtension, SynthesizedExtension, |
| /*isOptional=*/true); |
| Optional<int64_t> UsingSwiftArgs = Req.getOptionalInt64(KeyUsingSwiftArgs); |
| std::string swiftVer; |
| Optional<StringRef> swiftVerValStr = Req.getString(KeySwiftVersion); |
| if (swiftVerValStr.hasValue()) { |
| swiftVer = swiftVerValStr.getValue().str(); |
| } else { |
| Optional<int64_t> swiftVerVal = Req.getOptionalInt64(KeySwiftVersion); |
| if (swiftVerVal.hasValue()) |
| swiftVer = std::to_string(*swiftVerVal); |
| } |
| return Rec(editorOpenHeaderInterface(*Name, *HeaderName, Args, |
| UsingSwiftArgs.getValueOr(false), |
| SynthesizedExtension, swiftVer)); |
| } |
| |
| if (ReqUID == RequestEditorOpenSwiftSourceInterface) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| Optional<StringRef> FileName = Req.getString(KeySourceFile); |
| if (!FileName.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.sourcefile'")); |
| return editorOpenSwiftSourceInterface(*Name, *FileName, Args, Rec); |
| } |
| |
| if (ReqUID == RequestEditorOpenSwiftTypeInterface) { |
| Optional<StringRef> Usr = Req.getString(KeyUSR); |
| if (!Usr.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.usr'")); |
| return editorOpenSwiftTypeInterface(*Usr, Args, Rec); |
| } |
| |
| if (ReqUID == RequestEditorExtractTextFromComment) { |
| Optional<StringRef> Source = Req.getString(KeySourceText); |
| if (!Source.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.sourcetext'")); |
| return Rec(editorExtractTextFromComment(Source.getValue())); |
| } |
| |
| if (ReqUID == RequestMarkupToXML) { |
| Optional<StringRef> Source = Req.getString(KeySourceText); |
| if (!Source.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.sourcetext'")); |
| return Rec(editorConvertMarkupToXML(Source.getValue())); |
| } |
| |
| if (ReqUID == RequestEditorFindUSR) { |
| Optional<StringRef> Name = Req.getString(KeySourceFile); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.sourcefile'")); |
| Optional<StringRef> USR = Req.getString(KeyUSR); |
| if (!USR.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.usr'")); |
| return Rec(editorFindUSR(*Name, *USR)); |
| } |
| |
| if (ReqUID == RequestEditorFindInterfaceDoc) { |
| Optional<StringRef> ModuleName = Req.getString(KeyModuleName); |
| if (!ModuleName.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.modulename'")); |
| return Rec(editorFindInterfaceDoc(*ModuleName, Args)); |
| } |
| |
| if (ReqUID == RequestModuleGroups) { |
| Optional<StringRef> ModuleName = Req.getString(KeyModuleName); |
| if (!ModuleName.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.modulename'")); |
| return Rec(editorFindModuleGroups(*ModuleName, Args)); |
| } |
| |
| if (ReqUID == RequestSyntacticRename) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = getInputBufForRequest( |
| SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| |
| std::vector<RenameLocations> RenameLocations; |
| if (buildRenameLocationsFromDict(Req, true, RenameLocations, ErrBuf)) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| return Rec(syntacticRename(InputBuf.get(), RenameLocations, Args)); |
| } |
| |
| if (ReqUID == RequestFindRenameRanges) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = getInputBufForRequest( |
| SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| |
| std::vector<RenameLocations> RenameLocations; |
| if (buildRenameLocationsFromDict(Req, false, RenameLocations, ErrBuf)) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| return Rec(findRenameRanges(InputBuf.get(), RenameLocations, Args)); |
| } |
| |
| if (ReqUID == RequestCodeCompleteClose) { |
| // Unlike opening code completion, this is not a semantic request. |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("missing 'key.offset'")); |
| return Rec(codeCompleteClose(*Name, Offset)); |
| } |
| |
| if (ReqUID == RequestCodeCompleteCacheOnDisk) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.codeCompleteCacheOnDisk(*Name); |
| ResponseBuilder b; |
| return Rec(b.createResponse()); |
| } |
| |
| if (ReqUID == RequestCodeCompleteSetPopularAPI) { |
| llvm::SmallVector<const char *, 0> popular; |
| llvm::SmallVector<const char *, 0> unpopular; |
| Req.getStringArray(KeyPopular, popular, /*isOptional=*/false); |
| Req.getStringArray(KeyUnpopular, unpopular, /*isOptional=*/false); |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.codeCompleteSetPopularAPI(popular, unpopular); |
| ResponseBuilder b; |
| return Rec(b.createResponse()); |
| } |
| |
| if (ReqUID == RequestCodeCompleteSetCustom) { |
| SmallVector<CustomCompletionInfo, 16> customCompletions; |
| sourcekitd_response_t err = nullptr; |
| bool failed = Req.dictionaryArrayApply(KeyResults, [&](RequestDict dict) { |
| CustomCompletionInfo CCInfo; |
| Optional<StringRef> Name = dict.getString(KeyName); |
| if (!Name.hasValue()) { |
| err = createErrorRequestInvalid("missing 'key.name'"); |
| return true; |
| } |
| CCInfo.Name = (*Name).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()); |
| } |
| |
| if (ReqUID == RequestStatistics) { |
| 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); |
| }; |
| |
| addStat(&numRequests); |
| addStat(&numSemaRequests); |
| std::for_each(stats.begin(), stats.end(), addStat); |
| |
| Rec(builder.createResponse()); |
| }); |
| return; |
| } |
| |
| if (!SourceFile.hasValue() && !SourceText.hasValue() && |
| ReqUID != RequestCodeCompleteUpdate) |
| return Rec(createErrorRequestInvalid( |
| "missing 'key.sourcefile' or 'key.sourcetext'")); |
| |
| // Requests that need semantic typechecking. |
| |
| // Typechecking arrays can blow up the stack currently. |
| // Run them under a malloc'ed stack. |
| |
| static WorkQueue SemaQueue{ WorkQueue::Dequeuing::Concurrent, |
| "sourcekit.request.semantic" }; |
| sourcekitd_request_retain(ReqObj); |
| ++numSemaRequests; |
| SemaQueue.dispatch( |
| [ReqObj, Rec, ReqUID, SourceFile, SourceText, Args] { |
| RequestDict Req(ReqObj); |
| auto vfsOptions = getVFSOptions(Req); |
| handleSemanticRequest(Req, Rec, ReqUID, SourceFile, SourceText, Args, |
| std::move(vfsOptions)); |
| sourcekitd_request_release(ReqObj); |
| }, |
| /*isStackDeep=*/true); |
| } |
| |
| static void handleSemanticRequest( |
| RequestDict Req, ResponseReceiver Rec, sourcekitd_uid_t ReqUID, |
| Optional<StringRef> SourceFile, Optional<StringRef> SourceText, |
| ArrayRef<const char *> Args, Optional<VFSOptions> vfsOptions) { |
| llvm::SmallString<64> ErrBuf; |
| |
| if (isSemanticEditorDisabled()) |
| return Rec(createErrorRequestFailed("semantic editor is disabled")); |
| |
| if (ReqUID == RequestCodeComplete) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = |
| getInputBufForRequest(SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("missing 'key.offset'")); |
| Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions); |
| return Rec(codeComplete(InputBuf.get(), Offset, options, Args, |
| std::move(vfsOptions))); |
| } |
| |
| if (ReqUID == RequestCodeCompleteOpen) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = getInputBufForRequest( |
| SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("missing 'key.offset'")); |
| Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions); |
| return Rec(codeCompleteOpen(*Name, InputBuf.get(), Offset, options, Args, |
| std::move(vfsOptions))); |
| } |
| |
| if (ReqUID == RequestCodeCompleteUpdate) { |
| Optional<StringRef> Name = Req.getString(KeyName); |
| if (!Name.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.name'")); |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("missing 'key.offset'")); |
| Optional<RequestDict> options = Req.getDictionary(KeyCodeCompleteOptions); |
| return Rec(codeCompleteUpdate(*Name, Offset, options)); |
| } |
| |
| if (ReqUID == RequestTypeContextInfo) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = getInputBufForRequest( |
| SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("missing 'key.offset'")); |
| Optional<RequestDict> options = |
| Req.getDictionary(KeyTypeContextInfoOptions); |
| return Rec(typeContextInfo(InputBuf.get(), Offset, options, Args, |
| std::move(vfsOptions))); |
| } |
| |
| if (ReqUID == RequestConformingMethodList) { |
| std::unique_ptr<llvm::MemoryBuffer> InputBuf = getInputBufForRequest( |
| SourceFile, SourceText, vfsOptions, ErrBuf); |
| if (!InputBuf) |
| return Rec(createErrorRequestFailed(ErrBuf.c_str())); |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("missing 'key.offset'")); |
| 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))); |
| } |
| |
| if (!SourceFile.hasValue()) |
| return Rec(createErrorRequestInvalid("missing 'key.sourcefile'")); |
| |
| if (ReqUID == RequestIndex) { |
| return Rec(indexSource(*SourceFile, Args)); |
| } |
| |
| if (ReqUID == RequestCursorInfo) { |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| |
| // For backwards compatibility, the default is 1. |
| int64_t CancelOnSubsequentRequest = 1; |
| Req.getInt64(KeyCancelOnSubsequentRequest, CancelOnSubsequentRequest, |
| /*isOptional=*/true); |
| |
| int64_t Offset; |
| if (!Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) { |
| int64_t Length = 0; |
| Req.getInt64(KeyLength, Length, /*isOptional=*/true); |
| int64_t Actionables = false; |
| Req.getInt64(KeyRetrieveRefactorActions, Actionables, /*isOptional=*/true); |
| int64_t SymbolGraph = false; |
| Req.getInt64(KeyRetrieveSymbolGraph, SymbolGraph, /*isOptional=*/true); |
| return Lang.getCursorInfo( |
| *SourceFile, Offset, Length, Actionables, SymbolGraph, |
| CancelOnSubsequentRequest, Args, std::move(vfsOptions), |
| [Rec](const RequestResult<CursorInfoData> &Result) { |
| reportCursorInfo(Result, Rec); |
| }); |
| } |
| if (auto USR = Req.getString(KeyUSR)) { |
| return Lang.getCursorInfoFromUSR( |
| *SourceFile, *USR, CancelOnSubsequentRequest, Args, std::move(vfsOptions), |
| [Rec](const RequestResult<CursorInfoData> &Result) { |
| reportCursorInfo(Result, Rec); |
| }); |
| } |
| |
| return Rec(createErrorRequestInvalid( |
| "either 'key.offset' or 'key.usr' is required")); |
| } |
| |
| if (ReqUID == RequestRangeInfo) { |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| int64_t Offset; |
| int64_t Length; |
| // For backwards compatibility, the default is 1. |
| int64_t CancelOnSubsequentRequest = 1; |
| Req.getInt64(KeyCancelOnSubsequentRequest, CancelOnSubsequentRequest, |
| /*isOptional=*/true); |
| if (!Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) { |
| if (!Req.getInt64(KeyLength, Length, /*isOptional=*/false)) { |
| return Lang.getRangeInfo(*SourceFile, Offset, Length, |
| CancelOnSubsequentRequest, Args, |
| [Rec](const RequestResult<RangeInfo> &Result) { |
| reportRangeInfo(Result, Rec); |
| }); |
| } |
| } |
| |
| return Rec(createErrorRequestInvalid( |
| "'key.offset' or 'key.length' is required")); |
| } |
| |
| if (ReqUID == RequestSemanticRefactoring) { |
| int64_t Line = 0; |
| int64_t Column = 0; |
| int64_t Length = 0; |
| auto KA = Req.getUID(KeyActionUID); |
| if (!KA) { |
| return Rec(createErrorRequestInvalid("'key.actionuid' is required")); |
| } |
| SemanticRefactoringInfo Info; |
| Info.Kind = SemanticRefactoringKind::None; |
| |
| #define SEMANTIC_REFACTORING(KIND, NAME, ID) \ |
| if (KA == KindRefactoring##KIND) Info.Kind = SemanticRefactoringKind::KIND; |
| #include "swift/IDE/RefactoringKinds.def" |
| |
| if (Info.Kind == SemanticRefactoringKind::None) |
| return Rec(createErrorRequestInvalid("'key.actionuid' isn't recognized")); |
| |
| if (!Req.getInt64(KeyLine, Line, /*isOptional=*/false)) { |
| if (!Req.getInt64(KeyColumn, Column, /*isOptional=*/false)) { |
| Req.getInt64(KeyLength, Length, /*isOptional=*/true); |
| if (auto N = Req.getString(KeyName)) |
| Info.PreferredName = *N; |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Info.Line = Line; |
| Info.Column = Column; |
| Info.Length = Length; |
| return Lang.semanticRefactoring(*SourceFile, Info, Args, |
| [Rec](const RequestResult<ArrayRef<CategorizedEdits>> &Result) { |
| Rec(createCategorizedEditsResponse(Result)); |
| }); |
| } |
| } |
| return Rec(createErrorRequestInvalid("'key.line' or 'key.column' are required")); |
| } |
| |
| if (ReqUID == RequestCollectExpressionType) { |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| |
| SmallVector<const char *, 8> ExpectedProtocols; |
| if (Req.getStringArray(KeyExpectedTypes, ExpectedProtocols, true)) |
| return Rec(createErrorRequestInvalid("invalid 'key.expectedtypes'")); |
| int64_t CanonicalTy = false; |
| Req.getInt64(KeyCanonicalizeType, CanonicalTy, /*isOptional=*/true); |
| return Lang.collectExpressionTypes(*SourceFile, Args, ExpectedProtocols, |
| CanonicalTy, |
| [Rec](const RequestResult<ExpressionTypesInFile> &Result) { |
| reportExpressionTypeInfo(Result, Rec); |
| }); |
| } |
| |
| if (ReqUID == RequestFindLocalRenameRanges) { |
| int64_t Line = 0, Column = 0, Length = 0; |
| if (Req.getInt64(KeyLine, Line, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("'key.line' is required")); |
| if (Req.getInt64(KeyColumn, Column, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("'key.column' is required")); |
| Req.getInt64(KeyLength, Length, /*isOptional=*/true); |
| |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| return Lang.findLocalRenameRanges( |
| *SourceFile, Line, Column, Length, Args, |
| [Rec](const RequestResult<ArrayRef<CategorizedRenameRanges>> &Result) { |
| Rec(createCategorizedRenameRangesResponse(Result)); |
| }); |
| } |
| |
| if (ReqUID == RequestNameTranslation) { |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) { |
| return Rec(createErrorRequestInvalid("'key.offset' is required")); |
| } |
| NameTranslatingInfo Input; |
| auto NK = Req.getUID(KeyNameKind); |
| if (!NK) { |
| return Rec(createErrorRequestInvalid("'key.namekind' is required")); |
| } |
| |
| static UIdent UIDKindNameSwift(KindNameSwift.str()); |
| static UIdent UIDKindNameObjc(KindNameObjc.str()); |
| |
| if (NK == KindNameSwift.get()) |
| Input.NameKind = UIDKindNameSwift; |
| else if (NK == KindNameObjc.get()) |
| Input.NameKind = UIDKindNameObjc; |
| else |
| return Rec(createErrorRequestInvalid("'key.namekind' is unrecognizable")); |
| if (auto Base = Req.getString(KeyBaseName)) { |
| if (Input.NameKind == UIDKindNameSwift) { |
| Input.BaseName = Base.getValue().trim('`'); |
| } else { |
| Input.BaseName = Base.getValue(); |
| } |
| } |
| llvm::SmallVector<const char*, 4> ArgParts; |
| llvm::SmallVector<const char*, 4> Selectors; |
| Req.getStringArray(KeyArgNames, ArgParts, true); |
| Req.getStringArray(KeySelectorPieces, Selectors, true); |
| if (!ArgParts.empty() && !Selectors.empty()) { |
| return Rec(createErrorRequestInvalid("cannot specify 'key.selectorpieces' " |
| "and 'key.argnames' at the same time")); |
| } |
| std::transform(ArgParts.begin(), ArgParts.end(), |
| std::back_inserter(Input.ArgNames), |
| [](const char *C) { return StringRef(C).trim('`'); }); |
| std::transform(Selectors.begin(), Selectors.end(), |
| std::back_inserter(Input.ArgNames), |
| [](const char *C) { return StringRef(C); }); |
| return Lang.getNameInfo(*SourceFile, Offset, Input, Args, |
| [Rec](const RequestResult<NameTranslatingInfo> &Result) { |
| reportNameInfo(Result, Rec); |
| }); |
| } |
| |
| if (ReqUID == RequestRelatedIdents) { |
| int64_t Offset; |
| if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) |
| return Rec(createErrorRequestInvalid("missing 'key.offset'")); |
| |
| // For backwards compatibility, the default is 1. |
| int64_t CancelOnSubsequentRequest = 1; |
| Req.getInt64(KeyCancelOnSubsequentRequest, CancelOnSubsequentRequest, |
| /*isOptional=*/true); |
| |
| return findRelatedIdents(*SourceFile, Offset, CancelOnSubsequentRequest, |
| Args, Rec); |
| } |
| |
| { |
| llvm::raw_svector_ostream OSErr(ErrBuf); |
| OSErr << "unknown request: " << UIdentFromSKDUID(ReqUID).getName(); |
| } |
| return Rec(createErrorRequestInvalid(ErrBuf.c_str())); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Index |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| class SKIndexingConsumer : public IndexingConsumer { |
| struct Entity { |
| UIdent Kind; |
| ResponseBuilder::Dictionary Data; |
| ResponseBuilder::Array Entities; |
| ResponseBuilder::Array Related; |
| }; |
| SmallVector<Entity, 6> EntitiesStack; |
| |
| struct Dependency { |
| UIdent Kind; |
| ResponseBuilder::Dictionary Data; |
| ResponseBuilder::Array Dependencies; |
| }; |
| SmallVector<Dependency, 6> DependenciesStack; |
| |
| ResponseBuilder::Dictionary TopDict; |
| bool Cancelled = false; |
| |
| public: |
| std::string ErrorDescription; |
| |
| explicit SKIndexingConsumer(ResponseBuilder &RespBuilder) { |
| TopDict = RespBuilder.getDictionary(); |
| |
| // First in stack is the top-level "key.entities" container. |
| EntitiesStack.push_back( |
| { UIdent(), |
| TopDict, |
| ResponseBuilder::Array(), |
| ResponseBuilder::Array() }); |
| |
| DependenciesStack.push_back({UIdent(), TopDict, ResponseBuilder::Array() }); |
| } |
| ~SKIndexingConsumer() override { |
| assert(Cancelled || |
| (EntitiesStack.size() == 1 && DependenciesStack.size() == 1)); |
| (void) Cancelled; |
| } |
| |
| void failed(StringRef ErrDescription) override; |
| |
| bool 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.getValue()); |
| |
| 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 handleDiagnostic(const DiagnosticEntryInfo &Info) override; |
| }; |
| } // end anonymous namespace |
| |
| static sourcekitd_response_t demangleNames(ArrayRef<const char *> MangledNames, |
| bool Simplified) { |
| swift::Demangle::DemangleOptions DemangleOptions; |
| if (Simplified) { |
| DemangleOptions = |
| swift::Demangle::DemangleOptions::SimplifiedUIDemangleOptions(); |
| } |
| |
| auto getDemangledName = [&](StringRef MangledName) -> std::string { |
| if (!swift::Demangle::isSwiftSymbol(MangledName)) |
| return std::string(); // Not a mangled name |
| |
| std::string Result = swift::Demangle::demangleSymbolAsString( |
| MangledName, DemangleOptions); |
| |
| if (Result == MangledName) |
| return std::string(); // Not a mangled name |
| |
| return Result; |
| }; |
| |
| ResponseBuilder RespBuilder; |
| auto Arr = RespBuilder.getDictionary().setArray(KeyResults); |
| for (auto MangledName : MangledNames) { |
| std::string Result = getDemangledName(MangledName); |
| auto Entry = Arr.appendDictionary(); |
| Entry.set(KeyName, Result.c_str()); |
| } |
| |
| return RespBuilder.createResponse(); |
| } |
| |
| static std::string mangleSimpleClass(StringRef moduleName, |
| StringRef className) { |
| using namespace swift::Demangle; |
| Demangler Dem; |
| auto moduleNode = Dem.createNode(Node::Kind::Module, moduleName); |
| auto IdNode = Dem.createNode(Node::Kind::Identifier, className); |
| auto classNode = Dem.createNode(Node::Kind::Class); |
| auto typeNode = Dem.createNode(Node::Kind::Type); |
| auto typeManglingNode = Dem.createNode(Node::Kind::TypeMangling); |
| auto globalNode = Dem.createNode(Node::Kind::Global); |
| |
| classNode->addChild(moduleNode, Dem); |
| classNode->addChild(IdNode, Dem); |
| typeNode->addChild(classNode, Dem); |
| typeManglingNode->addChild(typeNode, Dem); |
| globalNode->addChild(typeManglingNode, Dem); |
| return mangleNode(globalNode); |
| } |
| |
| static sourcekitd_response_t |
| mangleSimpleClassNames(ArrayRef<std::pair<StringRef, StringRef>> ModuleClassPairs) { |
| ResponseBuilder RespBuilder; |
| auto Arr = RespBuilder.getDictionary().setArray(KeyResults); |
| for (auto &pair : ModuleClassPairs) { |
| std::string Result = mangleSimpleClass(pair.first, pair.second); |
| auto Entry = Arr.appendDictionary(); |
| Entry.set(KeyName, Result.c_str()); |
| } |
| |
| return RespBuilder.createResponse(); |
| } |
| |
| static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf, |
| StringRef ModuleName, |
| ArrayRef<const char *> Args) { |
| ResponseBuilder RespBuilder; |
| SKDocConsumer DocConsumer(RespBuilder); |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.getDocInfo(InputBuf, ModuleName, Args, DocConsumer); |
| |
| if (!DocConsumer.ErrorDescription.empty()) |
| return createErrorRequestFailed(DocConsumer.ErrorDescription.c_str()); |
| |
| return DocConsumer.createResponse(); |
| } |
| |
| void SKDocConsumer::addDocEntityInfoToDict(const DocEntityInfo &Info, |
| ResponseBuilder::Dictionary Elem) { |
| Elem.set(KeyKind, Info.Kind); |
| if (!Info.Name.empty()) |
| Elem.set(KeyName, Info.Name); |
| if (!Info.Argument.empty()) |
| Elem.set(KeyKeyword, Info.Argument); |
| if (!Info.SubModuleName.empty()) |
| Elem.set(KeyModuleName, Info.SubModuleName); |
| if (!Info.USR.empty()) |
| Elem.set(KeyUSR, Info.USR); |
| if (!Info.OriginalUSR.empty()) |
| Elem.set(KeyOriginalUSR, Info.OriginalUSR); |
| if (!Info.ProvideImplementationOfUSR.empty()) |
| Elem.set(KeyDefaultImplementationOf, Info.ProvideImplementationOfUSR); |
| if (Info.Length > 0) { |
| Elem.set(KeyOffset, Info.Offset); |
| Elem.set(KeyLength, Info.Length); |
| } |
| if (Info.IsUnavailable) |
| Elem.set(KeyIsUnavailable, Info.IsUnavailable); |
| if (Info.IsDeprecated) |
| Elem.set(KeyIsDeprecated, Info.IsDeprecated); |
| if (Info.IsOptional) |
| Elem.set(KeyIsOptional, Info.IsOptional); |
| if (!Info.DocComment.empty()) |
| Elem.set(KeyDocFullAsXML, Info.DocComment); |
| if (!Info.FullyAnnotatedDecl.empty()) |
| Elem.set(KeyFullyAnnotatedDecl, Info.FullyAnnotatedDecl); |
| if (!Info.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.hasValue()) |
| Elem.set(KeyIntroduced, Info.Introduced.getValue().getAsString()); |
| if (Info.Deprecated.hasValue()) |
| Elem.set(KeyDeprecated, Info.Deprecated.getValue().getAsString()); |
| if (Info.Obsoleted.hasValue()) |
| Elem.set(KeyObsoleted, Info.Obsoleted.getValue().getAsString()); |
| |
| return true; |
| } |
| |
| bool SKDocConsumer::finishSourceEntity(UIdent Kind) { |
| Entity &CurrEnt = EntitiesStack.back(); |
| assert(CurrEnt.Kind == Kind); |
| (void) CurrEnt; |
| EntitiesStack.pop_back(); |
| return true; |
| } |
| |
| bool SKDocConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info) { |
| ResponseBuilder::Array &Arr = Diags; |
| if (Arr.isNull()) |
| Arr = TopDict.setArray(KeyDiagnostics); |
| |
| auto Elem = Arr.appendDictionary(); |
| fillDictionaryForDiagnosticInfo(Elem, Info); |
| return true; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ReportCursorInfo |
| //===----------------------------------------------------------------------===// |
| |
| 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; |
| if (!Info.InternalDiagnostic.empty()) { |
| auto Elem = RespBuilder.getDictionary(); |
| Elem.set(KeyInternalDiagnostic, Info.InternalDiagnostic); |
| return Rec(RespBuilder.createResponse()); |
| } |
| if (Info.Kind.isInvalid()) |
| return Rec(RespBuilder.createResponse()); |
| |
| auto Elem = RespBuilder.getDictionary(); |
| Elem.set(KeyKind, Info.Kind); |
| Elem.set(KeyName, Info.Name); |
| if (!Info.USR.empty()) |
| Elem.set(KeyUSR, Info.USR); |
| if (!Info.TypeName.empty()) |
| Elem.set(KeyTypeName, Info.TypeName); |
| if (!Info.DocComment.empty()) |
| Elem.set(KeyDocFullAsXML, Info.DocComment); |
| if (!Info.AnnotatedDeclaration.empty()) |
| Elem.set(KeyAnnotatedDecl, Info.AnnotatedDeclaration); |
| if (!Info.FullyAnnotatedDeclaration.empty()) |
| Elem.set(KeyFullyAnnotatedDecl, Info.FullyAnnotatedDeclaration); |
| if (!Info.ModuleName.empty()) |
| Elem.set(KeyModuleName, Info.ModuleName); |
| if (!Info.GroupName.empty()) |
| Elem.set(KeyGroupName, Info.GroupName); |
| if (!Info.LocalizationKey.empty()) |
| Elem.set(KeyLocalizationKey, Info.LocalizationKey); |
| if (!Info.ModuleInterfaceName.empty()) |
| Elem.set(KeyModuleInterfaceName, Info.ModuleInterfaceName); |
| if (Info.DeclarationLoc.hasValue()) { |
| Elem.set(KeyOffset, Info.DeclarationLoc.getValue().first); |
| Elem.set(KeyLength, Info.DeclarationLoc.getValue().second); |
| if (!Info.Filename.empty()) |
| Elem.set(KeyFilePath, Info.Filename); |
| } |
| if (!Info.OverrideUSRs.empty()) { |
| auto Overrides = Elem.setArray(KeyOverrides); |
| for (auto USR : Info.OverrideUSRs) { |
| auto Override = Overrides.appendDictionary(); |
| Override.set(KeyUSR, USR); |
| } |
| } |
| if (!Info.ModuleGroupArray.empty()) { |
| auto Groups = Elem.setArray(KeyModuleGroups); |
| for (auto Name : Info.ModuleGroupArray) { |
| auto Entry = Groups.appendDictionary(); |
| Entry.set(KeyGroupName, Name); |
| } |
| } |
| if (!Info.AvailableActions.empty()) { |
| auto Actions = Elem.setArray(KeyRefactorActions); |
| for (auto Info : Info.AvailableActions) { |
| auto Entry = Actions.appendDictionary(); |
| Entry.set(KeyActionUID, Info.Kind); |
| Entry.set(KeyActionName, Info.KindName); |
| if (!Info.UnavailableReason.empty()) |
| Entry.set(KeyActionUnavailableReason, Info.UnavailableReason); |
| } |
| } |
| if (Info.ParentNameOffset) { |
| Elem.set(KeyParentLoc, Info.ParentNameOffset.getValue()); |
| } |
| if (!Info.AnnotatedRelatedDeclarations.empty()) { |
| auto RelDecls = Elem.setArray(KeyRelatedDecls); |
| for (auto AnnotDecl : Info.AnnotatedRelatedDeclarations) { |
| auto RelDecl = RelDecls.appendDictionary(); |
| RelDecl.set(KeyAnnotatedDecl, AnnotDecl); |
| } |
| } |
| if (Info.IsSystem) |
| Elem.setBool(KeyIsSystem, true); |
| if (!Info.TypeInterface.empty()) |
| Elem.set(KeyTypeInterface, Info.TypeInterface); |
| if (!Info.TypeUSR.empty()) |
| Elem.set(KeyTypeUsr, Info.TypeUSR); |
| if (!Info.ContainerTypeUSR.empty()) |
| Elem.set(KeyContainerTypeUsr, Info.ContainerTypeUSR); |
| if (!Info.SymbolGraph.empty()) |
| Elem.set(KeySymbolGraph, Info.SymbolGraph); |
| |
| return 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()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // FindRelatedIdents |
| //===----------------------------------------------------------------------===// |
| |
| static void findRelatedIdents(StringRef Filename, |
| int64_t Offset, |
| bool CancelOnSubsequentRequest, |
| ArrayRef<const char *> Args, |
| ResponseReceiver Rec) { |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.findRelatedIdentifiersInFile(Filename, Offset, CancelOnSubsequentRequest, |
| Args, |
| [Rec](const RequestResult<RelatedIdentsInfo> &Result) { |
| if (Result.isCancelled()) |
| return Rec(createErrorRequestCancelled()); |
| if (Result.isError()) |
| return Rec(createErrorRequestFailed(Result.getError())); |
| |
| const RelatedIdentsInfo &Info = Result.value(); |
| |
| ResponseBuilder RespBuilder; |
| auto Arr = RespBuilder.getDictionary().setArray(KeyResults); |
| for (auto R : Info.Ranges) { |
| auto Elem = Arr.appendDictionary(); |
| Elem.set(KeyOffset, R.first); |
| Elem.set(KeyLength, R.second); |
| } |
| |
| Rec(RespBuilder.createResponse()); |
| }); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // CodeComplete |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| class SKCodeCompletionConsumer : public CodeCompletionConsumer { |
| ResponseBuilder &RespBuilder; |
| CodeCompletionResultsArrayBuilder ResultsBuilder; |
| |
| std::string ErrorDescription; |
| |
| public: |
| explicit SKCodeCompletionConsumer(ResponseBuilder &RespBuilder) |
| : RespBuilder(RespBuilder) { |
| } |
| |
| sourcekitd_response_t createResponse() { |
| if (!ErrorDescription.empty()) |
| return createErrorRequestFailed(ErrorDescription.c_str()); |
| |
| RespBuilder.getDictionary().setCustomBuffer(KeyResults, |
| ResultsBuilder.createBuffer()); |
| return RespBuilder.createResponse(); |
| } |
| |
| |
| void failed(StringRef ErrDescription) 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) { |
| 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)); |
| return CCC.createResponse(); |
| } |
| |
| void SKCodeCompletionConsumer::failed(StringRef ErrDescription) { |
| ErrorDescription = ErrDescription.str(); |
| } |
| |
| 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; |
| |
| public: |
| explicit SKGroupedCodeCompletionConsumer(ResponseBuilder &RespBuilder) |
| : RespBuilder(RespBuilder) {} |
| |
| sourcekitd_response_t createResponse() { |
| if (!ErrorDescription.empty()) |
| return createErrorRequestFailed(ErrorDescription.c_str()); |
| assert(GroupContentsStack.empty() && "mismatched start/endGroup"); |
| return RespBuilder.createResponse(); |
| } |
| |
| void failed(StringRef ErrDescription) override; |
| bool handleResult(const CodeCompletionInfo &Info) override; |
| void startGroup(UIdent kind, StringRef name) override; |
| void endGroup() override; |
| void setNextRequestStart(unsigned offset) override; |
| 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) { |
| 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: { |
| SmallVector<const char *, 8> names; |
| if (dict.getStringArray(KeyNames, names, false)) { |
| failed = true; |
| CCC.failed("filter rule missing required key 'key.names'"); |
| return true; |
| } |
| rule.names.assign(names.begin(), names.end()); |
| break; |
| } |
| case FilterRule::Description: { |
| SmallVector<const char *, 8> names; |
| if (dict.getStringArray(KeyNames, names, false)) { |
| failed = true; |
| CCC.failed("filter rule missing required key 'key.names'"); |
| return true; |
| } |
| rule.names.assign(names.begin(), names.end()); |
| break; |
| } |
| case FilterRule::Keyword: |
| case FilterRule::Literal: |
| case FilterRule::CustomCompletion: { |
| SmallVector<sourcekitd_uid_t, 8> uids; |
| dict.getUIDArray(KeyUIDs, uids, true); |
| for (auto uid : uids) |
| rule.uids.push_back(UIdentFromSKDUID(uid)); |
| break; |
| } |
| } |
| |
| filterRules.push_back(std::move(rule)); |
| return false; // continue |
| }); |
| |
| if (failed) |
| return CCC.createResponse(); |
| } |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.codeCompleteOpen(Name, InputBuf, Offset, options.get(), filterRules, CCC, |
| Args, std::move(vfsOptions)); |
| return CCC.createResponse(); |
| } |
| |
| static sourcekitd_response_t codeCompleteClose(StringRef Name, int64_t Offset) { |
| ResponseBuilder RespBuilder; |
| SKGroupedCodeCompletionConsumer CCC(RespBuilder); |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.codeCompleteClose(Name, Offset, CCC); |
| return CCC.createResponse(); |
| } |
| |
| static sourcekitd_response_t |
| codeCompleteUpdate(StringRef name, int64_t offset, |
| Optional<RequestDict> optionsDict) { |
| ResponseBuilder RespBuilder; |
| SKGroupedCodeCompletionConsumer CCC(RespBuilder); |
| std::unique_ptr<SKOptionsDictionary> options; |
| if (optionsDict) |
| options = std::make_unique<SKOptionsDictionary>(*optionsDict); |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.codeCompleteUpdate(name, offset, options.get(), CCC); |
| return CCC.createResponse(); |
| } |
| |
| void SKGroupedCodeCompletionConsumer::failed(StringRef ErrDescription) { |
| ErrorDescription = ErrDescription.str(); |
| } |
| |
| 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); |
| addRange(structure, KeyThrowOffset, KeyThrowLength, |
| R.descriptionStructure->throwsRange); |
| |
| if (R.parametersStructure) { |
| auto params = structure.setArray(KeySubStructure); |
| for (auto &P : *R.parametersStructure) { |
| auto param = params.appendDictionary(); |
| addRange(param, KeyNameOffset, KeyNameLength, P.name); |
| addRange(param, KeyBodyOffset, KeyBodyLength, P.afterColon); |
| if (P.isLocalName) |
| param.set(KeyIsLocal, true); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void SKGroupedCodeCompletionConsumer::startGroup(UIdent kind, StringRef name) { |
| ResponseBuilder::Dictionary group; |
| if (GroupContentsStack.empty()) { |
| group = RespBuilder.getDictionary(); |
| Response = group; |
| } else { |
| group = GroupContentsStack.back().appendDictionary(); |
| } |
| group.set(KeyKind, kind); |
| group.set(KeyName, name); |
| auto contents = group.setArray(KeyResults); |
| GroupContentsStack.push_back(contents); |
| } |
| void SKGroupedCodeCompletionConsumer::endGroup() { |
| assert(!GroupContentsStack.empty()); |
| GroupContentsStack.pop_back(); |
| } |
| void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) { |
| assert(!Response.isNull()); |
| Response.set(KeyNextRequestStart, offset); |
| } |
| 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) { |
| ResponseBuilder RespBuilder; |
| |
| class Consumer : public TypeContextInfoConsumer { |
| ResponseBuilder RespBuilder; |
| ResponseBuilder::Array SKResults; |
| Optional<std::string> ErrorDescription; |
| |
| 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(); |
| } |
| |
| bool isError() const { return ErrorDescription.hasValue(); } |
| 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, Consumer, |
| std::move(vfsOptions)); |
| |
| if (Consumer.isError()) |
| return createErrorRequestFailed(Consumer.getErrorDescription()); |
| 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) { |
| ResponseBuilder RespBuilder; |
| |
| class Consumer : public ConformingMethodListConsumer { |
| ResponseBuilder::Dictionary SKResult; |
| Optional<std::string> ErrorDescription; |
| |
| 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(); |
| } |
| |
| bool isError() const { return ErrorDescription.hasValue(); } |
| 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, Consumer, std::move(vfsOptions)); |
| |
| if (Consumer.isError()) |
| return createErrorRequestFailed(Consumer.getErrorDescription()); |
| return RespBuilder.createResponse(); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Editor |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| class SKEditorConsumer : public EditorConsumer { |
| ResponseReceiver RespReceiver; |
| ResponseBuilder RespBuilder; |
| |
| public: |
| ResponseBuilder::Dictionary Dict; |
| DocStructureArrayBuilder DocStructure; |
| TokenAnnotationsArrayBuilder SyntaxMap; |
| TokenAnnotationsArrayBuilder SemanticAnnotations; |
| |
| ResponseBuilder::Array Diags; |
| 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; |
| |
| void setDiagnosticStage(UIdent DiagStage) override; |
| void handleDiagnostic(const DiagnosticEntryInfo &Info, |
| UIdent DiagStage) override; |
| |
| void handleSourceText(StringRef Text) override; |
| |
| void handleSyntaxTree(const swift::syntax::SourceFileSyntax &SyntaxTree, |
| std::unordered_set<unsigned> &ReusedNodeIds) override; |
| |
| SyntaxTreeTransferMode syntaxTreeTransferMode() override { |
| return Opts.SyntaxTransferMode; |
| } |
| |
| 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, |
| 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, 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 fillDictionaryForDiagnosticInfoBase( |
| ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info); |
| |
| static void fillDictionaryForDiagnosticInfo( |
| ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfo &Info) { |
| |
| 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); |
| |
| 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); |
| } |
| } |
| } |
| |
| static void fillDictionaryForDiagnosticInfoBase( |
| ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info) { |
| |
| Elem.set(KeyDescription, Info.Description); |
| if (Info.Line != 0) { |
| Elem.set(KeyLine, Info.Line); |
| Elem.set(KeyColumn, Info.Column); |
| } else { |
| Elem.set(KeyOffset, Info.Offset); |
| } |
| if (!Info.Filename.empty()) |
| Elem.set(KeyFilePath, Info.Filename); |
| |
| if (!Info.EducationalNotePaths.empty()) |
| Elem.set(KeyEducationalNotePaths, Info.EducationalNotePaths); |
| |
| if (!Info.Ranges.empty()) { |
| auto RangesArr = Elem.setArray(KeyRanges); |
| for (auto R : Info.Ranges) { |
| auto RangeElem = RangesArr.appendDictionary(); |
| RangeElem.set(KeyOffset, R.first); |
| RangeElem.set(KeyLength, R.second); |
| } |
| } |
| |
| if (!Info.Fixits.empty()) { |
| auto FixitsArr = Elem.setArray(KeyFixits); |
| for (auto F : Info.Fixits) { |
| auto FixitElem = FixitsArr.appendDictionary(); |
| FixitElem.set(KeyOffset, F.Offset); |
| FixitElem.set(KeyLength, F.Length); |
| FixitElem.set(KeySourceText, F.Text); |
| } |
| } |
| } |
| |
| void SKEditorConsumer::setDiagnosticStage(UIdent DiagStage) { |
| Dict.set(KeyDiagnosticStage, DiagStage); |
| } |
| |
| void SKEditorConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info, |
| UIdent DiagStage) { |
| if (!Opts.EnableDiagnostics) |
| return; |
| |
| ResponseBuilder::Array &Arr = Diags; |
| if (Arr.isNull()) |
| Arr = Dict.setArray(KeyDiagnostics); |
| |
| auto Elem = Arr.appendDictionary(); |
| Elem.set(KeyDiagnosticStage, DiagStage); |
| fillDictionaryForDiagnosticInfo(Elem, Info); |
| } |
| |
| void SKEditorConsumer::handleSourceText(StringRef Text) { |
| Dict.set(KeySourceText, Text); |
| } |
| |
| void serializeSyntaxTreeAsByteTree( |
| const swift::syntax::SourceFileSyntax &SyntaxTree, |
| std::unordered_set<unsigned> &ReusedNodeIds, |
| ResponseBuilder::Dictionary &Dict) { |
| auto StartClock = clock(); |
| // Serialize the syntax tree as a ByteTree |
| auto Stream = swift::ExponentialGrowthAppendingBinaryByteStream(); |
| Stream.reserve(32 * 1024); |
| std::map<void *, void *> UserInfo; |
| UserInfo[swift::byteTree::UserInfoKeyReusedNodeIds] = &ReusedNodeIds; |
| swift::byteTree::ByteTreeWriter::write(Stream, |
| swift::byteTree::SYNTAX_TREE_VERSION, |
| *SyntaxTree.getRaw(), UserInfo); |
| |
| std::unique_ptr<llvm::WritableMemoryBuffer> Buf = |
| llvm::WritableMemoryBuffer::getNewUninitMemBuffer(sizeof(uint64_t) + Stream.data().size()); |
| *reinterpret_cast<uint64_t*>(Buf->getBufferStart()) = |
| (uint64_t)CustomBufferKind::RawData; |
| memcpy(Buf->getBufferStart() + sizeof(uint64_t), |
| Stream.data().data(), Stream.data().size()); |
| |
| Dict.setCustomBuffer(KeySerializedSyntaxTree, std::move(Buf)); |
| |
| auto EndClock = clock(); |
| LOG_SECTION("incrParse Performance", InfoLowPrio) { |
| Log->getOS() << "Serialized " << Stream.data().size() |
| << " bytes as ByteTree in "; |
| auto Seconds = (double)(EndClock - StartClock) * 1000 / CLOCKS_PER_SEC; |
| llvm::write_double(Log->getOS(), Seconds, llvm::FloatStyle::Fixed, 2); |
| Log->getOS() << "ms"; |
| } |
| } |
| |
| void serializeSyntaxTreeAsJson( |
| const swift::syntax::SourceFileSyntax &SyntaxTree, |
| std::unordered_set<unsigned> ReusedNodeIds, |
| ResponseBuilder::Dictionary &Dict) { |
| auto StartClock = clock(); |
| // 4096 is a heuristic buffer size that appears to usually be able to fit an |
| // incremental syntax tree |
| size_t ReserveBufferSize = 4096; |
| std::string SyntaxTreeString; |
| SyntaxTreeString.reserve(ReserveBufferSize); |
| { |
| llvm::raw_string_ostream SyntaxTreeStream(SyntaxTreeString); |
| SyntaxTreeStream.SetBufferSize(ReserveBufferSize); |
| swift::json::Output::UserInfoMap JsonUserInfo; |
| JsonUserInfo[swift::json::OmitNodesUserInfoKey] = &ReusedNodeIds; |
| swift::json::Output SyntaxTreeOutput(SyntaxTreeStream, JsonUserInfo, |
| /*PrettyPrint=*/false); |
| SyntaxTreeOutput << *SyntaxTree.getRaw(); |
| } |
| Dict.set(KeySerializedSyntaxTree, SyntaxTreeString); |
| |
| auto EndClock = clock(); |
| LOG_SECTION("incrParse Performance", InfoLowPrio) { |
| Log->getOS() << "Serialized " << SyntaxTreeString.size() |
| << " bytes as JSON in "; |
| auto Seconds = (double)(EndClock - StartClock) * 1000 / CLOCKS_PER_SEC; |
| llvm::write_double(Log->getOS(), Seconds, llvm::FloatStyle::Fixed, 2); |
| Log->getOS() << "ms"; |
| } |
| } |
| |
| void SKEditorConsumer::handleSyntaxTree( |
| const swift::syntax::SourceFileSyntax &SyntaxTree, |
| std::unordered_set<unsigned> &ReusedNodeIds) { |
| |
| std::unordered_set<unsigned> OmitNodes; |
| switch (Opts.SyntaxTransferMode) { |
| case SourceKit::SyntaxTreeTransferMode::Off: |
| // Don't serialize the tree at all |
| return; |
| case SourceKit::SyntaxTreeTransferMode::Full: |
| // Serialize the tree without omitting any nodes |
| OmitNodes = {}; |
| break; |
| case SourceKit::SyntaxTreeTransferMode::Incremental: |
| // Serialize the tree and omit all nodes that have been reused |
| OmitNodes = ReusedNodeIds; |
| break; |
| } |
| |
| switch (Opts.SyntaxSerializationFormat) { |
| case SourceKit::SyntaxTreeSerializationFormat::JSON: |
| serializeSyntaxTreeAsJson(SyntaxTree, OmitNodes, Dict); |
| break; |
| case SourceKit::SyntaxTreeSerializationFormat::ByteTree: |
| serializeSyntaxTreeAsByteTree(SyntaxTree, OmitNodes, Dict); |
| break; |
| } |
| } |
| |
| static sourcekitd_response_t |
| editorFindUSR(StringRef DocumentName, StringRef USR) { |
| ResponseBuilder RespBuilder; |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| llvm::Optional<std::pair<unsigned, unsigned>> |
| Range = Lang.findUSRRange(DocumentName, USR); |
| if (!Range) { |
| // If cannot find the synthesized USR, find the actual USR instead. |
| Range = Lang.findUSRRange(DocumentName, |
| USR.split(LangSupport::SynthesizedUSRSeparator). |
| first); |
| } |
| if (Range.hasValue()) { |
| RespBuilder.getDictionary().set(KeyOffset, Range->first); |
| RespBuilder.getDictionary().set(KeyLength, Range->second); |
| } |
| return RespBuilder.createResponse(); |
| } |
| |
| static sourcekitd_response_t |
| editorFindInterfaceDoc(StringRef ModuleName, ArrayRef<const char *> Args) { |
| ResponseBuilder RespBuilder; |
| sourcekitd_response_t Resp; |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| Lang.findInterfaceDocument(ModuleName, Args, |
| [&](const 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(RequestDict &Req, bool UseNewName, |
| std::vector<RenameLocations> &RenameLocations, |
| llvm::SmallString<64> &Error) { |
| bool Failed = Req.dictionaryArrayApply(KeyRenameLocations, |
| [&](RequestDict RenameLocation) { |
| int64_t IsFunctionLike = false; |
| if (RenameLocation.getInt64(KeyIsFunctionLike, IsFunctionLike, false)) { |
| Error = "missing key.is_function_like"; |
| return true; |
| } |
| |
| int64_t IsNonProtocolType = false; |
| if (RenameLocation.getInt64(KeyIsNonProtocolType, IsNonProtocolType, false)) { |
| Error = "missing key.is_non_protocol_type"; |
| return true; |
| } |
| |
| Optional<StringRef> OldName = RenameLocation.getString(KeyName); |
| if (!OldName.hasValue()) { |
| Error = "missing key.name"; |
| return true; |
| } |
| |
| Optional<StringRef> NewName; |
| if (UseNewName) { |
| NewName = RenameLocation.getString(KeyNewName); |
| if (!NewName.hasValue()) { |
| Error = "missing key.newname"; |
| return true; |
| } |
| } |
| |
| RenameLocations.push_back({*OldName, |
| UseNewName ? *NewName : "", |
| static_cast<bool>(IsFunctionLike), |
| static_cast<bool>(IsNonProtocolType), |
| {}}); |
| auto &LineCols = RenameLocations.back().LineColumnLocs; |
| bool Failed = RenameLocation.dictionaryArrayApply(KeyLocations, |
| [&](RequestDict LineAndCol) { |
| int64_t Line = 0; |
| int64_t Column = 0; |
| |
| if (LineAndCol.getInt64(KeyLine, Line, false)) { |
| Error = "missing key.line"; |
| return true; |
| } |
| if (LineAndCol.getInt64(KeyColumn, Column, false)) { |
| Error = "missing key.column"; |
| return true; |
| } |
| |
| sourcekitd_uid_t NameType = LineAndCol.getUID(KeyNameType); |
| if (!NameType) { |
| Error = "missing key.nametype"; |
| return true; |
| } |
| RenameType RenameType = RenameType::Unknown; |
| if (NameType == KindDefinition) { |
| RenameType = RenameType::Definition; |
| } else if (NameType == KindReference) { |
| RenameType = RenameType::Reference; |
| } else if (NameType == KindCall) { |
| RenameType = RenameType::Call; |
| } else if (NameType != KindUnknown) { |
| Error = "invalid value for 'key.nametype'"; |
| return true; |
| } |
| LineCols.push_back({static_cast<unsigned>(Line), |
| static_cast<unsigned>(Column), RenameType}); |
| return false; |
| }); |
| if (Failed && Error.empty()) { |
| Error = "invalid key.locations"; |
| } |
| return Failed; |
| }); |
| if (Failed && Error.empty()) { |
| Error = "invalid key.renamelocations"; |
| } |
| return Failed; |
| } |
| |
| static sourcekitd_response_t |
| createCategorizedEditsResponse(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(); |
| Edit.set(KeyLine, E.StartLine); |
| Edit.set(KeyColumn, E.StartColumn); |
| Edit.set(KeyEndLine, E.EndLine); |
| Edit.set(KeyEndColumn, E.EndColumn); |
| Edit.set(KeyText, E.NewText); |
| if (!E.RegionsWithNote.empty()) { |
| auto Notes = Edit.setArray(KeyRangesWorthNote); |
| for (auto R : E.RegionsWithNote) { |
| auto N = Notes.appendDictionary(); |
| N.set(KeyKind, R.Kind); |
| N.set(KeyLine, R.StartLine); |
| N.set(KeyColumn, R.StartColumn); |
| N.set(KeyEndLine, R.EndLine); |
| N.set(KeyEndColumn, R.EndColumn); |
| if (R.ArgIndex) |
| N.set(KeyArgIndex, *R.ArgIndex); |
| } |
| } |
| } |
| } |
| return RespBuilder.createResponse(); |
| } |
| |
| static sourcekitd_response_t |
| syntacticRename(llvm::MemoryBuffer *InputBuf, |
| ArrayRef<RenameLocations> RenameLocations, |
| ArrayRef<const char*> Args) { |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| sourcekitd_response_t Result; |
| Lang.syntacticRename(InputBuf, RenameLocations, Args, |
| [&](const RequestResult<ArrayRef<CategorizedEdits>> &ReqResult) { |
| Result = createCategorizedEditsResponse(ReqResult); |
| }); |
| return Result; |
| } |
| |
| static sourcekitd_response_t |
| createCategorizedRenameRangesResponse(const RequestResult<ArrayRef<CategorizedRenameRanges>> &Result) { |
| if (Result.isCancelled()) |
| return createErrorRequestCancelled(); |
| if (Result.isError()) |
| return createErrorRequestFailed(Result.getError()); |
| |
| const ArrayRef<CategorizedRenameRanges> &Ranges = Result.value(); |
| |
| ResponseBuilder RespBuilder; |
| auto Dict = RespBuilder.getDictionary(); |
| auto Arr = Dict.setArray(KeyCategorizedRanges); |
| for (const auto &CategorizedRange : Ranges) { |
| auto Entry = Arr.appendDictionary(); |
| Entry.set(KeyCategory, CategorizedRange.Category); |
| auto Ranges = Entry.setArray(KeyRanges); |
| for (const auto &R : CategorizedRange.Ranges) { |
| auto Range = Ranges.appendDictionary(); |
| Range.set(KeyLine, R.StartLine); |
| Range.set(KeyColumn, R.StartColumn); |
| Range.set(KeyEndLine, R.EndLine); |
| Range.set(KeyEndColumn, R.EndColumn); |
| Range.set(KeyKind, R.Kind); |
| if (R.ArgIndex) { |
| Range.set(KeyArgIndex, *R.ArgIndex); |
| } |
| } |
| } |
| return RespBuilder.createResponse(); |
| } |
| |
| static sourcekitd_response_t |
| findRenameRanges(llvm::MemoryBuffer *InputBuf, |
| ArrayRef<RenameLocations> RenameLocations, |
| ArrayRef<const char *> Args) { |
| LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); |
| sourcekitd_response_t Result; |
| Lang.findRenameRanges( |
| InputBuf, RenameLocations, Args, |
| [&](const RequestResult<ArrayRef<CategorizedRenameRanges>> &ReqResult) { |
| Result = createCategorizedRenameRangesResponse(ReqResult); |
| }); |
| return Result; |
| } |
| |
| static bool isSemanticEditorDisabled() { |
| enum class SemaInfoToggle : char { |
| None, Disable, Enable |
| }; |
| static SemaInfoToggle Toggle = SemaInfoToggle::None; |
| |
| if (Toggle == SemaInfoToggle::None) { |
| static std::once_flag flag; |
| std::call_once(flag, []() { |
| Toggle = SemaInfoToggle::Enable; |
| |
| const char *EnvOpt = ::getenv("SOURCEKIT_DELAY_SEMA_EDITOR"); |
| if (!EnvOpt) { |
| return; |
| } |
| |
| unsigned Seconds; |
| if (StringRef(EnvOpt).getAsInteger(10, Seconds)) |
| return; |
| |
| // A crash occurred previously. Disable semantic info in the editor for |
| // the given amount, to avoid repeated crashers. |
| LOG_WARN_FUNC("delaying semantic editor for " << Seconds << " seconds"); |
| Toggle = SemaInfoToggle::Disable; |
| dispatch_time_t When = dispatch_time(DISPATCH_TIME_NOW, |
| NSEC_PER_SEC * Seconds); |
| dispatch_after(When, dispatch_get_main_queue(), ^{ |
| Toggle = SemaInfoToggle::Enable; |
| 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); |
| } |
| } |