Nebiroth updated this revision to Diff 108997.
Nebiroth added a comment.

Implemention of Code Hover as described in LSP definition.
Removed unintentional changes to formatting.
Removed file id field in Location struct.


https://reviews.llvm.org/D35894

Files:
  clangd/ClangdLSPServer.cpp
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/ClangdUnit.h
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  clangd/clients/clangd-vscode/src/extension.ts
  test/clangd/hover.test

Index: test/clangd/hover.test
===================================================================
--- /dev/null
+++ test/clangd/hover.test
@@ -0,0 +1,26 @@
+# RUN: clangd -run-synchronously < %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: 172
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}
+
+Content-Length: 143
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":0,"character":5}}}
+# Go to local variable
+# CHECK: {"jsonrpc":"2.0","id":1,"result":{"contents": {"language": "C++", "value": "int main() {\nint a;\na;\n}"}, "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}}
+
+Content-Length: 143
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":5}}}
+# Go to local variable
+# CHECK: {"jsonrpc":"2.0","id":1,"result":{"contents": {"language": "C++", "value": "int a"}, "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}}
+
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Index: clangd/clients/clangd-vscode/src/extension.ts
===================================================================
--- clangd/clients/clangd-vscode/src/extension.ts
+++ clangd/clients/clangd-vscode/src/extension.ts
@@ -1,5 +1,6 @@
 import * as vscode from 'vscode';
 import * as vscodelc from 'vscode-languageclient';
+import * as vscodejsonrpc from 'vscode-jsonrpc';
 
 /**
  * Method to get workspace configuration option
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -48,6 +48,8 @@
                             JSONOutput &Out) = 0;
   virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
                             JSONOutput &Out) = 0;
+  virtual void onCodeHover(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) = 0;
 };
 
 void regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -204,6 +204,24 @@
   ProtocolCallbacks &Callbacks;
 };
 
+struct CodeHoverHandler : Handler {
+  CodeHoverHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto TDPP = TextDocumentPositionParams::parse(Params);
+    if (!TDPP) {
+      Output.log("Failed to decode TextDocumentPositionParams!\n");
+      return;
+    }
+
+    Callbacks.onCodeHover(*TDPP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
 } // namespace
 
 void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher,
@@ -239,4 +257,7 @@
       llvm::make_unique<CompletionHandler>(Out, Callbacks));
   Dispatcher.registerHandler("textDocument/definition",
       llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/hover",
+      llvm::make_unique<CodeHoverHandler>(Out, Callbacks));
 }
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -23,6 +23,7 @@
 
 #include "llvm/ADT/Optional.h"
 #include "llvm/Support/YAMLParser.h"
+#include "clang/Basic/SourceLocation.h"
 #include <string>
 #include <vector>
 
@@ -304,7 +305,59 @@
   parse(llvm::yaml::MappingNode *Params);
 };
 
-/// The kind of a completion entry.
+struct MarkedString {
+  /**
+ * MarkedString can be used to render human readable text. It is either a
+ * markdown string
+ * or a code-block that provides a language and a code snippet. The language
+ * identifier
+ * is sematically equal to the optional language identifier in fenced code
+ * blocks in GitHub
+ * issues. See
+ * https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
+ *
+ * The pair of a language and a value is an equivalent to markdown:
+ * ```${language}
+ * ${value}
+ * ```
+ *
+ * Note that markdown strings will be sanitized - that means html will be
+ * escaped.
+ */
+
+  MarkedString(std::string markdown)
+      : markdownString(markdown), codeBlockLanguage(""), codeBlockValue("") {}
+
+  MarkedString(std::string blockLanguage, std::string blockValue)
+      : markdownString(""), codeBlockLanguage(blockLanguage),
+        codeBlockValue(blockValue) {}
+
+  std::string markdownString;
+  std::string codeBlockLanguage;
+  std::string codeBlockValue;
+
+  static std::string unparse(const MarkedString &MS);
+};
+
+struct Hover {
+
+  Hover(MarkedString ms, Range r) : contents(ms), range(r) {}
+
+  /**
+   * The hover's content
+   */
+  MarkedString contents;
+
+  /**
+   * An optional range is a range inside a text document
+   * that is used to visualize a hover, e.g. by changing the background color.
+   */
+  Range range;
+
+  static std::string unparse(const Hover &H);
+};
+
+/// The kind of a completion entry.std::string*
 enum class CompletionItemKind {
   Missing = 0,
   Text = 1,
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -746,3 +746,26 @@
   Result.back() = '}';
   return Result;
 }
