ioeric updated this revision to Diff 127118.
ioeric added a comment.

- Merge remote-tracking branch 'origin/master' into index-completion
- Fix merge with origin/master.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D41281

Files:
  clangd/CMakeLists.txt
  clangd/ClangdLSPServer.cpp
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/CodeComplete.cpp
  clangd/CodeComplete.h
  clangd/Position.cpp
  clangd/Position.h
  clangd/index/FileIndex.cpp
  clangd/index/FileIndex.h
  clangd/index/Index.h
  clangd/index/MemIndex.cpp
  clangd/index/MemIndex.h
  unittests/clangd/CodeCompleteTests.cpp

Index: unittests/clangd/CodeCompleteTests.cpp
===================================================================
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -10,8 +10,10 @@
 #include "Compiler.h"
 #include "Context.h"
 #include "Matchers.h"
+#include "Position.h"
 #include "Protocol.h"
 #include "TestFS.h"
+#include "index/MemIndex.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -77,14 +79,33 @@
   return arg.insertTextFormat == clangd::InsertTextFormat::Snippet &&
          arg.insertText == Text;
 }
+MATCHER_P(Edit, Text, "") {
+  return arg.textEdit.hasValue() && arg.textEdit.getValue().newText == Text;
+}
+MATCHER_P2(Edit, Text, Range, "") {
+  return arg.textEdit.hasValue() && arg.textEdit.getValue().newText == Text &&
+         arg.textEdit.getValue().range == Range;
+}
 // Shorthand for Contains(Named(Name)).
 Matcher<const std::vector<CompletionItem> &> Has(std::string Name) {
   return Contains(Named(std::move(Name)));
 }
 Matcher<const std::vector<CompletionItem> &> Has(std::string Name,
                                                  CompletionItemKind K) {
   return Contains(AllOf(Named(std::move(Name)), Kind(K)));
 }
+// Shorthand for Contains(Edit(Name)).
+Matcher<const std::vector<CompletionItem> &> HasEdit(std::string Name) {
+  return Contains(Edit(std::move(Name)));
+}
+Matcher<const std::vector<CompletionItem> &> HasEdit(std::string Name,
+                                                     CompletionItemKind K) {
+  return Contains(AllOf(Edit(std::move(Name)), Kind(K)));
+}
+Matcher<const std::vector<CompletionItem> &>
+HasEditWithRange(std::string Name, Range R) {
+  return Contains(Edit(std::move(Name), std::move(R)));
+}
 MATCHER(IsDocumented, "") { return !arg.documentation.empty(); }
 
 CompletionList completions(StringRef Text,
@@ -368,6 +389,135 @@
   EXPECT_THAT(Results.items, Has("namespace", CompletionItemKind::Snippet));
 }
 
