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

Rebased on master.


https://reviews.llvm.org/D38425

Files:
  clangd/ClangdLSPServer.cpp
  clangd/ClangdLSPServer.h
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/ClangdUnit.cpp
  clangd/ClangdUnit.h
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  test/clangd/documenthighlight.test
  test/clangd/initialize-params-invalid.test
  test/clangd/initialize-params.test

Index: test/clangd/initialize-params.test
===================================================================
--- test/clangd/initialize-params.test
+++ test/clangd/initialize-params.test
@@ -5,18 +5,21 @@
 Content-Length: 143
 
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
-# CHECK: Content-Length: 535
+# CHECK: Content-Length: 580
 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
 # CHECK:   "textDocumentSync": 1,
 # CHECK:   "documentFormattingProvider": true,
 # CHECK:   "documentRangeFormattingProvider": true,
 # CHECK:   "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
 # CHECK:   "codeActionProvider": true,
 # CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
 # CHECK:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
-# CHECK:   "definitionProvider": true
+# CHECK:   "definitionProvider": true,
+# CHECK:   "documentHighlightProvider": true
 # CHECK: }}}
 #
 Content-Length: 44
 
 {"jsonrpc":"2.0","id":3,"method":"shutdown"}
+
+
Index: test/clangd/initialize-params-invalid.test
===================================================================
--- test/clangd/initialize-params-invalid.test
+++ test/clangd/initialize-params-invalid.test
@@ -5,16 +5,17 @@
 Content-Length: 142
 
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":"","rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}
-# CHECK: Content-Length: 535
+# CHECK: Content-Length: 580
 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
 # CHECK:   "textDocumentSync": 1,
 # CHECK:   "documentFormattingProvider": true,
 # CHECK:   "documentRangeFormattingProvider": true,
 # CHECK:   "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
 # CHECK:   "codeActionProvider": true,
 # CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
 # CHECK:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
-# CHECK:   "definitionProvider": true
+# CHECK:   "definitionProvider": true,
+# CHECK:   "documentHighlightProvider": true
 # CHECK: }}}
 #
 Content-Length: 44
Index: test/clangd/documenthighlight.test
===================================================================
--- /dev/null
+++ test/clangd/documenthighlight.test
@@ -0,0 +1,42 @@
+# 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"}}
+# CHECK: Content-Length: 580
+# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
+# CHECK:   "textDocumentSync": 1,
+# CHECK:   "documentFormattingProvider": true,
+# CHECK:   "documentRangeFormattingProvider": true,
+# CHECK:   "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
+# CHECK:   "codeActionProvider": true,
+# CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
+# CHECK:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
+# CHECK:   "definitionProvider": true,
+# CHECK:   "documentHighlightProvider": true
+# CHECK: }}}
+#
+
+Content-Length: 455
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"#define MACRO 1\nnamespace ns1 {\nstruct MyClass {\nint xasd;\nvoid anotherOperation() {\n}\nstatic int foo(MyClass*) {\nreturn 0;\n}\n\n};\nstruct Foo {\nint xasd;\n};\n}\nint main() {\nint bonjour;\nbonjour = 2;\nns1::Foo bar = { xasd : 1};\nbar.xasd = 3;\nns1::MyClass* Params;\nParams->anotherOperation();}\n"}}}
+
+Content-Length: 156
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":17,"character":2}}}
+# Go to local variable
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"range": {"start": {"line": 16, "character": 4}, "end": {"line": 16, "character": 12}}, "kind": 1},{"range": {"start": {"line": 17, "character": 0}, "end": {"line": 17, "character": 7}}, "kind": 1}]}
+
+Content-Length: 157
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":18,"character":17}}}
+# Go to local variable
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"range": {"start": {"line": 12, "character": 4}, "end": {"line": 12, "character": 9}}, "kind": 1},{"range": {"start": {"line": 18, "character": 17}, "end": {"line": 18, "character": 21}}, "kind": 1},{"range": {"start": {"line": 19, "character": 4}, "end": {"line": 19, "character": 8}}, "kind": 1}]}
+
+Content-Length: 157
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":21,"character":10}}}
+# Go to local variable
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"range": {"start": {"line": 4, "character": 5}, "end": {"line": 4, "character": 22}}, "kind": 1},{"range": {"start": {"line": 21, "character": 8}, "end": {"line": 21, "character": 25}}, "kind": 1}]}
+
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -53,7 +53,9 @@
                                 JSONOutput &Out) = 0;
   virtual void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
                                     JSONOutput &Out) = 0;
-  virtual void onFileEvent(const DidChangeWatchedFilesParams &Params) = 0;
+  virtual void onFileEvent(const DidChangeWatchedFilesParams &Params)  = 0;
+  virtual void onDocumentHighlight(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) = 0;
 };
 
 void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -262,6 +262,24 @@
   ProtocolCallbacks &Callbacks;
 };
 
