[clang-tools-extra] r315055 - [clangd] Add textDocument/signatureHelp

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 6 04:54:17 PDT 2017


Author: ibiryukov
Date: Fri Oct  6 04:54:17 2017
New Revision: 315055

URL: http://llvm.org/viewvc/llvm-project?rev=315055&view=rev
Log:
[clangd] Add textDocument/signatureHelp

Summary:
Makes clangd respond to a client's "textDocument/signatureHelp" request by
presenting function/method overloads.

Patch by Raoul Wols.

Reviewers: bkramer, ilya-biryukov, krasimir

Reviewed By: ilya-biryukov

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D38048

Added:
    clang-tools-extra/trunk/test/clangd/signature-help.test
Modified:
    clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
    clang-tools-extra/trunk/clangd/ClangdLSPServer.h
    clang-tools-extra/trunk/clangd/ClangdServer.cpp
    clang-tools-extra/trunk/clangd/ClangdServer.h
    clang-tools-extra/trunk/clangd/ClangdUnit.cpp
    clang-tools-extra/trunk/clangd/ClangdUnit.h
    clang-tools-extra/trunk/clangd/Protocol.cpp
    clang-tools-extra/trunk/clangd/Protocol.h
    clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
    clang-tools-extra/trunk/clangd/ProtocolHandlers.h
    clang-tools-extra/trunk/test/clangd/formatting.test
    clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
    clang-tools-extra/trunk/test/clangd/initialize-params.test

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Fri Oct  6 04:54:17 2017
@@ -48,6 +48,7 @@ void ClangdLSPServer::onInitialize(Strin
           "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
           "codeActionProvider": true,
           "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
+          "signatureHelpProvider": {"triggerCharacters": ["(",","]},
           "definitionProvider": true
         }}})");
   if (IP.rootUri && !IP.rootUri->file.empty())
