dgoldman updated this revision to Diff 359878.
dgoldman added a comment.

Fetch marks from preamble as well

Move Range helper into SourceCode.h


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D105904/new/

https://reviews.llvm.org/D105904

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/FindSymbols.cpp
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/ParsedAST.h
  clang-tools-extra/clangd/Preamble.cpp
  clang-tools-extra/clangd/Preamble.h
  clang-tools-extra/clangd/SourceCode.cpp
  clang-tools-extra/clangd/SourceCode.h
  clang-tools-extra/clangd/TextMarks.cpp
  clang-tools-extra/clangd/TextMarks.h
  clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
  clang/include/clang/Lex/PPCallbacks.h

Index: clang/include/clang/Lex/PPCallbacks.h
===================================================================
--- clang/include/clang/Lex/PPCallbacks.h
+++ clang/include/clang/Lex/PPCallbacks.h
@@ -492,6 +492,11 @@
     Second->PragmaComment(Loc, Kind, Str);
   }
 
+  void PragmaMark(SourceLocation Loc, StringRef Trivia) override {
+    First->PragmaMark(Loc, Trivia);
+    Second->PragmaMark(Loc, Trivia);
+  }
+
   void PragmaDetectMismatch(SourceLocation Loc, StringRef Name,
                             StringRef Value) override {
     First->PragmaDetectMismatch(Loc, Name, Value);
Index: clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
+++ clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
@@ -1027,6 +1027,105 @@
                     AllOf(WithName("-pur"), WithKind(SymbolKind::Method))))));
 }
 