+std::unique_ptr<SymbolIndex> simpleIndexFromSymbols(
+    std::vector<std::pair<std::string, index::SymbolKind>> Symbols) {
+  auto I = llvm::make_unique<MemIndex>();
+  struct Snapshot {
+    SymbolSlab Slab;
+    std::vector<const Symbol *> Pointers;
+  };
+  auto Snap = std::make_shared<Snapshot>();
+  for (const auto &Pair : Symbols) {
+    Symbol Sym;
+    Sym.ID = SymbolID(Pair.first);
+    Sym.QualifiedName = Pair.first;
+    Sym.SymInfo.Kind = Pair.second;
+    Snap->Slab.insert(std::move(Sym));
+  }
+  for (auto &Iter : Snap->Slab)
+    Snap->Pointers.push_back(&Iter.second);
+  auto S = std::shared_ptr<std::vector<const Symbol *>>(std::move(Snap),
+                                                        &Snap->Pointers);
+  I->build(std::move(S));
+  return I;
+}
+
+TEST(CompletionTest, NoIndex) {
+  clangd::CodeCompleteOptions Opts;
+  Opts.Index = nullptr;
+
+  auto Results = completions(R"cpp(
+      namespace ns { class No {}; }
+      void f() { ns::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, Has("No"));
+}
+
+TEST(CompletionTest, SimpleIndexBased) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class},
+                              {"ns::foo", index::SymbolKind::Function}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      namespace ns { class No {}; }
+      void f() { ns::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, HasEdit("ns::XYZ", CompletionItemKind::Class));
+  EXPECT_THAT(Results.items, HasEdit("ns::foo", CompletionItemKind::Function));
+  EXPECT_THAT(Results.items, Not(Has("No")));
+}
+
+TEST(CompletionTest, IndexBasedWithFilter) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class},
+                              {"ns::foo", index::SymbolKind::Function}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ns::x^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, HasEdit("ns::XYZ", CompletionItemKind::Class));
+  EXPECT_THAT(Results.items, Not(HasEdit("ns::foo")));
+}
+
+TEST(CompletionTest, GlobalQualified) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"XYZ", index::SymbolKind::Class}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, HasEdit("::XYZ", CompletionItemKind::Class));
+}
+
+TEST(CompletionTest, EditRangeGlobal) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"XYZ", index::SymbolKind::Class}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, HasEditWithRange("::XYZ", {{1, 17}, {1, 17}}));
+}
+
+TEST(CompletionTest, EditRangeQualified) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ns::^ }
+  )cpp",
+                             Opts);
+  // "::" is not in the buffer.
+  EXPECT_THAT(Results.items, HasEditWithRange("ns::XYZ", {{1, 17}, {1, 19}}));
+}
+
+TEST(CompletionTest, EditRangeQualifiedWithFilter) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ns::X^ }
+  )cpp",
+                             Opts);
+  // Filter is not in the buffer.
+  EXPECT_THAT(Results.items, HasEditWithRange("ns::XYZ", {{1, 17}, {1, 19}}));
+}
+
+TEST(CompletionTest, RecognizedSpecifierAndRange) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      namespace ns {}
+      void f() { ns::X^ }
+  )cpp",
+                             Opts);
+  // Filter is not in the buffer.
+  EXPECT_THAT(Results.items, HasEditWithRange("ns::XYZ", {{2, 17}, {2, 19}}));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/index/MemIndex.h
===================================================================
--- clangd/index/MemIndex.h
+++ clangd/index/MemIndex.h
@@ -24,7 +24,7 @@
   /// accessible as long as `Symbols` is kept alive.
   void build(std::shared_ptr<std::vector<const Symbol *>> Symbols);
 
-  bool fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+  bool fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
                  std::function<void(const Symbol &)> Callback) const override;
 
 private:
Index: clangd/index/MemIndex.cpp
===================================================================
--- clangd/index/MemIndex.cpp
+++ clangd/index/MemIndex.cpp
@@ -25,7 +25,7 @@
   }
 }
 
-bool MemIndex::fuzzyFind(Context & /*Ctx*/, const FuzzyFindRequest &Req,
+bool MemIndex::fuzzyFind(const Context & /*Ctx*/, const FuzzyFindRequest &Req,
                          std::function<void(const Symbol &)> Callback) const {
   std::string LoweredQuery = llvm::StringRef(Req.Query).lower();
   unsigned Matched = 0;
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -142,7 +142,7 @@
   /// Returns true if the result list is complete, false if it was truncated due
   /// to MaxCandidateCount
   virtual bool
-  fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+  fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
             std::function<void(const Symbol &)> Callback) const = 0;
 
   // FIXME: add interfaces for more index use cases:
Index: clangd/index/FileIndex.h
===================================================================
--- clangd/index/FileIndex.h
+++ clangd/index/FileIndex.h
@@ -58,9 +58,9 @@
 public:
   /// \brief Update symbols in \p Path with symbols in \p AST. If \p AST is
   /// nullptr, this removes all symbols in the file
-  void update(Context &Ctx, PathRef Path, ParsedAST *AST);
+  void update(const Context &Ctx, PathRef Path, ParsedAST *AST);
 
-  bool fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+  bool fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
                  std::function<void(const Symbol &)> Callback) const override;
 
 private:
Index: clangd/index/FileIndex.cpp
===================================================================
--- clangd/index/FileIndex.cpp
+++ clangd/index/FileIndex.cpp
@@ -63,7 +63,7 @@
   return {std::move(Snap), Pointers};
 }
 