+
+std::string Hover::unparse(const Hover &H) {
+  std::string Result;
+  llvm::raw_string_ostream(Result) << llvm::format(
+      R"({"contents": %s, "range": %s})", MarkedString::unparse(H.contents).c_str(),
+      Range::unparse(H.range).c_str());
+  return Result;
+}
+
+std::string MarkedString::unparse(const MarkedString &MS) {
+  std::string Result;
+  if (MS.markdownString != "")
+  {
+    llvm::raw_string_ostream(Result) << llvm::format(R"({"markdown": "%s"})", MS.markdownString.c_str());
+  }
+  else
+  {
+    
+    llvm::raw_string_ostream(Result) << llvm::format(R"({"language": "%s", "value": "%s"})", (llvm::yaml::escape(MS.codeBlockLanguage)).c_str(), (llvm::yaml::escape(MS.codeBlockValue)).c_str());
+  }
+  
+  return Result;
+}
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -69,6 +69,24 @@
   /// unserialized Decls, so use with care.
   void dumpAST(llvm::raw_ostream &OS) const;
 
+  Hover getHover(Location L){
+
+    MarkedString MS = MarkedString("", "");
+    Range R;
+    const FileEntry *FE = Unit->getFileManager().getFile(L.uri.file);
+    FileID id = Unit->getSourceManager().translateFile(FE);
+    StringRef ref = Unit->getSourceManager().getBufferData(id);
+    start = Unit->getSourceManager().getFileOffset(Unit->getSourceManager().translateFileLineCol(FE, L.range.start.line + 1, L.range.start.character + 1));
+    end = Unit->getSourceManager().getFileOffset(Unit->getSourceManager().translateFileLineCol(FE, L.range.end.line + 1, L.range.end.character + 1)); 
+    ref = ref.slice(start, end);
+    MS = MarkedString("C++", ref);
+    R = L.range;
+    Hover H(MS, R);
+    return H;
+  }
+  unsigned start;
+  unsigned end;
+
 private:
   Path FileName;
   std::unique_ptr<ASTUnit> Unit;
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -182,6 +182,8 @@
   /// Get definition of symbol at a specified \p Line and \p Column in \p File.
   Tagged<std::vector<Location>> findDefinitions(PathRef File, Position Pos);
 
+  Tagged<Hover> findHover(PathRef File, Position Pos);
+
   /// Run formatting for \p Rng inside \p File.
   std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
   /// Run formatting for the whole \p File.
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -8,6 +8,7 @@
 //===-------------------------------------------------------------------===//
 
 #include "ClangdServer.h"
+#include "Protocol.h"
 #include "clang/Format/Format.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
@@ -286,3 +287,28 @@
       });
   return make_tagged(std::move(Result), TaggedFS.Tag);
 }
+
+Tagged<Hover> ClangdServer::findHover(PathRef File, Position Pos) {
+  auto FileContents = DraftMgr.getDraft(File);
+  assert(FileContents.Draft &&
+         "findHover is called for non-added document");
+
+  
+  MarkedString MS = MarkedString("", "");
+  Range R;
+  Hover FinalHover(MS, R);
+  auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+  Units.runOnUnit(File, *FileContents.Draft, ResourceDir, CDB, PCHs,
+                  TaggedFS.Value, [&](ClangdUnit &Unit) {
+                    std::vector<Location> Result = Unit.findDefinitions(Pos);
+                    if (!Result.empty()) {
+                      Location FoundLocation = Result[0];
+                      FinalHover = Unit.getHover(FoundLocation);
+                    }
+                  });
+
+
+  return make_tagged(std::move(FinalHover), TaggedFS.Tag);
+}
+
+
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -71,6 +71,8 @@
                     JSONOutput &Out) override;
   void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
                             JSONOutput &Out) override;
+  void onCodeHover(TextDocumentPositionParams Params, StringRef ID,
+                   JSONOutput &Out) override;
 
 private:
   ClangdLSPServer &LangServer;
@@ -87,7 +89,8 @@
           "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
           "codeActionProvider": true,
           "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
-          "definitionProvider": true
+          "definitionProvider": true,
+          "hoverProvider": true
         }}})");
 }
 
@@ -216,6 +219,18 @@
       R"(,"result":[)" + Locations + R"(]})");
 }
 
+void ClangdLSPServer::LSPProtocolCallbacks::onCodeHover(
+    TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+  Hover H = LangServer.Server.findHover(
+      Params.textDocument.uri.file,
+      Position{Params.position.line, Params.position.character}) .Value;
+
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID.str() +
+      R"(,"result":)" + Hover::unparse(H) + R"(})");
+}
+
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously)
     : Out(Out), DiagConsumer(*this),
       Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to