+TEST(DocumentSymbolsTest, PragmaMarkGroups) {
+  TestTU TU;
+  TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
+  Annotations Main(R"cpp(
+      $DogDef[[@interface Dog
+      @end]]
+
+      $DogImpl[[@implementation Dog
+
+      + (id)sharedDoggo { return 0; }
+
+      #pragma $Overrides[[mark - Overrides
+
+      - (id)init {
+        return self;
+      }
+      - (void)bark {}]]
+
+      #pragma $Specifics[[mark - Dog Specifics
+
+      - (int)isAGoodBoy {
+        return 1;
+      }]]
+      @]]end  // FIXME: Why doesn't this include the 'end'?
+
+      #pragma $End[[mark - End
+]]
+    )cpp");
+  TU.Code = Main.code().str();
+  EXPECT_THAT(
+      getSymbols(TU.build()),
+      ElementsAre(
+          AllOf(WithName("Dog"), SymRange(Main.range("DogDef"))),
+          AllOf(WithName("Dog"), SymRange(Main.range("DogImpl")),
+                Children(AllOf(WithName("+sharedDoggo"),
+                               WithKind(SymbolKind::Method)),
+                         AllOf(WithName("Overrides"),
+                               SymRange(Main.range("Overrides")),
+                               Children(AllOf(WithName("-init"),
+                                              WithKind(SymbolKind::Method)),
+                                        AllOf(WithName("-bark"),
+                                              WithKind(SymbolKind::Method)))),
+                         AllOf(WithName("Dog Specifics"),
+                               SymRange(Main.range("Specifics")),
+                               Children(AllOf(WithName("-isAGoodBoy"),
+                                              WithKind(SymbolKind::Method)))))),
+          AllOf(WithName("End"), SymRange(Main.range("End")))));
+}
+
+TEST(DocumentSymbolsTest, PragmaMarkGroupsNoNesting) {
+  TestTU TU;
+  TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
+  Annotations Main(R"cpp(
+      #pragma mark Helpers
+      void helpA(id obj) {}
+
+      #pragma mark -
+      #pragma mark Core
+
+      void coreMethod() {}
+    )cpp");
+  TU.Code = Main.code().str();
+  EXPECT_THAT(getSymbols(TU.build()),
+              ElementsAre(AllOf(WithName("Helpers")), AllOf(WithName("helpA")),
+                          AllOf(WithName("(unnamed group)")),
+                          AllOf(WithName("Core")),
+                          AllOf(WithName("coreMethod"))));
+}
+
+TEST(DocumentSymbolsTest, SymbolsAreSorted) {
+  TestTU TU;
+  TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
+  Annotations Main(R"cpp(
+      @interface MYObject
+      @end
+
+      void someFunctionAbove() {}
+
+      @implementation MYObject
+      - (id)init { return self; }
+
+      void someHelperFunction() {}
+
+      - (void)retain {}
+      - (void)release {}
+      @end
+    )cpp");
+  TU.Code = Main.code().str();
+  EXPECT_THAT(getSymbols(TU.build()),
+              ElementsAre(AllOf(WithName("MYObject")),
+                          AllOf(WithName("someFunctionAbove")),
+                          // FIXME: This should be nested under MYObject below.
+                          AllOf(WithName("someHelperFunction")),
+                          AllOf(WithName("MYObject"),
+                                Children(AllOf(WithName("-init")),
+                                         AllOf(WithName("-retain")),
+                                         AllOf(WithName("-release"))))));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/TextMarks.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/TextMarks.h
@@ -0,0 +1,73 @@
+//===--- TextMarks.h ---------------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_TEXTMARKS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_TEXTMARKS_H
+
+#include "AST.h"
+#include "Protocol.h"
+#include "SourceCode.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Lex/PPCallbacks.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+/// Represents a programmer specified mark/note, typically used to easily locate
+/// or differentiate code. e.g. a `pragma mark`, a `TODO`.
+///
+/// TextMarks occupy the entire line; it is not possible to have more than one
+/// `TextMark` per line.
+struct TextMark {
+  SourceLocation Loc;
+  std::string Text;
+  bool IsGroup;
+  DocumentSymbol toSymbol(const SourceManager &SM) const;
+};
+
+/// All `TextMarks` in the main file. Used to display the marks e.g. in the
+/// document symbols list.
+struct MainFileMarks {
+  /// Marks from the main file in order.
+  std::vector<TextMark> Marks;
+};
+
+/// Collect `#pragma mark` occurences in the main file.
+class CollectPragmaMarks : public PPCallbacks {
+public:
+  explicit CollectPragmaMarks(const SourceManager &SM, MainFileMarks &Out)
+      : SM(SM), Out(Out) {}
+  void PragmaMark(SourceLocation Loc, StringRef Trivia) override {
+    if (isInsideMainFile(Loc, SM)) {
+      StringRef Name = Trivia.trim();
+      bool IsGroup = false;
+      // "-\s+<group name>" or "<name>" after an initial trim. The former is
+      // considered a group, the latter just a mark. We need to include a name
+      // here since editors won't properly render the symbol otherwise.
+      StringRef MaybeGroupName = Name;
+      if (MaybeGroupName.consume_front("-") &&
+          (MaybeGroupName.ltrim() != MaybeGroupName ||
+           MaybeGroupName.empty())) {
+        Name =
+            MaybeGroupName.empty() ? "(unnamed group)" : MaybeGroupName.ltrim();
+        IsGroup = true;
+      } else if (Name.empty()) {
+        Name = "(unnamed mark)";
+      }
+      Out.Marks.emplace_back(TextMark{Loc, Name.str(), IsGroup});
+    }
+  }
+
+private:
+  const SourceManager &SM;
+  MainFileMarks &Out;
+};
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_TEXTMARKS_H
Index: clang-tools-extra/clangd/TextMarks.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/TextMarks.cpp
@@ -0,0 +1,28 @@
+//===--- TextMarks.cpp -------------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TextMarks.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace clangd {
+
+DocumentSymbol TextMark::toSymbol(const SourceManager &SM) const {
+  DocumentSymbol Sym;
+  Sym.name = Text;
+  Sym.kind = SymbolKind::File;
+  Position Start = sourceLocToPosition(SM, Loc);
+  Position End = {Start.line + 1, 0};
+  Sym.range = Range{Start, End};
+  Sym.selectionRange = Range{Start, End};
+  return Sym;
+}
+
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/SourceCode.h
===================================================================
--- clang-tools-extra/clangd/SourceCode.h
+++ clang-tools-extra/clangd/SourceCode.h
@@ -129,6 +129,9 @@
 // Note that clang also uses closed source ranges, which this can't handle!
 Range halfOpenToRange(const SourceManager &SM, CharSourceRange R);
 
+// Expand range `A` to also contain `B`.
+void unionRanges(Range &A, Range B);
+
 // Converts an offset to a clang line/column (1-based, columns are bytes).
 // The offset must be in range [0, Code.size()].
 // Prefer to use SourceManager if one is available.
Index: clang-tools-extra/clangd/SourceCode.cpp
===================================================================
--- clang-tools-extra/clangd/SourceCode.cpp
+++ clang-tools-extra/clangd/SourceCode.cpp
@@ -471,6 +471,13 @@
   return {Begin, End};
 }
 