-void FileIndex::update(Context &Ctx, PathRef Path, ParsedAST *AST) {
+void FileIndex::update(const Context &Ctx, PathRef Path, ParsedAST *AST) {
   if (!AST) {
     FSymbols.update(Path, nullptr);
   } else {
@@ -74,7 +74,7 @@
   Index.build(std::move(Symbols));
 }
 
-bool FileIndex::fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+bool FileIndex::fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
                           std::function<void(const Symbol &)> Callback) const {
   return Index.fuzzyFind(Ctx, Req, std::move(Callback));
 }
Index: clangd/Position.h
===================================================================
--- /dev/null
+++ clangd/Position.h
@@ -0,0 +1,27 @@
+//===--- Position.h - Positions in code. -------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_POSITION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_POSITION_H
+
+#include "Protocol.h"
+
+namespace clang {
+namespace clangd {
+
+/// Turn a [line, column] pair into an offset in Code.
+size_t positionToOffset(llvm::StringRef Code, Position P);
+
+/// Turn an offset in Code into a [line, column] pair.
+Position offsetToPosition(llvm::StringRef Code, size_t Offset);
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_POSITION_H
Index: clangd/Position.cpp
===================================================================
--- /dev/null
+++ clangd/Position.cpp
@@ -0,0 +1,40 @@
+//===--- Position.cpp - Positions in code. -------------------------*- +-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Position.h"
+
+namespace clang {
+namespace clangd {
+
+size_t positionToOffset(llvm::StringRef Code, Position P) {
+  size_t Offset = 0;
+  for (int I = 0; I != P.line; ++I) {
+    // FIXME: \r\n
+    // FIXME: UTF-8
+    size_t F = Code.find('\n', Offset);
+    if (F == llvm::StringRef::npos)
+      return 0; // FIXME: Is this reasonable?
+    Offset = F + 1;
+  }
+  return (Offset == 0 ? 0 : (Offset - 1)) + P.character;
+}
+
+/// Turn an offset in Code into a [line, column] pair.
+Position offsetToPosition(llvm::StringRef Code, size_t Offset) {
+  llvm::StringRef JustBefore = Code.substr(0, Offset);
+  // FIXME: \r\n
+  // FIXME: UTF-8
+  int Lines = JustBefore.count('\n');
+  int Cols = JustBefore.size() - JustBefore.rfind('\n') - 1;
+  return {Lines, Cols};
+}
+
+
+} // namespace clangd
+} // namespace clang
Index: clangd/CodeComplete.h
===================================================================
--- clangd/CodeComplete.h
+++ clangd/CodeComplete.h
@@ -19,6 +19,7 @@
 #include "Logger.h"
 #include "Path.h"
 #include "Protocol.h"
+#include "index/Index.h"
 #include "clang/Frontend/PrecompiledPreamble.h"
 #include "clang/Sema/CodeCompleteOptions.h"
 #include "clang/Tooling/CompilationDatabase.h"
@@ -59,6 +60,11 @@
   /// Limit the number of results returned (0 means no limit).
   /// If more results are available, we set CompletionList.isIncomplete.
   size_t Limit = 0;
+
+  // Populated internally by clangd, do not set.
+  /// If `Index` is set, it is used to augment the code completion
+  /// results.
+  const SymbolIndex *Index = nullptr;
 };
 
 /// Get code completions at a specified \p Pos in \p FileName.
Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -16,6 +16,8 @@
 
 #include "CodeComplete.h"
 #include "Compiler.h"
+#include "Position.h"
+#include "index/Index.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
@@ -85,6 +87,53 @@
   llvm_unreachable("Unhandled CodeCompletionResult::ResultKind.");
 }
 
+CompletionItemKind getKindOfSymbol(index::SymbolKind Kind) {
+  using SK = index::SymbolKind;
+  switch (Kind) {
+  case SK::Unknown:
+    return CompletionItemKind::Missing;
+  case SK::Module:
+  case SK::Namespace:
+  case SK::NamespaceAlias:
+    return CompletionItemKind::Module;
+  case SK::Macro:
+    return CompletionItemKind::Text;
+  case SK::Enum:
+    return CompletionItemKind::Enum;
+  case SK::Struct:
+  case SK::Class:
+  case SK::Protocol:
+  case SK::Extension:
+  case SK::Union:
+    return CompletionItemKind::Class;
+  case SK::TypeAlias:
+  case SK::Using:
+    return CompletionItemKind::Reference;
+  case SK::Function:
+  case SK::ConversionFunction:
+    return CompletionItemKind::Function;
+  case SK::Variable:
+  case SK::Parameter:
+    return CompletionItemKind::Variable;
+  case SK::Field:
+    return CompletionItemKind::Field;
+  case SK::EnumConstant:
+    return CompletionItemKind::Value;
+  case SK::InstanceMethod:
+  case SK::ClassMethod:
+  case SK::StaticMethod:
+    return CompletionItemKind::Method;
+  case SK::InstanceProperty:
+  case SK::ClassProperty:
+  case SK::StaticProperty:
+    return CompletionItemKind::Property;
+  case SK::Constructor:
+  case SK::Destructor:
+    return CompletionItemKind::Constructor;
+  }
+  llvm_unreachable("Unhandled clang::index::SymbolKind.");
+}
+
 std::string escapeSnippet(const llvm::StringRef Text) {
   std::string Result;
   Result.reserve(Text.size()); // Assume '$', '}' and '\\' are rare.
@@ -228,28 +277,137 @@
   }
 };
 