+struct DocumentHighlightHandler : Handler {
+  DocumentHighlightHandler(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.onDocumentHighlight(*TDPP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
 } // namespace
 
 void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
@@ -307,4 +325,8 @@
   Dispatcher.registerHandler(
       "workspace/didChangeWatchedFiles",
       llvm::make_unique<WorkspaceDidChangeWatchedFilesHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/documentHighlight",
+      llvm::make_unique<DocumentHighlightHandler>(Out, Callbacks));    
+
 }
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -522,6 +522,41 @@
   static std::string unparse(const SignatureHelp &);
 };
 
+enum class DocumentHighlightKind {
+  Text = 1,
+  Read = 2,
+  Write = 3
+};
+
+/**
+ * A document highlight is a range inside a text document which deserves
+ * special attention. Usually a document highlight is visualized by changing
+ * the background color of its range.
+ *
+ */
+struct DocumentHighlight{
+  /**
+	 * The range this highlight applies to.
+	 */
+  Range range;
+
+  /**
+	 * The highlight kind, default is DocumentHighlightKind.Text.
+	 */
+   DocumentHighlightKind kind = DocumentHighlightKind::Text;
+
+   friend bool operator<(const DocumentHighlight &LHS, const DocumentHighlight &RHS) {
+    return std::tie(LHS.range) < std::tie(RHS.range);
+   }
+
+   friend bool operator==(const DocumentHighlight &LHS, const DocumentHighlight &RHS) {
+    return LHS.kind == RHS.kind && LHS.range == RHS.range;
+  }
+
+  static std::string unparse(const DocumentHighlight &DH);
+};
+
+
 } // namespace clangd
 } // namespace clang
 
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -968,3 +968,10 @@
   Result.push_back('}');
   return Result;
 }
+
+std::string DocumentHighlight::unparse(const DocumentHighlight &DH) {
+    std::string Result;
+    llvm::raw_string_ostream(Result) << llvm::format(
+        R"({"range": %s, "kind": %d})", Range::unparse(DH.range).c_str(), DH.kind);
+    return Result;
+  }
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -271,6 +271,9 @@
 std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
                                       clangd::Logger &Logger);
 
+std::vector<DocumentHighlight>
+findDocumentHighlights(ParsedAST &AST, Position Pos, clangd::Logger &Logger);
+
 /// For testing/debugging purposes. Note that this method deserializes all
 /// unserialized Decls, so use with care.
 void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS);
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -896,6 +896,7 @@
                       ArrayRef<index::SymbolRelation> Relations, FileID FID,
                       unsigned Offset,
                       index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
+
     if (isSearchedLocation(FID, Offset)) {
       addDeclarationLocation(D->getSourceRange());
     }
@@ -959,6 +960,167 @@
   }
 };
 
