1 // Copyright 2017-2018 ccls Authors
2 // SPDX-License-Identifier: Apache-2.0
3
4 #include "message_handler.hh"
5 #include "query.hh"
6
7 namespace ccls {
8 namespace {
9 struct MarkedString {
10 std::optional<std::string> language;
11 std::string value;
12 };
13 struct Hover {
14 std::vector<MarkedString> contents;
15 std::optional<lsRange> range;
16 };
17
reflect(JsonWriter & vis,MarkedString & v)18 void reflect(JsonWriter &vis, MarkedString &v) {
19 // If there is a language, emit a `{language:string, value:string}` object. If
20 // not, emit a string.
21 if (v.language) {
22 vis.startObject();
23 REFLECT_MEMBER(language);
24 REFLECT_MEMBER(value);
25 vis.endObject();
26 } else {
27 reflect(vis, v.value);
28 }
29 }
30 REFLECT_STRUCT(Hover, contents, range);
31
languageIdentifier(LanguageId lang)32 const char *languageIdentifier(LanguageId lang) {
33 switch (lang) {
34 // clang-format off
35 case LanguageId::C: return "c";
36 case LanguageId::Cpp: return "cpp";
37 case LanguageId::ObjC: return "objective-c";
38 case LanguageId::ObjCpp: return "objective-cpp";
39 default: return "";
40 // clang-format on
41 }
42 }
43
44 // Returns the hover or detailed name for `sym`, if any.
45 std::pair<std::optional<MarkedString>, std::optional<MarkedString>>
getHover(DB * db,LanguageId lang,SymbolRef sym,int file_id)46 getHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) {
47 const char *comments = nullptr;
48 std::optional<MarkedString> ls_comments, hover;
49 withEntity(db, sym, [&](const auto &entity) {
50 for (auto &d : entity.def) {
51 if (!comments && d.comments[0])
52 comments = d.comments;
53 if (d.spell) {
54 if (d.comments[0])
55 comments = d.comments;
56 if (const char *s =
57 d.hover[0] ? d.hover
58 : d.detailed_name[0] ? d.detailed_name : nullptr) {
59 if (!hover)
60 hover = {languageIdentifier(lang), s};
61 else if (strlen(s) > hover->value.size())
62 hover->value = s;
63 }
64 if (d.spell->file_id == file_id)
65 break;
66 }
67 }
68 if (!hover && entity.def.size()) {
69 auto &d = entity.def[0];
70 hover = {languageIdentifier(lang)};
71 if (d.hover[0])
72 hover->value = d.hover;
73 else if (d.detailed_name[0])
74 hover->value = d.detailed_name;
75 }
76 if (comments)
77 ls_comments = MarkedString{std::nullopt, comments};
78 });
79 return {hover, ls_comments};
80 }
81 } // namespace
82
textDocument_hover(TextDocumentPositionParam & param,ReplyOnce & reply)83 void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m,
84 ReplyOnce &reply) {
85 auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
86 if (!wf)
87 return;
88
89 Hover result;
90 for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
91 std::optional<lsRange> ls_range =
92 getLsRange(wfiles->getFile(file->def->path), sym.range);
93 if (!ls_range)
94 continue;
95
96 auto [hover, comments] = getHover(db, file->def->language, sym, file->id);
97 if (comments || hover) {
98 result.range = *ls_range;
99 if (comments)
100 result.contents.push_back(*comments);
101 if (hover)
102 result.contents.push_back(*hover);
103 break;
104 }
105 }
106
107 reply(result);
108 }
109 } // namespace ccls
110