Files
swift-mirror/include/swift/IDE/CodeCompletion.h
Ben Langmuir f3ecb63f30 Move caching logic into the code completion consumer
This will let us implement caching in the client (e.g. SourceKit) at
some point and simplifies adding more levels of caching. Requires a
corresponding SourceKit change.

Swift SVN r28365
2015-05-09 18:51:30 +00:00

657 lines
20 KiB
C++

//===- 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 <functional>
#include <memory>
#include <string>
#include <vector>
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<Chunk> 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<Chunk> Chunks);
ArrayRef<Chunk> getChunks() const {
return llvm::makeArrayRef(reinterpret_cast<const Chunk *>(this + 1),
NumChunks);
}
StringRef getFirstTextChunk() const;
Optional<unsigned> 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<StringRef> 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<StringRef> 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<ResultKind>(Kind); }
CodeCompletionDeclKind getAssociatedDeclKind() const {
assert(getKind() == Declaration);
return static_cast<CodeCompletionDeclKind>(AssociatedDeclKind);
}
SemanticContextKind getSemanticContext() const {
return static_cast<SemanticContextKind>(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<StringRef> 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<llvm::BumpPtrAllocator>;
/// 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<AllocatorPtr> ForeignAllocators;
std::vector<CodeCompletionResult *> Results;
/// A single-element cache for module names stored in Allocator, keyed by a
/// clang::Module * or swift::ModuleDecl *.
std::pair<void *, StringRef> LastModule;
CodeCompletionResultSink()
: Allocator(std::make_shared<llvm::BumpPtrAllocator>()) {}
};
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<CodeCompletionCacheImpl> Impl;
public:
/// \brief Cache key.
struct Key {
std::string ModuleFilename;
std::string ModuleName;
std::vector<std::string> 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<Value> {
llvm::sys::TimeValue ModuleModificationTime;
CodeCompletionResultSink Sink;
};
using ValueRefCntPtr = llvm::IntrusiveRefCntPtr<Value>;
CodeCompletionCache();
~CodeCompletionCache();
ValueRefCntPtr createValue();
Optional<ValueRefCntPtr> 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<CodeCompletionResult *> takeResults();
/// \brief Sort code completion results in an implementetion-defined order
/// in place.
static void sortCompletionResults(
MutableArrayRef<CodeCompletionResult *> 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<RequestedCachedModule> 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<RequestedCachedModule> requestedModules,
DeclContext *DCForModules) override;
/// Clients should overrride this method to receive \p Results.
virtual void handleResults(
MutableArrayRef<CodeCompletionResult *> 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<CodeCompletionResult *> 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<std::string> 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