+/// \brief Information about the scope specifier in the qualfiied-id code
+/// completion (e.g. "ns::ab?").
+struct ScopeSpecifierInfo {
+  static ScopeSpecifierInfo create(Sema &S, const CXXScopeSpec &SS) {
+    ScopeSpecifierInfo Info;
+    auto &SM = S.getSourceManager();
+    auto SpecifierRange = SS.getRange();
+    Info.WrittenSpecifier =
+        Lexer::getSourceText(CharSourceRange::getCharRange(SpecifierRange), SM,
+                             clang::LangOptions());
+    if (SS.isValid()) {
+      DeclContext *DC = S.computeDeclContext(SS);
+      if (auto *NS = llvm::dyn_cast<NamespaceDecl>(DC)) {
+        Info.SpecifiedContextName = NS->getQualifiedNameAsString();
+      } else if (auto *TU = llvm::dyn_cast<TranslationUnitDecl>(DC)) {
+        Info.SpecifiedContextName = "::";
+        // Sema does not include the suffix "::" in the range of SS, so we add
+        // it back here.
+        Info.WrittenSpecifier = "::";
+      }
+    }
+
+    Info.SpecifierBeginOffset = SM.getFileOffset(SpecifierRange.getBegin());
+    Info.SpecifierEndOffset = SM.getFileOffset(SpecifierRange.getEnd());
+    return Info;
+  }
+
+  // The range of scope specifier as written. This does not include the
+  // filter text following the specifier. For example, for completion at
+  // "ns::ab?", the range will be "ns".
+  unsigned int SpecifierBeginOffset;
+  unsigned int SpecifierEndOffset;
+
+  // The scope specifier as written. For example, for completion "ns::ab?",
+  // the written scope specifier is "ns".
+  std::string WrittenSpecifier;
+  // If this scope specifier is recognized in Sema (e.g. as a namespace
+  // context), this will be set to the fully qualfied name of the corresponding
+  // context.
+  std::string SpecifiedContextName;
+};
+
+CompletionItem symbolToCompletionItem(const Symbol &Sym, llvm::StringRef Code,
+                                      const ScopeSpecifierInfo &SSInfo) {
+  CompletionItem Item;
+  bool FullyQualified =
+      llvm::StringRef(SSInfo.WrittenSpecifier).startswith("::");
+  if (FullyQualified)
+    Item.label = "::";
+  // FIXME: use more symbol information (e.g. documentation, label) to populate
+  // the completion item.
+  Item.label += Sym.QualifiedName;
+  Item.kind = getKindOfSymbol(Sym.SymInfo.Kind);
+  Item.insertTextFormat = InsertTextFormat::PlainText;
+  // FIXME: sort symbols appropriately.
+  Item.sortText = "";
+
+  TextEdit Edit;
+  Edit.newText =
+      FullyQualified ? ("::" + Sym.QualifiedName) : Sym.QualifiedName;
+  Edit.range.start =
+      offsetToPosition(Code, SSInfo.SpecifierBeginOffset);
+  Edit.range.end = offsetToPosition(Code, SSInfo.SpecifierEndOffset);
+  Item.textEdit = std::move(Edit);
+  return Item;
+}
+
+void qualifiedIdCompletionWithIndex(const Context &Ctx,
+                                    const SymbolIndex &Index,
+                                    llvm::StringRef Code,
+                                    const ScopeSpecifierInfo &SSInfo,
+                                    llvm::StringRef Filter,
+                                    CompletionList *Items) {
+  FuzzyFindRequest Req;
+  Req.Query = SSInfo.SpecifiedContextName.empty() ? SSInfo.WrittenSpecifier
+                                                  : SSInfo.SpecifiedContextName;
+  if (!llvm::StringRef(Req.Query).endswith("::"))
+    Req.Query += "::";
+  // FIXME: for now we simply cancatenate specifier with the typed filter. We
+  // might want to fix the specifier prefix if it is a recognized context (e.g.
+  // a known namespace in the AST).
+  if (!Filter.empty())
+    Req.Query += Filter;
+
+  // Global qualifier means all symbols.
+  if (Req.Query == "::")
+    Req.Query = "";
+
+  Items->isIncomplete = !Index.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) {
+    Items->items.push_back(symbolToCompletionItem(Sym, Code, SSInfo));
+  });
+}
+
+/// brief Collects information about an invocation of sema code completion.
+struct SemaCompletionInfo {
+  /// Code completion filter.
+  std::string Filter;
+
+  /// This is set if the completion is for qualified IDs, e.g. "abc::x^".
+  llvm::Optional<ScopeSpecifierInfo> SSInfo;
+  // FIXME: add more information for other completion cases that we care about.
+  // For example, non-qualified id completion.
+};
+
 class CompletionItemsCollector : public CodeCompleteConsumer {
 public:
   CompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
-                           CompletionList &Items)
+                           CompletionList &Items, SemaCompletionInfo &SCInfo)
       : CodeCompleteConsumer(CodeCompleteOpts.getClangCompleteOpts(),
                              /*OutputIsBinary=*/false),
         ClangdOpts(CodeCompleteOpts), Items(Items),
         Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