@@ -166,6 +167,18 @@ void ClangdLSPServer::onCompletion(TextD
       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) {
 

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Fri Oct  6 04:54:17 2017
@@ -67,6 +67,8 @@ private:
                     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,

Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Fri Oct  6 04:54:17 2017
@@ -248,6 +248,35 @@ ClangdServer::codeComplete(PathRef File,
   return Future;
 }
 
+Tagged<SignatureHelp>
+ClangdServer::signatureHelp(PathRef File, Position Pos,
+                            llvm::Optional<StringRef> OverridenContents,
+                            IntrusiveRefCntPtr<vfs::FileSystem> *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<CppFile> 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<tooling::Replacement> ClangdServer::formatRange(PathRef File,
                                                             Range Rng) {
   std::string Code = getDocument(File);

Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Fri Oct  6 04:54:17 2017
@@ -253,6 +253,18 @@ public:
                llvm::Optional<StringRef> OverridenContents = llvm::None,
                IntrusiveRefCntPtr<vfs::FileSystem> *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>
+  signatureHelp(PathRef File, Position Pos,
+                llvm::Optional<StringRef> OverridenContents = llvm::None,
+                IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
+
   /// Get definition of symbol at a specified \p Line and \p Column in \p File.
   Tagged<std::vector<Location>> findDefinitions(PathRef File, Position Pos);
 

Modified: clang-tools-extra/trunk/clangd/ClangdUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.cpp?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdUnit.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdUnit.cpp Fri Oct  6 04:54:17 2017
@@ -119,6 +119,44 @@ static int getSeverity(DiagnosticsEngine
   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<ParameterInformation> &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<DiagWithFixIts> toClangdDiag(StoredDiagnostic D) {
   auto Location = D.getLocation();
   if (!Location.isValid() || !Location.getManager().isInMainFile(Location))
@@ -284,8 +322,36 @@ std::string escapeSnippet(const llvm::St
   return Result;
 }
 
-class CompletionItemsCollector : public CodeCompleteConsumer {
+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;
+}
 
+class CompletionItemsCollector : public CodeCompleteConsumer {
 public:
   CompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
                            std::vector<CompletionItem> &Items)
@@ -322,7 +388,7 @@ private:
     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.
@@ -339,35 +405,6 @@ private:
   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 +597,107 @@ private:
     }
   }
 }; // SnippetCompletionItemsCollector
-} // namespace
 
-std::vector<CompletionItem>
-clangd::codeComplete(PathRef FileName, tooling::CompileCommand Command,
-                     PrecompiledPreamble const *Preamble, StringRef Contents,
-                     Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
-                     std::shared_ptr<PCHContainerOperations> 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<clang::GlobalCodeCompletionAllocator>()),
+        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<int>::max() &&
+           "too many arguments");
+    SigHelp.activeParameter = static_cast<int>(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;
+    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<clang::GlobalCodeCompletionAllocator> Allocator;
+  CodeCompletionTUInfo CCTUInfo;
+
+}; // SignatureHelpCollector
+
+bool invokeCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
+                        const CodeCompleteOptions &Options, PathRef FileName,
+                        const tooling::CompileCommand &Command,
+                        PrecompiledPreamble const *Preamble, StringRef Contents,
+                        Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+                        std::shared_ptr<PCHContainerOperations> PCHs,
+                        clangd::Logger &Logger) {
   std::vector<const char *> ArgStrs;
   for (const auto &S : Command.CommandLine)
     ArgStrs.push_back(S.c_str());
@@ -603,38 +733,73 @@ clangd::codeComplete(PathRef FileName, t
 
   auto &FrontendOpts = Clang->getFrontendOpts();
   FrontendOpts.SkipFunctionBodies = true;
-
-  FrontendOpts.CodeCompleteOpts.IncludeGlobals = true;
-  FrontendOpts.CodeCompleteOpts.IncludeMacros = true;
-  FrontendOpts.CodeCompleteOpts.IncludeBriefComments = true;
-
+  FrontendOpts.CodeCompleteOpts = Options;
   FrontendOpts.CodeCompletionAt.FileName = FileName;
   FrontendOpts.CodeCompletionAt.Line = Pos.line + 1;
   FrontendOpts.CodeCompletionAt.Column = Pos.character + 1;
 
-  std::vector<CompletionItem> 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<CompletionItem>
+clangd::codeComplete(PathRef FileName, tooling::CompileCommand Command,
+                     PrecompiledPreamble const *Preamble, StringRef Contents,
+                     Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+                     std::shared_ptr<PCHContainerOperations> PCHs,
+                     bool SnippetCompletions, clangd::Logger &Logger) {
+  std::vector<CompletionItem> Results;
+  CodeCompleteOptions Options;
+  std::unique_ptr<CodeCompleteConsumer> Consumer;
+  Options.IncludeGlobals = true;
+  Options.IncludeMacros = true;
+  Options.IncludeBriefComments = true;
+  if (SnippetCompletions) {
+    Options.IncludeCodePatterns = true;
+    Consumer =
+        llvm::make_unique<SnippetCompletionItemsCollector>(Options, Results);
+  } else {
+    Options.IncludeCodePatterns = false;
+    Consumer =
+        llvm::make_unique<PlainTextCompletionItemsCollector>(Options, Results);
+  }
+  invokeCodeComplete(std::move(Consumer), Options, 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::FileSystem> VFS,
+                      std::shared_ptr<PCHContainerOperations> PCHs,
+                      clangd::Logger &Logger) {
+  SignatureHelp Result;
+  CodeCompleteOptions Options;
+  Options.IncludeGlobals = false;
+  Options.IncludeMacros = false;
+  Options.IncludeCodePatterns = false;
+  Options.IncludeBriefComments = true;
+  invokeCodeComplete(llvm::make_unique<SignatureHelpCollector>(Options, Result),
+                     Options, FileName, Command, Preamble, Contents, Pos,
+                     std::move(VFS), std::move(PCHs), Logger);
+  return Result;
 }
 
 void clangd::dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) {

Modified: clang-tools-extra/trunk/clangd/ClangdUnit.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.h?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdUnit.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdUnit.h Fri Oct  6 04:54:17 2017
@@ -259,6 +259,14 @@ codeComplete(PathRef FileName, tooling::
              std::shared_ptr<PCHContainerOperations> 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::FileSystem> VFS,
+                            std::shared_ptr<PCHContainerOperations> PCHs,
+                            clangd::Logger &Logger);
+
 /// Get definition of symbol at a specified \p Pos.
 std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
                                       clangd::Logger &Logger);

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Fri Oct  6 04:54:17 2017
@@ -909,3 +909,58 @@ std::string CompletionItem::unparse(cons
   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;
+}

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Fri Oct  6 04:54:17 2017
@@ -480,6 +480,48 @@ struct CompletionItem {
   static std::string unparse(const CompletionItem &P);
 };
 
+/// A single parameter of a particular signature.
+struct ParameterInformation {
+
+  /// The label of this parameter. Mandatory.
+  std::string label;
+
+  /// The documentation of this parameter. Optional.
+  std::string documentation;
+
+  static std::string unparse(const ParameterInformation &);
+};
+
+/// Represents the signature of something callable.
+struct SignatureInformation {
+
+  /// The label of this signature. Mandatory.
+  std::string label;
+
+  /// The documentation of this signature. Optional.
+  std::string documentation;
+
+  /// The parameters of this signature.
+  std::vector<ParameterInformation> parameters;
+
+  static std::string unparse(const SignatureInformation &);
+};
+
+/// Represents the signature of a callable.
+struct SignatureHelp {
+
+  /// The resulting signatures.
+  std::vector<SignatureInformation> signatures;
+
+  /// The active signature.
+  int activeSignature = 0;
+
+  /// The active parameter of the active signature.
+  int activeParameter = 0;
+
+  static std::string unparse(const SignatureHelp &);
+};
+
 } // namespace clangd
 } // namespace clang
 

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Fri Oct  6 04:54:17 2017
@@ -192,6 +192,23 @@ private:
   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) {}
