mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1265 lines
48 KiB
C++
1265 lines
48 KiB
C++
//===--- SwiftCompletion.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 "CodeCompletionOrganizer.h"
|
|
#include "SwiftASTManager.h"
|
|
#include "SwiftLangSupport.h"
|
|
#include "SwiftEditorDiagConsumer.h"
|
|
#include "SourceKit/Support/FileSystemProvider.h"
|
|
#include "SourceKit/Support/Logging.h"
|
|
#include "SourceKit/Support/UIdent.h"
|
|
|
|
#include "swift/AST/ASTPrinter.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
|
|
#include "swift/IDE/CodeCompletionCache.h"
|
|
#include "swift/IDE/CodeCompletionResultPrinter.h"
|
|
#include "swift/IDE/CompletionInstance.h"
|
|
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
|
|
using namespace SourceKit;
|
|
using namespace swift;
|
|
using namespace ide;
|
|
using CodeCompletion::SwiftCompletionInfo;
|
|
using CodeCompletion::Completion;
|
|
using CodeCompletion::CodeCompletionView;
|
|
using CodeCompletion::CodeCompletionViewRef;
|
|
using CodeCompletion::NameToPopularityMap;
|
|
|
|
static_assert(swift::ide::CodeCompletionResult::MaxNumBytesToErase == 127,
|
|
"custom array implementation for code completion results "
|
|
"has this limit hardcoded");
|
|
|
|
namespace {
|
|
struct SwiftToSourceKitCompletionAdapter {
|
|
static bool handleResult(SourceKit::CodeCompletionConsumer &consumer,
|
|
CodeCompletionResult *result,
|
|
bool annotatedDescription) {
|
|
llvm::SmallString<64> name;
|
|
{
|
|
llvm::raw_svector_ostream OSS(name);
|
|
CodeCompletion::CompletionBuilder::getFilterName(
|
|
result->getCompletionString(), OSS);
|
|
}
|
|
|
|
llvm::SmallString<64> description;
|
|
{
|
|
llvm::raw_svector_ostream OSS(description);
|
|
// FIXME: The leading punctuation (e.g. "?." in an optional completion)
|
|
// should really be part of a structured result description and clients
|
|
// can
|
|
// decide whether to display it or not. For now just include it in the
|
|
// description only in the new code path.
|
|
ide::printCodeCompletionResultDescription(*result, OSS,
|
|
/*leadingPunctuation=*/false);
|
|
}
|
|
|
|
Completion extended(*result, name, description);
|
|
return handleResult(consumer, &extended, /*leadingPunctuation=*/false,
|
|
/*legacyLiteralToKeyword=*/true, annotatedDescription);
|
|
}
|
|
|
|
static bool handleResult(SourceKit::CodeCompletionConsumer &consumer,
|
|
Completion *result, bool leadingPunctuation,
|
|
bool legacyLiteralToKeyword,
|
|
bool annotatedDescription);
|
|
|
|
static void getResultAssociatedUSRs(ArrayRef<StringRef> AssocUSRs,
|
|
raw_ostream &OS);
|
|
};
|
|
|
|
struct SwiftCodeCompletionConsumer
|
|
: public ide::SimpleCachingCodeCompletionConsumer {
|
|
using HandlerFunc = std::function<void(
|
|
MutableArrayRef<CodeCompletionResult *>, SwiftCompletionInfo &)>;
|
|
HandlerFunc handleResultsImpl;
|
|
SwiftCompletionInfo swiftContext;
|
|
|
|
SwiftCodeCompletionConsumer(HandlerFunc handleResultsImpl)
|
|
: handleResultsImpl(handleResultsImpl) {}
|
|
|
|
void setContext(swift::ASTContext *context,
|
|
const swift::CompilerInvocation *invocation,
|
|
swift::ide::CodeCompletionContext *completionContext) {
|
|
swiftContext.swiftASTContext = context;
|
|
swiftContext.invocation = invocation;
|
|
swiftContext.completionContext = completionContext;
|
|
}
|
|
void clearContext() { swiftContext = SwiftCompletionInfo(); }
|
|
|
|
void handleResults(MutableArrayRef<CodeCompletionResult *> Results) override {
|
|
assert(swiftContext.swiftASTContext);
|
|
handleResultsImpl(Results, swiftContext);
|
|
}
|
|
};
|
|
} // anonymous namespace
|
|
|
|
/// Returns completion context kind \c UIdent to report to the client.
|
|
/// For now, only returns "unresolved member" kind because others are unstable.
|
|
/// Returns invalid \c UIents other cases.
|
|
static UIdent getUIDForCodeCompletionKindToReport(CompletionKind kind) {
|
|
switch (kind) {
|
|
case CompletionKind::UnresolvedMember:
|
|
return UIdent("source.lang.swift.completion.unresolvedmember");
|
|
default:
|
|
return UIdent();
|
|
}
|
|
}
|
|
|
|
static bool swiftCodeCompleteImpl(
|
|
SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile,
|
|
unsigned Offset, SwiftCodeCompletionConsumer &SwiftConsumer,
|
|
ArrayRef<const char *> Args,
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
|
|
bool EnableASTCaching, bool annotateDescription, std::string &Error) {
|
|
return Lang.performCompletionLikeOperation(
|
|
UnresolvedInputFile, Offset, Args, FileSystem, EnableASTCaching, Error,
|
|
[&](CompilerInstance &CI, bool reusingASTContext) {
|
|
// Create a factory for code completion callbacks that will feed the
|
|
// Consumer.
|
|
auto swiftCache = Lang.getCodeCompletionCache(); // Pin the cache.
|
|
ide::CodeCompletionContext CompletionContext(swiftCache->getCache());
|
|
CompletionContext.ReusingASTContext = reusingASTContext;
|
|
CompletionContext.setAnnotateResult(annotateDescription);
|
|
std::unique_ptr<CodeCompletionCallbacksFactory> callbacksFactory(
|
|
ide::makeCodeCompletionCallbacksFactory(CompletionContext,
|
|
SwiftConsumer));
|
|
|
|
SwiftConsumer.setContext(&CI.getASTContext(), &CI.getInvocation(),
|
|
&CompletionContext);
|
|
|
|
auto *SF = CI.getCodeCompletionFile();
|
|
performCodeCompletionSecondPass(*SF, *callbacksFactory);
|
|
SwiftConsumer.clearContext();
|
|
});
|
|
}
|
|
|
|
static void translateCodeCompletionOptions(OptionsDictionary &from,
|
|
CodeCompletion::Options &to,
|
|
StringRef &filterText,
|
|
unsigned &resultOffset,
|
|
unsigned &maxResults);
|
|
|
|
void SwiftLangSupport::codeComplete(
|
|
llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset,
|
|
OptionsDictionary *options,
|
|
SourceKit::CodeCompletionConsumer &SKConsumer, ArrayRef<const char *> Args,
|
|
Optional<VFSOptions> vfsOptions) {
|
|
|
|
CodeCompletion::Options CCOpts;
|
|
if (options) {
|
|
StringRef filterText;
|
|
unsigned resultOffset = 0;
|
|
unsigned maxResults = 0;
|
|
translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset,
|
|
maxResults);
|
|
}
|
|
|
|
std::string error;
|
|
// FIXME: the use of None as primary file is to match the fact we do not read
|
|
// the document contents using the editor documents infrastructure.
|
|
auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error);
|
|
if (!fileSystem)
|
|
return SKConsumer.failed(error);
|
|
|
|
SwiftCodeCompletionConsumer SwiftConsumer([&](
|
|
MutableArrayRef<CodeCompletionResult *> Results,
|
|
SwiftCompletionInfo &info) {
|
|
|
|
auto kind = getUIDForCodeCompletionKindToReport(
|
|
info.completionContext->CodeCompletionKind);
|
|
if (kind.isValid())
|
|
SKConsumer.setCompletionKind(kind);
|
|
|
|
bool hasRequiredType = info.completionContext->typeContextKind == TypeContextKind::Required;
|
|
if (CCOpts.sortByName)
|
|
CodeCompletionContext::sortCompletionResults(Results);
|
|
// FIXME: this adhoc filtering should be configurable like it is in the
|
|
// codeCompleteOpen path.
|
|
for (auto *Result : Results) {
|
|
if (Result->getKind() == CodeCompletionResult::Literal) {
|
|
switch (Result->getLiteralKind()) {
|
|
case CodeCompletionLiteralKind::NilLiteral:
|
|
case CodeCompletionLiteralKind::BooleanLiteral:
|
|
break;
|
|
case CodeCompletionLiteralKind::ImageLiteral:
|
|
case CodeCompletionLiteralKind::ColorLiteral:
|
|
if (hasRequiredType &&
|
|
Result->getExpectedTypeRelation() <
|
|
CodeCompletionResult::Convertible)
|
|
continue;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
if (!SwiftToSourceKitCompletionAdapter::handleResult(
|
|
SKConsumer, Result, CCOpts.annotatedDescription))
|
|
break;
|
|
}
|
|
|
|
SKConsumer.setReusingASTContext(info.completionContext->ReusingASTContext);
|
|
SKConsumer.setAnnotatedTypename(info.completionContext->getAnnotateResult());
|
|
});
|
|
|
|
std::string Error;
|
|
if (!swiftCodeCompleteImpl(*this, UnresolvedInputFile, Offset, SwiftConsumer,
|
|
Args, fileSystem,
|
|
CCOpts.reuseASTContextIfPossible,
|
|
CCOpts.annotatedDescription, Error)) {
|
|
SKConsumer.failed(Error);
|
|
}
|
|
}
|
|
|
|
static void getResultStructure(
|
|
CodeCompletion::SwiftResult *result, bool leadingPunctuation,
|
|
CodeCompletionInfo::DescriptionStructure &structure,
|
|
std::vector<CodeCompletionInfo::ParameterStructure> ¶meters) {
|
|
auto *CCStr = result->getCompletionString();
|
|
auto FirstTextChunk = CCStr->getFirstTextChunkIndex(leadingPunctuation);
|
|
|
|
if (!FirstTextChunk.hasValue())
|
|
return;
|
|
|
|
bool isOperator = result->isOperator();
|
|
|
|
auto chunks = CCStr->getChunks();
|
|
using ChunkKind = CodeCompletionString::Chunk::ChunkKind;
|
|
|
|
unsigned i = *FirstTextChunk;
|
|
unsigned textSize = 0;
|
|
|
|
// The result name.
|
|
for (; i < chunks.size(); ++i) {
|
|
auto C = chunks[i];
|
|
if (C.is(ChunkKind::TypeAnnotation) ||
|
|
C.is(ChunkKind::CallParameterClosureType) ||
|
|
C.is(ChunkKind::CallParameterClosureExpr) ||
|
|
C.is(ChunkKind::Whitespace))
|
|
continue;
|
|
|
|
if (C.is(ChunkKind::LeftParen) || C.is(ChunkKind::LeftBracket) ||
|
|
C.is(ChunkKind::BraceStmtWithCursor) ||
|
|
C.is(ChunkKind::CallParameterBegin))
|
|
break;
|
|
|
|
if (C.hasText())
|
|
textSize += C.getText().size();
|
|
}
|
|
|
|
structure.baseName.begin = 0;
|
|
structure.baseName.end = textSize;
|
|
|
|
// The parameters.
|
|
for (; i < chunks.size(); ++i) {
|
|
auto &C = chunks[i];
|
|
if (C.is(ChunkKind::TypeAnnotation) ||
|
|
C.is(ChunkKind::CallParameterClosureType) ||
|
|
C.is(ChunkKind::CallParameterClosureExpr) ||
|
|
C.is(ChunkKind::Whitespace))
|
|
continue;
|
|
|
|
if (C.is(ChunkKind::BraceStmtWithCursor))
|
|
break;
|
|
|
|
if (C.is(ChunkKind::ThrowsKeyword) ||
|
|
C.is(ChunkKind::RethrowsKeyword)) {
|
|
structure.throwsRange.begin = textSize;
|
|
structure.throwsRange.end = textSize + C.getText().size();
|
|
}
|
|
|
|
if (C.is(ChunkKind::CallParameterBegin)) {
|
|
CodeCompletionInfo::ParameterStructure param;
|
|
|
|
++i;
|
|
bool inName = false;
|
|
bool inAfterColon = false;
|
|
for (; i < chunks.size(); ++i) {
|
|
if (chunks[i].endsPreviousNestedGroup(C.getNestingLevel()))
|
|
break;
|
|
if (chunks[i].is(ChunkKind::CallParameterClosureType) ||
|
|
chunks[i].is(ChunkKind::CallParameterClosureExpr))
|
|
continue;
|
|
if (isOperator && chunks[i].is(ChunkKind::CallParameterType))
|
|
continue;
|
|
|
|
// Parameter name
|
|
if (chunks[i].is(ChunkKind::CallParameterName) ||
|
|
chunks[i].is(ChunkKind::CallParameterInternalName)) {
|
|
param.name.begin = textSize;
|
|
param.isLocalName =
|
|
chunks[i].is(ChunkKind::CallParameterInternalName);
|
|
inName = true;
|
|
}
|
|
|
|
// Parameter type
|
|
if (chunks[i].is(ChunkKind::CallParameterType)) {
|
|
unsigned start = textSize;
|
|
unsigned prev = i - 1; // if i == 0, prev = ~0u.
|
|
|
|
// Combine & for inout into the type name.
|
|
if (prev != ~0u && chunks[prev].is(ChunkKind::Ampersand)) {
|
|
start -= chunks[prev].getText().size();
|
|
prev -= 1;
|
|
}
|
|
|
|
// Combine the whitespace after ':' into the type name.
|
|
if (prev != ~0u && chunks[prev].is(ChunkKind::CallParameterColon))
|
|
start -= 1;
|
|
|
|
param.afterColon.begin = start;
|
|
inAfterColon = true;
|
|
if (inName) {
|
|
param.name.end = start;
|
|
inName = false;
|
|
}
|
|
}
|
|
|
|
if (chunks[i].hasText())
|
|
textSize += chunks[i].getText().size();
|
|
}
|
|
|
|
// If we had a name with no type following it, finish it now.
|
|
if (inName)
|
|
param.name.end = textSize;
|
|
|
|
// Finish the type name.
|
|
if (inAfterColon)
|
|
param.afterColon.end = textSize;
|
|
if (!param.range().empty())
|
|
parameters.push_back(std::move(param));
|
|
|
|
if (i < chunks.size() && chunks[i].hasText())
|
|
textSize += chunks[i].getText().size();
|
|
}
|
|
|
|
if (C.hasText())
|
|
textSize += C.getText().size();
|
|
}
|
|
|
|
if (!parameters.empty()) {
|
|
structure.parameterRange.begin = parameters.front().range().begin;
|
|
structure.parameterRange.end = parameters.back().range().end;
|
|
}
|
|
}
|
|
|
|
static UIdent KindLiteralArray("source.lang.swift.literal.array");
|
|
static UIdent KindLiteralBoolean("source.lang.swift.literal.boolean");
|
|
static UIdent KindLiteralColor("source.lang.swift.literal.color");
|
|
static UIdent KindLiteralImage("source.lang.swift.literal.image");
|
|
static UIdent KindLiteralDictionary("source.lang.swift.literal.dictionary");
|
|
static UIdent KindLiteralInteger("source.lang.swift.literal.integer");
|
|
static UIdent KindLiteralNil("source.lang.swift.literal.nil");
|
|
static UIdent KindLiteralString("source.lang.swift.literal.string");
|
|
static UIdent KindLiteralTuple("source.lang.swift.literal.tuple");
|
|
|
|
static UIdent
|
|
getUIDForCodeCompletionLiteralKind(CodeCompletionLiteralKind kind) {
|
|
switch (kind) {
|
|
case CodeCompletionLiteralKind::ArrayLiteral:
|
|
return KindLiteralArray;
|
|
case CodeCompletionLiteralKind::BooleanLiteral:
|
|
return KindLiteralBoolean;
|
|
case CodeCompletionLiteralKind::ColorLiteral:
|
|
return KindLiteralColor;
|
|
case CodeCompletionLiteralKind::ImageLiteral:
|
|
return KindLiteralImage;
|
|
case CodeCompletionLiteralKind::DictionaryLiteral:
|
|
return KindLiteralDictionary;
|
|
case CodeCompletionLiteralKind::IntegerLiteral:
|
|
return KindLiteralInteger;
|
|
case CodeCompletionLiteralKind::NilLiteral:
|
|
return KindLiteralNil;
|
|
case CodeCompletionLiteralKind::StringLiteral:
|
|
return KindLiteralString;
|
|
case CodeCompletionLiteralKind::Tuple:
|
|
return KindLiteralTuple;
|
|
}
|
|
}
|
|
|
|
static UIdent KeywordUID("source.lang.swift.keyword");
|
|
static UIdent KeywordLetUID("source.lang.swift.keyword.let");
|
|
static UIdent KeywordVarUID("source.lang.swift.keyword.var");
|
|
static UIdent KeywordIfUID("source.lang.swift.keyword.if");
|
|
static UIdent KeywordForUID("source.lang.swift.keyword.for");
|
|
static UIdent KeywordWhileUID("source.lang.swift.keyword.while");
|
|
static UIdent KeywordFuncUID("source.lang.swift.keyword.func");
|
|
|
|
bool SwiftToSourceKitCompletionAdapter::handleResult(
|
|
SourceKit::CodeCompletionConsumer &Consumer, Completion *Result,
|
|
bool leadingPunctuation, bool legacyLiteralToKeyword,
|
|
bool annotatedDescription) {
|
|
|
|
static UIdent KeywordUID("source.lang.swift.keyword");
|
|
static UIdent PatternUID("source.lang.swift.pattern");
|
|
|
|
CodeCompletionInfo Info;
|
|
if (Result->hasCustomKind()) {
|
|
Info.CustomKind = Result->getCustomKind();
|
|
} else if (Result->getKind() == CodeCompletionResult::Keyword) {
|
|
Info.Kind = KeywordUID;
|
|
} else if (Result->getKind() == CodeCompletionResult::Pattern) {
|
|
Info.Kind = PatternUID;
|
|
} else if (Result->getKind() == CodeCompletionResult::BuiltinOperator) {
|
|
Info.Kind = PatternUID; // FIXME: add a UID for operators
|
|
} else if (Result->getKind() == CodeCompletionResult::Declaration) {
|
|
Info.Kind = SwiftLangSupport::getUIDForCodeCompletionDeclKind(
|
|
Result->getAssociatedDeclKind());
|
|
} else if (Result->getKind() == CodeCompletionResult::Literal) {
|
|
auto literalKind = Result->getLiteralKind();
|
|
if (legacyLiteralToKeyword &&
|
|
(literalKind == CodeCompletionLiteralKind::BooleanLiteral ||
|
|
literalKind == CodeCompletionLiteralKind::NilLiteral)) {
|
|
// Fallback to keywords as appropriate.
|
|
Info.Kind = KeywordUID;
|
|
} else {
|
|
Info.Kind = getUIDForCodeCompletionLiteralKind(literalKind);
|
|
}
|
|
}
|
|
|
|
if (Info.Kind.isInvalid() && !Info.CustomKind)
|
|
return true;
|
|
|
|
llvm::SmallString<512> SS;
|
|
|
|
unsigned DescBegin = SS.size();
|
|
{
|
|
llvm::raw_svector_ostream ccOS(SS);
|
|
if (annotatedDescription)
|
|
ide::printCodeCompletionResultDescriptionAnnotated(*Result, ccOS,
|
|
leadingPunctuation);
|
|
else
|
|
ide::printCodeCompletionResultDescription(*Result, ccOS,
|
|
leadingPunctuation);
|
|
}
|
|
unsigned DescEnd = SS.size();
|
|
|
|
if (DescBegin == DescEnd) {
|
|
LOG_FUNC_SECTION_WARN {
|
|
llvm::SmallString<64> LogMessage;
|
|
llvm::raw_svector_ostream LogMessageOs(LogMessage);
|
|
|
|
LogMessageOs << "Code completion result with empty description "
|
|
"was ignored: \n";
|
|
Result->printPrefix(LogMessageOs);
|
|
Result->getCompletionString()->print(LogMessageOs);
|
|
|
|
*Log << LogMessage;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
unsigned TextBegin = SS.size();
|
|
{
|
|
llvm::raw_svector_ostream ccOS(SS);
|
|
ide::printCodeCompletionResultSourceText(*Result, ccOS);
|
|
}
|
|
unsigned TextEnd = SS.size();
|
|
|
|
unsigned TypeBegin = SS.size();
|
|
{
|
|
llvm::raw_svector_ostream ccOS(SS);
|
|
if (annotatedDescription)
|
|
ide::printCodeCompletionResultTypeNameAnnotated(*Result, ccOS);
|
|
else
|
|
ide::printCodeCompletionResultTypeName(*Result, ccOS);
|
|
}
|
|
unsigned TypeEnd = SS.size();
|
|
|
|
unsigned USRsBegin = SS.size();
|
|
{
|
|
llvm::raw_svector_ostream ccOS(SS);
|
|
getResultAssociatedUSRs(Result->getAssociatedUSRs(), ccOS);
|
|
}
|
|
unsigned USRsEnd = SS.size();
|
|
|
|
Info.Name = Result->getName();
|
|
Info.Description = StringRef(SS.begin() + DescBegin, DescEnd - DescBegin);
|
|
Info.SourceText = StringRef(SS.begin() + TextBegin, TextEnd - TextBegin);
|
|
Info.TypeName = StringRef(SS.begin() + TypeBegin, TypeEnd - TypeBegin);
|
|
Info.AssocUSRs = StringRef(SS.begin() + USRsBegin, USRsEnd - USRsBegin);
|
|
|
|
static UIdent CCCtxNone("source.codecompletion.context.none");
|
|
static UIdent CCCtxExpressionSpecific(
|
|
"source.codecompletion.context.exprspecific");
|
|
static UIdent CCCtxLocal("source.codecompletion.context.local");
|
|
static UIdent CCCtxCurrentNominal("source.codecompletion.context.thisclass");
|
|
static UIdent CCCtxSuper("source.codecompletion.context.superclass");
|
|
static UIdent CCCtxOutsideNominal("source.codecompletion.context.otherclass");
|
|
static UIdent CCCtxCurrentModule("source.codecompletion.context.thismodule");
|
|
static UIdent CCCtxOtherModule("source.codecompletion.context.othermodule");
|
|
|
|
switch (Result->getSemanticContext()) {
|
|
case SemanticContextKind::None:
|
|
Info.SemanticContext = CCCtxNone; break;
|
|
case SemanticContextKind::ExpressionSpecific:
|
|
Info.SemanticContext = CCCtxExpressionSpecific; break;
|
|
case SemanticContextKind::Local:
|
|
Info.SemanticContext = CCCtxLocal; break;
|
|
case SemanticContextKind::CurrentNominal:
|
|
Info.SemanticContext = CCCtxCurrentNominal; break;
|
|
case SemanticContextKind::Super:
|
|
Info.SemanticContext = CCCtxSuper; break;
|
|
case SemanticContextKind::OutsideNominal:
|
|
Info.SemanticContext = CCCtxOutsideNominal; break;
|
|
case SemanticContextKind::CurrentModule:
|
|
Info.SemanticContext = CCCtxCurrentModule; break;
|
|
case SemanticContextKind::OtherModule:
|
|
Info.SemanticContext = CCCtxOtherModule; break;
|
|
}
|
|
|
|
static UIdent CCTypeRelNotApplicable("source.codecompletion.typerelation.notapplicable");
|
|
static UIdent CCTypeRelUnknown("source.codecompletion.typerelation.unknown");
|
|
static UIdent CCTypeRelUnrelated("source.codecompletion.typerelation.unrelated");
|
|
static UIdent CCTypeRelInvalid("source.codecompletion.typerelation.invalid");
|
|
static UIdent CCTypeRelConvertible("source.codecompletion.typerelation.convertible");
|
|
static UIdent CCTypeRelIdentical("source.codecompletion.typerelation.identical");
|
|
|
|
switch (Result->getExpectedTypeRelation()) {
|
|
case CodeCompletionResult::NotApplicable:
|
|
Info.TypeRelation = CCTypeRelNotApplicable; break;
|
|
case CodeCompletionResult::Unknown:
|
|
Info.TypeRelation = CCTypeRelUnknown; break;
|
|
case CodeCompletionResult::Unrelated:
|
|
Info.TypeRelation = CCTypeRelUnrelated; break;
|
|
case CodeCompletionResult::Invalid:
|
|
Info.TypeRelation = CCTypeRelInvalid; break;
|
|
case CodeCompletionResult::Convertible:
|
|
Info.TypeRelation = CCTypeRelConvertible; break;
|
|
case CodeCompletionResult::Identical:
|
|
Info.TypeRelation = CCTypeRelIdentical; break;
|
|
}
|
|
|
|
Info.ModuleName = Result->getModuleName();
|
|
Info.DocBrief = Result->getBriefDocComment();
|
|
Info.NotRecommended = Result->isNotRecommended();
|
|
Info.IsSystem = Result->isSystem();
|
|
|
|
Info.NumBytesToErase = Result->getNumBytesToErase();
|
|
|
|
// Extended result values.
|
|
Info.ModuleImportDepth = Result->getModuleImportDepth();
|
|
|
|
// Description structure.
|
|
std::vector<CodeCompletionInfo::ParameterStructure> parameters;
|
|
CodeCompletionInfo::DescriptionStructure structure;
|
|
getResultStructure(Result, leadingPunctuation, structure, parameters);
|
|
Info.descriptionStructure = structure;
|
|
if (!parameters.empty())
|
|
Info.parametersStructure = parameters;
|
|
|
|
return Consumer.handleResult(Info);
|
|
}
|
|
|
|
static CodeCompletionLiteralKind
|
|
getCodeCompletionLiteralKindForUID(UIdent uid) {
|
|
if (uid == KindLiteralArray) {
|
|
return CodeCompletionLiteralKind::ArrayLiteral;
|
|
} else if (uid == KindLiteralBoolean) {
|
|
return CodeCompletionLiteralKind::BooleanLiteral;
|
|
} else if (uid == KindLiteralColor) {
|
|
return CodeCompletionLiteralKind::ColorLiteral;
|
|
} else if (uid == KindLiteralImage) {
|
|
return CodeCompletionLiteralKind::ImageLiteral;
|
|
} else if (uid == KindLiteralDictionary) {
|
|
return CodeCompletionLiteralKind::DictionaryLiteral;
|
|
} else if (uid == KindLiteralInteger) {
|
|
return CodeCompletionLiteralKind::IntegerLiteral;
|
|
} else if (uid == KindLiteralNil) {
|
|
return CodeCompletionLiteralKind::NilLiteral;
|
|
} else if (uid == KindLiteralString) {
|
|
return CodeCompletionLiteralKind::StringLiteral;
|
|
} else if (uid == KindLiteralTuple) {
|
|
return CodeCompletionLiteralKind::Tuple;
|
|
} else {
|
|
llvm_unreachable("unexpected literal kind");
|
|
}
|
|
}
|
|
|
|
static CodeCompletionKeywordKind
|
|
getCodeCompletionKeywordKindForUID(UIdent uid) {
|
|
#define SWIFT_KEYWORD(kw) \
|
|
static UIdent Keyword##kw##UID("source.lang.swift.keyword." #kw); \
|
|
if (uid == Keyword##kw##UID) { \
|
|
return CodeCompletionKeywordKind::kw_##kw; \
|
|
}
|
|
#include "swift/Syntax/TokenKinds.def"
|
|
|
|
// FIXME: should warn about unexpected keyword kind.
|
|
return CodeCompletionKeywordKind::None;
|
|
}
|
|
|
|
void SwiftToSourceKitCompletionAdapter::getResultAssociatedUSRs(
|
|
ArrayRef<StringRef> AssocUSRs, raw_ostream &OS) {
|
|
bool First = true;
|
|
for (auto USR : AssocUSRs) {
|
|
if (!First)
|
|
OS << " ";
|
|
First = false;
|
|
OS << USR;
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// CodeCompletion::SessionCache
|
|
//===----------------------------------------------------------------------===//
|
|
void CodeCompletion::SessionCache::setSortedCompletions(
|
|
std::vector<Completion *> &&completions) {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
sortedCompletions = std::move(completions);
|
|
}
|
|
ArrayRef<Completion *> CodeCompletion::SessionCache::getSortedCompletions() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return sortedCompletions;
|
|
}
|
|
llvm::MemoryBuffer *CodeCompletion::SessionCache::getBuffer() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return buffer.get();
|
|
}
|
|
ArrayRef<std::string> CodeCompletion::SessionCache::getCompilerArgs() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return args;
|
|
}
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> CodeCompletion::SessionCache::getFileSystem() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return fileSystem;
|
|
}
|
|
CompletionKind CodeCompletion::SessionCache::getCompletionKind() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return completionKind;
|
|
}
|
|
TypeContextKind CodeCompletion::SessionCache::getCompletionTypeContextKind() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return typeContextKind;
|
|
}
|
|
bool CodeCompletion::SessionCache::getCompletionMayUseImplicitMemberExpr() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return completionMayUseImplicitMemberExpr;
|
|
}
|
|
const CodeCompletion::FilterRules &
|
|
CodeCompletion::SessionCache::getFilterRules() {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
return filterRules;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// CodeCompletion::SessionCacheMap
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
unsigned CodeCompletion::SessionCacheMap::getBufferID(StringRef name) const {
|
|
auto pair = nameToBufferMap.insert(std::make_pair(name, nextBufferID));
|
|
if (pair.second)
|
|
++nextBufferID;
|
|
return pair.first->getValue();
|
|
}
|
|
|
|
CodeCompletion::SessionCacheRef
|
|
CodeCompletion::SessionCacheMap::get(StringRef name, unsigned offset) const {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
auto key = std::make_pair(getBufferID(name), offset);
|
|
auto I = sessions.find(key);
|
|
if (I == sessions.end())
|
|
return nullptr;
|
|
return I->second;
|
|
}
|
|
bool CodeCompletion::SessionCacheMap::set(StringRef name, unsigned offset,
|
|
CodeCompletion::SessionCacheRef s) {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
auto key = std::make_pair(getBufferID(name), offset);
|
|
return sessions.insert(std::make_pair(key, s)).second;
|
|
}
|
|
bool CodeCompletion::SessionCacheMap::remove(StringRef name, unsigned offset) {
|
|
llvm::sys::ScopedLock L(mtx);
|
|
auto key = std::make_pair(getBufferID(name), offset);
|
|
return sessions.erase(key);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// (New) Code completion interface
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
class SwiftGroupedCodeCompletionConsumer : public CodeCompletionView::Walker {
|
|
GroupedCodeCompletionConsumer &consumer;
|
|
|
|
public:
|
|
SwiftGroupedCodeCompletionConsumer(GroupedCodeCompletionConsumer &consumer)
|
|
: consumer(consumer) {}
|
|
bool handleResult(Completion *result) override {
|
|
return SwiftToSourceKitCompletionAdapter::handleResult(
|
|
consumer, result, /*leadingPunctuation=*/true,
|
|
/*legacyLiteralToKeyword=*/false, /*annotatedDescription=*/false);
|
|
}
|
|
void startGroup(StringRef name) override {
|
|
static UIdent GroupUID("source.lang.swift.codecomplete.group");
|
|
consumer.startGroup(GroupUID, name);
|
|
}
|
|
void endGroup() override { consumer.endGroup(); }
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static void translateCodeCompletionOptions(OptionsDictionary &from,
|
|
CodeCompletion::Options &to,
|
|
StringRef &filterText,
|
|
unsigned &resultOffset,
|
|
unsigned &maxResults) {
|
|
static UIdent KeySortByName("key.codecomplete.sort.byname");
|
|
static UIdent KeyUseImportDepth("key.codecomplete.sort.useimportdepth");
|
|
static UIdent KeyGroupOverloads("key.codecomplete.group.overloads");
|
|
static UIdent KeyGroupStems("key.codecomplete.group.stems");
|
|
static UIdent KeyFilterText("key.codecomplete.filtertext");
|
|
static UIdent KeyRequestLimit("key.codecomplete.requestlimit");
|
|
static UIdent KeyRequestStart("key.codecomplete.requeststart");
|
|
static UIdent KeyHideUnderscores("key.codecomplete.hideunderscores");
|
|
static UIdent KeyHideLowPriority("key.codecomplete.hidelowpriority");
|
|
static UIdent KeyHideByName("key.codecomplete.hidebyname");
|
|
static UIdent KeyIncludeExactMatch("key.codecomplete.includeexactmatch");
|
|
static UIdent KeyAddInnerResults("key.codecomplete.addinnerresults");
|
|
static UIdent KeyAddInnerOperators("key.codecomplete.addinneroperators");
|
|
static UIdent KeyAddInitsToTopLevel("key.codecomplete.addinitstotoplevel");
|
|
static UIdent KeyCallPatternHeuristics("key.codecomplete.callpatternheuristics");
|
|
static UIdent KeyFuzzyMatching("key.codecomplete.fuzzymatching");
|
|
static UIdent KeyTopNonLiteral("key.codecomplete.showtopnonliteralresults");
|
|
static UIdent KeyContextWeight("key.codecomplete.sort.contextweight");
|
|
static UIdent KeyFuzzyWeight("key.codecomplete.sort.fuzzyweight");
|
|
static UIdent KeyPopularityBonus("key.codecomplete.sort.popularitybonus");
|
|
static UIdent KeyReuseASTContext("key.codecomplete.reuseastcontext");
|
|
static UIdent KeyAnnotatedDescription("key.codecomplete.annotateddescription");
|
|
|
|
from.valueForOption(KeySortByName, to.sortByName);
|
|
from.valueForOption(KeyUseImportDepth, to.useImportDepth);
|
|
from.valueForOption(KeyGroupOverloads, to.groupOverloads);
|
|
from.valueForOption(KeyGroupStems, to.groupStems);
|
|
from.valueForOption(KeyFilterText, filterText);
|
|
from.valueForOption(KeyRequestLimit, maxResults);
|
|
from.valueForOption(KeyRequestStart, resultOffset);
|
|
unsigned howMuchHiding = 1;
|
|
from.valueForOption(KeyHideUnderscores, howMuchHiding);
|
|
to.hideUnderscores = howMuchHiding;
|
|
to.reallyHideAllUnderscores = howMuchHiding > 1;
|
|
from.valueForOption(KeyHideLowPriority, to.hideLowPriority);
|
|
from.valueForOption(KeyIncludeExactMatch, to.includeExactMatch);
|
|
from.valueForOption(KeyAddInnerResults, to.addInnerResults);
|
|
from.valueForOption(KeyAddInnerOperators, to.addInnerOperators);
|
|
from.valueForOption(KeyAddInitsToTopLevel, to.addInitsToTopLevel);
|
|
from.valueForOption(KeyCallPatternHeuristics, to.callPatternHeuristics);
|
|
from.valueForOption(KeyFuzzyMatching, to.fuzzyMatching);
|
|
from.valueForOption(KeyContextWeight, to.semanticContextWeight);
|
|
from.valueForOption(KeyFuzzyWeight, to.fuzzyMatchWeight);
|
|
from.valueForOption(KeyPopularityBonus, to.popularityBonus);
|
|
from.valueForOption(KeyHideByName, to.hideByNameStyle);
|
|
from.valueForOption(KeyTopNonLiteral, to.showTopNonLiteralResults);
|
|
from.valueForOption(KeyReuseASTContext, to.reuseASTContextIfPossible);
|
|
from.valueForOption(KeyAnnotatedDescription, to.annotatedDescription);
|
|
}
|
|
|
|
/// Canonicalize a name that is in the format of a reference to a function into
|
|
/// the name format used internally for filtering.
|
|
///
|
|
/// Returns true if the name is invalid.
|
|
static bool canonicalizeFilterName(const char *origName,
|
|
SmallVectorImpl<char> &Result) {
|
|
assert(origName);
|
|
const char *p = origName;
|
|
char curr = '\0';
|
|
char prev;
|
|
|
|
// FIXME: disallow unnamed parameters without underscores `foo(::)`.
|
|
while (true) {
|
|
prev = curr;
|
|
curr = *p++;
|
|
switch (curr) {
|
|
case '\0':
|
|
return false; // Done.
|
|
case '_':
|
|
// Remove the _ underscore for an unnamed parameter.
|
|
if (prev == ':' || prev == '(') {
|
|
char next = *p;
|
|
if (next == ':' || next == ')')
|
|
continue;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
default:
|
|
Result.push_back(curr);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void translateFilterRules(ArrayRef<FilterRule> rawFilterRules,
|
|
CodeCompletion::FilterRules &filterRules) {
|
|
for (auto &rule : rawFilterRules) {
|
|
switch (rule.kind) {
|
|
case FilterRule::Everything:
|
|
filterRules.hideAll = rule.hide;
|
|
break;
|
|
case FilterRule::Identifier:
|
|
for (auto name : rule.names) {
|
|
SmallString<128> canonName;
|
|
// Note: name is null-terminated.
|
|
if (canonicalizeFilterName(name.data(), canonName))
|
|
continue;
|
|
filterRules.hideByFilterName[canonName] = rule.hide;
|
|
}
|
|
break;
|
|
case FilterRule::Description:
|
|
for (auto name : rule.names) {
|
|
filterRules.hideByDescription[name] = rule.hide;
|
|
}
|
|
break;
|
|
case FilterRule::Module:
|
|
for (auto name : rule.names) {
|
|
filterRules.hideModule[name] = rule.hide;
|
|
}
|
|
break;
|
|
case FilterRule::Keyword:
|
|
if (rule.uids.empty())
|
|
filterRules.hideAllKeywords = rule.hide;
|
|
for (auto uid : rule.uids) {
|
|
auto kind = getCodeCompletionKeywordKindForUID(uid);
|
|
filterRules.hideKeyword[kind] = rule.hide;
|
|
}
|
|
break;
|
|
case FilterRule::Literal:
|
|
if (rule.uids.empty())
|
|
filterRules.hideAllValueLiterals = rule.hide;
|
|
for (auto uid : rule.uids) {
|
|
auto kind = getCodeCompletionLiteralKindForUID(uid);
|
|
filterRules.hideValueLiteral[kind] = rule.hide;
|
|
}
|
|
break;
|
|
case FilterRule::CustomCompletion:
|
|
if (rule.uids.empty())
|
|
filterRules.hideCustomCompletions = rule.hide;
|
|
// FIXME: hide individual custom completions
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool checkInnerResult(CodeCompletionResult *result, bool &hasDot,
|
|
bool &hasQDot, bool &hasInit) {
|
|
auto chunks = result->getCompletionString()->getChunks();
|
|
if (!chunks.empty() &&
|
|
chunks[0].is(CodeCompletionString::Chunk::ChunkKind::Dot)) {
|
|
hasDot = true;
|
|
return true;
|
|
} else if (chunks.size() >= 2 &&
|
|
chunks[0].is(
|
|
CodeCompletionString::Chunk::ChunkKind::QuestionMark) &&
|
|
chunks[1].is(CodeCompletionString::Chunk::ChunkKind::Dot)) {
|
|
hasQDot = true;
|
|
return true;
|
|
} else if (result->getKind() ==
|
|
CodeCompletion::SwiftResult::ResultKind::Declaration &&
|
|
result->getAssociatedDeclKind() ==
|
|
CodeCompletionDeclKind::Constructor) {
|
|
hasInit = true;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template <typename Result>
|
|
static std::vector<Result *>
|
|
filterInnerResults(ArrayRef<Result *> results, bool includeInner,
|
|
bool includeInnerOperators,
|
|
bool &hasDot, bool &hasQDot, bool &hasInit,
|
|
const CodeCompletion::FilterRules &rules) {
|
|
std::vector<Result *> topResults;
|
|
for (auto *result : results) {
|
|
if (!includeInnerOperators && result->isOperator())
|
|
continue;
|
|
|
|
llvm::SmallString<64> filterName;
|
|
{
|
|
llvm::raw_svector_ostream OSS(filterName);
|
|
CodeCompletion::CompletionBuilder::getFilterName(
|
|
result->getCompletionString(), OSS);
|
|
}
|
|
llvm::SmallString<64> description;
|
|
{
|
|
llvm::raw_svector_ostream OSS(description);
|
|
ide::printCodeCompletionResultDescription(*result, OSS,
|
|
/*leadingPunctuation=*/false);
|
|
}
|
|
if (rules.hideCompletion(result, filterName, description))
|
|
continue;
|
|
|
|
bool inner = checkInnerResult(result, hasDot, hasQDot, hasInit);
|
|
|
|
if (!inner ||
|
|
(includeInner &&
|
|
result->getSemanticContext() <= SemanticContextKind::CurrentNominal))
|
|
topResults.push_back(result);
|
|
}
|
|
return topResults;
|
|
}
|
|
|
|
static void transformAndForwardResults(
|
|
GroupedCodeCompletionConsumer &consumer, SwiftLangSupport &lang,
|
|
CodeCompletion::SessionCacheRef session,
|
|
const NameToPopularityMap *nameToPopularity,
|
|
CodeCompletion::Options options, unsigned offset, StringRef filterText,
|
|
unsigned resultOffset, unsigned maxResults) {
|
|
|
|
CodeCompletion::CompletionSink innerSink;
|
|
Completion *exactMatch = nullptr;
|
|
auto buildInnerResult = [&](ArrayRef<CodeCompletionString::Chunk> chunks) {
|
|
auto *completionString =
|
|
CodeCompletionString::create(innerSink.allocator, chunks);
|
|
CodeCompletion::SwiftResult paren(
|
|
CodeCompletion::SwiftResult::ResultKind::BuiltinOperator,
|
|
SemanticContextKind::ExpressionSpecific,
|
|
exactMatch ? exactMatch->getNumBytesToErase() : 0, completionString,
|
|
CodeCompletionResult::ExpectedTypeRelation::NotApplicable);
|
|
|
|
SwiftCompletionInfo info;
|
|
std::vector<Completion *> extended =
|
|
extendCompletions(&paren, innerSink, info, nameToPopularity, options,
|
|
exactMatch, SemanticContextKind::ExpressionSpecific);
|
|
assert(extended.size() == 1);
|
|
return extended.front();
|
|
};
|
|
auto buildParen = [&]() {
|
|
return buildInnerResult(CodeCompletionString::Chunk::createWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::LeftParen, 0, "("));
|
|
};
|
|
auto buildDot = [&]() {
|
|
return buildInnerResult(CodeCompletionString::Chunk::createWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::Dot, 0, "."));
|
|
};
|
|
auto buildQDot = [&]() {
|
|
CodeCompletionString::Chunk chunks[2] = {
|
|
CodeCompletionString::Chunk::createWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::QuestionMark, 0, "?"),
|
|
CodeCompletionString::Chunk::createWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::Dot, 0, ".")};
|
|
return buildInnerResult(chunks);
|
|
};
|
|
|
|
CodeCompletion::CodeCompletionOrganizer organizer(
|
|
options, session->getCompletionKind(),
|
|
session->getCompletionTypeContextKind());
|
|
|
|
auto &rules = session->getFilterRules();
|
|
|
|
bool hasEarlyInnerResults =
|
|
session->getCompletionKind() == CompletionKind::PostfixExpr;
|
|
|
|
if (!hasEarlyInnerResults) {
|
|
organizer.addCompletionsWithFilter(session->getSortedCompletions(),
|
|
filterText, rules, exactMatch);
|
|
// Add leading dot?
|
|
if (options.addInnerOperators && !rules.hideFilterName(".") &&
|
|
session->getCompletionMayUseImplicitMemberExpr()) {
|
|
organizer.addCompletionsWithFilter(
|
|
buildDot(), filterText, CodeCompletion::FilterRules(), exactMatch);
|
|
}
|
|
}
|
|
|
|
if (hasEarlyInnerResults &&
|
|
(options.addInnerResults || options.addInnerOperators)) {
|
|
bool hasInit = false;
|
|
bool hasDot = false;
|
|
bool hasQDot = false;
|
|
auto completions = session->getSortedCompletions();
|
|
auto innerResults =
|
|
filterInnerResults(completions, options.addInnerResults,
|
|
options.addInnerOperators, hasDot, hasQDot, hasInit,
|
|
rules);
|
|
if (options.addInnerOperators) {
|
|
if (hasInit && !rules.hideFilterName("("))
|
|
innerResults.insert(innerResults.begin(), buildParen());
|
|
if (hasDot && !rules.hideFilterName("."))
|
|
innerResults.insert(innerResults.begin(), buildDot());
|
|
if (hasQDot && !rules.hideFilterName("?."))
|
|
innerResults.insert(innerResults.begin(), buildQDot());
|
|
}
|
|
|
|
organizer.addCompletionsWithFilter(innerResults, filterText,
|
|
CodeCompletion::FilterRules(), exactMatch);
|
|
}
|
|
|
|
organizer.groupAndSort(options);
|
|
|
|
if ((options.addInnerResults || options.addInnerOperators) &&
|
|
exactMatch && exactMatch->getKind() == Completion::Declaration) {
|
|
std::vector<Completion *> innerResults;
|
|
bool hasDot = false;
|
|
bool hasQDot = false;
|
|
bool hasInit = false;
|
|
SwiftCodeCompletionConsumer swiftConsumer([&](
|
|
MutableArrayRef<CodeCompletionResult *> results,
|
|
SwiftCompletionInfo &info) {
|
|
auto *context = info.completionContext;
|
|
if (!context || context->CodeCompletionKind != CompletionKind::PostfixExpr)
|
|
return;
|
|
auto topResults = filterInnerResults(results, options.addInnerResults,
|
|
options.addInnerOperators, hasDot,
|
|
hasQDot, hasInit, rules);
|
|
// FIXME: Overriding the default to context "None" is a hack so that they
|
|
// won't overwhelm other results that also match the filter text.
|
|
innerResults = extendCompletions(
|
|
topResults, innerSink, info, nameToPopularity, options, exactMatch,
|
|
SemanticContextKind::None, SemanticContextKind::None);
|
|
});
|
|
|
|
auto *inputBuf = session->getBuffer();
|
|
std::string str = inputBuf->getBuffer().slice(0, offset).str();
|
|
{
|
|
llvm::raw_string_ostream OSS(str);
|
|
ide::printCodeCompletionResultSourceText(*exactMatch, OSS);
|
|
}
|
|
|
|
auto buffer =
|
|
llvm::MemoryBuffer::getMemBuffer(str, inputBuf->getBufferIdentifier());
|
|
auto args = session->getCompilerArgs();
|
|
std::vector<const char *> cargs;
|
|
for (auto &arg : args)
|
|
cargs.push_back(arg.c_str());
|
|
std::string error;
|
|
if (!swiftCodeCompleteImpl(lang, buffer.get(), str.size(), swiftConsumer,
|
|
cargs, session->getFileSystem(),
|
|
options.reuseASTContextIfPossible,
|
|
options.annotatedDescription, error)) {
|
|
consumer.failed(error);
|
|
return;
|
|
}
|
|
|
|
if (options.addInnerOperators) {
|
|
if (hasInit && !rules.hideFilterName("("))
|
|
innerResults.insert(innerResults.begin(), buildParen());
|
|
if (hasDot && !rules.hideFilterName("."))
|
|
innerResults.insert(innerResults.begin(), buildDot());
|
|
if (hasQDot && !rules.hideFilterName("?."))
|
|
innerResults.insert(innerResults.begin(), buildQDot());
|
|
}
|
|
|
|
// Add the inner results (and don't filter them).
|
|
exactMatch = nullptr; // No longer needed.
|
|
organizer.addCompletionsWithFilter(innerResults, filterText,
|
|
CodeCompletion::FilterRules(), exactMatch);
|
|
|
|
CodeCompletion::Options noGroupOpts = options;
|
|
noGroupOpts.groupStems = false;
|
|
noGroupOpts.groupOverloads = false;
|
|
organizer.groupAndSort(noGroupOpts);
|
|
}
|
|
|
|
// Build the final results view.
|
|
auto view = organizer.takeResultsView();
|
|
CodeCompletion::LimitedResultView limitedResults(*view, resultOffset,
|
|
maxResults);
|
|
|
|
// Forward results to the SourceKit consumer.
|
|
SwiftGroupedCodeCompletionConsumer groupedConsumer(consumer);
|
|
limitedResults.walk(groupedConsumer);
|
|
consumer.setNextRequestStart(limitedResults.getNextOffset());
|
|
}
|
|
|
|
void SwiftLangSupport::codeCompleteOpen(
|
|
StringRef name, llvm::MemoryBuffer *inputBuf, unsigned offset,
|
|
OptionsDictionary *options, ArrayRef<FilterRule> rawFilterRules,
|
|
GroupedCodeCompletionConsumer &consumer, ArrayRef<const char *> args,
|
|
Optional<VFSOptions> vfsOptions) {
|
|
StringRef filterText;
|
|
unsigned resultOffset = 0;
|
|
unsigned maxResults = 0;
|
|
CodeCompletion::Options CCOpts;
|
|
if (options)
|
|
translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset,
|
|
maxResults);
|
|
|
|
std::string error;
|
|
// FIXME: the use of None as primary file is to match the fact we do not read
|
|
// the document contents using the editor documents infrastructure.
|
|
auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error);
|
|
if (!fileSystem)
|
|
return consumer.failed(error);
|
|
|
|
CodeCompletion::FilterRules filterRules;
|
|
translateFilterRules(rawFilterRules, filterRules);
|
|
|
|
// Set up the code completion consumer to pass results to organizer.
|
|
CodeCompletion::CompletionSink sink;
|
|
std::vector<Completion *> completions;
|
|
|
|
NameToPopularityMap *nameToPopularity = nullptr;
|
|
// This reference must outlive the uses of nameToPopularity.
|
|
auto popularAPIRef = PopularAPI;
|
|
if (popularAPIRef) {
|
|
nameToPopularity = &popularAPIRef->nameToFactor;
|
|
}
|
|
|
|
CompletionKind completionKind = CompletionKind::None;
|
|
TypeContextKind typeContextKind = TypeContextKind::None;
|
|
bool mayUseImplicitMemberExpr = false;
|
|
|
|
SwiftCodeCompletionConsumer swiftConsumer(
|
|
[&](MutableArrayRef<CodeCompletionResult *> results,
|
|
SwiftCompletionInfo &info) {
|
|
auto &completionCtx = *info.completionContext;
|
|
completionKind = completionCtx.CodeCompletionKind;
|
|
typeContextKind = completionCtx.typeContextKind;
|
|
mayUseImplicitMemberExpr = completionCtx.MayUseImplicitMemberExpr;
|
|
consumer.setReusingASTContext(completionCtx.ReusingASTContext);
|
|
consumer.setAnnotatedTypename(completionCtx.getAnnotateResult());
|
|
completions =
|
|
extendCompletions(results, sink, info, nameToPopularity, CCOpts);
|
|
});
|
|
|
|
// Add any codecomplete.open specific flags.
|
|
std::vector<const char *> extendedArgs(args.begin(), args.end());
|
|
if (CCOpts.addInitsToTopLevel) {
|
|
extendedArgs.push_back("-Xfrontend");
|
|
extendedArgs.push_back("-code-complete-inits-in-postfix-expr");
|
|
}
|
|
if (CCOpts.callPatternHeuristics) {
|
|
extendedArgs.push_back("-Xfrontend");
|
|
extendedArgs.push_back("-code-complete-call-pattern-heuristics");
|
|
}
|
|
|
|
// Invoke completion.
|
|
if (!swiftCodeCompleteImpl(*this, inputBuf, offset, swiftConsumer,
|
|
extendedArgs, fileSystem,
|
|
CCOpts.reuseASTContextIfPossible,
|
|
CCOpts.annotatedDescription, error)) {
|
|
consumer.failed(error);
|
|
return;
|
|
}
|
|
|
|
// Add any relevant custom completions.
|
|
if (auto custom = CustomCompletions)
|
|
addCustomCompletions(sink, completions, custom->customCompletions,
|
|
completionKind);
|
|
|
|
// Pre-sort the completions.
|
|
CodeCompletion::CodeCompletionOrganizer::preSortCompletions(completions);
|
|
|
|
// Store in the session.
|
|
using CodeCompletion::SessionCache;
|
|
using CodeCompletion::SessionCacheRef;
|
|
auto bufferCopy = llvm::MemoryBuffer::getMemBufferCopy(
|
|
inputBuf->getBuffer(), inputBuf->getBufferIdentifier());
|
|
std::vector<std::string> argsCopy(extendedArgs.begin(), extendedArgs.end());
|
|
SessionCacheRef session{new SessionCache(
|
|
std::move(sink), std::move(bufferCopy), std::move(argsCopy), fileSystem,
|
|
completionKind, typeContextKind, mayUseImplicitMemberExpr,
|
|
std::move(filterRules))};
|
|
session->setSortedCompletions(std::move(completions));
|
|
|
|
if (!CCSessions.set(name, offset, session)) {
|
|
std::string err;
|
|
llvm::raw_string_ostream OS(err);
|
|
OS << "codecomplete.open: code completion session for '" << name << "', "
|
|
<< offset << " already exists";
|
|
consumer.failed(OS.str());
|
|
}
|
|
|
|
transformAndForwardResults(consumer, *this, session, nameToPopularity, CCOpts,
|
|
offset, filterText, resultOffset, maxResults);
|
|
}
|
|
|
|
void SwiftLangSupport::codeCompleteClose(
|
|
StringRef name, unsigned offset, GroupedCodeCompletionConsumer &consumer) {
|
|
if (!CCSessions.remove(name, offset)) {
|
|
std::string err;
|
|
llvm::raw_string_ostream OS(err);
|
|
OS << "codecomplete.close: no code completion session for '" << name
|
|
<< "', " << offset;
|
|
consumer.failed(OS.str());
|
|
}
|
|
}
|
|
|
|
void SwiftLangSupport::codeCompleteUpdate(
|
|
StringRef name, unsigned offset, OptionsDictionary *options,
|
|
GroupedCodeCompletionConsumer &consumer) {
|
|
auto session = CCSessions.get(name, offset);
|
|
if (!session) {
|
|
std::string err;
|
|
llvm::raw_string_ostream OS(err);
|
|
OS << "codecomplete.update: no code completion session for '" << name
|
|
<< "', " << offset;
|
|
consumer.failed(OS.str());
|
|
return;
|
|
}
|
|
|
|
StringRef filterText;
|
|
unsigned resultOffset = 0;
|
|
unsigned maxResults = 0;
|
|
|
|
// FIXME: consider whether CCOpts has changed since the 'open'.
|
|
CodeCompletion::Options CCOpts;
|
|
if (options)
|
|
translateCodeCompletionOptions(*options, CCOpts, filterText, resultOffset,
|
|
maxResults);
|
|
|
|
NameToPopularityMap *nameToPopularity = nullptr;
|
|
// This reference must outlive the uses of nameToPopularity.
|
|
auto popularAPIRef = PopularAPI;
|
|
if (popularAPIRef) {
|
|
nameToPopularity = &popularAPIRef->nameToFactor;
|
|
}
|
|
|
|
transformAndForwardResults(consumer, *this, session, nameToPopularity, CCOpts,
|
|
offset, filterText, resultOffset, maxResults);
|
|
}
|
|
|
|
swift::ide::CodeCompletionCache &SwiftCompletionCache::getCache() {
|
|
return *inMemory;
|
|
}
|
|
SwiftCompletionCache::~SwiftCompletionCache() {}
|
|
|
|
void SwiftLangSupport::codeCompleteCacheOnDisk(StringRef path) {
|
|
ThreadSafeRefCntPtr<SwiftCompletionCache> newCache(new SwiftCompletionCache);
|
|
newCache->onDisk = std::make_unique<ide::OnDiskCodeCompletionCache>(path);
|
|
newCache->inMemory =
|
|
std::make_unique<ide::CodeCompletionCache>(newCache->onDisk.get());
|
|
|
|
CCCache = newCache; // replace the old cache.
|
|
}
|
|
|
|
void SwiftLangSupport::codeCompleteSetPopularAPI(
|
|
ArrayRef<const char *> popularAPI, ArrayRef<const char *> unpopularAPI) {
|
|
using Factor = CodeCompletion::PopularityFactor;
|
|
ThreadSafeRefCntPtr<SwiftPopularAPI> newPopularAPI(new SwiftPopularAPI);
|
|
auto &nameToFactor = newPopularAPI->nameToFactor;
|
|
for (unsigned i = 0, n = popularAPI.size(); i < n; ++i) {
|
|
SmallString<64> name;
|
|
if (canonicalizeFilterName(popularAPI[i], name))
|
|
continue;
|
|
nameToFactor[name] = Factor(double(n - i) / n);
|
|
}
|
|
for (unsigned i = 0, n = unpopularAPI.size(); i < n; ++i) {
|
|
SmallString<64> name;
|
|
if (canonicalizeFilterName(unpopularAPI[i], name))
|
|
continue;
|
|
nameToFactor[name] = Factor(-double(n - i) / n);
|
|
}
|
|
|
|
PopularAPI = newPopularAPI; // replace the old popular API.
|
|
}
|
|
|
|
void SwiftLangSupport::codeCompleteSetCustom(
|
|
ArrayRef<CustomCompletionInfo> completions) {
|
|
ThreadSafeRefCntPtr<SwiftCustomCompletions> newCustomCompletions(
|
|
new SwiftCustomCompletions);
|
|
newCustomCompletions->customCompletions.assign(completions.begin(),
|
|
completions.end());
|
|
CustomCompletions = newCustomCompletions; // Replace the existing completions.
|
|
}
|