+void unionRanges(Range &A, Range B) {
+  if (B.start < A.start)
+    A.start = B.start;
+  if (A.end < B.end)
+    A.end = B.end;
+}
+
 std::pair<size_t, size_t> offsetToClangLineColumn(llvm::StringRef Code,
                                                   size_t Offset) {
   Offset = std::min(Code.size(), Offset);
Index: clang-tools-extra/clangd/Preamble.h
===================================================================
--- clang-tools-extra/clangd/Preamble.h
+++ clang-tools-extra/clangd/Preamble.h
@@ -27,6 +27,7 @@
 #include "Diagnostics.h"
 #include "FS.h"
 #include "Headers.h"
+#include "TextMarks.h"
 #include "index/CanonicalIncludes.h"
 #include "support/Path.h"
 #include "clang/Frontend/CompilerInvocation.h"
@@ -49,7 +50,7 @@
 struct PreambleData {
   PreambleData(const ParseInputs &Inputs, PrecompiledPreamble Preamble,
                std::vector<Diag> Diags, IncludeStructure Includes,
-               MainFileMacros Macros,
+               MainFileMacros Macros, MainFileMarks Marks,
                std::unique_ptr<PreambleFileStatusCache> StatCache,
                CanonicalIncludes CanonIncludes);
 
@@ -65,6 +66,8 @@
   // Users care about headers vs main-file, not preamble vs non-preamble.
   // These should be treated as main-file entities e.g. for code completion.
   MainFileMacros Macros;
+  // TextMarks defined in the preamble section of the main file.
+  MainFileMarks Marks;
   // Cache of FS operations performed when building the preamble.
   // When reusing a preamble, this cache can be consumed to save IO.
   std::unique_ptr<PreambleFileStatusCache> StatCache;
Index: clang-tools-extra/clangd/Preamble.cpp
===================================================================
--- clang-tools-extra/clangd/Preamble.cpp
+++ clang-tools-extra/clangd/Preamble.cpp
@@ -10,6 +10,7 @@
 #include "Compiler.h"
 #include "Headers.h"
 #include "SourceCode.h"
+#include "TextMarks.h"
 #include "support/Logger.h"
 #include "support/ThreadsafeFS.h"
 #include "support/Trace.h"
@@ -73,6 +74,8 @@
 
   MainFileMacros takeMacros() { return std::move(Macros); }
 
+  MainFileMarks takeMarks() { return std::move(Marks); }
+
   CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
 
   void AfterExecute(CompilerInstance &CI) override {
@@ -94,7 +97,9 @@
 
     return std::make_unique<PPChainedCallbacks>(
         collectIncludeStructureCallback(*SourceMgr, &Includes),
-        std::make_unique<CollectMainFileMacros>(*SourceMgr, Macros));
+        std::make_unique<PPChainedCallbacks>(
+            std::make_unique<CollectMainFileMacros>(*SourceMgr, Macros),
+            std::make_unique<CollectPragmaMarks>(*SourceMgr, Marks)));
   }
 
   CommentHandler *getCommentHandler() override {
@@ -121,6 +126,7 @@
   IncludeStructure Includes;
   CanonicalIncludes CanonIncludes;
   MainFileMacros Macros;
+  MainFileMarks Marks;
   std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
   const clang::LangOptions *LangOpts = nullptr;
   const SourceManager *SourceMgr = nullptr;
@@ -305,14 +311,14 @@
 PreambleData::PreambleData(const ParseInputs &Inputs,
                            PrecompiledPreamble Preamble,
                            std::vector<Diag> Diags, IncludeStructure Includes,
-                           MainFileMacros Macros,
+                           MainFileMacros Macros, MainFileMarks Marks,
                            std::unique_ptr<PreambleFileStatusCache> StatCache,
                            CanonicalIncludes CanonIncludes)
     : Version(Inputs.Version), CompileCommand(Inputs.CompileCommand),
       Preamble(std::move(Preamble)), Diags(std::move(Diags)),
       Includes(std::move(Includes)), Macros(std::move(Macros)),
-      StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
-}
+      Marks(std::move(Marks)), StatCache(std::move(StatCache)),
+      CanonIncludes(std::move(CanonIncludes)) {}
 
 std::shared_ptr<const PreambleData>
 buildPreamble(PathRef FileName, CompilerInvocation CI,
@@ -387,7 +393,8 @@
     return std::make_shared<PreambleData>(
         Inputs, std::move(*BuiltPreamble), std::move(Diags),
         SerializedDeclsCollector.takeIncludes(),
-        SerializedDeclsCollector.takeMacros(), std::move(StatCache),
+        SerializedDeclsCollector.takeMacros(),
+        SerializedDeclsCollector.takeMarks(), std::move(StatCache),
         SerializedDeclsCollector.takeCanonicalIncludes());
   } else {
     elog("Could not build a preamble for file {0} version {1}: {2}", FileName,
Index: clang-tools-extra/clangd/ParsedAST.h
===================================================================
--- clang-tools-extra/clangd/ParsedAST.h
+++ clang-tools-extra/clangd/ParsedAST.h
@@ -25,6 +25,7 @@
 #include "Diagnostics.h"
 #include "Headers.h"
 #include "Preamble.h"
+#include "TextMarks.h"
 #include "index/CanonicalIncludes.h"
 #include "support/Path.h"
 #include "clang/Frontend/FrontendAction.h"
@@ -101,6 +102,8 @@
   /// Gets all macro references (definition, expansions) present in the main
   /// file, including those in the preamble region.
   const MainFileMacros &getMacros() const;
+  /// Gets all textual marks in the main file.
+  const MainFileMarks &getMarks() const;
   /// Tokens recorded while parsing the main file.
   /// (!) does not have tokens from the preamble.
   const syntax::TokenBuffer &getTokens() const { return Tokens; }
@@ -121,7 +124,8 @@
             std::shared_ptr<const PreambleData> Preamble,
             std::unique_ptr<CompilerInstance> Clang,
             std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens,
-            MainFileMacros Macros, std::vector<Decl *> LocalTopLevelDecls,
+            MainFileMacros Macros, MainFileMarks Marks,
+            std::vector<Decl *> LocalTopLevelDecls,
             llvm::Optional<std::vector<Diag>> Diags, IncludeStructure Includes,
             CanonicalIncludes CanonIncludes);
 
@@ -144,6 +148,8 @@
 
   /// All macro definitions and expansions in the main file.
   MainFileMacros Macros;
+  // Textual marks in the main file (e.g. pragma marks, TODOs).
+  MainFileMarks Marks;
   // Data, stored after parsing. None if AST was built with a stale preamble.
   llvm::Optional<std::vector<Diag>> Diags;
   // Top-level decls inside the current file. Not that this does not include
Index: clang-tools-extra/clangd/ParsedAST.cpp
===================================================================
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -21,6 +21,7 @@
 #include "IncludeFixer.h"
 #include "Preamble.h"
 #include "SourceCode.h"
+#include "TextMarks.h"
 #include "TidyProvider.h"
 #include "index/CanonicalIncludes.h"
 #include "index/Index.h"
@@ -426,6 +427,12 @@
       std::make_unique<CollectMainFileMacros>(Clang->getSourceManager(),
                                               Macros));
 
+  MainFileMarks Marks;
+  if (Preamble)
+    Marks = Preamble->Marks;
+  Clang->getPreprocessor().addPPCallbacks(
+      std::make_unique<CollectPragmaMarks>(Clang->getSourceManager(), Marks));
+
   // Copy over the includes from the preamble, then combine with the
   // non-preamble includes below.
   CanonicalIncludes CanonIncludes;
@@ -487,7 +494,7 @@
   }
   return ParsedAST(Inputs.Version, std::move(Preamble), std::move(Clang),
                    std::move(Action), std::move(Tokens), std::move(Macros),
-                   std::move(ParsedDecls), std::move(Diags),
+                   std::move(Marks), std::move(ParsedDecls), std::move(Diags),
                    std::move(Includes), std::move(CanonIncludes));
 }
 
