[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:
Sam McCall 2017-11-15 09:16:29 +00:00
parent 1102a861f5
commit a40371bcb6
15 changed files with 430 additions and 311 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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;
} }

View File

@ -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,

View File

@ -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}};

View File

@ -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 {

View File

@ -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"}

View File

@ -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}

View File

@ -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}

View File

@ -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"}

View File

@ -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"}

View File

@ -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",

View File

@ -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}}}

View File

@ -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;