Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -27,6 +27,7 @@ class ClangdLSPServer { public: ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, + bool SnippetCompletions, llvm::Optional ResourceDir); /// Run LSP server loop, receiving input for it from \p In. \p In must be Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -221,9 +221,11 @@ } ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, + bool SnippetCompletions, llvm::Optional ResourceDir) : Out(Out), DiagConsumer(*this), - Server(CDB, DiagConsumer, FSProvider, AsyncThreadsCount, ResourceDir) {} + Server(CDB, DiagConsumer, FSProvider, AsyncThreadsCount, + SnippetCompletions, ResourceDir) {} void ClangdLSPServer::run(std::istream &In) { assert(!IsDone && "Run was called before"); Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -179,6 +179,9 @@ /// AsyncThreadsCount worker threads. However, if \p AsyncThreadsCount is 0, /// all requests will be processed on the calling thread. /// + /// When \p SnippetCompletions is true, completion items will be presented + /// with embedded snippets. Otherwise, plaintext items will be presented. + /// /// ClangdServer uses \p FSProvider to get an instance of vfs::FileSystem for /// each parsing request. Results of code completion and diagnostics also /// include a tag, that \p FSProvider returns along with the vfs::FileSystem. @@ -201,6 +204,7 @@ ClangdServer(GlobalCompilationDatabase &CDB, DiagnosticsConsumer &DiagConsumer, FileSystemProvider &FSProvider, unsigned AsyncThreadsCount, + bool SnippetCompletions, llvm::Optional ResourceDir = llvm::None); /// Add a \p File to the list of tracked C++ files or update the contents if @@ -274,6 +278,7 @@ // called before all other members to stop the worker thread that references // ClangdServer ClangdScheduler WorkScheduler; + bool SnippetCompletions; }; } // namespace clangd Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -144,12 +144,13 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, DiagnosticsConsumer &DiagConsumer, FileSystemProvider &FSProvider, - unsigned AsyncThreadsCount, + unsigned AsyncThreadsCount, bool SnippetCompletions, llvm::Optional ResourceDir) : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), ResourceDir(ResourceDir ? ResourceDir->str() : getStandardResourceDir()), PCHs(std::make_shared()), - WorkScheduler(AsyncThreadsCount) {} + WorkScheduler(AsyncThreadsCount), SnippetCompletions(SnippetCompletions) { +} std::future ClangdServer::addDocument(PathRef File, StringRef Contents) { DocVersion Version = DraftMgr.updateDraft(File, Contents); @@ -206,10 +207,10 @@ assert(Resources && "Calling completion on non-added file"); auto Preamble = Resources->getPossiblyStalePreamble(); - std::vector Result = - clangd::codeComplete(File, Resources->getCompileCommand(), - Preamble ? &Preamble->Preamble : nullptr, - *OverridenContents, Pos, TaggedFS.Value, PCHs); + std::vector Result = clangd::codeComplete( + File, Resources->getCompileCommand(), + Preamble ? &Preamble->Preamble : nullptr, *OverridenContents, Pos, + TaggedFS.Value, PCHs, SnippetCompletions); return make_tagged(std::move(Result), TaggedFS.Tag); } Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -253,7 +253,8 @@ codeComplete(PathRef FileName, tooling::CompileCommand Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs); + std::shared_ptr PCHs, + bool SnippetCompletions); /// Get definition of symbol at a specified \p Pos. std::vector findDefinitions(ParsedAST &AST, Position Pos); Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -272,14 +272,22 @@ } } +std::string escapeSnippet(const llvm::StringRef Text) { + std::string Result; + Result.reserve(Text.size()); // Assume '$', '}' and '\\' are rare. + for (const auto Character : Text) { + if (Character == '$' || Character == '}' || Character == '\\') + Result.push_back('\\'); + Result.push_back(Character); + } + return Result; +} + class CompletionItemsCollector : public CodeCompleteConsumer { - std::vector *Items; - std::shared_ptr Allocator; - CodeCompletionTUInfo CCTUInfo; public: - CompletionItemsCollector(std::vector *Items, - const CodeCompleteOptions &CodeCompleteOpts) + CompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts, + std::vector &Items) : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false), Items(Items), Allocator(std::make_shared()), @@ -287,53 +295,237 @@ void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, CodeCompletionResult *Results, - unsigned NumResults) override { - for (unsigned I = 0; I != NumResults; ++I) { - CodeCompletionResult &Result = Results[I]; - CodeCompletionString *CCS = Result.CreateCodeCompletionString( + unsigned NumResults) override final { + Items.reserve(NumResults); + for (unsigned I = 0; I < NumResults; ++I) { + auto &Result = Results[I]; + const auto *CCS = Result.CreateCodeCompletionString( S, Context, *Allocator, CCTUInfo, CodeCompleteOpts.IncludeBriefComments); - if (CCS) { - CompletionItem Item; - for (CodeCompletionString::Chunk C : *CCS) { - switch (C.Kind) { - case CodeCompletionString::CK_ResultType: - Item.detail = C.Text; - break; - case CodeCompletionString::CK_Optional: - break; - default: - Item.label += C.Text; - break; - } - } - assert(CCS->getTypedText()); - Item.kind = getKind(Result.CursorKind); - // Priority is a 16-bit integer, hence at most 5 digits. - assert(CCS->getPriority() < 99999 && "Expecting code completion result " - "priority to have at most " - "5-digits"); - llvm::raw_string_ostream(Item.sortText) - << llvm::format("%05d%s", CCS->getPriority(), CCS->getTypedText()); - Item.insertText = Item.filterText = CCS->getTypedText(); - if (CCS->getBriefComment()) - Item.documentation = CCS->getBriefComment(); - Items->push_back(std::move(Item)); - } + assert(CCS && "Expected the CodeCompletionString to be non-null"); + Items.push_back(ProcessCodeCompleteResult(Result, *CCS)); } } GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } -}; + +private: + CompletionItem + ProcessCodeCompleteResult(const CodeCompletionResult &Result, + const CodeCompletionString &CCS) const { + + // Adjust this to InsertTextFormat::Snippet iff we encounter a + // CK_Placeholder chunk in SnippetCompletionItemsCollector. + CompletionItem Item; + Item.insertTextFormat = InsertTextFormat::PlainText; + + FillDocumentation(CCS, Item); + + // 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.CursorKind); + + FillSortText(CCS, Item); + + return Item; + } + + virtual void ProcessChunks(const CodeCompletionString &CCS, + CompletionItem &Item) const = 0; + + void FillDocumentation(const CodeCompletionString &CCS, + CompletionItem &Item) const { + // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this + // information in the documentation field. + const unsigned AnnotationCount = CCS.getAnnotationCount(); + if (AnnotationCount > 0) { + Item.documentation += "Annotation"; + if (AnnotationCount == 1) { + Item.documentation += ": "; + } else /* AnnotationCount > 1 */ { + Item.documentation += "s: "; + } + for (unsigned I = 0; I < AnnotationCount; ++I) { + Item.documentation += CCS.getAnnotation(I); + Item.documentation.push_back(I == AnnotationCount - 1 ? '\n' : ' '); + } + } + + // Add brief documentation (if there is any). + if (CCS.getBriefComment() != nullptr) { + if (!Item.documentation.empty()) { + // This means we previously added annotations. Add an extra newline + // character to make the annotations stand out. + Item.documentation.push_back('\n'); + } + Item.documentation += CCS.getBriefComment(); + } + } + + void FillSortText(const CodeCompletionString &CCS, + CompletionItem &Item) const { + // Fill in the sortText of the CompletionItem. + assert(CCS.getPriority() < 99999 && "Expecting code completion result " + "priority to have at most 5-digits"); + llvm::raw_string_ostream(Item.sortText) + << llvm::format("%05d%s", CCS.getPriority(), Item.filterText.c_str()); + } + + std::vector &Items; + std::shared_ptr Allocator; + CodeCompletionTUInfo CCTUInfo; + +}; // CompletionItemsCollector + +class PlainTextCompletionItemsCollector final + : public CompletionItemsCollector { + +public: + PlainTextCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts, + std::vector &Items) + : CompletionItemsCollector(CodeCompleteOpts, Items) {} + +private: + void ProcessChunks(const CodeCompletionString &CCS, + CompletionItem &Item) const override { + for (const auto &Chunk : CCS) { + switch (Chunk.Kind) { + case CodeCompletionString::CK_TypedText: + // There's always exactly one CK_TypedText chunk. + Item.insertText = Item.filterText = Chunk.Text; + Item.label += Chunk.Text; + break; + case CodeCompletionString::CK_ResultType: + assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType"); + Item.detail = Chunk.Text; + break; + case CodeCompletionString::CK_Optional: + break; + default: + Item.label += Chunk.Text; + break; + } + } + } +}; // PlainTextCompletionItemsCollector + +class SnippetCompletionItemsCollector final : public CompletionItemsCollector { + +public: + SnippetCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts, + std::vector &Items) + : CompletionItemsCollector(CodeCompleteOpts, Items) {} + +private: + void ProcessChunks(const CodeCompletionString &CCS, + CompletionItem &Item) const override { + unsigned ArgCount = 0; + for (const auto &Chunk : CCS) { + switch (Chunk.Kind) { + case CodeCompletionString::CK_TypedText: + // The piece of text that the user is expected to type to match + // the code-completion string, typically a keyword or the name of + // a declarator or macro. + Item.filterText = Chunk.Text; + // Note intentional fallthrough here. + case CodeCompletionString::CK_Text: + // A piece of text that should be placed in the buffer, + // e.g., parentheses or a comma in a function call. + Item.label += Chunk.Text; + Item.insertText += Chunk.Text; + break; + case CodeCompletionString::CK_Optional: + // A code completion string that is entirely optional. + // For example, an optional code completion string that + // describes the default arguments in a function call. + + // FIXME: Maybe add an option to allow presenting the optional chunks? + break; + case CodeCompletionString::CK_Placeholder: + // A string that acts as a placeholder for, e.g., a function call + // argument. + ++ArgCount; + Item.insertText += "${" + std::to_string(ArgCount) + ':' + + escapeSnippet(Chunk.Text) + '}'; + Item.label += Chunk.Text; + Item.insertTextFormat = InsertTextFormat::Snippet; + break; + case CodeCompletionString::CK_Informative: + // A piece of text that describes something about the result + // but should not be inserted into the buffer. + // For example, the word "const" for a const method, or the name of + // the base class for methods that are part of the base class. + Item.label += Chunk.Text; + // Don't put the informative chunks in the insertText. + break; + case CodeCompletionString::CK_ResultType: + // A piece of text that describes the type of an entity or, + // for functions and methods, the return type. + assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType"); + Item.detail = Chunk.Text; + break; + case CodeCompletionString::CK_CurrentParameter: + // A piece of text that describes the parameter that corresponds to + // the code-completion location within a function call, message send, + // macro invocation, etc. + // + // This should never be present while collecting completion items, + // only while collecting overload candidates. + llvm_unreachable("Unexpected CK_CurrentParameter while collecting " + "CompletionItems"); + break; + case CodeCompletionString::CK_LeftParen: + // A left parenthesis ('('). + case CodeCompletionString::CK_RightParen: + // A right parenthesis (')'). + case CodeCompletionString::CK_LeftBracket: + // A left bracket ('['). + case CodeCompletionString::CK_RightBracket: + // A right bracket (']'). + case CodeCompletionString::CK_LeftBrace: + // A left brace ('{'). + case CodeCompletionString::CK_RightBrace: + // A right brace ('}'). + case CodeCompletionString::CK_LeftAngle: + // A left angle bracket ('<'). + case CodeCompletionString::CK_RightAngle: + // A right angle bracket ('>'). + case CodeCompletionString::CK_Comma: + // A comma separator (','). + case CodeCompletionString::CK_Colon: + // A colon (':'). + case CodeCompletionString::CK_SemiColon: + // A semicolon (';'). + case CodeCompletionString::CK_Equal: + // An '=' sign. + case CodeCompletionString::CK_HorizontalSpace: + // Horizontal whitespace (' '). + Item.insertText += Chunk.Text; + Item.label += Chunk.Text; + break; + case CodeCompletionString::CK_VerticalSpace: + // Vertical whitespace ('\n' or '\r\n', depending on the + // platform). + Item.insertText += Chunk.Text; + // Don't even add a space to the label. + break; + } + } + } +}; // SnippetCompletionItemsCollector } // namespace std::vector clangd::codeComplete(PathRef FileName, tooling::CompileCommand Command, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs) { + std::shared_ptr PCHs, + bool SnippetCompletions) { std::vector ArgStrs; for (const auto &S : Command.CommandLine) ArgStrs.push_back(S.c_str()); @@ -371,8 +563,6 @@ FrontendOpts.SkipFunctionBodies = true; FrontendOpts.CodeCompleteOpts.IncludeGlobals = true; - // we don't handle code patterns properly yet, disable them. - FrontendOpts.CodeCompleteOpts.IncludeCodePatterns = false; FrontendOpts.CodeCompleteOpts.IncludeMacros = true; FrontendOpts.CodeCompleteOpts.IncludeBriefComments = true; @@ -381,8 +571,15 @@ FrontendOpts.CodeCompletionAt.Column = Pos.character + 1; std::vector Items; - Clang->setCodeCompletionConsumer( - new CompletionItemsCollector(&Items, FrontendOpts.CodeCompleteOpts)); + if (SnippetCompletions) { + FrontendOpts.CodeCompleteOpts.IncludeCodePatterns = true; + Clang->setCodeCompletionConsumer(new SnippetCompletionItemsCollector( + FrontendOpts.CodeCompleteOpts, Items)); + } else { + FrontendOpts.CodeCompleteOpts.IncludeCodePatterns = false; + Clang->setCodeCompletionConsumer(new PlainTextCompletionItemsCollector( + FrontendOpts.CodeCompleteOpts, Items)); + } SyntaxOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -724,8 +724,8 @@ if (!CI.insertText.empty()) Os << R"("insertText":")" << llvm::yaml::escape(CI.insertText) << R"(",)"; if (CI.insertTextFormat != InsertTextFormat::Missing) { - Os << R"("insertTextFormat":")" << static_cast(CI.insertTextFormat) - << R"(",)"; + Os << R"("insertTextFormat":)" << static_cast(CI.insertTextFormat) + << R"(,)"; } if (CI.textEdit) Os << R"("textEdit":)" << TextEdit::unparse(*CI.textEdit) << ','; Index: clangd/clients/clangd-vscode/package.json =================================================================== --- clangd/clients/clangd-vscode/package.json +++ clangd/clients/clangd-vscode/package.json @@ -24,8 +24,8 @@ "test": "node ./node_modules/vscode/bin/test" }, "dependencies": { - "vscode-languageclient": "^2.6.3", - "vscode-languageserver": "^2.6.2" + "vscode-languageclient": "^3.3.0", + "vscode-languageserver": "^3.3.0" }, "devDependencies": { "typescript": "^2.0.3", Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -26,6 +26,12 @@ llvm::cl::desc("Number of async workers used by clangd"), llvm::cl::init(getDefaultAsyncThreadsCount())); +static llvm::cl::opt EnableSnippets( + "enable-snippets", + llvm::cl::desc( + "Present snippet completions instead of plaintext completions"), + llvm::cl::init(false)); + static llvm::cl::opt RunSynchronously( "run-synchronously", llvm::cl::desc("Parse on main thread. If set, -j is ignored"), @@ -61,6 +67,7 @@ if (!ResourceDir.empty()) ResourceDirRef = ResourceDir; - ClangdLSPServer LSPServer(Out, WorkerThreadsCount, ResourceDirRef); + ClangdLSPServer LSPServer(Out, WorkerThreadsCount, EnableSnippets, + ResourceDirRef); LSPServer.run(std::cin); } Index: test/clangd/authority-less-uri.test =================================================================== --- test/clangd/authority-less-uri.test +++ test/clangd/authority-less-uri.test @@ -16,7 +16,7 @@ # Test authority-less URI # # CHECK: {"jsonrpc":"2.0","id":1,"result":[ -# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a"} +# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a","insertTextFormat":1} # CHECK: ]} Content-Length: 172 @@ -25,7 +25,7 @@ # Test params parsing in the presence of a 1.x-compatible client (inlined "uri") # # CHECK: {"jsonrpc":"2.0","id":2,"result":[ -# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a"} +# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a","insertTextFormat":1} # CHECK: ]} Content-Length: 44 Index: test/clangd/completion-snippet.test =================================================================== --- /dev/null +++ test/clangd/completion-snippet.test @@ -0,0 +1,54 @@ +# RUN: clangd -run-synchronously -enable-snippets < %s | FileCheck %s +# It is absolutely vital that this file has CRLF line endings. +# +Content-Length: 125 + +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} + +Content-Length: 246 + +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"struct fake { int a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n fake f;\n f.\n}\n"}}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}} +# The order of results returned by codeComplete seems to be +# nondeterministic, so we check regardless of order. +# +# CHECK: {"jsonrpc":"2.0","id":1,"result":[ +# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a","insertTextFormat":1} +# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"00035bb","filterText":"bb","insertText":"bb","insertTextFormat":1} +# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"00035ccc","filterText":"ccc","insertText":"ccc","insertTextFormat":1} +# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"00034operator=","filterText":"operator=","insertText":"operator=(${1:const fake &})","insertTextFormat":2} +# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"00034~fake","filterText":"~fake","insertText":"~fake()","insertTextFormat":1} +# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"00035f","filterText":"f","insertText":"f(${1:int i}, ${2:const float f})","insertTextFormat":2} +# CHECK: ]} +Content-Length: 148 + +{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}} +# Repeat the completion request, expect the same results. +# +# CHECK: {"jsonrpc":"2.0","id":2,"result":[ +# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a","insertTextFormat":1} +# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"00035bb","filterText":"bb","insertText":"bb","insertTextFormat":1} +# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"00035ccc","filterText":"ccc","insertText":"ccc","insertTextFormat":1} +# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"00034operator=","filterText":"operator=","insertText":"operator=(${1:const fake &})","insertTextFormat":2} +# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"00034~fake","filterText":"~fake","insertText":"~fake()","insertTextFormat":1} +# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"00035f","filterText":"f","insertText":"f(${1:int i}, ${2:const float f})","insertTextFormat":2} +# CHECK: ]} +# Update the source file and check for completions again. +Content-Length: 226 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct fancy { int (*func())(int, int); };\nint main() {\n fancy f;\n f.\n}\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}} +# Repeat the completion request, expect the same results. +# +# CHECK: {"jsonrpc":"2.0","id":3,"result":[ +# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int, int)","sortText":"00034func","filterText":"func","insertText":"func()","insertTextFormat":1} +# CHECK: ]} +Content-Length: 44 + +{"jsonrpc":"2.0","id":4,"method":"shutdown"} Index: test/clangd/completion.test =================================================================== --- test/clangd/completion.test +++ test/clangd/completion.test @@ -16,12 +16,12 @@ # nondeterministic, so we check regardless of order. # # CHECK: {"jsonrpc":"2.0","id":1,"result":[ -# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a"} -# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"00035bb","filterText":"bb","insertText":"bb"} -# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"00035ccc","filterText":"ccc","insertText":"ccc"} -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"00034operator=","filterText":"operator=","insertText":"operator="} -# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"00034~fake","filterText":"~fake","insertText":"~fake"} -# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"00035f","filterText":"f","insertText":"f"} +# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a","insertTextFormat":1} +# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"00035bb","filterText":"bb","insertText":"bb","insertTextFormat":1} +# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"00035ccc","filterText":"ccc","insertText":"ccc","insertTextFormat":1} +# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"00034operator=","filterText":"operator=","insertText":"operator=","insertTextFormat":1} +# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"00034~fake","filterText":"~fake","insertText":"~fake","insertTextFormat":1} +# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"00035f","filterText":"f","insertText":"f","insertTextFormat":1} # CHECK: ]} Content-Length: 148 @@ -29,12 +29,12 @@ # Repeat the completion request, expect the same results. # # CHECK: {"jsonrpc":"2.0","id":2,"result":[ -# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a"} -# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"00035bb","filterText":"bb","insertText":"bb"} -# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"00035ccc","filterText":"ccc","insertText":"ccc"} -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"00034operator=","filterText":"operator=","insertText":"operator="} -# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"00034~fake","filterText":"~fake","insertText":"~fake"} -# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"00035f","filterText":"f","insertText":"f"} +# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"00035a","filterText":"a","insertText":"a","insertTextFormat":1} +# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"00035bb","filterText":"bb","insertText":"bb","insertTextFormat":1} +# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"00035ccc","filterText":"ccc","insertText":"ccc","insertTextFormat":1} +# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"00034operator=","filterText":"operator=","insertText":"operator=","insertTextFormat":1} +# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"00034~fake","filterText":"~fake","insertText":"~fake","insertTextFormat":1} +# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"00035f","filterText":"f","insertText":"f","insertTextFormat":1} # CHECK: ]} # Update the source file and check for completions again. Content-Length: 226 @@ -47,7 +47,7 @@ # Repeat the completion request, expect the same results. # # CHECK: {"jsonrpc":"2.0","id":3,"result":[ -# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int, int)","sortText":"00034func","filterText":"func","insertText":"func"} +# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int, int)","sortText":"00034func","filterText":"func","insertText":"func","insertTextFormat":1} # CHECK: ]} Content-Length: 44