+/// Finds declarations locations that a given source location refers to.
+class TargetDeclarationFinder : public index::IndexDataConsumer {
+  std::vector<Location> DeclarationLocations;
+  const SourceLocation &SearchedLocation;
+  const ASTContext &AST;
+  Preprocessor &PP;
+
+public:
+  TargetDeclarationFinder(raw_ostream &OS,
+                             const SourceLocation &SearchedLocation,
+                             ASTContext &AST, Preprocessor &PP)
+      : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {}
+
+  unsigned TargetDeclFileOffset;
+  FileID TargetDeclFileID;
+
+  bool
+  handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
+                      ArrayRef<index::SymbolRelation> Relations, FileID FID,
+                      unsigned Offset,
+                      index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
+
+    if (isSearchedLocation(FID, Offset)) {
+      TargetDeclFileOffset =
+          AST.getSourceManager().getFileOffset(D->getSourceRange().getBegin());
+      TargetDeclFileID = FID;
+    }
+    return true;
+  }
+
+private:
+  bool isSearchedLocation(FileID FID, unsigned Offset) const {
+    const SourceManager &SourceMgr = AST.getSourceManager();
+    return SourceMgr.getFileOffset(SearchedLocation) == Offset &&
+           SourceMgr.getFileID(SearchedLocation) == FID;
+  }
+};
+
+/// Finds document highlights that a given FileID and file offset refers to.
+class DocumentHighlightsFinder : public index::IndexDataConsumer {
+  std::vector<DocumentHighlight> DeclarationLocations;
+  const SourceLocation &SearchedLocation;
+  const ASTContext &AST;
+  Preprocessor &PP;
+
+public:
+  unsigned TargetDeclFileOffset;
+  FileID TargetDeclFileID;
+
+  DocumentHighlightsFinder(raw_ostream &OS,
+                           const SourceLocation &SearchedLocation,
+                           ASTContext &AST, Preprocessor &PP,
+                           unsigned TargetDeclFileOffset,
+                           FileID TargetDeclFileID)
+      : SearchedLocation(SearchedLocation), AST(AST), PP(PP),
+        TargetDeclFileOffset(TargetDeclFileOffset),
+        TargetDeclFileID(TargetDeclFileID) {}
+
+  std::vector<DocumentHighlight> takeHighlights() {
+    // Don't keep the same location multiple times.
+    // This can happen when nodes in the AST are visited twice.
+    std::sort(DeclarationLocations.begin(), DeclarationLocations.end());
+    auto last =
+        std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
+    DeclarationLocations.erase(last, DeclarationLocations.end());
+    return std::move(DeclarationLocations);
+  }
+
+  bool
+  handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
+                      ArrayRef<index::SymbolRelation> Relations, FileID FID,
+                      unsigned Offset,
+                      index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
+    const SourceManager &SourceMgr = AST.getSourceManager();
+
+    if (isValidHighlight(
+            SourceMgr.getFileID(D->getSourceRange().getBegin()),
+            SourceMgr.getFileOffset(D->getSourceRange().getBegin()))) {
+      SourceLocation Begin, End;
+      const LangOptions &LangOpts = AST.getLangOpts();
+      SourceLocation StartOfFileLoc = SourceMgr.getLocForStartOfFile(FID);
+      SourceLocation HightlightStartLoc =
+          StartOfFileLoc.getLocWithOffset(Offset);
+      End = Lexer::getLocForEndOfToken(HightlightStartLoc, 0, SourceMgr,
+                                       LangOpts);
+      SourceRange SR(HightlightStartLoc, End);
+      DocumentHighlightKind Kind;
+      switch (Roles) {
+      case (unsigned)index::SymbolRole::Read:
+        Kind = DocumentHighlightKind::Read;
+        break;
+      case (unsigned)index::SymbolRole::Write:
+        Kind = DocumentHighlightKind::Write;
+        break;
+      default:
+        Kind = DocumentHighlightKind::Text;
+        break;
+      }
+
+      addHighlightLocation(SR, Kind);
+    }
+
+    return true;
+  }
+
+private:
+  bool isValidHighlight(FileID FID, unsigned Offset) const {
+    return TargetDeclFileOffset == Offset && TargetDeclFileID == FID;
+  }
+
+  void addHighlightLocation(const SourceRange &ValSourceRange,
+                            DocumentHighlightKind Kind) {
+    const SourceManager &SourceMgr = AST.getSourceManager();
+    const LangOptions &LangOpts = AST.getLangOpts();
+    SourceLocation LocStart = ValSourceRange.getBegin();
+    SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
+                                                       0, SourceMgr, LangOpts);
+    Position Begin;
+    Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+    Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+    Position End;
+    End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
+    End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+    Range R = {Begin, End};
+    DocumentHighlight DH;
+    DH.range = R;
+    DH.kind = Kind;
+    DeclarationLocations.push_back(DH);
+  }
+
+  void finish() override {
+    // Also handle possible macro at the searched location.
+    Token Result;
+    if (!Lexer::getRawToken(SearchedLocation, Result, AST.getSourceManager(),
+                            AST.getLangOpts(), false)) {
+      if (Result.is(tok::raw_identifier)) {
+        PP.LookUpIdentifierInfo(Result);
+      }
+      IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo();
+      if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) {
+        std::pair<FileID, unsigned int> DecLoc =
+            AST.getSourceManager().getDecomposedExpansionLoc(SearchedLocation);
+        // Get the definition just before the searched location so that a macro
+        // referenced in a '#undef MACRO' can still be found.
+        SourceLocation BeforeSearchedLocation = getMacroArgExpandedLocation(
+            AST.getSourceManager(),
+            AST.getSourceManager().getFileEntryForID(DecLoc.first),
+            DecLoc.second - 1);
+        MacroDefinition MacroDef =
+            PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation);
+        MacroInfo *MacroInf = MacroDef.getMacroInfo();
+        if (MacroInf) {
+          addHighlightLocation(SourceRange(MacroInf->getDefinitionLoc(),
+                                           MacroInf->getDefinitionEndLoc()),
+                               DocumentHighlightKind::Text);
+        }
+      }
+    }
+  }
+};
+
 SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos,
                                         const FileEntry *FE) {
   // The language server protocol uses zero-based line and column numbers.
@@ -1022,6 +1184,42 @@
   return DeclLocationsFinder->takeLocations();
 }
 
