[clangd] Support returning a limited number of completion results.
Summary: All results are scored, we only process CodeCompletionStrings for the winners. We now return CompletionList rather than CompletionItem[] (both are valid). sortText is now based on CodeCompletionResult::orderedName (mostly the same). This is the first clangd-only completion option, so plumbing changed. It requires a small clangd patch (exposing CodeCompletionResult::orderedName). (This can't usefully be enabled yet: we don't support server-side filtering) Reviewers: ilya-biryukov Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D39852 llvm-svn: 318287
This commit is contained in:
parent
1102a861f5
commit
a40371bcb6
|
@ -195,15 +195,15 @@ void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) {
|
void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) {
|
||||||
auto Items = Server
|
auto List = Server
|
||||||
.codeComplete(Params.textDocument.uri.file,
|
.codeComplete(
|
||||||
Position{Params.position.line,
|
Params.textDocument.uri.file,
|
||||||
Params.position.character})
|
Position{Params.position.line, Params.position.character})
|
||||||
.get() // FIXME(ibiryukov): This could be made async if we
|
.get() // FIXME(ibiryukov): This could be made async if we
|
||||||
// had an API that would allow to attach callbacks to
|
// had an API that would allow to attach callbacks to
|
||||||
// futures returned by ClangdServer.
|
// futures returned by ClangdServer.
|
||||||
.Value;
|
.Value;
|
||||||
C.reply(json::ary(Items));
|
C.reply(List);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdLSPServer::onSignatureHelp(Ctx C,
|
void ClangdLSPServer::onSignatureHelp(Ctx C,
|
||||||
|
|
|
@ -222,11 +222,11 @@ std::future<void> ClangdServer::forceReparse(PathRef File) {
|
||||||
std::move(TaggedFS));
|
std::move(TaggedFS));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future<Tagged<std::vector<CompletionItem>>>
|
std::future<Tagged<CompletionList>>
|
||||||
ClangdServer::codeComplete(PathRef File, Position Pos,
|
ClangdServer::codeComplete(PathRef File, Position Pos,
|
||||||
llvm::Optional<StringRef> OverridenContents,
|
llvm::Optional<StringRef> OverridenContents,
|
||||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) {
|
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) {
|
||||||
using ResultType = Tagged<std::vector<CompletionItem>>;
|
using ResultType = Tagged<CompletionList>;
|
||||||
|
|
||||||
std::promise<ResultType> ResultPromise;
|
std::promise<ResultType> ResultPromise;
|
||||||
|
|
||||||
|
@ -242,11 +242,10 @@ ClangdServer::codeComplete(PathRef File, Position Pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdServer::codeComplete(
|
void ClangdServer::codeComplete(
|
||||||
UniqueFunction<void(Tagged<std::vector<CompletionItem>>)> Callback,
|
UniqueFunction<void(Tagged<CompletionList>)> Callback, PathRef File,
|
||||||
PathRef File, Position Pos, llvm::Optional<StringRef> OverridenContents,
|
Position Pos, llvm::Optional<StringRef> OverridenContents,
|
||||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) {
|
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) {
|
||||||
using CallbackType =
|
using CallbackType = UniqueFunction<void(Tagged<CompletionList>)>;
|
||||||
UniqueFunction<void(Tagged<std::vector<CompletionItem>>)>;
|
|
||||||
|
|
||||||
std::string Contents;
|
std::string Contents;
|
||||||
if (OverridenContents) {
|
if (OverridenContents) {
|
||||||
|
@ -283,7 +282,7 @@ void ClangdServer::codeComplete(
|
||||||
// FIXME(ibiryukov): even if Preamble is non-null, we may want to check
|
// FIXME(ibiryukov): even if Preamble is non-null, we may want to check
|
||||||
// both the old and the new version in case only one of them matches.
|
// both the old and the new version in case only one of them matches.
|
||||||
|
|
||||||
std::vector<CompletionItem> Result = clangd::codeComplete(
|
CompletionList Result = clangd::codeComplete(
|
||||||
File, Resources->getCompileCommand(),
|
File, Resources->getCompileCommand(),
|
||||||
Preamble ? &Preamble->Preamble : nullptr, Contents, Pos,
|
Preamble ? &Preamble->Preamble : nullptr, Contents, Pos,
|
||||||
TaggedFS.Value, PCHs, CodeCompleteOpts, Logger);
|
TaggedFS.Value, PCHs, CodeCompleteOpts, Logger);
|
||||||
|
|
|
@ -251,18 +251,17 @@ public:
|
||||||
/// This method should only be called for currently tracked files. However, it
|
/// This method should only be called for currently tracked files. However, it
|
||||||
/// is safe to call removeDocument for \p File after this method returns, even
|
/// is safe to call removeDocument for \p File after this method returns, even
|
||||||
/// while returned future is not yet ready.
|
/// while returned future is not yet ready.
|
||||||
std::future<Tagged<std::vector<CompletionItem>>>
|
std::future<Tagged<CompletionList>>
|
||||||
codeComplete(PathRef File, Position Pos,
|
codeComplete(PathRef File, Position Pos,
|
||||||
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
||||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
|
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
|
||||||
|
|
||||||
/// A version of `codeComplete` that runs \p Callback on the processing thread
|
/// A version of `codeComplete` that runs \p Callback on the processing thread
|
||||||
/// when codeComplete results become available.
|
/// when codeComplete results become available.
|
||||||
void codeComplete(
|
void codeComplete(UniqueFunction<void(Tagged<CompletionList>)> Callback,
|
||||||
UniqueFunction<void(Tagged<std::vector<CompletionItem>>)> Callback,
|
PathRef File, Position Pos,
|
||||||
PathRef File, Position Pos,
|
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
||||||
llvm::Optional<StringRef> OverridenContents = llvm::None,
|
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
|
||||||
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
|
|
||||||
|
|
||||||
/// Provide signature help for \p File at \p Pos. If \p OverridenContents is
|
/// Provide signature help for \p File at \p Pos. If \p OverridenContents is
|
||||||
/// not None, they will used only for signature help, i.e. no diagnostics
|
/// not None, they will used only for signature help, i.e. no diagnostics
|
||||||
|
|
|
@ -368,69 +368,42 @@ std::string getDocumentation(const CodeCompletionString &CCS) {
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CompletionItemsCollector : public CodeCompleteConsumer {
|
/// A scored code completion result.
|
||||||
public:
|
/// It may be promoted to a CompletionItem if it's among the top-ranked results.
|
||||||
CompletionItemsCollector(const clang::CodeCompleteOptions &CodeCompleteOpts,
|
struct CompletionCandidate {
|
||||||
std::vector<CompletionItem> &Items)
|
CompletionCandidate(CodeCompletionResult &Result)
|
||||||
: CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
|
: Result(&Result), Score(score(Result)) {}
|
||||||
Items(Items),
|
|
||||||
Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
|
||||||
CCTUInfo(Allocator) {}
|
|
||||||
|
|
||||||
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
CodeCompletionResult *Result;
|
||||||
CodeCompletionResult *Results,
|
// Higher score is worse. FIXME: use a more natural scale!
|
||||||
unsigned NumResults) override final {
|
int Score;
|
||||||
Items.reserve(NumResults);
|
|
||||||
for (unsigned I = 0; I < NumResults; ++I) {
|
// Comparison reflects rank: better candidates are smaller.
|
||||||
auto &Result = Results[I];
|
bool operator<(const CompletionCandidate &C) const {
|
||||||
const auto *CCS = Result.CreateCodeCompletionString(
|
if (Score != C.Score)
|
||||||
S, Context, *Allocator, CCTUInfo,
|
return Score < C.Score;
|
||||||
CodeCompleteOpts.IncludeBriefComments);
|
return *Result < *C.Result;
|
||||||
assert(CCS && "Expected the CodeCompletionString to be non-null");
|
|
||||||
Items.push_back(ProcessCodeCompleteResult(Result, *CCS));
|
|
||||||
}
|
|
||||||
std::sort(Items.begin(), Items.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
|
std::string sortText() const {
|
||||||
|
// Fill in the sortText of the CompletionItem.
|
||||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
assert(Score <= 999999 && "Expecting score to have at most 6-digits");
|
||||||
|
std::string S, NameStorage;
|
||||||
|
StringRef Name = Result->getOrderedName(NameStorage);
|
||||||
|
llvm::raw_string_ostream(S)
|
||||||
|
<< llvm::format("%06d%.*s", Score, Name.size(), Name.data());
|
||||||
|
return S;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CompletionItem
|
static int score(const CodeCompletionResult &Result) {
|
||||||
ProcessCodeCompleteResult(const CodeCompletionResult &Result,
|
int Score = Result.Priority;
|
||||||
const CodeCompletionString &CCS) const {
|
|
||||||
|
|
||||||
// Adjust this to InsertTextFormat::Snippet iff we encounter a
|
|
||||||
// CK_Placeholder chunk in SnippetCompletionItemsCollector.
|
|
||||||
CompletionItem Item;
|
|
||||||
Item.insertTextFormat = InsertTextFormat::PlainText;
|
|
||||||
|
|
||||||
Item.documentation = getDocumentation(CCS);
|
|
||||||
|
|
||||||
// Fill in the label, detail, insertText and filterText fields of the
|
|
||||||
// CompletionItem.
|
|
||||||
ProcessChunks(CCS, Item);
|
|
||||||
|
|
||||||
// Fill in the kind field of the CompletionItem.
|
|
||||||
Item.kind = getKind(Result.Kind, Result.CursorKind);
|
|
||||||
|
|
||||||
FillSortText(CCS, Item);
|
|
||||||
|
|
||||||
return Item;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void ProcessChunks(const CodeCompletionString &CCS,
|
|
||||||
CompletionItem &Item) const = 0;
|
|
||||||
|
|
||||||
static int GetSortPriority(const CodeCompletionString &CCS) {
|
|
||||||
int Score = CCS.getPriority();
|
|
||||||
// Fill in the sortText of the CompletionItem.
|
// Fill in the sortText of the CompletionItem.
|
||||||
assert(Score <= 99999 && "Expecting code completion result "
|
assert(Score <= 99999 && "Expecting code completion result "
|
||||||
"priority to have at most 5-digits");
|
"priority to have at most 5-digits");
|
||||||
|
|
||||||
const int Penalty = 100000;
|
const int Penalty = 100000;
|
||||||
switch (static_cast<CXAvailabilityKind>(CCS.getAvailability())) {
|
switch (static_cast<CXAvailabilityKind>(Result.Availability)) {
|
||||||
case CXAvailability_Available:
|
case CXAvailability_Available:
|
||||||
// No penalty.
|
// No penalty.
|
||||||
break;
|
break;
|
||||||
|
@ -444,21 +417,75 @@ private:
|
||||||
Score += 3 * Penalty;
|
Score += 3 * Penalty;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Score;
|
return Score;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static void FillSortText(const CodeCompletionString &CCS,
|
class CompletionItemsCollector : public CodeCompleteConsumer {
|
||||||
CompletionItem &Item) {
|
public:
|
||||||
int Priority = GetSortPriority(CCS);
|
CompletionItemsCollector(const clangd::CodeCompleteOptions &CodeCompleteOpts,
|
||||||
// Fill in the sortText of the CompletionItem.
|
CompletionList &Items)
|
||||||
assert(Priority <= 999999 &&
|
: CodeCompleteConsumer(CodeCompleteOpts.getClangCompleteOpts(),
|
||||||
"Expecting sort priority to have at most 6-digits");
|
/*OutputIsBinary=*/false),
|
||||||
llvm::raw_string_ostream(Item.sortText)
|
ClangdOpts(CodeCompleteOpts), Items(Items),
|
||||||
<< llvm::format("%06d%s", Priority, Item.filterText.c_str());
|
Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||||
|
CCTUInfo(Allocator) {}
|
||||||
|
|
||||||
|
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
||||||
|
CodeCompletionResult *Results,
|
||||||
|
unsigned NumResults) override final {
|
||||||
|
std::priority_queue<CompletionCandidate> Candidates;
|
||||||
|
for (unsigned I = 0; I < NumResults; ++I) {
|
||||||
|
Candidates.emplace(Results[I]);
|
||||||
|
if (ClangdOpts.Limit && Candidates.size() > ClangdOpts.Limit) {
|
||||||
|
Candidates.pop();
|
||||||
|
Items.isIncomplete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!Candidates.empty()) {
|
||||||
|
auto &Candidate = Candidates.top();
|
||||||
|
const auto *CCS = Candidate.Result->CreateCodeCompletionString(
|
||||||
|
S, Context, *Allocator, CCTUInfo,
|
||||||
|
CodeCompleteOpts.IncludeBriefComments);
|
||||||
|
assert(CCS && "Expected the CodeCompletionString to be non-null");
|
||||||
|
Items.items.push_back(ProcessCodeCompleteResult(Candidate, *CCS));
|
||||||
|
Candidates.pop();
|
||||||
|
}
|
||||||
|
std::reverse(Items.items.begin(), Items.items.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CompletionItem> &Items;
|
GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
|
||||||
|
|
||||||
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
CompletionItem
|
||||||
|
ProcessCodeCompleteResult(const CompletionCandidate &Candidate,
|
||||||
|
const CodeCompletionString &CCS) const {
|
||||||
|
|
||||||
|
// Adjust this to InsertTextFormat::Snippet iff we encounter a
|
||||||
|
// CK_Placeholder chunk in SnippetCompletionItemsCollector.
|
||||||
|
CompletionItem Item;
|
||||||
|
Item.insertTextFormat = InsertTextFormat::PlainText;
|
||||||
|
|
||||||
|
Item.documentation = getDocumentation(CCS);
|
||||||
|
Item.sortText = Candidate.sortText();
|
||||||
|
|
||||||
|
// Fill in the label, detail, insertText and filterText fields of the
|
||||||
|
// CompletionItem.
|
||||||
|
ProcessChunks(CCS, Item);
|
||||||
|
|
||||||
|
// Fill in the kind field of the CompletionItem.
|
||||||
|
Item.kind = getKind(Candidate.Result->Kind, Candidate.Result->CursorKind);
|
||||||
|
|
||||||
|
return Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ProcessChunks(const CodeCompletionString &CCS,
|
||||||
|
CompletionItem &Item) const = 0;
|
||||||
|
|
||||||
|
clangd::CodeCompleteOptions ClangdOpts;
|
||||||
|
CompletionList &Items;
|
||||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
|
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
|
||||||
CodeCompletionTUInfo CCTUInfo;
|
CodeCompletionTUInfo CCTUInfo;
|
||||||
|
|
||||||
|
@ -474,8 +501,8 @@ class PlainTextCompletionItemsCollector final
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlainTextCompletionItemsCollector(
|
PlainTextCompletionItemsCollector(
|
||||||
const clang::CodeCompleteOptions &CodeCompleteOpts,
|
const clangd::CodeCompleteOptions &CodeCompleteOpts,
|
||||||
std::vector<CompletionItem> &Items)
|
CompletionList &Items)
|
||||||
: CompletionItemsCollector(CodeCompleteOpts, Items) {}
|
: CompletionItemsCollector(CodeCompleteOpts, Items) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -511,8 +538,8 @@ class SnippetCompletionItemsCollector final : public CompletionItemsCollector {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SnippetCompletionItemsCollector(
|
SnippetCompletionItemsCollector(
|
||||||
const clang::CodeCompleteOptions &CodeCompleteOpts,
|
const clangd::CodeCompleteOptions &CodeCompleteOpts,
|
||||||
std::vector<CompletionItem> &Items)
|
CompletionList &Items)
|
||||||
: CompletionItemsCollector(CodeCompleteOpts, Items) {}
|
: CompletionItemsCollector(CodeCompleteOpts, Items) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -795,7 +822,8 @@ clangd::CodeCompleteOptions::CodeCompleteOptions(bool EnableSnippets,
|
||||||
IncludeMacros(IncludeMacros), IncludeGlobals(IncludeGlobals),
|
IncludeMacros(IncludeMacros), IncludeGlobals(IncludeGlobals),
|
||||||
IncludeBriefComments(IncludeBriefComments) {}
|
IncludeBriefComments(IncludeBriefComments) {}
|
||||||
|
|
||||||
clang::CodeCompleteOptions clangd::CodeCompleteOptions::getClangCompleteOpts() {
|
clang::CodeCompleteOptions
|
||||||
|
clangd::CodeCompleteOptions::getClangCompleteOpts() const {
|
||||||
clang::CodeCompleteOptions Result;
|
clang::CodeCompleteOptions Result;
|
||||||
Result.IncludeCodePatterns = EnableSnippets && IncludeCodePatterns;
|
Result.IncludeCodePatterns = EnableSnippets && IncludeCodePatterns;
|
||||||
Result.IncludeMacros = IncludeMacros;
|
Result.IncludeMacros = IncludeMacros;
|
||||||
|
@ -805,25 +833,24 @@ clang::CodeCompleteOptions clangd::CodeCompleteOptions::getClangCompleteOpts() {
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CompletionItem>
|
CompletionList
|
||||||
clangd::codeComplete(PathRef FileName, const tooling::CompileCommand &Command,
|
clangd::codeComplete(PathRef FileName, const tooling::CompileCommand &Command,
|
||||||
PrecompiledPreamble const *Preamble, StringRef Contents,
|
PrecompiledPreamble const *Preamble, StringRef Contents,
|
||||||
Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
|
Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
|
||||||
std::shared_ptr<PCHContainerOperations> PCHs,
|
std::shared_ptr<PCHContainerOperations> PCHs,
|
||||||
clangd::CodeCompleteOptions Opts, clangd::Logger &Logger) {
|
clangd::CodeCompleteOptions Opts, clangd::Logger &Logger) {
|
||||||
std::vector<CompletionItem> Results;
|
CompletionList Results;
|
||||||
std::unique_ptr<CodeCompleteConsumer> Consumer;
|
std::unique_ptr<CodeCompleteConsumer> Consumer;
|
||||||
clang::CodeCompleteOptions ClangCompleteOpts = Opts.getClangCompleteOpts();
|
|
||||||
if (Opts.EnableSnippets) {
|
if (Opts.EnableSnippets) {
|
||||||
Consumer = llvm::make_unique<SnippetCompletionItemsCollector>(
|
Consumer =
|
||||||
ClangCompleteOpts, Results);
|
llvm::make_unique<SnippetCompletionItemsCollector>(Opts, Results);
|
||||||
} else {
|
} else {
|
||||||
Consumer = llvm::make_unique<PlainTextCompletionItemsCollector>(
|
Consumer =
|
||||||
ClangCompleteOpts, Results);
|
llvm::make_unique<PlainTextCompletionItemsCollector>(Opts, Results);
|
||||||
}
|
}
|
||||||
invokeCodeComplete(std::move(Consumer), ClangCompleteOpts, FileName, Command,
|
invokeCodeComplete(std::move(Consumer), Opts.getClangCompleteOpts(), FileName,
|
||||||
Preamble, Contents, Pos, std::move(VFS), std::move(PCHs),
|
Command, Preamble, Contents, Pos, std::move(VFS),
|
||||||
Logger);
|
std::move(PCHs), Logger);
|
||||||
return Results;
|
return Results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,7 +263,7 @@ struct CodeCompleteOptions {
|
||||||
bool IncludeBriefComments);
|
bool IncludeBriefComments);
|
||||||
|
|
||||||
/// Returns options that can be passed to clang's completion engine.
|
/// Returns options that can be passed to clang's completion engine.
|
||||||
clang::CodeCompleteOptions getClangCompleteOpts();
|
clang::CodeCompleteOptions getClangCompleteOpts() const;
|
||||||
|
|
||||||
/// When true, completion items will contain expandable code snippets in
|
/// When true, completion items will contain expandable code snippets in
|
||||||
/// completion (e.g. `return ${1:expression}` or `foo(${1:int a}, ${2:int
|
/// completion (e.g. `return ${1:expression}` or `foo(${1:int a}, ${2:int
|
||||||
|
@ -285,10 +285,14 @@ struct CodeCompleteOptions {
|
||||||
/// FIXME(ibiryukov): it looks like turning this option on significantly slows
|
/// FIXME(ibiryukov): it looks like turning this option on significantly slows
|
||||||
/// down completion, investigate if it can be made faster.
|
/// down completion, investigate if it can be made faster.
|
||||||
bool IncludeBriefComments = true;
|
bool IncludeBriefComments = true;
|
||||||
|
|
||||||
|
/// Limit the number of results returned (0 means no limit).
|
||||||
|
/// If more results are available, we set CompletionList.isIncomplete.
|
||||||
|
size_t Limit = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get code completions at a specified \p Pos in \p FileName.
|
/// Get code completions at a specified \p Pos in \p FileName.
|
||||||
std::vector<CompletionItem>
|
CompletionList
|
||||||
codeComplete(PathRef FileName, const tooling::CompileCommand &Command,
|
codeComplete(PathRef FileName, const tooling::CompileCommand &Command,
|
||||||
PrecompiledPreamble const *Preamble, StringRef Contents,
|
PrecompiledPreamble const *Preamble, StringRef Contents,
|
||||||
Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
|
Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
|
||||||
|
|
|
@ -1043,6 +1043,13 @@ bool clangd::operator<(const CompletionItem &L, const CompletionItem &R) {
|
||||||
(R.sortText.empty() ? R.label : R.sortText);
|
(R.sortText.empty() ? R.label : R.sortText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json::Expr CompletionList::unparse(const CompletionList &L) {
|
||||||
|
return json::obj{
|
||||||
|
{"isIncomplete", L.isIncomplete},
|
||||||
|
{"items", json::ary(L.items)},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
json::Expr ParameterInformation::unparse(const ParameterInformation &PI) {
|
json::Expr ParameterInformation::unparse(const ParameterInformation &PI) {
|
||||||
assert(!PI.label.empty() && "parameter information label is required");
|
assert(!PI.label.empty() && "parameter information label is required");
|
||||||
json::obj Result{{"label", PI.label}};
|
json::obj Result{{"label", PI.label}};
|
||||||
|
|
|
@ -547,6 +547,18 @@ struct CompletionItem {
|
||||||
|
|
||||||
bool operator<(const CompletionItem &, const CompletionItem &);
|
bool operator<(const CompletionItem &, const CompletionItem &);
|
||||||
|
|
||||||
|
/// Represents a collection of completion items to be presented in the editor.
|
||||||
|
struct CompletionList {
|
||||||
|
/// The list is not complete. Further typing should result in recomputing the
|
||||||
|
/// list.
|
||||||
|
bool isIncomplete = false;
|
||||||
|
|
||||||
|
/// The completion items.
|
||||||
|
std::vector<CompletionItem> items;
|
||||||
|
|
||||||
|
static json::Expr unparse(const CompletionList &);
|
||||||
|
};
|
||||||
|
|
||||||
/// A single parameter of a particular signature.
|
/// A single parameter of a particular signature.
|
||||||
struct ParameterInformation {
|
struct ParameterInformation {
|
||||||
|
|
||||||
|
|
|
@ -17,14 +17,17 @@ Content-Length: 146
|
||||||
#
|
#
|
||||||
# CHECK: "id": 1,
|
# CHECK: "id": 1,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK: "filterText": "fake",
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "insertText": "fake",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK: "filterText": "fake",
|
||||||
# CHECK-NEXT: "kind": 7,
|
# CHECK-NEXT: "insertText": "fake",
|
||||||
# CHECK-NEXT: "label": "fake::",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000075fake"
|
# CHECK-NEXT: "kind": 7,
|
||||||
# CHECK: ]
|
# CHECK-NEXT: "label": "fake::",
|
||||||
|
# CHECK-NEXT: "sortText": "000075fake"
|
||||||
|
# CHECK: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
Content-Length: 172
|
Content-Length: 172
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"uri":"file:///main.cpp","position":{"line":3,"character":5}}}
|
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"uri":"file:///main.cpp","position":{"line":3,"character":5}}}
|
||||||
|
@ -32,14 +35,17 @@ Content-Length: 172
|
||||||
#
|
#
|
||||||
# CHECK: "id": 2,
|
# CHECK: "id": 2,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK: "filterText": "fake",
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "insertText": "fake",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK: "filterText": "fake",
|
||||||
# CHECK-NEXT: "kind": 7,
|
# CHECK-NEXT: "insertText": "fake",
|
||||||
# CHECK-NEXT: "label": "fake::",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000075fake"
|
# CHECK-NEXT: "kind": 7,
|
||||||
# CHECK: ]
|
# CHECK-NEXT: "label": "fake::",
|
||||||
|
# CHECK-NEXT: "sortText": "000075fake"
|
||||||
|
# CHECK: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
Content-Length: 44
|
Content-Length: 44
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
|
||||||
|
|
|
@ -11,7 +11,7 @@ Content-Length: 148
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}
|
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}
|
||||||
Content-Length: 58
|
Content-Length: 58
|
||||||
# CHECK: {"id":1,"jsonrpc":"2.0","result":[
|
# CHECK: {"id":1,"jsonrpc":"2.0","result":{"isIncomplete":false,"items":
|
||||||
#
|
#
|
||||||
# Keyword
|
# Keyword
|
||||||
# CHECK-DAG: {"filterText":"int","insertText":"int","insertTextFormat":1,"kind":14,"label":"int","sortText":"000050int"}
|
# CHECK-DAG: {"filterText":"int","insertText":"int","insertTextFormat":1,"kind":14,"label":"int","sortText":"000050int"}
|
||||||
|
@ -31,6 +31,6 @@ Content-Length: 58
|
||||||
# Function
|
# Function
|
||||||
# CHECK-DAG: {"detail":"int","filterText":"function","insertText":"function()","insertTextFormat":1,"kind":3,"label":"function()","sortText":"000012function"}
|
# CHECK-DAG: {"detail":"int","filterText":"function","insertText":"function()","insertTextFormat":1,"kind":3,"label":"function()","sortText":"000012function"}
|
||||||
#
|
#
|
||||||
# CHECK-SAME: ]}
|
# CHECK-SAME: ]}}
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":3,"method":"shutdown","params":null}
|
{"jsonrpc":"2.0","id":3,"method":"shutdown","params":null}
|
||||||
|
|
|
@ -15,69 +15,74 @@ Content-Length: 151
|
||||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":12,"character":8}}}
|
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":12,"character":8}}}
|
||||||
# CHECK: "id": 2,
|
# CHECK: "id": 2,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "detail": "void",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "filterText": "priv",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "priv",
|
# CHECK-NEXT: "detail": "void",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "priv",
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "insertText": "priv",
|
||||||
# CHECK-NEXT: "label": "priv()",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000034priv"
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "priv()",
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "sortText": "000034priv"
|
||||||
# CHECK-NEXT: "detail": "void",
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: "filterText": "prot",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "prot",
|
# CHECK-NEXT: "detail": "void",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "prot",
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "insertText": "prot",
|
||||||
# CHECK-NEXT: "label": "prot()",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000034prot"
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "prot()",
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "sortText": "000034prot"
|
||||||
# CHECK-NEXT: "detail": "void",
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: "filterText": "pub",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "pub",
|
# CHECK-NEXT: "detail": "void",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "pub",
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "insertText": "pub",
|
||||||
# CHECK-NEXT: "label": "pub()",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000034pub"
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "pub()",
|
||||||
|
# CHECK-NEXT: "sortText": "000034pub"
|
||||||
|
# CHECK-NEXT: },
|
||||||
Content-Length: 151
|
Content-Length: 151
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":17,"character":4}}}
|
{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":17,"character":4}}}
|
||||||
# CHECK: "id": 3,
|
# CHECK: "id": 3,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "detail": "void",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "filterText": "pub",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "pub",
|
# CHECK-NEXT: "detail": "void",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "pub",
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "insertText": "pub",
|
||||||
# CHECK-NEXT: "label": "pub()",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000034pub"
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "pub()",
|
||||||
|
# CHECK-NEXT: "sortText": "000034pub"
|
||||||
|
# CHECK-NEXT: },
|
||||||
# priv() and prot() are at the end of the list
|
# priv() and prot() are at the end of the list
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK: "detail": "void",
|
# CHECK: "detail": "void",
|
||||||
# CHECK: "filterText": "priv",
|
# CHECK: "filterText": "priv",
|
||||||
# CHECK-NEXT: "insertText": "priv",
|
# CHECK-NEXT: "insertText": "priv",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: "label": "priv()",
|
# CHECK-NEXT: "label": "priv()",
|
||||||
# CHECK-NEXT: "sortText": "200034priv"
|
# CHECK-NEXT: "sortText": "200034priv"
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "detail": "void",
|
# CHECK-NEXT: "detail": "void",
|
||||||
# CHECK-NEXT: "filterText": "prot",
|
# CHECK-NEXT: "filterText": "prot",
|
||||||
# CHECK-NEXT: "insertText": "prot",
|
# CHECK-NEXT: "insertText": "prot",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: "label": "prot()",
|
# CHECK-NEXT: "label": "prot()",
|
||||||
# CHECK-NEXT: "sortText": "200034prot"
|
# CHECK-NEXT: "sortText": "200034prot"
|
||||||
# CHECK-NEXT: }
|
# CHECK-NEXT: }
|
||||||
# CHECK-NEXT: ]
|
# CHECK-NEXT: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
Content-Length: 58
|
Content-Length: 58
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":4,"method":"shutdown","params":null}
|
{"jsonrpc":"2.0","id":4,"method":"shutdown","params":null}
|
||||||
|
|
|
@ -10,37 +10,40 @@ Content-Length: 151
|
||||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":11,"character":8}}}
|
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":11,"character":8}}}
|
||||||
# CHECK: "id": 2,
|
# CHECK: "id": 2,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
|
# CHECK-NEXT: "items": [
|
||||||
# Eligible const functions are at the top of the list.
|
# Eligible const functions are at the top of the list.
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "filterText": "bar",
|
# CHECK-NEXT: "filterText": "bar",
|
||||||
# CHECK-NEXT: "insertText": "bar",
|
# CHECK-NEXT: "insertText": "bar",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: "label": "bar() const",
|
# CHECK-NEXT: "label": "bar() const",
|
||||||
# CHECK-NEXT: "sortText": "000037bar"
|
# CHECK-NEXT: "sortText": "000037bar"
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "filterText": "foo",
|
# CHECK-NEXT: "filterText": "foo",
|
||||||
# CHECK-NEXT: "insertText": "foo",
|
# CHECK-NEXT: "insertText": "foo",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: "label": "Foo::foo() const",
|
# CHECK-NEXT: "label": "Foo::foo() const",
|
||||||
# CHECK-NEXT: "sortText": "000037foo"
|
# CHECK-NEXT: "sortText": "000037foo"
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: },
|
||||||
# Ineligible non-const function is at the bottom of the list.
|
# Ineligible non-const function is at the bottom of the list.
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK: "detail": "int",
|
# CHECK: "detail": "int",
|
||||||
# CHECK: "filterText": "foo",
|
# CHECK: "filterText": "foo",
|
||||||
# CHECK-NEXT: "insertText": "foo",
|
# CHECK-NEXT: "insertText": "foo",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: "label": "foo() const",
|
# CHECK-NEXT: "label": "foo() const",
|
||||||
# CHECK-NEXT: "sortText": "200035foo"
|
# CHECK-NEXT: "sortText": "200035foo"
|
||||||
# CHECK-NEXT: }
|
# CHECK-NEXT: }
|
||||||
# CHECK-NEXT: ]
|
# CHECK-NEXT: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
Content-Length: 44
|
Content-Length: 44
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":4,"method":"shutdown"}
|
{"jsonrpc":"2.0","id":4,"method":"shutdown"}
|
||||||
|
|
|
@ -14,71 +14,74 @@ Content-Length: 148
|
||||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
||||||
# CHECK: "id": 1,
|
# CHECK: "id": 1,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "filterText": "a",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "a",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "a",
|
||||||
# CHECK-NEXT: "kind": 5,
|
# CHECK-NEXT: "insertText": "a",
|
||||||
# CHECK-NEXT: "label": "a",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000035a"
|
# CHECK-NEXT: "kind": 5,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "a",
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "sortText": "000035a"
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: "filterText": "bb",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "bb",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "bb",
|
||||||
# CHECK-NEXT: "kind": 5,
|
# CHECK-NEXT: "insertText": "bb",
|
||||||
# CHECK-NEXT: "label": "bb",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000035bb"
|
# CHECK-NEXT: "kind": 5,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "bb",
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "sortText": "000035bb"
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: "filterText": "ccc",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "ccc",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "ccc",
|
||||||
# CHECK-NEXT: "kind": 5,
|
# CHECK-NEXT: "insertText": "ccc",
|
||||||
# CHECK-NEXT: "label": "ccc",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000035ccc"
|
# CHECK-NEXT: "kind": 5,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "ccc",
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "sortText": "000035ccc"
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: "filterText": "f",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "f(${1:int i}, ${2:const float f})",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "insertTextFormat": 2,
|
# CHECK-NEXT: "filterText": "f",
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "insertText": "f(${1:int i}, ${2:const float f})",
|
||||||
# CHECK-NEXT: "label": "f(int i, const float f) const",
|
# CHECK-NEXT: "insertTextFormat": 2,
|
||||||
# CHECK-NEXT: "sortText": "000035f"
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "f(int i, const float f) const",
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "sortText": "000035f"
|
||||||
# CHECK-NEXT: "filterText": "fake",
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: "insertText": "fake::",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "fake",
|
||||||
# CHECK-NEXT: "kind": 7,
|
# CHECK-NEXT: "insertText": "fake::",
|
||||||
# CHECK-NEXT: "label": "fake::",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000075fake"
|
# CHECK-NEXT: "kind": 7,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "fake::",
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "sortText": "000075fake"
|
||||||
# CHECK-NEXT: "detail": "fake &",
|
# CHECK-NEXT: },
|
||||||
# CHECK-NEXT: "filterText": "operator=",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "operator=(${1:const fake &})",
|
# CHECK-NEXT: "detail": "fake &",
|
||||||
# CHECK-NEXT: "insertTextFormat": 2,
|
# CHECK-NEXT: "filterText": "operator=",
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "insertText": "operator=(${1:const fake &})",
|
||||||
# CHECK-NEXT: "label": "operator=(const fake &)",
|
# CHECK-NEXT: "insertTextFormat": 2,
|
||||||
# CHECK-NEXT: "sortText": "000079operator="
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "operator=(const fake &)",
|
||||||
|
# CHECK-NEXT: "sortText": "000079operator="
|
||||||
|
# CHECK-NEXT: },
|
||||||
# FIXME: Why do some buildbots show an extra operator==(fake&&) here?
|
# FIXME: Why do some buildbots show an extra operator==(fake&&) here?
|
||||||
# CHECK: {
|
# CHECK: {
|
||||||
# CHECK: "detail": "void",
|
# CHECK: "detail": "void",
|
||||||
# CHECK-NEXT: "filterText": "~fake",
|
# CHECK-NEXT: "filterText": "~fake",
|
||||||
# CHECK-NEXT: "insertText": "~fake()",
|
# CHECK-NEXT: "insertText": "~fake()",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "kind": 4,
|
# CHECK-NEXT: "kind": 4,
|
||||||
# CHECK-NEXT: "label": "~fake()",
|
# CHECK-NEXT: "label": "~fake()",
|
||||||
# CHECK-NEXT: "sortText": "000079~fake"
|
# CHECK-NEXT: "sortText": "000079~fake"
|
||||||
# CHECK-NEXT: }
|
# CHECK-NEXT: }
|
||||||
# CHECK-NEXT: ]
|
# CHECK-NEXT: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
# Update the source file and check for completions again.
|
# Update the source file and check for completions again.
|
||||||
Content-Length: 226
|
Content-Length: 226
|
||||||
|
|
||||||
|
@ -89,16 +92,18 @@ Content-Length: 148
|
||||||
{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
||||||
# CHECK: "id": 3,
|
# CHECK: "id": 3,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "detail": "int (*)(int, int)",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "filterText": "func",
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "insertText": "func()",
|
# CHECK-NEXT: "detail": "int (*)(int, int)",
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK-NEXT: "filterText": "func",
|
||||||
# CHECK-NEXT: "kind": 2,
|
# CHECK-NEXT: "insertText": "func()",
|
||||||
# CHECK-NEXT: "label": "func()",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000034func"
|
# CHECK-NEXT: "kind": 2,
|
||||||
# CHECK-NEXT: },
|
# CHECK-NEXT: "label": "func()",
|
||||||
|
# CHECK-NEXT: "sortText": "000034func"
|
||||||
|
# CHECK-NEXT: },
|
||||||
Content-Length: 44
|
Content-Length: 44
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":4,"method":"shutdown"}
|
{"jsonrpc":"2.0","id":4,"method":"shutdown"}
|
||||||
|
|
|
@ -14,7 +14,9 @@ Content-Length: 148
|
||||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
||||||
# CHECK: "id": 1
|
# CHECK: "id": 1
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "filterText": "a",
|
# CHECK-NEXT: "filterText": "a",
|
||||||
|
@ -84,7 +86,9 @@ Content-Length: 148
|
||||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
||||||
# CHECK: "id": 2
|
# CHECK: "id": 2
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "detail": "int",
|
# CHECK-NEXT: "detail": "int",
|
||||||
# CHECK-NEXT: "filterText": "a",
|
# CHECK-NEXT: "filterText": "a",
|
||||||
|
@ -158,7 +162,9 @@ Content-Length: 148
|
||||||
{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}
|
||||||
# CHECK: "id": 3,
|
# CHECK: "id": 3,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: {
|
# CHECK-NEXT: {
|
||||||
# CHECK-NEXT: "detail": "int (*)(int, int)",
|
# CHECK-NEXT: "detail": "int (*)(int, int)",
|
||||||
# CHECK-NEXT: "filterText": "func",
|
# CHECK-NEXT: "filterText": "func",
|
||||||
|
|
|
@ -31,14 +31,17 @@ Content-Length: 146
|
||||||
#
|
#
|
||||||
# CHECK: "id": 1,
|
# CHECK: "id": 1,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK: "filterText": "fake",
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "insertText": "fake",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK: "filterText": "fake",
|
||||||
# CHECK-NEXT: "kind": 7,
|
# CHECK-NEXT: "insertText": "fake",
|
||||||
# CHECK-NEXT: "label": "fake::",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000075fake"
|
# CHECK-NEXT: "kind": 7,
|
||||||
# CHECK: ]
|
# CHECK-NEXT: "label": "fake::",
|
||||||
|
# CHECK-NEXT: "sortText": "000075fake"
|
||||||
|
# CHECK: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
|
|
||||||
X-Test: Testing
|
X-Test: Testing
|
||||||
Content-Type: application/vscode-jsonrpc; charset-utf-8
|
Content-Type: application/vscode-jsonrpc; charset-utf-8
|
||||||
|
@ -57,14 +60,17 @@ Content-Length: 146
|
||||||
#
|
#
|
||||||
# CHECK: "id": 3,
|
# CHECK: "id": 3,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK: "filterText": "fake",
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "insertText": "fake",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK: "filterText": "fake",
|
||||||
# CHECK-NEXT: "kind": 7,
|
# CHECK-NEXT: "insertText": "fake",
|
||||||
# CHECK-NEXT: "label": "fake::",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000075fake"
|
# CHECK-NEXT: "kind": 7,
|
||||||
# CHECK: ]
|
# CHECK-NEXT: "label": "fake::",
|
||||||
|
# CHECK-NEXT: "sortText": "000075fake"
|
||||||
|
# CHECK: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
# STDERR: Warning: Duplicate Content-Length header received. The previous value for this message (10) was ignored.
|
# STDERR: Warning: Duplicate Content-Length header received. The previous value for this message (10) was ignored.
|
||||||
|
|
||||||
Content-Type: application/vscode-jsonrpc; charset-utf-8
|
Content-Type: application/vscode-jsonrpc; charset-utf-8
|
||||||
|
@ -83,14 +89,17 @@ Content-Length: 146
|
||||||
#
|
#
|
||||||
# CHECK: "id": 5,
|
# CHECK: "id": 5,
|
||||||
# CHECK-NEXT: "jsonrpc": "2.0",
|
# CHECK-NEXT: "jsonrpc": "2.0",
|
||||||
# CHECK-NEXT: "result": [
|
# CHECK-NEXT: "result": {
|
||||||
# CHECK: "filterText": "fake",
|
# CHECK-NEXT: "isIncomplete": false,
|
||||||
# CHECK-NEXT: "insertText": "fake",
|
# CHECK-NEXT: "items": [
|
||||||
# CHECK-NEXT: "insertTextFormat": 1,
|
# CHECK: "filterText": "fake",
|
||||||
# CHECK-NEXT: "kind": 7,
|
# CHECK-NEXT: "insertText": "fake",
|
||||||
# CHECK-NEXT: "label": "fake::",
|
# CHECK-NEXT: "insertTextFormat": 1,
|
||||||
# CHECK-NEXT: "sortText": "000075fake"
|
# CHECK-NEXT: "kind": 7,
|
||||||
# CHECK: ]
|
# CHECK-NEXT: "label": "fake::",
|
||||||
|
# CHECK-NEXT: "sortText": "000075fake"
|
||||||
|
# CHECK: ]
|
||||||
|
# CHECK-NEXT: }
|
||||||
Content-Length: 1024
|
Content-Length: 1024
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
|
{"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
|
||||||
|
|
|
@ -619,16 +619,15 @@ struct bar { T x; };
|
||||||
class ClangdCompletionTest : public ClangdVFSTest {
|
class ClangdCompletionTest : public ClangdVFSTest {
|
||||||
protected:
|
protected:
|
||||||
template <class Predicate>
|
template <class Predicate>
|
||||||
bool ContainsItemPred(std::vector<CompletionItem> const &Items,
|
bool ContainsItemPred(CompletionList const &Items, Predicate Pred) {
|
||||||
Predicate Pred) {
|
for (const auto &Item : Items.items) {
|
||||||
for (const auto &Item : Items) {
|
|
||||||
if (Pred(Item))
|
if (Pred(Item))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContainsItem(std::vector<CompletionItem> const &Items, StringRef Name) {
|
bool ContainsItem(CompletionList const &Items, StringRef Name) {
|
||||||
return ContainsItemPred(Items, [Name](clangd::CompletionItem Item) {
|
return ContainsItemPred(Items, [Name](clangd::CompletionItem Item) {
|
||||||
return Item.insertText == Name;
|
return Item.insertText == Name;
|
||||||
});
|
});
|
||||||
|
@ -694,6 +693,44 @@ int b = ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ClangdCompletionTest, Limit) {
|
||||||
|
MockFSProvider FS;
|
||||||
|
MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
|
||||||
|
CDB.ExtraClangFlags.push_back("-xc++");
|
||||||
|
ErrorCheckingDiagConsumer DiagConsumer;
|
||||||
|
clangd::CodeCompleteOptions Opts;
|
||||||
|
Opts.Limit = 2;
|
||||||
|
ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
|
||||||
|
Opts, EmptyLogger::getInstance());
|
||||||
|
|
||||||
|
auto FooCpp = getVirtualTestFilePath("foo.cpp");
|
||||||
|
FS.Files[FooCpp] = "";
|
||||||
|
FS.ExpectedFile = FooCpp;
|
||||||
|
StringWithPos Completion = parseTextMarker(R"cpp(
|
||||||
|
struct ClassWithMembers {
|
||||||
|
int AAA();
|
||||||
|
int BBB();
|
||||||
|
int CCC();
|
||||||
|
}
|
||||||
|
int main() { ClassWithMembers().{complete} }
|
||||||
|
)cpp",
|
||||||
|
"complete");
|
||||||
|
Server.addDocument(FooCpp, Completion.Text);
|
||||||
|
|
||||||
|
/// For after-dot completion we must always get consistent results.
|
||||||
|
auto Results = Server
|
||||||
|
.codeComplete(FooCpp, Completion.MarkerPos,
|
||||||
|
StringRef(Completion.Text))
|
||||||
|
.get()
|
||||||
|
.Value;
|
||||||
|
|
||||||
|
EXPECT_TRUE(Results.isIncomplete);
|
||||||
|
EXPECT_EQ(Opts.Limit, Results.items.size());
|
||||||
|
EXPECT_TRUE(ContainsItem(Results, "AAA"));
|
||||||
|
EXPECT_TRUE(ContainsItem(Results, "BBB"));
|
||||||
|
EXPECT_FALSE(ContainsItem(Results, "CCC"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ClangdCompletionTest, CompletionOptions) {
|
TEST_F(ClangdCompletionTest, CompletionOptions) {
|
||||||
MockFSProvider FS;
|
MockFSProvider FS;
|
||||||
ErrorCheckingDiagConsumer DiagConsumer;
|
ErrorCheckingDiagConsumer DiagConsumer;
|
||||||
|
|
Loading…
Reference in New Issue