//===- CodeCompletion.h - Routines for code completion --------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #ifndef SWIFT_IDE_CODE_COMPLETION_H #define SWIFT_IDE_CODE_COMPLETION_H #include "swift/AST/Identifier.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/ThreadSafeRefCounted.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/TimeValue.h" #include #include #include #include namespace swift { class CodeCompletionCallbacksFactory; class Decl; class DeclContext; class ModuleDecl; namespace ide { class CodeCompletionContext; class CodeCompletionResultBuilder; /// \brief A routine to remove code completion tokens from code completion /// tests. /// /// \code /// code-completion-token: /// '#^' identifier '^#' /// \endcode /// /// \param Input test source code /// \param TokenName names the token which position should be returned in /// \p CompletionOffset. /// \param CompletionOffset set to ~0U on error, or to a 0-based byte offset on /// success /// /// \returns test source code without any code completion tokens. std::string removeCodeCompletionTokens(StringRef Input, StringRef TokenName, unsigned *CompletionOffset); /// \brief A structured representation of a code completion string. class CodeCompletionString { friend class CodeCompletionResultBuilder; public: class Chunk { friend class CodeCompletionResultBuilder; public: enum class ChunkKind { /// "internal", "private" or "public". AccessControlKeyword, /// such as @"availability" DeclAttrKeyword, /// such as "unavailable" etc. for @availability DeclAttrParamKeyword, /// The "override" keyword. OverrideKeyword, /// The keyword part of a declaration before the name, like "func". DeclIntroducer, /// Normal text chunk. Text, /// The first chunk of an optional substring that continues until /// \c NestingLevel decreases. OptionalBegin, // Punctuation. LeftParen, RightParen, LeftBracket, RightBracket, LeftAngle, RightAngle, Dot, Ellipsis, Comma, ExclamationMark, QuestionMark, Ampersand, /// The first chunk of a substring that describes the parameter for a /// generic type. GenericParameterBegin, /// Generic type parameter name. GenericParameterName, /// The first chunk of a substring that describes the parameter for a /// function call. CallParameterBegin, /// Function call parameter name. CallParameterName, /// Function call parameter internal / local name. If the parameter has no /// formal API name, it can still have a local name which can be useful /// for display purposes. /// /// This chunk should not be inserted into the editor buffer. CallParameterInternalName, /// A colon between parameter name and value. Should be inserted in the /// editor buffer if the preceding CallParameterName was inserted. CallParameterColon, /// A equal sign between parameter name and value. Used in decl attribute. DeclAttrParamEqual, /// Required parameter type. CallParameterType, /// Desugared closure parameter type. This can be used to get the /// closure type if CallParameterType is a NameAliasType. CallParameterClosureType, /// A placeholder for \c ! or \c ? in a call to a method found by dynamic /// lookup. /// /// The default spelling is \c !, but clients may render it as \c ? if /// desired. DynamicLookupMethodCallTail, /// A placeholder for \c ! or \c ? in a call to an optional method. /// /// The default spelling is \c !, but clients may render it as \c ? if /// desired. OptionalMethodCallTail, /// Specifies the type of the whole entity that is returned in this code /// completion result. For example, for variable references it is the /// variable type, for function calls it is the return type. /// /// This chunk should not be inserted into the editor buffer. TypeAnnotation, /// A brace statement -- left brace and right brace. The preferred /// position to put the cursor after the completion result is inserted /// into the editor buffer is between the braces. /// /// The spelling as always "{}", but clients may choose to insert newline /// and indentation in between. BraceStmtWithCursor, }; static bool chunkStartsNestedGroup(ChunkKind Kind) { return Kind == ChunkKind::CallParameterBegin || Kind == ChunkKind::GenericParameterBegin || Kind == ChunkKind::OptionalBegin; } static bool chunkHasText(ChunkKind Kind) { return Kind == ChunkKind::AccessControlKeyword || Kind == ChunkKind::OverrideKeyword || Kind == ChunkKind::DeclAttrKeyword || Kind == ChunkKind::DeclIntroducer || Kind == ChunkKind::Text || Kind == ChunkKind::LeftParen || Kind == ChunkKind::RightParen || Kind == ChunkKind::LeftBracket || Kind == ChunkKind::RightBracket || Kind == ChunkKind::LeftAngle || Kind == ChunkKind::RightAngle || Kind == ChunkKind::Dot || Kind == ChunkKind::Ellipsis || Kind == ChunkKind::Comma || Kind == ChunkKind::ExclamationMark || Kind == ChunkKind::QuestionMark || Kind == ChunkKind::Ampersand || Kind == ChunkKind::CallParameterName || Kind == ChunkKind::CallParameterInternalName || Kind == ChunkKind::CallParameterColon || Kind == ChunkKind::DeclAttrParamEqual || Kind == ChunkKind::DeclAttrParamKeyword || Kind == ChunkKind::CallParameterType || Kind == ChunkKind::CallParameterClosureType || Kind == ChunkKind::GenericParameterName || Kind == ChunkKind::DynamicLookupMethodCallTail || Kind == ChunkKind::OptionalMethodCallTail || Kind == ChunkKind::TypeAnnotation || Kind == ChunkKind::BraceStmtWithCursor; } private: unsigned Kind : 8; unsigned NestingLevel : 8; /// \brief If true, then this chunk is an annotation that is included only /// for exposition and may not be inserted in the editor buffer. unsigned IsAnnotation : 1; StringRef Text; Chunk(ChunkKind Kind, unsigned NestingLevel, StringRef Text) : Kind(unsigned(Kind)), NestingLevel(NestingLevel), IsAnnotation(0), Text(Text) { assert(chunkHasText(Kind)); } Chunk(ChunkKind Kind, unsigned NestingLevel) : Kind(unsigned(Kind)), NestingLevel(NestingLevel), IsAnnotation(0) { assert(!chunkHasText(Kind)); } void setIsAnnotation() { IsAnnotation = 1; } public: ChunkKind getKind() const { return ChunkKind(Kind); } bool is(ChunkKind K) const { return getKind() == K; } unsigned getNestingLevel() const { return NestingLevel; } bool isAnnotation() const { return IsAnnotation; } bool hasText() const { return chunkHasText(getKind()); } StringRef getText() const { assert(hasText()); return Text; } bool endsPreviousNestedGroup(unsigned GroupNestingLevel) const { return NestingLevel < GroupNestingLevel || (NestingLevel == GroupNestingLevel && chunkStartsNestedGroup(getKind())); } static Chunk createWithText(ChunkKind Kind, unsigned NestingLevel, StringRef Text) { return Chunk(Kind, NestingLevel, Text); } static Chunk createSimple(ChunkKind Kind, unsigned NestingLevel) { return Chunk(Kind, NestingLevel); } }; private: unsigned NumChunks : 16; CodeCompletionString(ArrayRef Chunks); public: /// Creates a \c CodeCompletionString from a list of \c Chunks. /// /// \note The caller must ensure any text inside \c Chunks will outlive this /// object, typically by storing them inside a \c CodeCompletionResultSink. static CodeCompletionString *create(llvm::BumpPtrAllocator &Allocator, ArrayRef Chunks); ArrayRef getChunks() const { return llvm::makeArrayRef(reinterpret_cast(this + 1), NumChunks); } StringRef getFirstTextChunk() const; Optional getFirstTextChunkIndex() const; /// Concatenates all text chunks considered part of the name to \p OS. void getName(raw_ostream &OS) const; /// Print a debug representation of the code completion string to \p OS. void print(raw_ostream &OS) const; void dump() const; }; /// \brief Describes the origin of the code completion result. /// /// This enum is ordered from the contexts that are "nearest" to the code /// completion point to "outside" contexts. enum class SemanticContextKind { /// Used in cases when the concept of semantic context is not applicable. None, /// \brief This is a highly-likely expression-context-specific completion /// result. This description is intentionally vague: this is a catch-all /// category for all heuristics for highly-likely results. /// /// For example, the name of an overridden superclass member inside a nominal /// member function has ExpressionSpecific context: /// \code /// class Base { /// init() {} /// init(a: Int) {} /// func foo() {} /// func bar() {} /// } /// class Derived { /// init() { /// super. // init() -- ExpressionSpecific /// // init(a: Int) -- Super /// } /// /// func foo() { /// super. // foo() -- ExpressionSpecific /// // bar() -- Super /// } /// } /// \endcode /// /// In C-style for loop headers the iteration variable has ExpressionSpecific /// context: /// \code /// for var foo = 0; #^A^# // foo -- ExpressionSpecific /// \endcode ExpressionSpecific, /// A declaration from the same function. Local, /// A declaration found in the immediately enclosing nominal decl. CurrentNominal, /// A declaration found in the superclass of the immediately enclosing /// nominal decl. Super, /// A declaration found in the non-immediately enclosing nominal decl. /// /// For example, 'Foo' is visible at (1) because of this. /// \code /// struct A { /// typealias Foo = Int /// struct B { /// func foo() { /// // (1) /// } /// } /// } /// \endcode OutsideNominal, /// A declaration from the current module. CurrentModule, /// A declaration imported from other module. OtherModule, }; /// The declaration kind of a code completion result, if it is a declaration. enum class CodeCompletionDeclKind { Class, Struct, Enum, EnumElement, Protocol, TypeAlias, GenericTypeParam, Constructor, Destructor, Subscript, StaticMethod, InstanceMethod, OperatorFunction, FreeFunction, StaticVar, InstanceVar, LocalVar, GlobalVar, }; /// \brief A single code completion result. class CodeCompletionResult { friend class CodeCompletionResultBuilder; public: enum ResultKind { Declaration, Keyword, Pattern }; private: unsigned Kind : 2; unsigned AssociatedDeclKind : 8; unsigned SemanticContext : 3; unsigned NotRecommended : 1; /// The number of bytes to the left of the code completion point that /// should be erased first if this completion string is inserted in the /// editor buffer. unsigned NumBytesToErase : 7; public: static const unsigned MaxNumBytesToErase = 127; private: CodeCompletionString *const CompletionString; StringRef ModuleName; StringRef BriefDocComment; ArrayRef AssociatedUSRs; public: /// Constructs a \c Pattern or \c Keyword result. /// /// \note The caller must ensure \c CodeCompletionString outlives this result. CodeCompletionResult(ResultKind Kind, SemanticContextKind SemanticContext, unsigned NumBytesToErase, CodeCompletionString *CompletionString) : Kind(Kind), SemanticContext(unsigned(SemanticContext)), NotRecommended(false), NumBytesToErase(NumBytesToErase), CompletionString(CompletionString) { assert(Kind != Declaration && "use the other constructor"); assert(CompletionString); } /// Constructs a \c Declaration result. /// /// \note The caller must ensure \c CodeCompletionString and any StringRef /// arguments outlive this result, typically by storing them in the same /// \c CodeCompletionResultSink as the result itself. CodeCompletionResult(SemanticContextKind SemanticContext, unsigned NumBytesToErase, CodeCompletionString *CompletionString, const Decl *AssociatedDecl, StringRef ModuleName, bool NotRecommended, StringRef BriefDocComment, ArrayRef AssociatedUSRs) : Kind(ResultKind::Declaration), SemanticContext(unsigned(SemanticContext)), NotRecommended(NotRecommended), NumBytesToErase(NumBytesToErase), CompletionString(CompletionString), ModuleName(ModuleName), BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs) { assert(AssociatedDecl && "should have a decl"); AssociatedDeclKind = unsigned(getCodeCompletionDeclKind(AssociatedDecl)); assert(CompletionString); } ResultKind getKind() const { return static_cast(Kind); } CodeCompletionDeclKind getAssociatedDeclKind() const { assert(getKind() == Declaration); return static_cast(AssociatedDeclKind); } SemanticContextKind getSemanticContext() const { return static_cast(SemanticContext); } bool isNotRecommended() const { return NotRecommended; } unsigned getNumBytesToErase() const { return NumBytesToErase; } const CodeCompletionString *getCompletionString() const { return CompletionString; } StringRef getModuleName() const { return ModuleName; } StringRef getBriefDocComment() const { return BriefDocComment; } ArrayRef getAssociatedUSRs() const { return AssociatedUSRs; } /// Print a debug representation of the code completion result to \p OS. void print(raw_ostream &OS) const; void dump() const; static CodeCompletionDeclKind getCodeCompletionDeclKind(const Decl *D); }; struct CodeCompletionResultSink { using AllocatorPtr = std::shared_ptr; /// The allocator used to allocate results "native" to this sink. AllocatorPtr Allocator; /// Allocators that keep alive "foreign" results imported into this sink from /// other sinks. std::vector ForeignAllocators; std::vector Results; /// A single-element cache for module names stored in Allocator, keyed by a /// clang::Module * or swift::ModuleDecl *. std::pair LastModule; CodeCompletionResultSink() : Allocator(std::make_shared()) {} }; struct CodeCompletionCacheImpl; /// \brief In-memory per-module code completion result cache. /// /// These results persist between multiple code completion requests and can be /// used with different ASTContexts. class CodeCompletionCache { std::unique_ptr Impl; public: /// \brief Cache key. struct Key { std::string ModuleFilename; std::string ModuleName; std::vector AccessPath; bool ResultsHaveLeadingDot; bool ForTestableLookup; friend bool operator==(const Key &LHS, const Key &RHS) { return LHS.ModuleFilename == RHS.ModuleFilename && LHS.ModuleName == RHS.ModuleName && LHS.AccessPath == RHS.AccessPath && LHS.ResultsHaveLeadingDot == RHS.ResultsHaveLeadingDot && LHS.ForTestableLookup == RHS.ForTestableLookup; } }; struct Value : public ThreadSafeRefCountedBase { llvm::sys::TimeValue ModuleModificationTime; CodeCompletionResultSink Sink; }; using ValueRefCntPtr = llvm::IntrusiveRefCntPtr; CodeCompletionCache(); ~CodeCompletionCache(); ValueRefCntPtr createValue(); Optional get(const Key &K); void set(const Key &K, ValueRefCntPtr V); }; struct RequestedCachedModule { CodeCompletionCache::Key Key; const ModuleDecl *TheModule; bool OnlyTypes; }; class CodeCompletionContext { friend class CodeCompletionResultBuilder; /// \brief A set of current completion results, not yet delivered to the /// consumer. CodeCompletionResultSink CurrentResults; public: CodeCompletionCache &Cache; CodeCompletionContext(CodeCompletionCache &Cache) : Cache(Cache) {} /// \brief Allocate a string owned by the code completion context. StringRef copyString(StringRef Str); /// \brief Return current code completion results. MutableArrayRef takeResults(); /// \brief Sort code completion results in an implementetion-defined order /// in place. static void sortCompletionResults( MutableArrayRef Results); CodeCompletionResultSink &getResultSink() { return CurrentResults; } }; /// \brief An abstract base class for consumers of code completion results. /// \see \c SimpleCachingCodeCompletionConsumer. class CodeCompletionConsumer { public: virtual ~CodeCompletionConsumer() {} virtual void handleResultsAndModules(CodeCompletionContext &context, ArrayRef requestedModules, DeclContext *DCForModules) = 0; }; /// A simplified code completion consumer interface that clients can use to get /// CodeCompletionResults with automatic caching of top-level completions from /// imported modules. struct SimpleCachingCodeCompletionConsumer : public CodeCompletionConsumer { // Implement the CodeCompletionConsumer interface. void handleResultsAndModules(CodeCompletionContext &context, ArrayRef requestedModules, DeclContext *DCForModules) override; /// Clients should overrride this method to receive \p Results. virtual void handleResults( MutableArrayRef Results) = 0; }; /// \brief A code completion result consumer that prints the results to a /// \c raw_ostream. class PrintingCodeCompletionConsumer : public SimpleCachingCodeCompletionConsumer { llvm::raw_ostream &OS; bool IncludeKeywords; public: PrintingCodeCompletionConsumer(llvm::raw_ostream &OS, bool IncludeKeywords = true) : OS(OS), IncludeKeywords(IncludeKeywords) { } void handleResults( MutableArrayRef Results) override; }; /// \brief Create a factory for code completion callbacks. CodeCompletionCallbacksFactory * makeCodeCompletionCallbacksFactory(CodeCompletionContext &CompletionContext, CodeCompletionConsumer &Consumer); /// Lookup the top-level code completions from \p module and store them in /// \p targetSink. /// /// Results are looked up as if in \p currDeclContext, which may be null. void lookupCodeCompletionResultsFromModule(CodeCompletionResultSink &targetSink, const ModuleDecl *module, ArrayRef accessPath, bool needLeadingDot, const DeclContext *currDeclContext); /// Copy code completion results from \p sourceSink to \p targetSink, possibly /// restricting by \p onlyTypes. void copyCodeCompletionResults(CodeCompletionResultSink &targetSink, CodeCompletionResultSink &sourceSink, bool onlyTypes); } // namespace ide } // namespace swift #endif // SWIFT_IDE_CODE_COMPLETION_H