diff --git a/include/swift/IDE/CompletionInstance.h b/include/swift/IDE/CompletionInstance.h index b928fa997e79c..8eb345fee94da 100644 --- a/include/swift/IDE/CompletionInstance.h +++ b/include/swift/IDE/CompletionInstance.h @@ -18,6 +18,7 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Chrono.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/VirtualFileSystem.h" @@ -38,13 +39,22 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf, /// Manages \c CompilerInstance for completion like operations. class CompletionInstance { unsigned MaxASTReuseCount = 100; + unsigned DependencyCheckIntervalSecond = 5; std::mutex mtx; std::unique_ptr CachedCI; + ModuleDecl *CurrentModule = nullptr; llvm::hash_code CachedArgHash; + llvm::sys::TimePoint<> DependencyCheckedTimestamp; + llvm::StringMap InMemoryDependencyHash; unsigned CachedReuseCount = 0; + void cacheCompilerInstance(std::unique_ptr CI, + llvm::hash_code ArgsHash); + + bool shouldCheckDependencies() const; + /// Calls \p Callback with cached \c CompilerInstance if it's usable for the /// specified completion request. /// Returns \c if the callback was called. Returns \c false if the compiler @@ -52,6 +62,7 @@ class CompletionInstance { /// in function bodies, or the interface hash of the file has changed. bool performCachedOperationIfPossible( const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC, llvm::function_ref Callback); @@ -69,6 +80,8 @@ class CompletionInstance { llvm::function_ref Callback); public: + void setDependencyCheckIntervalSecond(unsigned Value); + /// Calls \p Callback with a \c CompilerInstance which is prepared for the /// second pass. \p Callback is resposible to perform the second pass on it. /// The \c CompilerInstance may be reused from the previous completions, diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index cb3f8c965b577..67955f93407dc 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -21,11 +21,14 @@ #include "swift/Basic/LangOptions.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/Basic/SourceManager.h" +#include "swift/ClangImporter/ClangModule.h" #include "swift/Driver/FrontendUtil.h" #include "swift/Frontend/Frontend.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/PersistentParserState.h" +#include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Subsystems.h" +#include "clang/AST/ASTContext.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/MemoryBuffer.h" @@ -162,10 +165,118 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC, return newDC; } +/// For each dependency file in \p CI, run \p callback until the callback +/// returns \c true. Returns \c true if any callback call returns \c true, \c +/// false otherwise. +static bool +forEachDependencyUntilTrue(CompilerInstance &CI, ModuleDecl *CurrentModule, + unsigned excludeBufferID, + llvm::function_ref callback) { + // Check files in the current module. + for (FileUnit *file : CurrentModule->getFiles()) { + StringRef filename; + if (auto SF = dyn_cast(file)) { + if (SF->getBufferID() == excludeBufferID) + continue; + filename = SF->getFilename(); + } else if (auto LF = dyn_cast(file)) + filename = LF->getFilename(); + else + continue; + + // Ignore synthesized files. + if (filename.empty() || filename.front() == '<') + continue; + + if (callback(filename)) + return true; + } + + // Check other non-system depenencies (e.g. modules, headers). + for (auto &dep : CI.getDependencyTracker()->getDependencies()) { + if (callback(dep)) + return true; + } + + return false; +} + +/// Collect hash codes of the dependencies into \c Map. +static void cacheDependencyHashIfNeeded(CompilerInstance &CI, + ModuleDecl *CurrentModule, + unsigned excludeBufferID, + llvm::StringMap &Map) { + auto &FS = CI.getFileSystem(); + forEachDependencyUntilTrue( + CI, CurrentModule, excludeBufferID, [&](StringRef filename) { + if (Map.count(filename)) + return false; + + auto stat = FS.status(filename); + if (!stat) + return false; + + // We will check the hash only if the modification time of the dependecy + // is zero. See 'areAnyDependentFilesInvalidated() below'. + if (stat->getLastModificationTime() != llvm::sys::TimePoint<>()) + return false; + + auto buf = FS.getBufferForFile(filename); + Map[filename] = llvm::hash_value(buf.get()->getBuffer()); + return false; + }); +} + +/// Check if any dependent files are modified since \p timestamp. +static bool areAnyDependentFilesInvalidated( + CompilerInstance &CI, ModuleDecl *CurrentModule, llvm::vfs::FileSystem &FS, + unsigned excludeBufferID, llvm::sys::TimePoint<> timestamp, + llvm::StringMap &Map) { + + return forEachDependencyUntilTrue( + CI, CurrentModule, excludeBufferID, [&](StringRef filePath) { + auto stat = FS.status(filePath); + if (!stat) + // Missing. + return true; + + auto lastModTime = stat->getLastModificationTime(); + if (lastModTime > timestamp) + // Modified. + return true; + + // If the last modification time is zero, this file is probably from a + // virtual file system. We need to check the content. + if (lastModTime == llvm::sys::TimePoint<>()) { + // Get the hash code of the last content. + auto oldHashEntry = Map.find(filePath); + if (oldHashEntry == Map.end()) + // Unreachable? Not virtual in old filesystem, but virtual in new + // one. + return true; + auto oldHash = oldHashEntry->second; + + // Calculate the hash code of the current content. + auto newContent = FS.getBufferForFile(filePath); + if (!newContent) + // Unreachable? stat succeeded, but coundn't get the content. + return true; + + auto newHash = llvm::hash_value(newContent.get()->getBuffer()); + + if (oldHash != newHash) + return true; + } + + return false; + }); +} + } // namespace bool CompletionInstance::performCachedOperationIfPossible( const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash, + llvm::IntrusiveRefCntPtr FileSystem, llvm::MemoryBuffer *completionBuffer, unsigned int Offset, DiagnosticConsumer *DiagC, llvm::function_ref Callback) { @@ -187,10 +298,18 @@ bool CompletionInstance::performCachedOperationIfPossible( auto &oldInfo = oldState->getCodeCompletionDelayedDeclState(); auto &SM = CI.getSourceMgr(); - if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != - completionBuffer->getBufferIdentifier()) + auto bufferName = completionBuffer->getBufferIdentifier(); + if (SM.getIdentifierForBuffer(SM.getCodeCompletionBufferID()) != bufferName) return false; + if (shouldCheckDependencies()) { + if (areAnyDependentFilesInvalidated( + CI, CurrentModule, *FileSystem, SM.getCodeCompletionBufferID(), + DependencyCheckedTimestamp, InMemoryDependencyHash)) + return false; + DependencyCheckedTimestamp = std::chrono::system_clock::now(); + } + // Parse the new buffer into temporary SourceFile. SourceManager tmpSM; auto tmpBufferID = tmpSM.addMemBufferCopy(completionBuffer); @@ -263,8 +382,7 @@ bool CompletionInstance::performCachedOperationIfPossible( completionBuffer->getBuffer().slice(startOffset, endOffset); auto newOffset = Offset - startOffset; - newBufferID = SM.addMemBufferCopy(sourceText, - completionBuffer->getBufferIdentifier()); + newBufferID = SM.addMemBufferCopy(sourceText, bufferName); SM.openVirtualFile(SM.getLocForBufferStart(newBufferID), tmpSM.getDisplayNameForLoc(startLoc), tmpSM.getLineAndColumn(startLoc).first - 1); @@ -310,8 +428,7 @@ bool CompletionInstance::performCachedOperationIfPossible( endOffset = tmpSM.getLocOffsetInBuffer(endLoc, tmpBufferID); sourceText = sourceText.slice(0, endOffset); } - newBufferID = SM.addMemBufferCopy(sourceText, - completionBuffer->getBufferIdentifier()); + newBufferID = SM.addMemBufferCopy(sourceText, bufferName); SM.setCodeCompletionPoint(newBufferID, Offset); // Create a new module and a source file using the current AST context. @@ -331,6 +448,7 @@ bool CompletionInstance::performCachedOperationIfPossible( performImportResolution(*newSF); bindExtensions(*newSF); + CurrentModule = newM; traceDC = newM; #ifndef NDEBUG const auto *reparsedState = newSF->getDelayedParserState(); @@ -357,6 +475,8 @@ bool CompletionInstance::performCachedOperationIfPossible( } CachedReuseCount += 1; + cacheDependencyHashIfNeeded(CI, CurrentModule, SM.getCodeCompletionBufferID(), + InMemoryDependencyHash); return true; } @@ -369,7 +489,15 @@ bool CompletionInstance::performNewOperation( llvm::function_ref Callback) { llvm::PrettyStackTraceString trace("While performing new completion"); + auto isCachedCompletionRequested = ArgsHash.hasValue(); + auto TheInstance = std::make_unique(); + + // Track dependencies in fast-completion mode to invalidate the compiler + // instance if any dependent files are modified. + if (isCachedCompletionRequested) + TheInstance->createDependencyTracker(false); + { auto &CI = *TheInstance; if (DiagC) @@ -407,15 +535,41 @@ bool CompletionInstance::performNewOperation( Callback(CI, /*reusingASTContext=*/false); } - if (ArgsHash.hasValue()) { - CachedCI = std::move(TheInstance); - CachedArgHash = *ArgsHash; - CachedReuseCount = 0; - } + // Cache the compiler instance if fast completion is enabled. + if (isCachedCompletionRequested) + cacheCompilerInstance(std::move(TheInstance), *ArgsHash); return true; } +void CompletionInstance::cacheCompilerInstance( + std::unique_ptr CI, llvm::hash_code ArgsHash) { + CachedCI = std::move(CI); + CurrentModule = CachedCI->getMainModule(); + CachedArgHash = ArgsHash; + auto now = std::chrono::system_clock::now(); + DependencyCheckedTimestamp = now; + CachedReuseCount = 0; + InMemoryDependencyHash.clear(); + cacheDependencyHashIfNeeded( + *CachedCI, CurrentModule, + CachedCI->getASTContext().SourceMgr.getCodeCompletionBufferID(), + InMemoryDependencyHash); +} + +bool CompletionInstance::shouldCheckDependencies() const { + assert(CachedCI); + using namespace std::chrono; + auto now = system_clock::now(); + return DependencyCheckedTimestamp + seconds(DependencyCheckIntervalSecond) < + now; +} + +void CompletionInstance::setDependencyCheckIntervalSecond(unsigned Value) { + std::lock_guard lock(mtx); + DependencyCheckIntervalSecond = Value; +} + bool swift::ide::CompletionInstance::performOperation( swift::CompilerInvocation &Invocation, llvm::ArrayRef Args, llvm::IntrusiveRefCntPtr FileSystem, @@ -451,8 +605,9 @@ bool swift::ide::CompletionInstance::performOperation( // the cached completion instance. std::lock_guard lock(mtx); - if (performCachedOperationIfPossible(Invocation, ArgsHash, completionBuffer, - Offset, DiagC, Callback)) + if (performCachedOperationIfPossible(Invocation, ArgsHash, FileSystem, + completionBuffer, Offset, DiagC, + Callback)) return true; if (performNewOperation(ArgsHash, Invocation, FileSystem, completionBuffer, diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/ClangFW.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/ClangFW.h new file mode 100644 index 0000000000000..3eab67881eee9 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/ClangFW.h @@ -0,0 +1,2 @@ + +#import diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/Funcs.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/Funcs.h new file mode 100644 index 0000000000000..d2a3d166d5c15 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Headers/Funcs.h @@ -0,0 +1,4 @@ +#ifndef CLANGFW_FUNCS_H +#define CLANGFW_FUNCS_H +int clangFWFunc(); +#endif diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Modules/module.modulemap b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..c1720d160f034 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework/Modules/module.modulemap @@ -0,0 +1,4 @@ +framework module ClangFW { + umbrella header "ClangFW.h" + export * +} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework_mod/Headers/Funcs.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework_mod/Headers/Funcs.h new file mode 100644 index 0000000000000..09c4450451ecd --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/ClangFW.framework_mod/Headers/Funcs.h @@ -0,0 +1,5 @@ +#ifndef CLANGFW_FUNCS_H +#define CLANGFW_FUNCS_H +int clangFWFunc_mod(); +#endif + diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Bridging.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Bridging.h new file mode 100644 index 0000000000000..aebbbe5cf5564 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Bridging.h @@ -0,0 +1 @@ +#import "LocalCFunc.h" diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift new file mode 100644 index 0000000000000..7e27f53052063 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/Library.swift @@ -0,0 +1,5 @@ +func localSwiftFunc() -> Int {} + +struct MyStruct { + func myStructMethod() {} +} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LibraryExt.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LibraryExt.swift new file mode 100644 index 0000000000000..67919b0f97b56 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LibraryExt.swift @@ -0,0 +1,3 @@ +extension MyStruct { + func extensionMethod() {} +} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LocalCFunc.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LocalCFunc.h new file mode 100644 index 0000000000000..12daa10870eaf --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject/LocalCFunc.h @@ -0,0 +1,6 @@ +#ifndef MYPROJECT_LOCALCFUNC_H +#define MYPROJECT_LOCALCFUNC_H + +int localClangFunc(); + +#endif diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift new file mode 100644 index 0000000000000..1e071aa1cc52b --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/Library.swift @@ -0,0 +1,5 @@ +func localSwiftFunc_mod() -> Int {} + +struct MyStruct { + func myStructMethod_mod() {} +} diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/LocalCFunc.h b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/LocalCFunc.h new file mode 100644 index 0000000000000..65ee9fd3abb10 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/MyProject_mod/LocalCFunc.h @@ -0,0 +1,7 @@ +#ifndef MYPROJECT_LOCALCFUNC_H +#define MYPROJECT_LOCALCFUNC_H + +int localClangFunc_mod(); + +#endif + diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src/Funcs.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src/Funcs.swift new file mode 100644 index 0000000000000..6ff2c2e73ee0f --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src/Funcs.swift @@ -0,0 +1,2 @@ +public func swiftFWFunc() -> Int { return 1 } + diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src_mod/Funcs.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src_mod/Funcs.swift new file mode 100644 index 0000000000000..98800199b2574 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/SwiftFW_src_mod/Funcs.swift @@ -0,0 +1 @@ +public func swiftFWFunc_mod() -> Int { return 1 } diff --git a/test/SourceKit/CodeComplete/Inputs/checkdeps/test.swift b/test/SourceKit/CodeComplete/Inputs/checkdeps/test.swift new file mode 100644 index 0000000000000..54e03f90fdef0 --- /dev/null +++ b/test/SourceKit/CodeComplete/Inputs/checkdeps/test.swift @@ -0,0 +1,5 @@ +import Foundation +import ClangFW // < 500 headers framework +func foo() { + /* HERE */ +} diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift b/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift new file mode 100644 index 0000000000000..2675887fc0dbf --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_bridged.swift @@ -0,0 +1,74 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// REQUIRES: shell + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Modify bridging header library file' == \ +// RUN: -shell -- cp -R $INPUT_DIR/MyProject_mod/LocalCFunc.h %t/MyProject/ == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify bridging header library file +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc_mod()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc_mod()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift b/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift new file mode 100644 index 0000000000000..68965b5e9a97d --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_clangmodule.swift @@ -0,0 +1,74 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// REQUIRES: shell + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Modify framework (c)' == \ +// RUN: -shell -- cp -R $INPUT_DIR/ClangFW.framework_mod/* %t/Frameworks/ClangFW.framework/ == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify framework (c) +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc_mod()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc_mod()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift b/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift new file mode 100644 index 0000000000000..d9d6f90daf3d6 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_otherfile.swift @@ -0,0 +1,73 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// REQUIRES: shell + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo "### Modify local library file" == \ +// RUN: -shell -- cp -R $INPUT_DIR/MyProject_mod/Library.swift %t/MyProject/ == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify local library file +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc_mod()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc_mod()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift b/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift new file mode 100644 index 0000000000000..30cbd7b97a37f --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_swiftmodule.swift @@ -0,0 +1,73 @@ +import ClangFW +import SwiftFW + +func foo() { + /*HERE*/ +} + +// REQUIRES: shell + +// RUN: %empty-directory(%t/Frameworks) +// RUN: %empty-directory(%t/MyProject) + +// RUN: COMPILER_ARGS=( \ +// RUN: -target %target-triple \ +// RUN: -module-name MyProject \ +// RUN: -F %t/Frameworks \ +// RUN: -I %t/MyProject \ +// RUN: -import-objc-header %t/MyProject/Bridging.h \ +// RUN: %t/MyProject/Library.swift \ +// RUN: %s \ +// RUN: ) +// RUN: INPUT_DIR=%S/Inputs/checkdeps +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: cp -R $INPUT_DIR/MyProject %t/ +// RUN: cp -R $INPUT_DIR/ClangFW.framework %t/Frameworks/ +// RUN: %empty-directory(%t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule) +// RUN: %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src/Funcs.swift + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Modify framework (s)' == \ +// RUN: -shell -- %target-swift-frontend -emit-module -module-name SwiftFW -o %t/Frameworks/SwiftFW.framework/Modules/SwiftFW.swiftmodule/%target-swiftmodule-name $INPUT_DIR/SwiftFW_src_mod/Funcs.swift == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} == \ + +// RUN: -shell -- echo '### Fast completion' == \ +// RUN: -shell -- sleep $SLEEP_TIME == \ +// RUN: -req=complete -pos=5:3 %s -- ${COMPILER_ARGS[@]} \ + +// RUN: | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify framework (s) +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc_mod()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Fast completion +// CHECK: key.results: [ +// CHECK-DAG: key.description: "clangFWFunc()" +// CHECK-DAG: key.description: "swiftFWFunc_mod()" +// CHECK-DAG: key.description: "localClangFunc()" +// CHECK-DAG: key.description: "localSwiftFunc()" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift new file mode 100644 index 0000000000000..388c09e57af33 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift @@ -0,0 +1,52 @@ +func foo(value: MyStruct) { + value./*HERE*/ +} + +// REQUIRES: shell + +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/VFS) +// RUN: cp %S/Inputs/checkdeps/MyProject/LibraryExt.swift %t/VFS/ + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ + +// RUN: -shell -- echo "### Modify" == \ +// RUN: -shell -- sleep ${SLEEP_TIME} == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ + +// RUN: -shell -- echo "### Keep" == \ +// RUN: -shell -- sleep ${SLEEP_TIME} == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift \ + +// RUN: | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Keep +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift new file mode 100644 index 0000000000000..1a8916dc0aceb --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs_open.swift @@ -0,0 +1,54 @@ +func foo(value: MyStruct) { + value./*HERE*/ +} + +// REQUIRES: shell + +// RUN: DEPCHECK_INTERVAL=1 +// RUN: SLEEP_TIME=2 + +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/VFS) +// RUN: cp %S/Inputs/checkdeps/MyProject/LibraryExt.swift %t/VFS/ + +// RUN: %sourcekitd-test \ +// RUN: -req=global-config -completion-check-dependency-interval ${DEPCHECK_INTERVAL} == \ + +// RUN: -shell -- echo "### Initial" == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s == \ + +// RUN: -shell -- echo "### Modify" == \ +// RUN: -shell -- sleep ${SLEEP_TIME} == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s == \ + +// RUN: -shell -- echo "### Keep" == \ +// RUN: -req=complete.open -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ +// RUN: -req=complete.close -pos=2:9 -name %t/VFS/Main.swift %s \ + +// RUN: | tee %t/trace | %FileCheck %s + +// CHECK-LABEL: ### Initial +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Modify +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Keep +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 diff --git a/tools/SourceKit/include/SourceKit/Core/Context.h b/tools/SourceKit/include/SourceKit/Core/Context.h index 056e727fe7e2e..cbfc287320924 100644 --- a/tools/SourceKit/include/SourceKit/Core/Context.h +++ b/tools/SourceKit/include/SourceKit/Core/Context.h @@ -36,6 +36,9 @@ class GlobalConfig { /// /// At the time of writing this just means ignoring .swiftsourceinfo files. bool OptimizeForIDE = false; + + /// Interval second for checking dependencies in fast code completion. + unsigned CompletionCheckDependencyInterval = 5; }; private: @@ -43,8 +46,10 @@ class GlobalConfig { mutable llvm::sys::Mutex Mtx; public: - Settings update(Optional OptimizeForIDE); + Settings update(Optional OptimizeForIDE, + Optional CompletionCheckDependencyInterval); bool shouldOptimizeForIDE() const; + unsigned getCompletionCheckDependencyInterval() const; }; class Context { diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index e7c5707da4305..cb3b13d3fabd5 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -37,6 +37,7 @@ class SourceFileSyntax; } // namespace swift namespace SourceKit { +class GlobalConfig; struct EntityInfo { UIdent Kind; @@ -145,7 +146,7 @@ class CodeCompletionConsumer { virtual void failed(StringRef ErrDescription) = 0; virtual void setCompletionKind(UIdent kind) {}; - virtual void setReusingASTContext(bool) {}; + virtual void setReusingASTContext(bool) = 0; virtual bool handleResult(const CodeCompletionInfo &Info) = 0; }; @@ -651,6 +652,8 @@ class LangSupport { virtual ~LangSupport() { } + virtual void globalConfigurationUpdated(std::shared_ptr Config) {}; + virtual void indexSource(StringRef Filename, IndexingConsumer &Consumer, ArrayRef Args) = 0; diff --git a/tools/SourceKit/lib/Core/Context.cpp b/tools/SourceKit/lib/Core/Context.cpp index 65a245283bd46..d93c510a85f7a 100644 --- a/tools/SourceKit/lib/Core/Context.cpp +++ b/tools/SourceKit/lib/Core/Context.cpp @@ -17,10 +17,13 @@ using namespace SourceKit; GlobalConfig::Settings -GlobalConfig::update(Optional OptimizeForIDE) { +GlobalConfig::update(Optional OptimizeForIDE, + Optional CompletionCheckDependencyInterval) { llvm::sys::ScopedLock L(Mtx); if (OptimizeForIDE.hasValue()) State.OptimizeForIDE = *OptimizeForIDE; + if (CompletionCheckDependencyInterval.hasValue()) + State.CompletionCheckDependencyInterval = *CompletionCheckDependencyInterval; return State; }; @@ -28,6 +31,10 @@ bool GlobalConfig::shouldOptimizeForIDE() const { llvm::sys::ScopedLock L(Mtx); return State.OptimizeForIDE; } +unsigned GlobalConfig::getCompletionCheckDependencyInterval() const { + llvm::sys::ScopedLock L(Mtx); + return State.CompletionCheckDependencyInterval; +} SourceKit::Context::Context(StringRef RuntimeLibPath, llvm::function_ref(Context &)> diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 1c8c8ccdb6edc..73a1e7b7aecc6 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -1221,6 +1221,7 @@ void SwiftLangSupport::codeCompleteOpen( completionKind = completionCtx.CodeCompletionKind; typeContextKind = completionCtx.typeContextKind; mayUseImplicitMemberExpr = completionCtx.MayUseImplicitMemberExpr; + consumer.setReusingASTContext(completionCtx.ReusingASTContext); completions = extendCompletions(results, sink, info, nameToPopularity, CCOpts); }); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index fcb4b4c5eadea..373306076dbaf 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -257,6 +257,13 @@ class InMemoryFileSystemProvider: public SourceKit::FileSystemProvider { }; } +static void +configureCompletionInstance(std::shared_ptr CompletionInst, + std::shared_ptr GlobalConfig) { + CompletionInst->setDependencyCheckIntervalSecond( + GlobalConfig->getCompletionCheckDependencyInterval()); +} + SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) : NotificationCtr(SKCtx.getNotificationCenter()), CCCache(new SwiftCompletionCache) { @@ -270,7 +277,8 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) SKCtx.getGlobalConfiguration(), Stats, RuntimeResourcePath); - CompletionInst = std::make_unique(); + CompletionInst = std::make_shared(); + configureCompletionInstance(CompletionInst, SKCtx.getGlobalConfiguration()); // By default, just use the in-memory cache. CCCache->inMemory = std::make_unique(); @@ -282,6 +290,11 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx) SwiftLangSupport::~SwiftLangSupport() { } +void SwiftLangSupport::globalConfigurationUpdated( + std::shared_ptr Config) { + configureCompletionInstance(CompletionInst, Config); +} + UIdent SwiftLangSupport::getUIDForDecl(const Decl *D, bool IsRef) { return UIdentVisitor(IsRef).visit(const_cast(D)); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 06cba4f8b89d9..b29e59a6b665c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -450,6 +450,8 @@ class SwiftLangSupport : public LangSupport { // LangSupport Interface //==========================================================================// + void globalConfigurationUpdated(std::shared_ptr Config) override; + void indexSource(StringRef Filename, IndexingConsumer &Consumer, ArrayRef Args) override; diff --git a/tools/SourceKit/tools/sourcekitd-test/Options.td b/tools/SourceKit/tools/sourcekitd-test/Options.td index 48b9d2101ecfa..5119a2e169470 100644 --- a/tools/SourceKit/tools/sourcekitd-test/Options.td +++ b/tools/SourceKit/tools/sourcekitd-test/Options.td @@ -143,6 +143,11 @@ def vfs_name : Separate<["-"], "vfs-name">, def optimize_for_ide : Joined<["-"], "for-ide=">, HelpText<"Value for the OptimizeForIde global configuration setting">; +def completion_check_dependency_interval : Separate<["-"], "completion-check-dependency-interval">, + HelpText<"Inteval seconds for checking dependencies in fast completion">; +def completion_check_dependency_interval_EQ : Joined<["-"], "completion-check-dependency-interval=">, + Alias; + def suppress_config_request : Flag<["-"], "suppress-config-request">, HelpText<"Suppress the default global configuration request, that is otherwise sent before any other request (except for the global-config request itself)">; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index 48330f4ac9080..ee577379698a8 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -381,6 +381,19 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { break; } + case OPT_completion_check_dependency_interval: { + int64_t Value; + if (StringRef(InputArg->getValue()).getAsInteger(10, Value)) { + llvm::errs() << "error: expected number for inteval\n"; + return true; + } else if (Value < 0) { + llvm::errs() << "error: completion-check-dependency-interval must be > 0\n"; + return true; + } + CompletionCheckDependencyInterval = Value; + break; + } + case OPT_suppress_config_request: SuppressDefaultConfigRequest = true; break; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index 7551ab48afee5..a0c6769bca1c0 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -114,6 +114,7 @@ struct TestOptions { bool timeRequest = false; llvm::Optional OptimizeForIde; bool SuppressDefaultConfigRequest = false; + llvm::Optional CompletionCheckDependencyInterval; unsigned repeatRequest = 1; struct VFSFile { std::string path; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 748a4d4e072b8..5bbc258a859b4 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -548,7 +548,14 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { case SourceKitRequest::GlobalConfiguration: sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGlobalConfiguration); if (Opts.OptimizeForIde.hasValue()) - sourcekitd_request_dictionary_set_int64(Req, KeyOptimizeForIDE, static_cast(Opts.OptimizeForIde.getValue())); + sourcekitd_request_dictionary_set_int64( + Req, KeyOptimizeForIDE, + static_cast(Opts.OptimizeForIde.getValue())); + if (Opts.CompletionCheckDependencyInterval.hasValue()) + sourcekitd_request_dictionary_set_int64( + Req, KeyCompletionCheckDependencyInterval, + static_cast( + Opts.CompletionCheckDependencyInterval.getValue())); break; case SourceKitRequest::ProtocolVersion: diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index 66d465e0ffbef..739fb0b436f26 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -438,9 +438,21 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) { if (!Req.getInt64(KeyOptimizeForIDE, EditorMode, true)) { OptimizeForIDE = EditorMode; } + Optional CompletionCheckDependencyInterval; + int64_t IntervalValue = 0; + if (!Req.getInt64(KeyCompletionCheckDependencyInterval, + IntervalValue, /*isOptional=*/true)) + CompletionCheckDependencyInterval = IntervalValue; + + GlobalConfig::Settings UpdatedConfig = Config->update( + OptimizeForIDE, CompletionCheckDependencyInterval); + + getGlobalContext().getSwiftLangSupport().globalConfigurationUpdated(Config); - GlobalConfig::Settings UpdatedConfig = Config->update(OptimizeForIDE); dict.set(KeyOptimizeForIDE, UpdatedConfig.OptimizeForIDE); + dict.set(KeyCompletionCheckDependencyInterval, + UpdatedConfig.CompletionCheckDependencyInterval); + return Rec(RB.createResponse()); } if (ReqUID == RequestProtocolVersion) { @@ -2022,6 +2034,7 @@ class SKGroupedCodeCompletionConsumer : public GroupedCodeCompletionConsumer { void startGroup(UIdent kind, StringRef name) override; void endGroup() override; void setNextRequestStart(unsigned offset) override; + void setReusingASTContext(bool flag) override; }; } // end anonymous namespace @@ -2219,6 +2232,10 @@ void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) { assert(!Response.isNull()); Response.set(KeyNextRequestStart, offset); } +void SKGroupedCodeCompletionConsumer::setReusingASTContext(bool flag) { + if (flag) + RespBuilder.getDictionary().setBool(KeyReusingASTContext, flag); +} //===----------------------------------------------------------------------===// // Type Context Info diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index 9bd7cb711ac86..d69e00ff7c62c 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -179,6 +179,8 @@ def __init__(self, internal_name, external_name): KEY('OptimizeForIDE', 'key.optimize_for_ide'), KEY('RequiredBystanders', 'key.required_bystanders'), KEY('ReusingASTContext', 'key.reusingastcontext'), + KEY('CompletionCheckDependencyInterval', + 'key.completion_check_dependency_interval'), ]