-        CCTUInfo(Allocator) {}
+        CCTUInfo(Allocator), SCInfo(SCInfo) {}
 
   void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
                                   CodeCompletionResult *Results,
                                   unsigned NumResults) override final {
-    StringRef Filter = S.getPreprocessor().getCodeCompletionFilter();
+    if (llvm::Optional<const CXXScopeSpec *> SS =
+            Context.getCXXScopeSpecifier())
+      SCInfo.SSInfo = ScopeSpecifierInfo::create(S, **SS);
+
+    SCInfo.Filter = S.getPreprocessor().getCodeCompletionFilter();
     std::priority_queue<CompletionCandidate> Candidates;
     for (unsigned I = 0; I < NumResults; ++I) {
       auto &Result = Results[I];
       if (!ClangdOpts.IncludeIneligibleResults &&
           (Result.Availability == CXAvailability_NotAvailable ||
            Result.Availability == CXAvailability_NotAccessible))
         continue;
-      if (!Filter.empty() && !fuzzyMatch(S, Context, Filter, Result))
+      if (!SCInfo.Filter.empty() &&
+          !fuzzyMatch(S, Context, SCInfo.Filter, Result))
         continue;
       Candidates.emplace(Result);
       if (ClangdOpts.Limit && Candidates.size() > ClangdOpts.Limit) {
@@ -336,7 +494,7 @@
   CompletionList &Items;
   std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
   CodeCompletionTUInfo CCTUInfo;
-
+  SemaCompletionInfo &SCInfo;
 }; // CompletionItemsCollector
 
 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
@@ -349,8 +507,9 @@
 
 public:
   PlainTextCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
-                                    CompletionList &Items)
-      : CompletionItemsCollector(CodeCompleteOpts, Items) {}
+                                    CompletionList &Items,
+                                    SemaCompletionInfo &SCInfo)
+      : CompletionItemsCollector(CodeCompleteOpts, Items, SCInfo) {}
 
 private:
   void ProcessChunks(const CodeCompletionString &CCS,
@@ -385,8 +544,9 @@
 
 public:
   SnippetCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
-                                  CompletionList &Items)
-      : CompletionItemsCollector(CodeCompleteOpts, Items) {}
+                                  CompletionList &Items,
+                                  SemaCompletionInfo &SCInfo)
+      : CompletionItemsCollector(CodeCompleteOpts, Items, SCInfo) {}
 
 private:
   void ProcessChunks(const CodeCompletionString &CCS,
@@ -657,6 +817,9 @@
   Result.IncludeGlobals = IncludeGlobals;
   Result.IncludeBriefComments = IncludeBriefComments;
 
+  // Enable index-based code completion when Index is provided.
+  Result.IncludeNamespaceLevelDecls = !Index;
+
   return Result;
 }
 