+std::vector<DocumentHighlight>
+clangd::findDocumentHighlights(ParsedAST &AST, Position Pos,
+                               clangd::Logger &Logger) {
+  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
+  const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+  if (!FE)
+    return {};
+
+  SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
+
+  auto DeclLocationsFinder = std::make_shared<TargetDeclarationFinder>(
+      llvm::errs(), SourceLocationBeg, AST.getASTContext(),
+      AST.getPreprocessor());
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+
+  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+                     DeclLocationsFinder, IndexOpts);
+
+  auto DocHighlightsFinder = std::make_shared<DocumentHighlightsFinder>(
+      llvm::errs(), SourceLocationBeg, AST.getASTContext(),
+      AST.getPreprocessor(), DeclLocationsFinder->TargetDeclFileOffset,
+      DeclLocationsFinder->TargetDeclFileID);
+
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+
+  indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(),
+                     DocHighlightsFinder, IndexOpts);
+
+  return DocHighlightsFinder->takeHighlights();
+}
+
 void ParsedAST::ensurePreambleDeclsDeserialized() {
   if (PendingTopLevelDecls.empty())
     return;
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -271,6 +271,10 @@
   /// given a header file and vice versa.
   llvm::Optional<Path> switchSourceHeader(PathRef Path);
 
+  /// Get document highlights for a symbol hovered on.
+  Tagged<std::vector<DocumentHighlight>> findDocumentHighlights(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
@@ -408,6 +408,26 @@
   return llvm::None;
 }
 
+Tagged<std::vector<DocumentHighlight>>
+ClangdServer::findDocumentHighlights(PathRef File, Position Pos) {
+  auto FileContents = DraftMgr.getDraft(File);
+  assert(FileContents.Draft &&
+         "findDocumentHighlights is called for non-added document");
+
+  auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+
+  std::shared_ptr<CppFile> Resources = Units.getFile(File);
+  assert(Resources && "Calling findDocumentHighlights on non-added file");
+
+  std::vector<DocumentHighlight> Result;
+  Resources->getAST().get()->runUnderLock([Pos, &Result, this](ParsedAST *AST) {
+    if (!AST)
+      return;
+    Result = clangd::findDocumentHighlights(*AST, Pos, Logger);
+  });
+  return make_tagged(std::move(Result), TaggedFS.Tag);
+}
+
 std::future<void> ClangdServer::scheduleReparseAndDiags(
     PathRef File, VersionedDraft Contents, std::shared_ptr<CppFile> Resources,
     Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) {
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -70,10 +70,12 @@
   void onSignatureHelp(TextDocumentPositionParams Params, StringRef ID,
                        JSONOutput &Out) override;
   void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
-                        JSONOutput &Out) override;
+                                JSONOutput &Out) override;
   void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
-                            JSONOutput &Out) override;
+                                    JSONOutput &Out) override;
   void onFileEvent(const DidChangeWatchedFilesParams &Params) override;
+  void onDocumentHighlight(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) override;
 
   std::vector<clang::tooling::Replacement>
   getFixIts(StringRef File, const clangd::Diagnostic &D);
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -38,7 +38,8 @@
 } // namespace
 
 void ClangdLSPServer::onInitialize(StringRef ID, InitializeParams IP,
-                                   JSONOutput &Out) {
+    JSONOutput &Out) {
+
   Out.writeMessage(
       R"({"jsonrpc":"2.0","id":)" + ID +
       R"(,"result":{"capabilities":{
@@ -49,14 +50,18 @@
           "codeActionProvider": true,
           "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
           "signatureHelpProvider": {"triggerCharacters": ["(",","]},
-          "definitionProvider": true
+          "definitionProvider": true,
+          "documentHighlightProvider": true
         }}})");
   if (IP.rootUri && !IP.rootUri->file.empty())
     Server.setRootPath(IP.rootUri->file);
   else if (IP.rootPath && !IP.rootPath->empty())
     Server.setRootPath(*IP.rootPath);
+
 }
 
+
+
 void ClangdLSPServer::onShutdown(JSONOutput &Out) { IsDone = true; }
 
 void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams Params,
@@ -200,6 +205,7 @@
       R"(,"result":[)" + Locations + R"(]})");
 }
 
+
 void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier Params,
                                            StringRef ID, JSONOutput &Out) {
   llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
@@ -214,6 +220,27 @@
       R"(,"result":)" + ResultUri + R"(})");
 }
 
+void ClangdLSPServer::onDocumentHighlight(
+    TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+  auto Items = Server.findDocumentHighlights(Params.textDocument.uri.file,
+                                           Position{Params.position.line,
+                                                    Params.position.character})
+                   .Value;
+
+  std::string Highlights;
+
+  for (const auto &Item : Items) {
+    Highlights += DocumentHighlight::unparse(Item);
+    Highlights += ",";
+  }
+  if (!Highlights.empty())
+    Highlights.pop_back();
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID.str() +
+      R"(,"result":[)" + Highlights + R"(]})");
+}
+
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
                                  bool SnippetCompletions,
                                  llvm::Optional<StringRef> ResourceDir,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to