@@ -527,6 +534,7 @@
 }
 
 const MainFileMacros &ParsedAST::getMacros() const { return Macros; }
+const MainFileMarks &ParsedAST::getMarks() const { return Marks; }
 
 std::size_t ParsedAST::getUsedBytes() const {
   auto &AST = getASTContext();
@@ -573,12 +581,14 @@
                      std::unique_ptr<CompilerInstance> Clang,
                      std::unique_ptr<FrontendAction> Action,
                      syntax::TokenBuffer Tokens, MainFileMacros Macros,
+                     MainFileMarks Marks,
                      std::vector<Decl *> LocalTopLevelDecls,
                      llvm::Optional<std::vector<Diag>> Diags,
                      IncludeStructure Includes, CanonicalIncludes CanonIncludes)
     : Version(Version), Preamble(std::move(Preamble)), Clang(std::move(Clang)),
       Action(std::move(Action)), Tokens(std::move(Tokens)),
-      Macros(std::move(Macros)), Diags(std::move(Diags)),
+      Macros(std::move(Macros)), Marks(std::move(Marks)),
+      Diags(std::move(Diags)),
       LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
       Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) {
   Resolver = std::make_unique<HeuristicResolver>(getASTContext());
Index: clang-tools-extra/clangd/FindSymbols.cpp
===================================================================
--- clang-tools-extra/clangd/FindSymbols.cpp
+++ clang-tools-extra/clangd/FindSymbols.cpp
@@ -289,6 +289,10 @@
 ///    - visiting decls is actually simple, so we don't hit the complicated
 ///      cases that RAV mostly helps with (types, expressions, etc.)
 class DocumentOutline {
+  struct TextMarkSymbol {
+    DocumentSymbol DocSym;
+    bool IsGroup;
+  };
   // A DocumentSymbol we're constructing.
   // We use this instead of DocumentSymbol directly so that we can keep track
   // of the nodes we insert for macros.
@@ -303,16 +307,99 @@
     DocumentSymbol build() && {
       for (SymBuilder &C : Children) {
         Symbol.children.push_back(std::move(C).build());
+      }
+      return std::move(Symbol);
+    }
+
+    /// Fix up ranges so each parent's range contains all of its descendants.
+    void standardizeRanges() {
+      for (SymBuilder &C : Children) {
+        C.standardizeRanges();
         // Expand range to ensure children nest properly, which editors expect.
         // This can fix some edge-cases in the AST, but is vital for macros.
         // A macro expansion "contains" AST node if it covers the node's primary
         // location, but it may not span the node's whole range.
-        Symbol.range.start =
-            std::min(Symbol.range.start, Symbol.children.back().range.start);
-        Symbol.range.end =
-            std::max(Symbol.range.end, Symbol.children.back().range.end);
+        Symbol.range.start = std::min(Symbol.range.start, C.Symbol.range.start);
+        Symbol.range.end = std::max(Symbol.range.end, C.Symbol.range.end);
+      }
+      // FIXME: We should also make sure nodes are properly sorted and nested
+      // here.
+    }
+
+    /// Merge in `TextMarks` which are sorted in descending order by line.
+    ///
+    /// This assumes this `SymBuilder` has standardized ranges and is
+    /// recursively sorted in ascending order by range.
+    void mergeTextMarks(std::vector<TextMarkSymbol> &TextMarks, bool IsRoot) {
+      size_t LastGroupIndex = 0;
+      size_t ChildIndex = 0;
+      bool HasOpenGroup = false;
+      while (!TextMarks.empty()) {
+        const auto &TextMark = TextMarks.back();
+        // If we don't contain the mark, we can break early, since it must
+        // be in a parent node instead. Note that we can still have children
+        // that need to be added to the last TextMark group though.
+        if (!Symbol.range.contains(TextMark.DocSym.range) && !IsRoot)
+          break;
+
+        while (ChildIndex < Children.size()) {
+          auto &C = Children[ChildIndex];
+
+          // Next mark is contained in the child, we need to recurse to let the
+          // child handle it.
+          if (C.Symbol.range.contains(TextMark.DocSym.range)) {
+            C.mergeTextMarks(TextMarks, /** IsRoot= */ false);
+            break;
+          }
+          // Text mark is after the child node, so we can process the child.
+          if (C.Symbol.range < TextMark.DocSym.range) {
+            if (HasOpenGroup) { // Child belongs to the previous TextMark group.
+              Children[LastGroupIndex].Children.push_back(C);
+              unionRanges(Children[LastGroupIndex].Symbol.range,
+                          C.Symbol.range);
+              Children.erase(Children.begin() + ChildIndex);
+            } else { // Child doesn't belong to a TextMark group. Skip it.
+              ChildIndex++;
+            }
+            continue;
+          }
+          // Text mark is before the child node.
+          SymBuilder Node;
+          Node.EnclosingMacroLoc = C.EnclosingMacroLoc;
+          Node.Symbol = TextMark.DocSym;
+          Children.insert(Children.begin() + ChildIndex, Node);
+          if (TextMark.IsGroup) {
+            LastGroupIndex = ChildIndex;
+            HasOpenGroup = true;
+          } else { // New text mark ends the previous group if one existed.
+            // FIXME: Consider letting non-groups nest inside a group.
+            HasOpenGroup = false;
+          }
+          TextMarks.pop_back();
+          ChildIndex++;
+          break;
+        }
+        // Reached the end of our children but still have TextMark(s) left,
+        // they should just be normal children.
+        if (ChildIndex == Children.size()) {
+          SymBuilder Node;
+          Node.EnclosingMacroLoc = EnclosingMacroLoc;
+          Node.Symbol = TextMark.DocSym;
+          Children.push_back(Node);
+          ChildIndex++;
+          TextMarks.pop_back();
+        }
+      }
+      // If we have a group open but still have children remaining, they belong
+      // to that group instead.
+      if (HasOpenGroup && ChildIndex < Children.size()) {
+        while (ChildIndex < Children.size()) {
+          auto &C = Children[ChildIndex];
+          Children[LastGroupIndex].Children.push_back(C);
+          unionRanges(Children[LastGroupIndex].Symbol.range, C.Symbol.range);
+          Children.erase(Children.begin() + ChildIndex);
+        }
       }
-      return std::move(Symbol);
     }
 
     // Add a symbol as a child of the current one.
@@ -379,6 +466,19 @@
     SymBuilder Root;
     for (auto &TopLevel : AST.getLocalTopLevelDecls())
       traverseDecl(TopLevel, Root);
+
+    // We need to fix up the ranges before adding TextMarks to be sure we add
+    // them in the correct spots.
+    Root.standardizeRanges();
+
+    const auto &SM = AST.getSourceManager();
+    const auto &TextMarks = AST.getMarks().Marks;
+    std::vector<TextMarkSymbol> TextMarkSyms;
+    for (auto It = TextMarks.rbegin(); It != TextMarks.rend(); ++It) {
+      TextMarkSyms.push_back({It->toSymbol(SM), It->IsGroup});
+    }
+    Root.mergeTextMarks(TextMarkSyms, /** IsRoot= */ true);
+
     return std::move(std::move(Root).build().children);
   }
 
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -97,6 +97,7 @@
   SemanticSelection.cpp
   SourceCode.cpp
   QueryDriverDatabase.cpp
+  TextMarks.cpp
   TidyProvider.cpp
   TUScheduler.cpp
   URI.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to