malaperle updated this revision to Diff 140272.
malaperle added a comment.

Use the DraftStore for offset -> line/col when there is a draft available.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D44882

Files:
  clangd/CMakeLists.txt
  clangd/ClangdLSPServer.cpp
  clangd/ClangdLSPServer.h
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/FindSymbols.cpp
  clangd/FindSymbols.h
  clangd/Protocol.cpp
  clangd/Protocol.h
  clangd/ProtocolHandlers.cpp
  clangd/ProtocolHandlers.h
  clangd/SourceCode.cpp
  clangd/SourceCode.h
  clangd/XRefs.cpp
  clangd/index/SymbolCollector.cpp
  clangd/tool/ClangdMain.cpp
  test/clangd/initialize-params-invalid.test
  test/clangd/initialize-params.test
  unittests/clangd/CMakeLists.txt
  unittests/clangd/FindSymbolsTests.cpp
  unittests/clangd/SyncAPI.cpp
  unittests/clangd/SyncAPI.h

Index: unittests/clangd/SyncAPI.h
===================================================================
--- unittests/clangd/SyncAPI.h
+++ unittests/clangd/SyncAPI.h
@@ -41,6 +41,10 @@
 
 std::string runDumpAST(ClangdServer &Server, PathRef File);
 
+llvm::Expected<std::vector<SymbolInformation>>
+runWorkspaceSymbols(ClangdServer &Server, StringRef Query,
+                    const WorkspaceSymbolOptions &Opts, const DraftStore &DS);
+
 } // namespace clangd
 } // namespace clang
 
Index: unittests/clangd/SyncAPI.cpp
===================================================================
--- unittests/clangd/SyncAPI.cpp
+++ unittests/clangd/SyncAPI.cpp
@@ -110,5 +110,13 @@
   return std::move(*Result);
 }
 