@@ -669,16 +832,24 @@
                             CodeCompleteOptions Opts) {
   CompletionList Results;
   std::unique_ptr<CodeCompleteConsumer> Consumer;
+  SemaCompletionInfo SCInfo;
   if (Opts.EnableSnippets) {
-    Consumer =
-        llvm::make_unique<SnippetCompletionItemsCollector>(Opts, Results);
+    Consumer = llvm::make_unique<SnippetCompletionItemsCollector>(Opts, Results,
+                                                                  SCInfo);
   } else {
-    Consumer =
-        llvm::make_unique<PlainTextCompletionItemsCollector>(Opts, Results);
+    Consumer = llvm::make_unique<PlainTextCompletionItemsCollector>(
+        Opts, Results, SCInfo);
   }
   invokeCodeComplete(Ctx, std::move(Consumer), Opts.getClangCompleteOpts(),
                      FileName, Command, Preamble, Contents, Pos, std::move(VFS),
                      std::move(PCHs));
+  if (Opts.Index && SCInfo.SSInfo) {
+    // FIXME: log warning with logger if sema code completion have collected
+    // results.
+    Results.items.clear();
+    qualifiedIdCompletionWithIndex(Ctx, *Opts.Index, Contents, *SCInfo.SSInfo,
+                                   SCInfo.Filter, &Results);
+  }
   return Results;
 }
 
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -32,15 +32,8 @@
 
 namespace clang {
 class PCHContainerOperations;
-
 namespace clangd {
 
-/// Turn a [line, column] pair into an offset in Code.
-size_t positionToOffset(StringRef Code, Position P);
-
-/// Turn an offset in Code into a [line, column] pair.
-Position offsetToPosition(StringRef Code, size_t Offset);
-
 /// A tag supplied by the FileSytemProvider.
 typedef std::string VFSTag;
 
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -8,6 +8,7 @@
 //===-------------------------------------------------------------------===//
 
 #include "ClangdServer.h"
+#include "Position.h"
 #include "clang/Format/Format.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
@@ -57,29 +58,6 @@
 
 } // namespace
 
-size_t clangd::positionToOffset(StringRef Code, Position P) {
-  size_t Offset = 0;
-  for (int I = 0; I != P.line; ++I) {
-    // FIXME: \r\n
-    // FIXME: UTF-8
-    size_t F = Code.find('\n', Offset);
-    if (F == StringRef::npos)
-      return 0; // FIXME: Is this reasonable?
-    Offset = F + 1;
-  }
-  return (Offset == 0 ? 0 : (Offset - 1)) + P.character;
-}
-
-/// Turn an offset in Code into a [line, column] pair.
-Position clangd::offsetToPosition(StringRef Code, size_t Offset) {
-  StringRef JustBefore = Code.substr(0, Offset);
-  // FIXME: \r\n
-  // FIXME: UTF-8
-  int Lines = JustBefore.count('\n');
-  int Cols = JustBefore.size() - JustBefore.rfind('\n') - 1;
-  return {Lines, Cols};
-}
-
 Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
 RealFileSystemProvider::getTaggedFileSystem(PathRef File) {
   return make_tagged(vfs::getRealFileSystem(), VFSTag());
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -9,6 +9,7 @@
 
 #include "ClangdLSPServer.h"
 #include "JSONRPCDispatcher.h"
+#include "Position.h"
 #include "llvm/Support/FormatVariadic.h"
 
 using namespace clang::clangd;
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -16,6 +16,7 @@
   JSONExpr.cpp
   JSONRPCDispatcher.cpp
   Logger.cpp
+  Position.cpp
   Protocol.cpp
   ProtocolHandlers.cpp
   Trace.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to