@@ -279,6 +296,9 @@ void clangd::registerCallbackHandlers(JS
       "textDocument/completion",
       llvm::make_unique<CompletionHandler>(Out, Callbacks));
   Dispatcher.registerHandler(
+      "textDocument/signatureHelp",
+      llvm::make_unique<SignatureHelpHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
       "textDocument/definition",
       llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
   Dispatcher.registerHandler(

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.h?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.h (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h Fri Oct  6 04:54:17 2017
@@ -47,6 +47,8 @@ public:
                             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,

Modified: clang-tools-extra/trunk/test/clangd/formatting.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/formatting.test?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/formatting.test (original)
+++ clang-tools-extra/trunk/test/clangd/formatting.test Fri Oct  6 04:54:17 2017
@@ -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,

Modified: clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test Fri Oct  6 04:54:17 2017
@@ -5,7 +5,7 @@
 Content-Length: 142
 
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":"","rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
-# CHECK: Content-Length: 466
+# CHECK: Content-Length: 535
 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
 # CHECK:   "textDocumentSync": 1,
 # CHECK:   "documentFormattingProvider": true,
@@ -13,6 +13,7 @@ Content-Length: 142
 # CHECK:   "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
 # CHECK:   "codeActionProvider": true,
 # CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
+# CHECK:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
 # CHECK:   "definitionProvider": true
 # CHECK: }}}
 #

Modified: clang-tools-extra/trunk/test/clangd/initialize-params.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params.test?rev=315055&r1=315054&r2=315055&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params.test Fri Oct  6 04:54:17 2017
@@ -5,7 +5,7 @@
 Content-Length: 143
 
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
-# CHECK: Content-Length: 466
+# CHECK: Content-Length: 535
 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
 # CHECK:   "textDocumentSync": 1,
 # CHECK:   "documentFormattingProvider": true,
@@ -13,6 +13,7 @@ Content-Length: 143
 # CHECK:   "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
 # CHECK:   "codeActionProvider": true,
 # CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
+# CHECK:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
 # CHECK:   "definitionProvider": true
 # CHECK: }}}
 #

Added: clang-tools-extra/trunk/test/clangd/signature-help.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/signature-help.test?rev=315055&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clangd/signature-help.test (added)
+++ clang-tools-extra/trunk/test/clangd/signature-help.test Fri Oct  6 04:54:17 2017
@@ -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"}




More information about the cfe-commits mailing list