+llvm::Expected<std::vector<SymbolInformation>>
+runWorkspaceSymbols(ClangdServer &Server, StringRef Query,
+                    const WorkspaceSymbolOptions &Opts, const DraftStore &DS) {
+  llvm::Optional<llvm::Expected<std::vector<SymbolInformation>>> Result;
+  Server.workspaceSymbols(Query, Opts, DS, capture(Result));
+  return std::move(*Result);
+}
+
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/FindSymbolsTests.cpp
===================================================================
--- /dev/null
+++ unittests/clangd/FindSymbolsTests.cpp
@@ -0,0 +1,383 @@
+//===-- FindSymbolsTests.cpp -------------------------*- C++ -*------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "Annotations.h"
+#include "ClangdServer.h"
+#include "DraftStore.h"
+#include "FindSymbols.h"
+#include "SyncAPI.h"
+#include "TestFS.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+
+void PrintTo(const SymbolInformation &I, std::ostream *O) {
+  llvm::raw_os_ostream OS(*O);
+  OS << I.containerName << I.name << " - " << toJSON(I);
+}
+
+namespace {
+
+using ::testing::AllOf;
+using ::testing::AnyOf;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAre;
+
+class IgnoreDiagnostics : public DiagnosticsConsumer {
+  void onDiagnosticsReady(PathRef File,
+                          std::vector<Diag> Diagnostics) override {}
+};
+
+// GMock helpers for matching SymbolInfos items.
+MATCHER_P(Named, Name, "") { return arg.name == Name; }
+MATCHER_P(InContainer, ContainerName, "") {
+  return arg.containerName == ContainerName;
+}
+MATCHER_P(WithKind, Kind, "") { return arg.kind == Kind; }
+
+class WorkspaceSymbolsTest : public ::testing::Test {
+protected:
+  std::unique_ptr<MockFSProvider> FSProvider;
+  std::unique_ptr<MockCompilationDatabase> CDB;
+  std::unique_ptr<IgnoreDiagnostics> DiagConsumer;
+  std::unique_ptr<ClangdServer> Server;
+  std::unique_ptr<WorkspaceSymbolOptions> Opts;
+  std::unique_ptr<DraftStore> DS;
+
+  virtual void SetUp() override {
+    FSProvider = llvm::make_unique<MockFSProvider>();
+    CDB = llvm::make_unique<MockCompilationDatabase>();
+    DiagConsumer = llvm::make_unique<IgnoreDiagnostics>();
+    auto ServerOpts = ClangdServer::optsForTest();
+    ServerOpts.BuildDynamicSymbolIndex = true;
+    Server = llvm::make_unique<ClangdServer>(*CDB, *FSProvider, *DiagConsumer,
+                                             ServerOpts);
+    Opts = llvm::make_unique<WorkspaceSymbolOptions>();
+    DS = llvm::make_unique<DraftStore>();
+  }
+
+  std::vector<SymbolInformation> getSymbols(StringRef Query) {
+    EXPECT_TRUE(Server->blockUntilIdleForTest()) << "Waiting for preamble";
+    auto SymbolInfos = runWorkspaceSymbols(*Server, Query, *Opts, *DS);
+    EXPECT_TRUE(bool(SymbolInfos)) << "workspaceSymbols returned an error";
+    return *SymbolInfos;
+  }
+
+  void addFile(StringRef FileName, StringRef Contents) {
+    auto Path = testPath(FileName);
+    FSProvider->Files[Path] = Contents;
+    Server->addDocument(Path, Contents);
+  }
+};
+
+} // namespace
+
+TEST_F(WorkspaceSymbolsTest, NoMacro) {
+  addFile("foo.cpp", R"cpp(
+      #define MACRO X
+      )cpp");
+
+  // Macros are not in the index.
+  EXPECT_THAT(getSymbols("macro"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, NoLocals) {
+  addFile("foo.cpp", R"cpp(
+      void test() {
+        struct LocalClass {};
+        int local_var;
+      })cpp");
+  EXPECT_THAT(getSymbols("local_var"), IsEmpty());
+  EXPECT_THAT(getSymbols("LocalClass"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, NoParams) {
+  addFile("foo.cpp", R"cpp(
+      void test(int FirstParam, int SecondParam) {
+      })cpp");
+  EXPECT_THAT(getSymbols("FirstParam"), IsEmpty());
+  EXPECT_THAT(getSymbols("SecondParam"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, ClassWithMembers) {
+  addFile("foo.h", R"cpp(
+      struct ClassWithMembers {
+        int method();
+      protected:
+        int field;
+      private:
+        int private_field;
+      };)cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("method"), IsEmpty());
+  EXPECT_THAT(getSymbols("ClassWithMembers::method"), IsEmpty());
+  EXPECT_THAT(getSymbols("ClassWithMembers::met"), IsEmpty());
+  EXPECT_THAT(getSymbols("field"), IsEmpty());
+  EXPECT_THAT(getSymbols("ClassWithMembers::"), IsEmpty());
+  EXPECT_THAT(getSymbols("ClassWithMembers:"), IsEmpty());
+  EXPECT_THAT(getSymbols("ClassWithMembers"),
+              ElementsAre(AllOf(Named("ClassWithMembers"), InContainer(""))));
+}
+
+TEST_F(WorkspaceSymbolsTest, ClassInNamespaceWithMembers) {
+  addFile("foo.h", R"cpp(
+      namespace ns1 {
+      struct ClassWithMembers {
+        int method();
+      };
+      })cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("ns1::ClassWithMembers::method"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, Globals) {
+  addFile("foo.h", R"cpp(
+      int global_var;
+
+      int global_func();
+
+      struct GlobalClass {};)cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(
+      getSymbols("global"),
+      UnorderedElementsAre(AllOf(Named("GlobalClass"), InContainer("")),
+                           AllOf(Named("global_func"), InContainer("")),
+                           AllOf(Named("global_var"), InContainer(""))));
+}
+
+TEST_F(WorkspaceSymbolsTest, Unnamed) {
+  addFile("foo.h", R"cpp(
+      struct {
+        int InUnnamed;
+      } UnnamedStruct;)cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("UnnamedStruct"), ElementsAre(Named("UnnamedStruct")));
+  EXPECT_THAT(getSymbols("InUnnamed"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, InMainFile) {
+  addFile("foo.cpp", R"cpp(
+      int test() {
+      }
+      )cpp");
+  EXPECT_THAT(getSymbols("test"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, AnonymousNamespace) {
+  addFile("foo.h", R"cpp(
+      namespace {
+      void test() {}
+      }
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("test"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, MultiFile) {
+  addFile("foo.h", R"cpp(
+      int foo() {
+      }
+      )cpp");
+  addFile("foo2.h", R"cpp(
+      int foo2() {
+      }
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      #include "foo2.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("foo"),
+              UnorderedElementsAre(AllOf(Named("foo"), InContainer("")),
+                                   AllOf(Named("foo2"), InContainer(""))));
+}
+
+TEST_F(WorkspaceSymbolsTest, GlobalNamespaceQueries) {
+  addFile("foo.h", R"cpp(
+      int foo() {
+      }
+      class Foo {
+        int a;
+      };
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("::"),
+              UnorderedElementsAre(AllOf(Named("Foo"), InContainer("")),
+                                   AllOf(Named("foo"), InContainer(""))));
+  EXPECT_THAT(getSymbols(":"), IsEmpty());
+  EXPECT_THAT(getSymbols(""), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, WithLimit) {
+  addFile("foo.h", R"cpp(
+      int foo;
+      int foo2;
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("foo"),
+              ElementsAre(AllOf(Named("foo"), InContainer("")),
+                          AllOf(Named("foo2"), InContainer(""))));
+
+  Opts->Limit = 1;
+  EXPECT_THAT(getSymbols("foo"),
+              ElementsAre(AnyOf((Named("foo"), InContainer("")),
+                                AllOf(Named("foo2"), InContainer("")))));
+}
+
+TEST_F(WorkspaceSymbolsTest, Enums) {
+  addFile("foo.h", R"cpp(
+      enum {
+        Red
+      };
+      enum Color1 {
+        Green
+      };
+      enum class Color2 {
+        Yellow
+      };
+      namespace ns {
+      enum {
+        Black
+      };
+      }
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("Red"),
+              ElementsAre(AllOf(Named("Red"), InContainer(""))));
+  EXPECT_THAT(getSymbols("Color1"),
+              ElementsAre(AllOf(Named("Color1"), InContainer(""))));
+  EXPECT_THAT(getSymbols("Green"),
+              ElementsAre(AllOf(Named("Green"), InContainer(""))));
+  EXPECT_THAT(getSymbols("Color2"),
+              ElementsAre(AllOf(Named("Color2"), InContainer(""))));
+  EXPECT_THAT(getSymbols("Yellow"), IsEmpty());
+  EXPECT_THAT(getSymbols("Color2::Yellow"), IsEmpty());
+  EXPECT_THAT(getSymbols("ns"),
+              ElementsAre(AllOf(Named("ns"), InContainer(""))));
+  EXPECT_THAT(getSymbols("Black"),
+              ElementsAre(AllOf(Named("Black"), InContainer("ns::"))));
+  EXPECT_THAT(getSymbols("ns::Black"),
+              ElementsAre(AllOf(Named("Black"), InContainer("ns::"))));
+}
+
+TEST_F(WorkspaceSymbolsTest, Union) {
+  addFile("foo.h", R"cpp(
+      union U {
+       int x;
+       bool y;
+      };
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("U"), ElementsAre(AllOf(Named("U"), InContainer(""))));
+  EXPECT_THAT(getSymbols("x"), IsEmpty());
+  EXPECT_THAT(getSymbols("y"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, InlineNamespace) {
+  addFile("foo.h", R"cpp(
+      namespace na {
+      inline namespace nb {
+      class Foo {};
+      }
+      }
+      namespace na {
+      // This is still inlined.
+      namespace nb {
+      class Bar {};
+      }
+      }
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+  EXPECT_THAT(getSymbols("na"),
+              ElementsAre(AllOf(Named("na"), InContainer(""))));
+  EXPECT_THAT(getSymbols("nb"),
+              ElementsAre(AllOf(Named("nb"), InContainer("na::"))));
+  EXPECT_THAT(getSymbols("Foo"),
+              ElementsAre(AllOf(Named("Foo"), InContainer("na::"))));
+  EXPECT_THAT(getSymbols("na::Foo"),
+              ElementsAre(AllOf(Named("Foo"), InContainer("na::"))));
+  // It would be good if it was possible to query with the inline namespace as
+  // well.
+  // EXPECT_THAT(getSymbols("na::nb::Foo"), ElementsAre(AllOf(Named("Foo"),
+  // InContainer("na::Foo"))));
+  EXPECT_THAT(getSymbols("Bar"),
+              ElementsAre(AllOf(Named("Bar"), InContainer("na::"))));
+  EXPECT_THAT(getSymbols("na::Bar"),
+              ElementsAre(AllOf(Named("Bar"), InContainer("na::"))));
+  EXPECT_THAT(getSymbols("nb::Bar"), IsEmpty());
+}
+
+TEST_F(WorkspaceSymbolsTest, SymbolKindCapabilities) {
+  addFile("foo.h", R"cpp(
+      struct Foo {};
+      class Foo2 {};
+      enum {
+        FOO_VAL
+      }
+      )cpp");
+  addFile("foo.cpp", R"cpp(
+      #include "foo.h"
+      )cpp");
+
+  EXPECT_THAT(
+      getSymbols("Foo"),
+      UnorderedElementsAre(
+          AllOf(Named("Foo"), InContainer(""), WithKind(SymbolKind::Class)),
+          AllOf(Named("Foo2"), InContainer(""), WithKind(SymbolKind::Class)),
+          AllOf(Named("FOO_VAL"), InContainer(""),
+                WithKind(SymbolKind::Enum))));
+
+  using SymbolKindType = std::underlying_type<SymbolKind>::type;
+  std::vector<SymbolKind> BaseKinds;
+  for (SymbolKindType I = 0; I < SymbolKindType(SymbolKind::Array); ++I)
+    BaseKinds.push_back(SymbolKind(I));
+
+  Opts->supportedSymbolKinds = BaseKinds;
+  EXPECT_THAT(
+      getSymbols("Foo"),
+      UnorderedElementsAre(
+          AllOf(Named("Foo"), InContainer(""), WithKind(SymbolKind::Class)),
+          AllOf(Named("Foo2"), InContainer(""), WithKind(SymbolKind::Class)),
+          AllOf(Named("FOO_VAL"), InContainer(""),
+                WithKind(SymbolKind::Enum))));
+
+  Opts->supportedSymbolKinds->push_back(SymbolKind::Struct);
+  Opts->supportedSymbolKinds->push_back(SymbolKind::EnumMember);
+  EXPECT_THAT(
+      getSymbols("Foo"),
+      UnorderedElementsAre(
+          AllOf(Named("Foo"), InContainer(""), WithKind(SymbolKind::Struct)),
+          AllOf(Named("Foo2"), InContainer(""), WithKind(SymbolKind::Class)),
+          AllOf(Named("FOO_VAL"), InContainer(""),
+                WithKind(SymbolKind::EnumMember))));
+}
+
+} // namespace clangd
+} // namespace clang
Index: unittests/clangd/CMakeLists.txt
===================================================================
--- unittests/clangd/CMakeLists.txt
+++ unittests/clangd/CMakeLists.txt
@@ -17,6 +17,7 @@
   ContextTests.cpp
   DraftStoreTests.cpp
   FileIndexTests.cpp
+  FindSymbolsTests.cpp
   FuzzyMatchTests.cpp
   HeadersTests.cpp
   IndexTests.cpp
Index: test/clangd/initialize-params.test
===================================================================
--- test/clangd/initialize-params.test
+++ test/clangd/initialize-params.test
@@ -36,7 +36,8 @@
 # CHECK-NEXT:          ","
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
-# CHECK-NEXT:      "textDocumentSync": 2
+# CHECK-NEXT:      "textDocumentSync": 2,
+# CHECK-NEXT:      "workspaceSymbolProvider": true
 # CHECK-NEXT:    }
 # CHECK-NEXT:  }
 ---
Index: test/clangd/initialize-params-invalid.test
===================================================================
--- test/clangd/initialize-params-invalid.test
+++ test/clangd/initialize-params-invalid.test
@@ -36,7 +36,8 @@
 # CHECK-NEXT:          ","
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
-# CHECK-NEXT:      "textDocumentSync": 2
+# CHECK-NEXT:      "textDocumentSync": 2,
+# CHECK-NEXT:      "workspaceSymbolProvider": true
 # CHECK-NEXT:    }
 # CHECK-NEXT:  }
 ---
Index: clangd/tool/ClangdMain.cpp
===================================================================
--- clangd/tool/ClangdMain.cpp
+++ clangd/tool/ClangdMain.cpp
@@ -96,9 +96,9 @@
         clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")),
     llvm::cl::init(PCHStorageFlag::Disk));
 
-static llvm::cl::opt<int> LimitCompletionResult(
-    "completion-limit",
-    llvm::cl::desc("Limit the number of completion results returned by clangd. "
+static llvm::cl::opt<int> LimitResults(
+    "limit-results",
+    llvm::cl::desc("Limit the number of results returned by clangd. "
                    "0 means no limit."),
     llvm::cl::init(100));
 
@@ -118,11 +118,11 @@
         "Mirror all LSP input to the specified file. Useful for debugging."),
     llvm::cl::init(""), llvm::cl::Hidden);
 
-static llvm::cl::opt<bool> EnableIndexBasedCompletion(
-    "enable-index-based-completion",
-    llvm::cl::desc(
-        "Enable index-based global code completion. "
-        "Clang uses an index built from symbols in opened files"),
+static llvm::cl::opt<bool> EnableIndex(
+    "index",
+    llvm::cl::desc("Enable index-based features such as global code completion "
+                   "and searching for symbols."
+                   "Clang uses an index built from symbols in opened files"),
     llvm::cl::init(true));
 
 static llvm::cl::opt<Path> YamlSymbolFile(
@@ -220,20 +220,23 @@
   }
   if (!ResourceDir.empty())
     Opts.ResourceDir = ResourceDir;
-  Opts.BuildDynamicSymbolIndex = EnableIndexBasedCompletion;
+  Opts.BuildDynamicSymbolIndex = EnableIndex;
   std::unique_ptr<SymbolIndex> StaticIdx;
-  if (EnableIndexBasedCompletion && !YamlSymbolFile.empty()) {
+  if (EnableIndex && !YamlSymbolFile.empty()) {
     StaticIdx = BuildStaticIndex(YamlSymbolFile);
     Opts.StaticIndex = StaticIdx.get();
   }
   Opts.AsyncThreadsCount = WorkerThreadsCount;
 
   clangd::CodeCompleteOptions CCOpts;
   CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;
-  CCOpts.Limit = LimitCompletionResult;
+  CCOpts.Limit = LimitResults;
 
+  clangd::WorkspaceSymbolOptions WorkspaceSymOpts;
+  WorkspaceSymOpts.Limit = LimitResults;
   // Initialize and run ClangdLSPServer.
-  ClangdLSPServer LSPServer(Out, CCOpts, CompileCommandsDirPath, Opts);
+  ClangdLSPServer LSPServer(Out, CCOpts, WorkspaceSymOpts,
+                            CompileCommandsDirPath, Opts);
   constexpr int NoShutdownRequestErrorCode = 1;
   llvm::set_thread_name("clangd.main");
   // Change stdin to binary to not lose \r\n on windows.
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -11,6 +11,7 @@
 #include "../AST.h"
 #include "../CodeCompletionStrings.h"
 #include "../Logger.h"
+#include "../SourceCode.h"
 #include "../URI.h"
 #include "CanonicalIncludes.h"
 #include "clang/AST/DeclCXX.h"
@@ -80,16 +81,6 @@
   return llvm::None;
 }
 
-// "a::b::c", return {"a::b::", "c"}. Scope is empty if there's no qualifier.
-std::pair<llvm::StringRef, llvm::StringRef>
-splitQualifiedName(llvm::StringRef QName) {
-  assert(!QName.startswith("::") && "Qualified names should not start with ::");
-  size_t Pos = QName.rfind("::");
-  if (Pos == llvm::StringRef::npos)
-    return {StringRef(), QName};
-  return {QName.substr(0, Pos + 2), QName.substr(Pos + 2)};
-}
-
 bool shouldFilterDecl(const NamedDecl *ND, ASTContext *ASTCtx,
                       const SymbolCollector::Options &Opts) {
   using namespace clang::ast_matchers;
@@ -299,6 +290,8 @@
 
   Symbol S;
   S.ID = std::move(ID);
+  assert(!StringRef(QName).startswith("::") &&
+         "Qualified names should not start with '::' here.");
   std::tie(S.Scope, S.Name) = splitQualifiedName(QName);
   S.SymInfo = index::getSymbolInfo(&ND);
   std::string FileURI;
Index: clangd/XRefs.cpp
===================================================================
--- clangd/XRefs.cpp
+++ clangd/XRefs.cpp
@@ -143,45 +143,14 @@
   }
 };
 
-llvm::Optional<Location>
-makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) {
-  const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
-  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
-  SourceLocation LocStart = ValSourceRange.getBegin();
-
-  const FileEntry *F =
-      SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart));
-  if (!F)
-    return llvm::None;
-  SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0,
-                                                     SourceMgr, LangOpts);
-  Position Begin = sourceLocToPosition(SourceMgr, LocStart);
-  Position End = sourceLocToPosition(SourceMgr, LocEnd);
-  Range R = {Begin, End};
-  Location L;
-
-  SmallString<64> FilePath = F->tryGetRealPathName();
-  if (FilePath.empty())
-    FilePath = F->getName();
-  if (!llvm::sys::path::is_absolute(FilePath)) {
-    if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) {
-      log("Could not turn relative path to absolute: " + FilePath);
-      return llvm::None;
-    }
-  }
-
-  L.uri = URIForFile(FilePath.str());
-  L.range = R;
-  return L;
-}
-
 } // namespace
 
 std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos) {
   const SourceManager &SourceMgr = AST.getASTContext().getSourceManager();
   const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
   if (!FE)
     return {};
+  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
 
   SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE);
 
@@ -213,14 +182,16 @@
 
   for (auto D : Decls) {
     auto Loc = findNameLoc(D);
-    auto L = makeLocation(AST, SourceRange(Loc, Loc));
+    auto EndLoc = Lexer::getLocForEndOfToken(Loc, 0, SourceMgr, LangOpts);
+    auto L = sourceRangeToLocation(SourceMgr, SourceRange(Loc, EndLoc));
     if (L)
       Result.push_back(*L);
   }
 
   for (auto Item : MacroInfos) {
     auto Loc = Item.Info->getDefinitionLoc();
-    auto L = makeLocation(AST, SourceRange(Loc, Loc));
+    auto EndLoc = Lexer::getLocForEndOfToken(Loc, 0, SourceMgr, LangOpts);
+    auto L = sourceRangeToLocation(SourceMgr, SourceRange(Loc, EndLoc));
     if (L)
       Result.push_back(*L);
   }
Index: clangd/SourceCode.h
===================================================================
--- clangd/SourceCode.h
+++ clangd/SourceCode.h
@@ -20,6 +20,7 @@
 class SourceManager;
 
 namespace clangd {
+class DraftStore;
 
 /// Turn a [line, column] pair into an offset in Code.
 ///
@@ -49,6 +50,21 @@
 // Note that clang also uses closed source ranges, which this can't handle!
 Range halfOpenToRange(const SourceManager &SM, CharSourceRange R);
 
+/// Turn a SourceRange into a Location.
+llvm::Optional<Location>
+sourceRangeToLocation(const SourceManager &SourceMgr,
+                      const SourceRange &ValSourceRange);
+
+/// Turn a pair of offsets into a Location.
+llvm::Optional<Location>
+offsetRangeToLocation(const DraftStore &DS, SourceManager &SourceMgr,
+                      StringRef File, size_t OffsetStart, size_t OffsetEnd);
+
+/// From "a::b::c", return {"a::b::", "c"}. Scope is empty if there's no
+/// qualifier.
+std::pair<llvm::StringRef, llvm::StringRef>
+splitQualifiedName(llvm::StringRef QName);
+
 } // namespace clangd
 } // namespace clang
 #endif
Index: clangd/SourceCode.cpp
===================================================================
--- clangd/SourceCode.cpp
+++ clangd/SourceCode.cpp
@@ -7,10 +7,13 @@
 //
 //===----------------------------------------------------------------------===//
 #include "SourceCode.h"
+#include "DraftStore.h"
+#include "Logger.h"
 
 #include "clang/Basic/SourceManager.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
 
 namespace clang {
 namespace clangd {
@@ -76,5 +79,75 @@
   return {Begin, End};
 }
 
+llvm::Optional<Location>
+sourceRangeToLocation(const SourceManager &SourceMgr,
+                      const SourceRange &ValSourceRange) {
+  SourceLocation LocStart = ValSourceRange.getBegin();
+  const FileEntry *FE =
+      SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart));
+  if (!FE)
+    return llvm::None;
+  SourceLocation LocEnd = ValSourceRange.getEnd();
+  Position Begin = sourceLocToPosition(SourceMgr, LocStart);
+  Position End = sourceLocToPosition(SourceMgr, LocEnd);
+  Range R = {Begin, End};
+  Location L;
+
+  SmallString<64> FilePath = FE->tryGetRealPathName();
+  if (FilePath.empty())
+    FilePath = FE->getName();
+  if (!llvm::sys::path::is_absolute(FilePath)) {
+    if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) {
+      log("Could not turn relative path to absolute: " + FilePath);
+      return llvm::None;
+    }
+  }
+
+  L.uri = URIForFile(FilePath.str());
+  L.range = R;
+  return L;
+}
+
+llvm::Optional<Location>
+offsetRangeToLocation(const DraftStore &DS, SourceManager &SourceMgr,
+                      StringRef File, size_t OffsetStart, size_t OffsetEnd) {
+  auto Code = DS.getDraft(File);
+  // If an unsaved file exists, use its buffer to to the offset -> Position
+  // otherwise it might end up being the wrong location. This assumes
+  // OffsetStart/OffsetEnd are the most up to date in regards to unsaved files.
+  if (Code) {
+    Position Begin = offsetToPosition(*Code, OffsetStart);
+    Position End = offsetToPosition(*Code, OffsetEnd);
+    Range R = {Begin, End};
+    Location L;
+    L.uri = URIForFile(File);
+    L.range = R;
+    return L;
+  }
+
+  const FileEntry *FE = SourceMgr.getFileManager().getFile(File);
+  if (!FE)
+    return llvm::None;
+
+  FileID FID = SourceMgr.getOrCreateFileID(FE, SrcMgr::C_User);
+
+  SourceLocation LocStart = SourceMgr.getComposedLoc(FID, OffsetStart);
+  SourceLocation LocEnd = SourceMgr.getComposedLoc(FID, OffsetEnd);
+  if (LocStart.isInvalid() || LocEnd.isInvalid())
+    return llvm::None;
+
+  return sourceRangeToLocation(SourceMgr, {LocStart, LocEnd});
+}
+
+/// From "a::b::c", return {"a::b::", "c"}. Scope is empty if there's no
+/// qualifier.
+std::pair<llvm::StringRef, llvm::StringRef>
+splitQualifiedName(llvm::StringRef QName) {
+  size_t Pos = QName.rfind("::");
+  if (Pos == llvm::StringRef::npos)
+    return {StringRef(), QName};
+  return {QName.substr(0, Pos + 2), QName.substr(Pos + 2)};
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/ProtocolHandlers.h
===================================================================
--- clangd/ProtocolHandlers.h
+++ clangd/ProtocolHandlers.h
@@ -49,6 +49,7 @@
   virtual void onSwitchSourceHeader(TextDocumentIdentifier &Params) = 0;
   virtual void onFileEvent(DidChangeWatchedFilesParams &Params) = 0;
   virtual void onCommand(ExecuteCommandParams &Params) = 0;
+  virtual void onWorkspaceSymbol(WorkspaceSymbolParams &Params) = 0;
   virtual void onRename(RenameParams &Parames) = 0;
   virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0;
   virtual void onHover(TextDocumentPositionParams &Params) = 0;
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ clangd/ProtocolHandlers.cpp
@@ -72,4 +72,5 @@
            &ProtocolCallbacks::onDocumentHighlight);
   Register("workspace/didChangeConfiguration",
            &ProtocolCallbacks::onChangeConfiguration);
+  Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol);
 }
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -237,6 +237,59 @@
 };
 bool fromJSON(const json::Expr &, CompletionClientCapabilities &);
 
