Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -63,6 +63,8 @@ JSONOutput &Out) override; void onCompletion(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) override; + void onSignatureHelp(TextDocumentPositionParams Params, StringRef ID, + JSONOutput &Out) override; void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) override; void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID, Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -48,6 +48,7 @@ "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]}, "codeActionProvider": true, "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]}, + "signatureHelpProvider": {"triggerCharacters": ["(", ","]}, "definitionProvider": true }}})"); if (IP.rootUri && !IP.rootUri->file.empty()) @@ -159,6 +160,18 @@ R"(,"result":[)" + Completions + R"(]})"); } +void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams Params, + StringRef ID, JSONOutput &Out) { + const auto SigHelp = SignatureHelp::unparse( + Server + .signatureHelp( + Params.textDocument.uri.file, + Position{Params.position.line, Params.position.character}) + .Value); + Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() + R"(,"result":)" + + SigHelp + "}"); +} + void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) { Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -244,6 +244,19 @@ codeComplete(PathRef File, Position Pos, llvm::Optional OverridenContents = llvm::None, IntrusiveRefCntPtr *UsedFS = nullptr); + + /// 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 + /// update will be scheduled and a draft for \p File will not be updated. If + /// \p OverridenContents is None, contents of the current draft for \p File + /// will be used. If \p UsedFS is non-null, it will be overwritten by + /// vfs::FileSystem used for signature help. This method should only be called + /// for currently tracked files. + Tagged + signatureHelp(PathRef File, Position Pos, + llvm::Optional OverridenContents = llvm::None, + IntrusiveRefCntPtr *UsedFS = nullptr); + /// Get definition of symbol at a specified \p Line and \p Column in \p File. Tagged> findDefinitions(PathRef File, Position Pos); Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -223,6 +223,35 @@ return make_tagged(std::move(Result), TaggedFS.Tag); } +Tagged +ClangdServer::signatureHelp(PathRef File, Position Pos, + llvm::Optional OverridenContents, + IntrusiveRefCntPtr *UsedFS) { + std::string DraftStorage; + if (!OverridenContents) { + auto FileContents = DraftMgr.getDraft(File); + assert(FileContents.Draft && + "signatureHelp is called for non-added document"); + + DraftStorage = std::move(*FileContents.Draft); + OverridenContents = DraftStorage; + } + + auto TaggedFS = FSProvider.getTaggedFileSystem(File); + if (UsedFS) + *UsedFS = TaggedFS.Value; + + std::shared_ptr Resources = Units.getFile(File); + assert(Resources && "Calling signatureHelp on non-added file"); + + auto Preamble = Resources->getPossiblyStalePreamble(); + auto Result = clangd::signatureHelp(File, Resources->getCompileCommand(), + Preamble ? &Preamble->Preamble : nullptr, + *OverridenContents, Pos, TaggedFS.Value, + PCHs, Logger); + return make_tagged(std::move(Result), TaggedFS.Tag); +} + std::vector ClangdServer::formatRange(PathRef File, Range Rng) { std::string Code = getDocument(File); Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -259,6 +259,14 @@ std::shared_ptr PCHs, bool SnippetCompletions, clangd::Logger &Logger); +/// Get signature help at a specified \p Pos in \p FileName. +SignatureHelp signatureHelp(PathRef FileName, tooling::CompileCommand Command, + PrecompiledPreamble const *Preamble, + StringRef Contents, Position Pos, + IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, + clangd::Logger &Logger); + /// Get definition of symbol at a specified \p Pos. std::vector findDefinitions(ParsedAST &AST, Position Pos, clangd::Logger &Logger); Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -119,6 +119,44 @@ llvm_unreachable("Unknown diagnostic level!"); } +/// Get the optional chunk as a string. This function is possibly recursive. +/// +/// The parameter info for each parameter is appended to the Parameters. +std::string +getOptionalParameters(const CodeCompletionString &CCS, + std::vector &Parameters) { + std::string Result; + for (const auto &Chunk : CCS) { + switch (Chunk.Kind) { + case CodeCompletionString::CK_Optional: + assert(Chunk.Optional && + "Expected the optional code completion string to be non-null."); + Result += getOptionalParameters(*Chunk.Optional, Parameters); + break; + case CodeCompletionString::CK_VerticalSpace: + break; + case CodeCompletionString::CK_Placeholder: + // A string that acts as a placeholder for, e.g., a function call + // argument. + // Intentional fallthrough here. + 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. + Result += Chunk.Text; + ParameterInformation Info; + Info.label = Chunk.Text; + Parameters.push_back(std::move(Info)); + break; + } + default: + Result += Chunk.Text; + break; + } + } + return Result; +} + llvm::Optional toClangdDiag(StoredDiagnostic D) { auto Location = D.getLocation(); if (!Location.isValid() || !Location.getManager().isInMainFile(Location)) @@ -284,6 +322,43 @@ return Result; } +std::string getDocumentation(const CodeCompletionString &CCS) { + // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this + // information in the documentation field. + std::string Result; + const unsigned AnnotationCount = CCS.getAnnotationCount(); + if (AnnotationCount > 0) { + Result += "Annotation"; + if (AnnotationCount == 1) { + Result += ": "; + } else /* AnnotationCount > 1 */ { + Result += "s: "; + } + for (unsigned I = 0; I < AnnotationCount; ++I) { + Result += CCS.getAnnotation(I); + Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); + } + } + // Add brief documentation (if there is any). + if (CCS.getBriefComment() != nullptr) { + if (!Result.empty()) { + // This means we previously added annotations. Add an extra newline + // character to make the annotations stand out. + Result.push_back('\n'); + } + Result += CCS.getBriefComment(); + } + return Result; +} + +void fillSortText(const CodeCompletionString &CCS, CompletionItem &Item) { + // 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()); +} + class CompletionItemsCollector : public CodeCompleteConsumer { public: @@ -322,7 +397,7 @@ CompletionItem Item; Item.insertTextFormat = InsertTextFormat::PlainText; - FillDocumentation(CCS, Item); + Item.documentation = getDocumentation(CCS); // Fill in the label, detail, insertText and filterText fields of the // CompletionItem. @@ -331,7 +406,7 @@ // Fill in the kind field of the CompletionItem. Item.kind = getKind(Result.CursorKind); - FillSortText(CCS, Item); + fillSortText(CCS, Item); return Item; } @@ -339,35 +414,6 @@ 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(); - } - } - static int GetSortPriority(const CodeCompletionString &CCS) { int Score = CCS.getPriority(); // Fill in the sortText of the CompletionItem. @@ -560,14 +606,108 @@ } } }; // SnippetCompletionItemsCollector -} // namespace -std::vector -clangd::codeComplete(PathRef FileName, tooling::CompileCommand Command, - PrecompiledPreamble const *Preamble, StringRef Contents, - Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs, - bool SnippetCompletions, clangd::Logger &Logger) { +class SignatureHelpCollector final : public CodeCompleteConsumer { + +public: + SignatureHelpCollector(const CodeCompleteOptions &CodeCompleteOpts, + SignatureHelp &SigHelp) + : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false), + SigHelp(SigHelp), + Allocator(std::make_shared()), + CCTUInfo(Allocator) {} + + void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates) override { + SigHelp.signatures.reserve(NumCandidates); + // FIXME(rwols): How can we determine the "active overload candidate"? + // Right now the overloaded candidates seem to be provided in a "best fit" + // order, so I'm not too worried about this. + SigHelp.activeSignature = 0; + assert(CurrentArg <= std::numeric_limits::max() && + "too many arguments"); + SigHelp.activeParameter = static_cast(CurrentArg); + for (unsigned I = 0; I < NumCandidates; ++I) { + const auto &Candidate = Candidates[I]; + const auto *CCS = Candidate.CreateSignatureString( + CurrentArg, S, *Allocator, CCTUInfo, true); + assert(CCS && "Expected the CodeCompletionString to be non-null"); + SigHelp.signatures.push_back(ProcessOverloadCandidate(Candidate, *CCS)); + } + } + + GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } + +private: + SignatureInformation + ProcessOverloadCandidate(const OverloadCandidate &Candidate, + const CodeCompletionString &CCS) const { + SignatureInformation Result; + std::string OptionalParameterLabel; + const char *ReturnType = nullptr; + + Result.documentation = getDocumentation(CCS); + + for (const auto &Chunk : CCS) { + switch (Chunk.Kind) { + case CodeCompletionString::CK_ResultType: + // A piece of text that describes the type of an entity or, + // for functions and methods, the return type. + assert(!ReturnType && "Unexpected CK_ResultType"); + ReturnType = Chunk.Text; + break; + case CodeCompletionString::CK_Placeholder: + // A string that acts as a placeholder for, e.g., a function call + // argument. + // Intentional fallthrough here. + 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. + Result.label += Chunk.Text; + ParameterInformation Info; + Info.label = Chunk.Text; + Result.parameters.push_back(std::move(Info)); + break; + } + case CodeCompletionString::CK_Optional: { + // The rest of the parameters are defaulted/optional. + assert(Chunk.Optional && + "Expected the optional code completion string to be non-null."); + Result.label += + getOptionalParameters(*Chunk.Optional, Result.parameters); + break; + } + case CodeCompletionString::CK_VerticalSpace: + break; + default: + Result.label += Chunk.Text; + break; + } + } + if (ReturnType) { + Result.label += " -> "; + Result.label += ReturnType; + } + return Result; + } + + SignatureHelp &SigHelp; + std::shared_ptr Allocator; + CodeCompletionTUInfo CCTUInfo; + +}; // SignatureHelpCollector + +bool invokeCodeComplete(std::unique_ptr Consumer, + PathRef FileName, + const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, StringRef Contents, + Position Pos, IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, + clangd::Logger &Logger) { std::vector ArgStrs; for (const auto &S : Command.CommandLine) ArgStrs.push_back(S.c_str()); @@ -604,37 +744,79 @@ auto &FrontendOpts = Clang->getFrontendOpts(); FrontendOpts.SkipFunctionBodies = true; - FrontendOpts.CodeCompleteOpts.IncludeGlobals = true; - FrontendOpts.CodeCompleteOpts.IncludeMacros = true; - FrontendOpts.CodeCompleteOpts.IncludeBriefComments = true; + FrontendOpts.CodeCompleteOpts.IncludeGlobals = Consumer->includeGlobals(); + FrontendOpts.CodeCompleteOpts.IncludeMacros = Consumer->includeMacros(); + FrontendOpts.CodeCompleteOpts.IncludeBriefComments = + Consumer->includeBriefComments(); + FrontendOpts.CodeCompleteOpts.IncludeBriefComments = + Consumer->includeBriefComments(); FrontendOpts.CodeCompletionAt.FileName = FileName; FrontendOpts.CodeCompletionAt.Line = Pos.line + 1; FrontendOpts.CodeCompletionAt.Column = Pos.character + 1; - std::vector Items; - 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)); - } + Clang->setCodeCompletionConsumer(Consumer.release()); SyntaxOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { Logger.log("BeginSourceFile() failed when running codeComplete for " + FileName); - return Items; + return false; } - if (!Action.Execute()) + if (!Action.Execute()) { Logger.log("Execute() failed when running codeComplete for " + FileName); + return false; + } Action.EndSourceFile(); - return Items; + return true; +} + +} // namespace + +std::vector +clangd::codeComplete(PathRef FileName, tooling::CompileCommand Command, + PrecompiledPreamble const *Preamble, StringRef Contents, + Position Pos, IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, + bool SnippetCompletions, clangd::Logger &Logger) { + std::vector Results; + CodeCompleteOptions Options; + std::unique_ptr Consumer; + Options.IncludeGlobals = true; + Options.IncludeMacros = true; + Options.IncludeBriefComments = true; + if (SnippetCompletions) { + Options.IncludeCodePatterns = true; + Consumer = + llvm::make_unique(Options, Results); + } else { + Options.IncludeCodePatterns = false; + Consumer = + llvm::make_unique(Options, Results); + } + invokeCodeComplete(std::move(Consumer), FileName, Command, Preamble, Contents, + Pos, std::move(VFS), std::move(PCHs), Logger); + return Results; +} + +SignatureHelp +clangd::signatureHelp(PathRef FileName, tooling::CompileCommand Command, + PrecompiledPreamble const *Preamble, StringRef Contents, + Position Pos, IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, + clangd::Logger &Logger) { + SignatureHelp Result; + CodeCompleteOptions Options; + Options.IncludeGlobals = false; + Options.IncludeMacros = false; + Options.IncludeCodePatterns = false; + Options.IncludeBriefComments = true; + invokeCodeComplete(llvm::make_unique(Options, Result), + FileName, Command, Preamble, Contents, Pos, std::move(VFS), + std::move(PCHs), Logger); + return Result; } void clangd::dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) { Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -453,6 +453,71 @@ static std::string unparse(const CompletionItem &P); }; +/// Represents a parameter of a callable-signature. +/// +/// A parameter can have a label and a doc-comment. +struct ParameterInformation { + + /// The label of this parameter. Will be shown in the UI. + std::string label; + + /// The human-readable doc-comment of this parameter. Will be shown in the UI + /// but can be omitted. + std::string documentation; + + static std::string unparse(const ParameterInformation &); +}; + +/// Represents the signature of something callable. +/// +/// A signature can have a label, like a function-name, a doc-comment, and a set +/// of parameters. +struct SignatureInformation { + + /// The label of this signature. Will be shown in the UI. + std::string label; + + /// The human-readable doc-comment of this signature. Will be shown in the UI + /// but can be omitted. + std::string documentation; + + /// The parameters of this signature. + std::vector parameters; + + static std::string unparse(const SignatureInformation &); +}; + +/// Signature help represents the signature of something callable. +/// +/// There can be multiple signature but only one active and only one active +/// parameter. +struct SignatureHelp { + + /// One or more signatures. + std::vector signatures; + + /// The active signature. + /// + /// If omitted or the value lies outside the range of `signatures` the value + /// defaults to zero or is ignored if `signatures.length === 0`. Whenever + /// possible implementors should make an active decision about the active + /// signature and shouldn't rely on a default value. In future version of the + /// protocol this property might become mandantory to better express this. + int activeSignature = 0; + + /// The active parameter of the active signature. + /// + /// If omitted or the value lies outside the range of + /// `signatures[activeSignature].parameters` defaults to 0 if the active + /// signature has parameters. If the active signature has no parameters it is + /// ignored. In future version of the protocol this property might become + /// mandantory to better express the active parameter if the active signature + /// does have any. + int activeParameter = 0; + + static std::string unparse(const SignatureHelp &); +}; + } // namespace clangd } // namespace clang Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -831,3 +831,58 @@ Result.back() = '}'; return Result; } + +std::string ParameterInformation::unparse(const ParameterInformation &PI) { + std::string Result = "{"; + llvm::raw_string_ostream Os(Result); + assert(!PI.label.empty() && "parameter information label is required"); + Os << R"("label":")" << llvm::yaml::escape(PI.label) << '\"'; + if (!PI.documentation.empty()) + Os << R"(,"documentation":")" << llvm::yaml::escape(PI.documentation) + << '\"'; + Os << '}'; + Os.flush(); + return Result; +} + +std::string SignatureInformation::unparse(const SignatureInformation &SI) { + std::string Result = "{"; + llvm::raw_string_ostream Os(Result); + assert(!SI.label.empty() && "signature information label is required"); + Os << R"("label":")" << llvm::yaml::escape(SI.label) << '\"'; + if (!SI.documentation.empty()) + Os << R"(,"documentation":")" << llvm::yaml::escape(SI.documentation) + << '\"'; + Os << R"(,"parameters":[)"; + for (const auto &Parameter : SI.parameters) { + Os << ParameterInformation::unparse(Parameter) << ','; + } + Os.flush(); + if (SI.parameters.empty()) + Result.push_back(']'); + else + Result.back() = ']'; // Replace the last `,` with an `]`. + Result.push_back('}'); + return Result; +} + +std::string SignatureHelp::unparse(const SignatureHelp &SH) { + std::string Result = "{"; + llvm::raw_string_ostream Os(Result); + assert(SH.activeSignature >= 0 && + "Unexpected negative value for number of active signatures."); + assert(SH.activeParameter >= 0 && + "Unexpected negative value for active parameter index"); + Os << R"("activeSignature":)" << SH.activeSignature + << R"(,"activeParameter":)" << SH.activeParameter << R"(,"signatures":[)"; + for (const auto &Signature : SH.signatures) { + Os << SignatureInformation::unparse(Signature) << ','; + } + Os.flush(); + if (SH.signatures.empty()) + Result.push_back(']'); + else + Result.back() = ']'; // Replace the last `,` with an `]`. + Result.push_back('}'); + return Result; +} Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ clangd/ProtocolHandlers.h @@ -47,6 +47,8 @@ JSONOutput &Out) = 0; virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) = 0; + virtual void onSignatureHelp(TextDocumentPositionParams Params, StringRef ID, + JSONOutput &Out) = 0; virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) = 0; virtual void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID, Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -192,6 +192,23 @@ ProtocolCallbacks &Callbacks; }; +struct SignatureHelpHandler : Handler { + SignatureHelpHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks) + : Handler(Output), Callbacks(Callbacks) {} + + void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override { + auto TDPP = TextDocumentPositionParams::parse(Params, Output); + if (!TDPP) { + Output.log("Failed to decode TextDocumentPositionParams!\n"); + return; + } + Callbacks.onSignatureHelp(*TDPP, ID, Output); + } + +private: + ProtocolCallbacks &Callbacks; +}; + struct GotoDefinitionHandler : Handler { GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks) : Handler(Output), Callbacks(Callbacks) {} @@ -260,6 +277,9 @@ "textDocument/completion", llvm::make_unique(Out, Callbacks)); Dispatcher.registerHandler( + "textDocument/signatureHelp", + llvm::make_unique(Out, Callbacks)); + Dispatcher.registerHandler( "textDocument/definition", llvm::make_unique(Out, Callbacks)); Dispatcher.registerHandler( Index: test/clangd/formatting.test =================================================================== --- test/clangd/formatting.test +++ test/clangd/formatting.test @@ -4,7 +4,6 @@ Content-Length: 125 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} -# CHECK: Content-Length: 466 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{ # CHECK: "textDocumentSync": 1, # CHECK: "documentFormattingProvider": true, Index: test/clangd/signature-help.test =================================================================== --- /dev/null +++ test/clangd/signature-help.test @@ -0,0 +1,42 @@ +# RUN: clangd -run-synchronously < %s | FileCheck %s +# It is absolutely vital that this file has CRLF line endings. + +# Start a session. +Content-Length: 125 + +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} + +# Modify the document. +Content-Length: 333 + +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"void foo(int x, int y);\nvoid foo(int x, float y);\nvoid foo(float x, int y);\nvoid foo(float x, float y);\nvoid bar(int x, int y = 0);\nvoid bar(float x = 0, int y = 42);\nint main() { foo("}}} + +# Ask for signature help. +Content-Length: 151 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/signatureHelp","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":8,"character":9}}} +# CHECK: {"jsonrpc":"2.0","id":1,"result":{"activeSignature":0,"activeParameter":0,"signatures":[ +# CHECK-DAG: {"label":"foo(float x, float y) -> void","parameters":[{"label":"float x"},{"label":"float y"}]} +# CHECK-DAG: {"label":"foo(float x, int y) -> void","parameters":[{"label":"float x"},{"label":"int y"}]} +# CHECK-DAG: {"label":"foo(int x, float y) -> void","parameters":[{"label":"int x"},{"label":"float y"}]} +# CHECK-DAG: {"label":"foo(int x, int y) -> void","parameters":[{"label":"int x"},{"label":"int y"}]} +# CHECK: ]} + +# Modify the document +Content-Length: 333 + +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":2,"text":"void foo(int x, int y);\nvoid foo(int x, float y);\nvoid foo(float x, int y);\nvoid foo(float x, float y);\nvoid bar(int x, int y = 0);\nvoid bar(float x = 0, int y = 42);\nint main() { bar("}}} + +# Ask for signature help (this checks default argument handling). +Content-Length: 151 + +{"jsonrpc":"2.0","id":2,"method":"textDocument/signatureHelp","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":8,"character":9}}} +# CHECK: {"jsonrpc":"2.0","id":2,"result":{"activeSignature":0,"activeParameter":0,"signatures":[ +# CHECK-DAG: {"label":"bar(int x, int y = 0) -> void","parameters":[{"label":"int x"},{"label":"int y = 0"}]} +# CHECK-DAG: {"label":"bar(float x = 0, int y = 42) -> void","parameters":[{"label":"float x = 0"},{"label":"int y = 42"}]} +# CHECK: ]} + +# Shutdown. +Content-Length: 49 + +{"jsonrpc":"2.0","id":100000,"method":"shutdown"}