From b31a1c6b3ea71bc028ca8ddb28c9fba510b12fef Mon Sep 17 00:00:00 2001
From: Fangrui Song <i@maskray.me>
Date: Fri, 16 Nov 2018 15:23:26 -0800
Subject: [PATCH] hierarchicalDocumentSymbol: support SymbolKind::Function
 declaration and uniquify by range

Also ensure selectionRange is a subrange of range, otherwise VSCode won't show the item.
Use detailed_name for 'detail'
---
 src/lsp.hh                            |  3 +++
 src/messages/textDocument_document.cc | 25 ++++++++++++++++++++++---
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/src/lsp.hh b/src/lsp.hh
index 27c9727f..b6a60877 100644
--- a/src/lsp.hh
+++ b/src/lsp.hh
@@ -101,6 +101,9 @@ struct Position {
   bool operator<(const Position &o) const {
     return line != o.line ? line < o.line : character < o.character;
   }
+  bool operator<=(const Position &o) const {
+    return line != o.line ? line < o.line : character <= o.character;
+  }
   std::string ToString() const;
 };
 
diff --git a/src/messages/textDocument_document.cc b/src/messages/textDocument_document.cc
index 9135ce76..2ff5d218 100644
--- a/src/messages/textDocument_document.cc
+++ b/src/messages/textDocument_document.cc
@@ -137,6 +137,16 @@ template<>
 bool Ignore(const QueryVar::Def *def) {
   return !def || def->is_local();
 }
+
+void Uniquify(std::vector<std::unique_ptr<DocumentSymbol>> &cs) {
+  std::sort(cs.begin(), cs.end(),
+            [](auto &l, auto &r) { return l->range < r->range; });
+  cs.erase(std::unique(cs.begin(), cs.end(),
+                       [](auto &l, auto &r) { return l->range == r->range; }),
+           cs.end());
+  for (auto &c : cs)
+    Uniquify(c->children);
+}
 } // namespace
 
 void MessageHandler::textDocument_documentSymbol(Reader &reader,
@@ -177,8 +187,13 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
       if (auto range = GetLsRange(wfile, sym.range)) {
         ds->selectionRange = *range;
         ds->range = ds->selectionRange;
+        // For a macro expansion, M(name), we may use `M` for extent and `name`
+        // for spell, do the check as selectionRange must be a subrange of
+        // range.
         if (sym.extent.Valid())
-          if (auto range1 = GetLsRange(wfile, sym.extent))
+          if (auto range1 = GetLsRange(wfile, sym.extent);
+              range1 && range1->start <= range->start &&
+              range->end <= range1->end)
             ds->range = *range1;
       }
       std::vector<const void *> def_ptrs;
@@ -187,7 +202,7 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
         if (!def)
           return;
         ds->name = def->Name(false);
-        ds->detail = def->Name(true);
+        ds->detail = def->detailed_name;
         for (auto &def : entity.def)
           if (def.file_id == file_id && !Ignore(&def)) {
             ds->kind = def.kind;
@@ -196,6 +211,7 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
           }
       });
       if (!(param.all || sym.role & Role::Definition ||
+            ds->kind == SymbolKind::Function ||
             ds->kind == SymbolKind::Method ||
             ds->kind == SymbolKind::Namespace)) {
         ds.reset();
@@ -237,8 +253,11 @@ void MessageHandler::textDocument_documentSymbol(Reader &reader,
       }
     std::vector<std::unique_ptr<DocumentSymbol>> result;
     for (auto &[_, ds] : sym2ds)
-      if (ds)
+      if (ds) {
+        Uniquify(ds->children);
         result.push_back(std::move(ds));
+      }
+    Uniquify(result);
     reply(result);
   } else {
     std::vector<SymbolInformation> result;