+/// A symbol kind.
+enum class SymbolKind {
+  File = 1,
+  Module = 2,
+  Namespace = 3,
+  Package = 4,
+  Class = 5,
+  Method = 6,
+  Property = 7,
+  Field = 8,
+  Constructor = 9,
+  Enum = 10,
+  Interface = 11,
+  Function = 12,
+  Variable = 13,
+  Constant = 14,
+  String = 15,
+  Number = 16,
+  Boolean = 17,
+  Array = 18,
+  Object = 19,
+  Key = 20,
+  Null = 21,
+  EnumMember = 22,
+  Struct = 23,
+  Event = 24,
+  Operator = 25,
+  TypeParameter = 26
+};
+bool fromJSON(const json::Expr &, SymbolKind &);
+
+struct SymbolKindCapabilities {
+  /// The SymbolKinds that the client supports. If not set, the client only
+  /// supports <= SymbolKind::Array and will not fall back to a valid default
+  /// value.
+  llvm::Optional<std::vector<SymbolKind>> valueSet;
+};
+bool fromJSON(const json::Expr &, SymbolKindCapabilities &);
+
+struct WorkspaceSymbolCapabilities {
+  /// Capabilities SymbolKind.
+  llvm::Optional<SymbolKindCapabilities> symbolKind;
+};
+bool fromJSON(const json::Expr &, WorkspaceSymbolCapabilities &);
+
+// FIXME: most of the capabilities are missing from this struct. Only the ones
+// used by clangd are currently there.
+struct WorkspaceClientCapabilities {
+  /// Capabilities specific to `workspace/symbol`.
+  llvm::Optional<WorkspaceSymbolCapabilities> symbol;
+};
+bool fromJSON(const json::Expr &, WorkspaceClientCapabilities &);
+
 // FIXME: most of the capabilities are missing from this struct. Only the ones
 // used by clangd are currently there.
 struct TextDocumentClientCapabilities {
@@ -247,8 +300,7 @@
 
 struct ClientCapabilities {
   // Workspace specific client capabilities.
-  // NOTE: not used by clangd at the moment.
-  // WorkspaceClientCapabilities workspace;
+  llvm::Optional<WorkspaceClientCapabilities> workspace;
 
   // Text document specific client capabilities.
   TextDocumentClientCapabilities textDocument;
@@ -525,6 +577,30 @@
 
 json::Expr toJSON(const Command &C);
 
+/// Represents information about programming constructs like variables, classes,
+/// interfaces etc.
+struct SymbolInformation {
+  /// The name of this symbol.
+  std::string name;
+
+  /// The kind of this symbol.
+  SymbolKind kind;
+
+  /// The location of this symbol.
+  Location location;
+
+  /// The name of the symbol containing this symbol.
+  std::string containerName;
+};
+json::Expr toJSON(const SymbolInformation &);
+
+/// The parameters of a Workspace Symbol Request.
+struct WorkspaceSymbolParams {
+  /// A non-empty query string
+  std::string query;
+};
+bool fromJSON(const json::Expr &, WorkspaceSymbolParams &);
+
 struct ApplyWorkspaceEditParams {
   WorkspaceEdit edit;
 };
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -176,6 +176,41 @@
   return true;
 }
 
+bool fromJSON(const json::Expr &E, SymbolKind &Out) {
+  if (auto T = E.asInteger()) {
+    if (*T < static_cast<int>(SymbolKind::File) ||
+        *T > static_cast<int>(SymbolKind::TypeParameter))
+      return false;
+    Out = static_cast<SymbolKind>(*T);
+    return true;
+  }
+  return false;
+}
+
+bool fromJSON(const json::Expr &Params, SymbolKindCapabilities &R) {
+  json::ObjectMapper O(Params);
+  if (!O)
+    return false;
+  O.map("valueSet", R.valueSet);
+  return true;
+}
+
+bool fromJSON(const json::Expr &Params, WorkspaceSymbolCapabilities &R) {
+  json::ObjectMapper O(Params);
+  if (!O)
+    return false;
+  O.map("symbolKind", R.symbolKind);
+  return true;
+}
+
+bool fromJSON(const json::Expr &Params, WorkspaceClientCapabilities &R) {
+  json::ObjectMapper O(Params);
+  if (!O)
+    return false;
+  O.map("symbol", R.symbol);
+  return true;
+}
+
 bool fromJSON(const json::Expr &Params, TextDocumentClientCapabilities &R) {
   json::ObjectMapper O(Params);
   if (!O)
@@ -351,6 +386,20 @@
   return false; // Unrecognized command.
 }
 
+json::Expr toJSON(const SymbolInformation &P) {
+  return json::obj{
+      {"name", P.name},
+      {"kind", static_cast<int>(P.kind)},
+      {"location", P.location},
+      {"containerName", P.containerName},
+  };
+}
+
+bool fromJSON(const json::Expr &Params, WorkspaceSymbolParams &R) {
+  json::ObjectMapper O(Params);
+  return O && O.map("query", R.query);
+}
+
 json::Expr toJSON(const Command &C) {
   auto Cmd = json::obj{{"title", C.title}, {"command", C.command}};
   if (C.workspaceEdit)
Index: clangd/FindSymbols.h
===================================================================
--- /dev/null
+++ clangd/FindSymbols.h
@@ -0,0 +1,43 @@
+//===--- FindSymbols.h --------------------------------------*- C++-*------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Queries that provide a list of symbols matching a string.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_WORKSPACESYMBOL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_WORKSPACESYMBOL_H
+
+#include "Protocol.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace clangd {
+class SymbolIndex;
+class DraftStore;
+
+struct WorkspaceSymbolOptions {
+  /// Limit the number of results returned (0 means no limit).
+  size_t Limit = 0;
+
+  /// Symbol kinds supported by the requester.
+  llvm::Optional<std::vector<SymbolKind>> supportedSymbolKinds;
+};
+
+llvm::Expected<std::vector<SymbolInformation>>
+getWorkspaceSymbols(llvm::StringRef Query, const WorkspaceSymbolOptions &Opts,
+                    const SymbolIndex *const Index,
+                    llvm::IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+                    const DraftStore &DS);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
Index: clangd/FindSymbols.cpp
===================================================================
--- /dev/null
+++ clangd/FindSymbols.cpp
@@ -0,0 +1,170 @@
+//===--- FindSymbols.cpp ------------------------------------*- C++-*------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "FindSymbols.h"
+
+#include "Logger.h"
+#include "SourceCode.h"
+#include "index/Index.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Index/IndexSymbol.h"
+#include "llvm/Support/FormatVariadic.h"
+
+namespace clang {
+namespace clangd {
+
+namespace {
+
+SymbolKind adjustKindToCapability(
+    SymbolKind Kind,
+    const llvm::Optional<std::vector<SymbolKind>> &supportedSymbolKinds) {
+  // All clients should support those.
+  if (Kind >= SymbolKind::File && Kind <= SymbolKind::Array)
+    return Kind;
+
+  if (supportedSymbolKinds &&
+      std::find(supportedSymbolKinds->begin(), supportedSymbolKinds->end(),
+                Kind) != supportedSymbolKinds->end())
+    return Kind;
+
+  // Provide some fall backs for common kinds that are close enough.
+  if (Kind == SymbolKind::Struct)
+    return SymbolKind::Class;
+  if (Kind == SymbolKind::EnumMember)
+    return SymbolKind::Enum;
+
+  if (!supportedSymbolKinds) {
+    // Provide some sensible default when all fails.
+    return SymbolKind::Variable;
+  }
+  return Kind;
+}
+
+// Convert a index::SymbolKind to clangd::SymbolKind (LSP)
+// Note, some are not perfect matches and should be improved when this LSP
+// issue is addressed:
+// https://github.com/Microsoft/language-server-protocol/issues/344
+SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
+  switch (Kind) {
+  case index::SymbolKind::Unknown:
+    return SymbolKind::Variable;
+  case index::SymbolKind::Module:
+    return SymbolKind::Module;
+  case index::SymbolKind::Namespace:
+    return SymbolKind::Namespace;
+  case index::SymbolKind::NamespaceAlias:
+    return SymbolKind::Namespace;
+  case index::SymbolKind::Macro:
+    return SymbolKind::String;
+  case index::SymbolKind::Enum:
+    return SymbolKind::Enum;
+  case index::SymbolKind::Struct:
+    return SymbolKind::Struct;
+  case index::SymbolKind::Class:
+    return SymbolKind::Class;
+  case index::SymbolKind::Protocol:
+    return SymbolKind::Interface;
+  case index::SymbolKind::Extension:
+    return SymbolKind::Interface;
+  case index::SymbolKind::Union:
+    return SymbolKind::Class;
+  case index::SymbolKind::TypeAlias:
+    return SymbolKind::Class;
+  case index::SymbolKind::Function:
+    return SymbolKind::Function;
+  case index::SymbolKind::Variable:
+    return SymbolKind::Variable;
+  case index::SymbolKind::Field:
+    return SymbolKind::Field;
+  case index::SymbolKind::EnumConstant:
+    return SymbolKind::EnumMember;
+  case index::SymbolKind::InstanceMethod:
+  case index::SymbolKind::ClassMethod:
+  case index::SymbolKind::StaticMethod:
+    return SymbolKind::Method;
+  case index::SymbolKind::InstanceProperty:
+  case index::SymbolKind::ClassProperty:
+  case index::SymbolKind::StaticProperty:
+    return SymbolKind::Property;
+  case index::SymbolKind::Constructor:
+  case index::SymbolKind::Destructor:
+    return SymbolKind::Method;
+  case index::SymbolKind::ConversionFunction:
+    return SymbolKind::Function;
+  case index::SymbolKind::Parameter:
+    return SymbolKind::Variable;
+  case index::SymbolKind::Using:
+    return SymbolKind::Namespace;
+  }
+  llvm_unreachable("invalid symbol kind");
+}
+} // namespace
+
+llvm::Expected<std::vector<SymbolInformation>>
+getWorkspaceSymbols(StringRef Query, const WorkspaceSymbolOptions &Opts,
+                    const SymbolIndex *const Index,
+                    IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+                    const DraftStore &DS) {
+  std::vector<SymbolInformation> Result;
+  if (Query.empty() || !Index)
+    return Result;
+
+  auto Names = splitQualifiedName(Query);
+
+  // We'll use a temporary SourceManager to do the offset -> line/col mapping.
+  // We don't have any context from which this query was launched (working dir),
+  // so use defaults here.
+  // FIXME: If the index stored line/col directly, we woudln't have to read all
+  // the files here.
+  FileSystemOptions FileOpts;
+  FileManager FM(FileOpts, VFS);
+  IntrusiveRefCntPtr<DiagnosticsEngine> DE(
+      CompilerInstance::createDiagnostics(new DiagnosticOptions));
+  SourceManager TempSM(*DE, FM);
+
+  // Global scope is represented by "" in FuzzyFind.
+  if (Names.first.startswith("::"))
+    Names.first = Names.first.substr(2);
+
+  FuzzyFindRequest Req;
+  Req.Query = Names.second;
+  if (!Names.first.empty())
+    Req.Scopes.push_back(Names.first);
+  if (Opts.Limit)
+    Req.MaxCandidateCount = Opts.Limit;
+  Index->fuzzyFind(Req, [&DS, &TempSM, &Result, &Opts](const Symbol &Sym) {
+    auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration;
+    auto Uri = URI::parse(CD.FileURI);
+    if (!Uri) {
+      log(llvm::formatv(
+          "Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.",
+          CD.FileURI, Sym.Name));
+      return;
+    }
+    auto Path = URI::resolve(*Uri);
+    if (!Path) {
+      log(llvm::formatv("Workspace symbol: Could not resolve path for URI "
+                        "'{0}' for symbol '{1}'.",
+                        (*Uri).toString(), Sym.Name.str()));
+      return;
+    }
+    auto L = offsetRangeToLocation(DS, TempSM, *Path, CD.StartOffset,
+                                   CD.StartOffset);
+    if (L) {
+      SymbolKind SK =
+          adjustKindToCapability(indexSymbolKindToSymbolKind(Sym.SymInfo.Kind),
+                                 Opts.supportedSymbolKinds);
+      Result.push_back({Sym.Name, SK, *L, Sym.Scope});
+    }
+  });
+  return Result;
+}
+
+} // namespace clangd
+} // namespace clang
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -33,6 +33,8 @@
 class PCHContainerOperations;
 
 namespace clangd {
+class DraftStore;
+struct WorkspaceSymbolOptions;
 
 class DiagnosticsConsumer {
 public:
@@ -157,6 +159,11 @@
   /// Get code hover for a given position.
   void findHover(PathRef File, Position Pos, Callback<Hover> CB);
 
+  void workspaceSymbols(StringRef Query,
+                        const clangd::WorkspaceSymbolOptions &Opts,
+                        const DraftStore &DS,
+                        Callback<std::vector<SymbolInformation>> CB);
+
   /// Run formatting for \p Rng inside \p File with content \p Code.
   llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
                                                     PathRef File, Range Rng);
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -9,6 +9,7 @@
 
 #include "ClangdServer.h"
 #include "CodeComplete.h"
+#include "FindSymbols.h"
 #include "Headers.h"
 #include "SourceCode.h"
 #include "XRefs.h"
@@ -492,6 +493,13 @@
   // invalidating other caches.
 }
 
+void ClangdServer::workspaceSymbols(
+    StringRef Query, const clangd::WorkspaceSymbolOptions &Opts,
+    const DraftStore &DS, Callback<std::vector<SymbolInformation>> CB) {
+  CB(clangd::getWorkspaceSymbols(Query, Opts, Index, FSProvider.getFileSystem(),
+                                 DS));
+}
+
 std::vector<std::pair<Path, std::size_t>>
 ClangdServer::getUsedBytesPerFile() const {
   return WorkScheduler.getUsedBytesPerFile();
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -12,6 +12,7 @@
 
 #include "ClangdServer.h"
 #include "DraftStore.h"
+#include "FindSymbols.h"
 #include "GlobalCompilationDatabase.h"
 #include "Path.h"
 #include "Protocol.h"
@@ -33,6 +34,7 @@
   /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
   /// for compile_commands.json in all parent directories of each file.
   ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts,
+                  const clangd::WorkspaceSymbolOptions &WorkspaceSymOpts,
                   llvm::Optional<Path> CompileCommandsDir,
                   const ClangdServer::Options &Opts);
 
@@ -69,6 +71,7 @@
   void onDocumentHighlight(TextDocumentPositionParams &Params) override;
   void onFileEvent(DidChangeWatchedFilesParams &Params) override;
   void onCommand(ExecuteCommandParams &Params) override;
+  void onWorkspaceSymbol(WorkspaceSymbolParams &Params) override;
   void onRename(RenameParams &Parames) override;
   void onHover(TextDocumentPositionParams &Params) override;
   void onChangeConfiguration(DidChangeConfigurationParams &Params) override;
@@ -102,6 +105,8 @@
   RealFileSystemProvider FSProvider;
   /// Options used for code completion
   clangd::CodeCompleteOptions CCOpts;
+  /// Options used for workspace symbol.
+  clangd::WorkspaceSymbolOptions WorkspaceSymbolOpts;
 
   // Store of the current versions of the open documents.
   DraftStore DraftMgr;
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -96,6 +96,10 @@
 
   CCOpts.EnableSnippets =
       Params.capabilities.textDocument.completion.completionItem.snippetSupport;
+  if (Params.capabilities.workspace && Params.capabilities.workspace->symbol &&
+      Params.capabilities.workspace->symbol->symbolKind)
+    WorkspaceSymbolOpts.supportedSymbolKinds =
+        Params.capabilities.workspace->symbol->symbolKind->valueSet;
 
   reply(json::obj{
       {{"capabilities",
@@ -122,6 +126,7 @@
             {"documentHighlightProvider", true},
             {"hoverProvider", true},
             {"renameProvider", true},
+            {"workspaceSymbolProvider", true},
             {"executeCommandProvider",
              json::obj{
                  {"commands",
@@ -245,6 +250,17 @@
   }
 }
 
+void ClangdLSPServer::onWorkspaceSymbol(WorkspaceSymbolParams &Params) {
+  Server.workspaceSymbols(
+      Params.query, WorkspaceSymbolOpts, DraftMgr,
+      [](llvm::Expected<std::vector<SymbolInformation>> Items) {
+        if (!Items)
+          return replyError(ErrorCode::InvalidParams,
+                            llvm::toString(Items.takeError()));
+        reply(json::ary(*Items));
+      });
+}
+
 void ClangdLSPServer::onRename(RenameParams &Params) {
   Path File = Params.textDocument.uri.file();
   llvm::Optional<std::string> Code = DraftMgr.getDraft(File);
@@ -417,11 +433,12 @@
   }
 }
 
-ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
-                                 const clangd::CodeCompleteOptions &CCOpts,
-                                 llvm::Optional<Path> CompileCommandsDir,
-                                 const ClangdServer::Options &Opts)
+ClangdLSPServer::ClangdLSPServer(
+    JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts,
+    const clangd::WorkspaceSymbolOptions &WorkspaceSymOpts,
+    llvm::Optional<Path> CompileCommandsDir, const ClangdServer::Options &Opts)
     : Out(Out), CDB(std::move(CompileCommandsDir)), CCOpts(CCOpts),
+      WorkspaceSymbolOpts(WorkspaceSymOpts),
       Server(CDB, FSProvider, /*DiagConsumer=*/*this, Opts) {}
 
 bool ClangdLSPServer::run(std::istream &In, JSONStreamStyle InputStyle) {
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -14,6 +14,7 @@
   Context.cpp
   Diagnostics.cpp
   DraftStore.cpp
+  FindSymbols.cpp
   FuzzyMatch.cpp
   GlobalCompilationDatabase.cpp
   Headers.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to