77#include " Support/Ranges.h"
88#include " Feature/Hover.h"
99#include " Support/Logging.h"
10+ #include " llvm/Support/raw_ostream.h"
11+ #include " clang/Lex/Lexer.h"
12+ #include " clang/AST/ASTTypeTraits.h"
1013
1114namespace clice ::feature {
1215
1316namespace {
1417
15- std::vector<HoverItem> get_hover_items (CompilationUnit& unit,
16- const clang::NamedDecl* decl,
17- const config::HoverOptions& opt) {
18- clang::ASTContext& Ctx = unit.context ();
18+ static std::vector<HoverItem> get_hover_items (CompilationUnit& unit,
19+ const clang::NamedDecl* decl,
20+ const config::HoverOptions& opt) {
21+ clang::ASTContext& ctx = unit.context ();
1922 std::vector<HoverItem> items;
2023
21- auto addItem = [&items](HoverItem::HoverKind kind, uint32_t value ) {
22- items.emplace_back (kind, llvm::Twine (value). str () );
24+ auto add_item = [&items](HoverItem::HoverKind kind, std::string&& val ) {
25+ items.emplace_back (kind, val );
2326 };
2427
25- // / FIXME: Add other hover items.
26- if (auto FD = llvm::dyn_cast<clang::FieldDecl>(decl)) {
27- addItem (HoverItem::FieldIndex, FD->getFieldIndex ());
28- addItem (HoverItem::Offset, Ctx.getFieldOffset (FD));
29- addItem (HoverItem::Size, Ctx.getTypeSizeInChars (FD->getType ()).getQuantity ());
30- addItem (HoverItem::Align, Ctx.getTypeAlignInChars (FD->getType ()).getQuantity ());
31- if (FD->isBitField ()) {
32- // / FIXME:
33- // / addItem(HoverItem::BitWidth, FD->getBitWidthValue());
28+ // / TODO: Add other hover items.
29+ if (auto fd = llvm::dyn_cast<clang::FieldDecl>(decl)) {
30+ clice::logging::warn (" Got a field decl" );
31+ const auto record = fd->getParent ();
32+ add_item (HoverItem::Type, fd->getType ().getAsString ());
33+
34+ // / Remove in release mode
35+ // / add_item(HoverItem::FieldIndex, llvm::Twine(fd->getFieldIndex()).str());
36+ if (!record->isDependentType ()) {
37+ add_item (HoverItem::Offset, llvm::Twine (ctx.getFieldOffset (fd)).str ());
38+ add_item (HoverItem::Align,
39+ llvm::Twine (ctx.getTypeAlignInChars (fd->getType ()).getQuantity ()).str ());
40+ add_item (HoverItem::Size,
41+ llvm::Twine (ctx.getTypeSizeInChars (fd->getType ()).getQuantity ()).str ());
42+ }
43+
44+ if (record->isUnion ()) {
45+ add_item (HoverItem::Size,
46+ llvm::Twine (ctx.getTypeSizeInChars (fd->getType ()).getQuantity ()).str ());
47+ add_item (HoverItem::Align,
48+ llvm::Twine (ctx.getTypeAlignInChars (fd->getType ()).getQuantity ()).str ());
3449 }
50+
51+ if (fd->isBitField ()) {
52+ add_item (HoverItem::BitWidth, llvm::Twine (fd->getBitWidthValue ()).str ());
53+ clice::logging::warn (" Got bit field, name: {}, bitwidth: {}" ,
54+ fd->getName (),
55+ fd->getBitWidthValue ());
56+ }
57+ } else if (auto vd = llvm::dyn_cast<clang::VarDecl>(decl)) {
58+ clice::logging::warn (" Got a var decl" );
59+ add_item (HoverItem::Type, vd->getType ().getAsString ());
3560 }
3661
3762 return items;
3863}
3964
40- std::vector<HoverItem> get_hover_items (CompilationUnit& unit,
41- const clang::TypeLoc* typeloc,
42- const config::HoverOptions& opt) {
65+ static std::vector<HoverItem> get_hover_items (CompilationUnit& unit,
66+ const clang::TypeLoc* typeloc,
67+ const config::HoverOptions& opt) {
4368 return {};
4469}
4570
46- std::string getDocument (CompilationUnit& unit,
47- const clang::NamedDecl* decl,
48- config::HoverOptions opt) {
71+ static std::string get_document (CompilationUnit& unit,
72+ const clang::NamedDecl* decl,
73+ config::HoverOptions opt) {
4974 clang::ASTContext& Ctx = unit.context ();
5075 const clang::RawComment* comment = Ctx.getRawCommentForAnyRedecl (decl);
5176 if (!comment) {
5277 return " " ;
5378 }
54-
55- return comment-> getRawText (Ctx. getSourceManager ()). str () ;
79+ auto raw_string = comment-> getRawText (Ctx. getSourceManager ()). str ();
80+ return " " ;
5681}
5782
58- std::string getQualifier (CompilationUnit& unit,
59- const clang::NamedDecl* decl,
60- config::HoverOptions opt) {
83+ static std::string get_qualifier (CompilationUnit& unit,
84+ const clang::NamedDecl* decl,
85+ config::HoverOptions opt) {
6186 std::string result;
6287 llvm::raw_string_ostream os (result);
6388 decl->printNestedNameSpecifier (os);
6489 return result;
6590}
6691
67- std::string getSourceCode (CompilationUnit& unit,
68- const clang::NamedDecl* decl,
69- config::HoverOptions opt) {
70- clang::SourceRange range = decl->getSourceRange ();
71- // auto& TB = unit.tokBuf();
92+ // Get all source code
93+ static std::string get_source_code (CompilationUnit& unit, clang::SourceRange range) {
94+ clang::LangOptions lo;
7295 auto & sm = unit.context ().getSourceManager ();
73- // auto tokens = TB.expandedTokens(range);
74- // / FIXME: How to cut off the tokens?
75- return " " ;
96+ auto start_loc = sm.getSpellingLoc (range.getBegin ());
97+ auto last_token_loc = sm.getSpellingLoc (range.getEnd ());
98+ auto end_loc = clang::Lexer::getLocForEndOfToken (last_token_loc, 0 , sm, lo);
99+ return std::string{clang::Lexer::getSourceText (
100+ clang::CharSourceRange::getCharRange (clang::SourceRange{start_loc, end_loc}),
101+ sm,
102+ lo)};
76103}
77104
78- } // namespace
79-
80- static std::optional<clang::SourceLocation> src_loc_in_main_file (clang::SourceManager& sm,
81- uint32_t off) {
82- return sm.getLocForStartOfFile (sm.getMainFileID ()).getLocWithOffset (off);
105+ // TODO: How does clangd put together decl, name, scope and sometimes initialized value?
106+ // ```
107+ // // scope
108+ // <Access specifier> <type> <name> <initialized value>
109+ // ```
110+ static std::string get_source_code (CompilationUnit& unit,
111+ const clang::NamedDecl* decl,
112+ config::HoverOptions opt) {
113+ clang::SourceRange range = decl->getSourceRange ();
114+ return get_source_code (unit, range);
83115}
84116
85117static std::optional<Hover> hover (CompilationUnit& unit,
@@ -89,9 +121,9 @@ static std::optional<Hover> hover(CompilationUnit& unit,
89121 .kind = SymbolKind::from (decl),
90122 .name = ast::name_of (decl),
91123 .items = get_hover_items (unit, decl, opt),
92- .document = getDocument (unit, decl, opt),
93- .qualifier = getQualifier (unit, decl, opt),
94- .source = getSourceCode (unit, decl, opt),
124+ .document = get_document (unit, decl, opt),
125+ .qualifier = get_qualifier (unit, decl, opt),
126+ .source = get_source_code (unit, decl, opt),
95127 };
96128}
97129
@@ -100,57 +132,143 @@ static std::optional<Hover> hover(CompilationUnit& unit,
100132 const config::HoverOptions& opt) {
101133 // TODO: Hover for type
102134 clice::logging::warn (" Hit a typeloc" );
135+ typeloc->dump (llvm::errs (), unit.context ());
136+ auto ty = typeloc->getType ();
137+ return Hover{.kind = SymbolKind::Type, .name = ty.getAsString ()};
138+ }
139+
140+ static std::optional<Hover> hover (CompilationUnit& unit,
141+ const SelectionTree::Node* node,
142+ const config::HoverOptions& opt) {
143+ using namespace clang ;
144+ auto Kind = node->data .getNodeKind ();
145+ clice::logging::warn (" Node kind is: {}" , Kind.asStringRef ());
146+
147+ #define kind_flag_def (Ty ) static constexpr auto Flag##Ty = ASTNodeKind::getFromNodeKind<Ty>()
148+ kind_flag_def (QualType);
149+ kind_flag_def (TypeLoc);
150+ kind_flag_def (Decl);
151+ kind_flag_def (Stmt);
152+ kind_flag_def (Type);
153+ kind_flag_def (OMPClause);
154+ kind_flag_def (TemplateArgument);
155+ kind_flag_def (TemplateArgumentLoc);
156+ kind_flag_def (LambdaCapture);
157+ kind_flag_def (TemplateName);
158+ kind_flag_def (NestedNameSpecifierLoc);
159+ kind_flag_def (Attr);
160+ kind_flag_def (ObjCProtocolLoc);
161+
162+ #define is_in_range (LHS, RHS ) \
163+ (!((Kind < Flag##LHS) && (Kind.isSame (Flag##LHS))) && (Kind < Flag##RHS))
164+
165+ #define is (flag ) (Kind.isSame(Flag##flag))
166+
167+ if (is (NestedNameSpecifierLoc)) {
168+ clice::logging::warn (" Hit a `NestedNameSpecifierLoc`" );
169+ } else if (is_in_range (QualType, TypeLoc)) {
170+ // Typeloc
171+ clice::logging::warn (" Hit a `TypeLoc`" );
172+ if (auto typeloc = node->get <clang::TypeLoc>()) {
173+ return hover (unit, typeloc, opt);
174+ }
175+ } else if (is_in_range (Decl, Stmt)) {
176+ // Decl
177+ clice::logging::warn (" Hit a `Decl`" );
178+ if (auto decl = node->get <clang::NamedDecl>()) {
179+ return hover (unit, decl, opt);
180+ } else {
181+ clice::logging::warn (" Not intersted" );
182+ }
183+ } else if (is_in_range (Attr, ObjCProtocolLoc)) {
184+ clice::logging::warn (" Hit an `Attr`" );
185+ // TODO: Attr
186+ } else {
187+ // Not interested
188+ clice::logging::warn (" Not interested" );
189+ }
190+
191+ #undef is
192+ #undef is_in_range
193+ #undef kind_flag_def
194+
103195 return std::nullopt ;
104196}
105197
198+ } // namespace
199+
106200std::optional<Hover> hover (CompilationUnit& unit,
107201 std::uint32_t offset,
108202 const config::HoverOptions& opt) {
109203 auto & sm = unit.context ().getSourceManager ();
110- auto loc = src_loc_in_main_file (sm, offset);
204+
205+ auto src_loc_in_main_file = [&sm](uint32_t off) -> std::optional<clang::SourceLocation> {
206+ return sm.getLocForStartOfFile (sm.getMainFileID ()).getLocWithOffset (off);
207+ };
208+
209+ auto loc = src_loc_in_main_file (offset);
111210 if (!loc.has_value ()) {
211+ clice::logging::warn (" Invalid file offset, cannot get location" );
112212 return std::nullopt ;
113213 }
114214
115- auto tokens_under_cursor = unit.spelled_tokens_touch (*loc);
116- for (auto & tk: tokens_under_cursor) {
117- clice::logging::info (" Hit token '{}'" , tk.str ());
215+ // Handle includsions
216+ bool linenr_invalid = false ;
217+ unsigned linenr = sm.getPresumedLineNumber (*loc, &linenr_invalid);
218+ if (linenr_invalid) {
219+ clice::logging::warn (" Invalid location, cannot get linenr" );
220+ return std::nullopt ;
221+ }
222+ clice::logging::warn (" Hover at linenr: {}" , linenr);
223+ auto directive = unit.directives ()[sm.getMainFileID ()];
224+ for (auto & inclusion: directive.includes ) {
225+ bool invalid = false ;
226+ auto inc_linenr = sm.getPresumedLineNumber (inclusion.location , &invalid);
227+ if (!invalid && inc_linenr == linenr) {
228+ auto raw_name = get_source_code (unit, inclusion.filename_range );
229+ auto file_name = llvm::StringRef{raw_name}.trim (" <>\" " );
230+ Hover hi;
231+ hi.kind = SymbolKind::Directive;
232+ hi.name = file_name;
233+ auto dir = sm.getFileEntryForID (inclusion.fid )->tryGetRealPathName ();
234+ hi.source = dir;
235+ // TODO: Provides symbol
236+ return hi;
237+ }
118238 }
119-
120- // Find the token under cursor
121-
122- // TODO: Hover include and macros
123- // TODO: Range of highlighted tokens
124- // TODO: Handle `auto` and `decltype`
125239
126240 auto tree = SelectionTree::create_right (unit, {offset, offset});
127241 if (auto node = tree.common_ancestor ()) {
128- if (auto decl = node->get <clang::NamedDecl>()) {
129- return hover (unit, decl, opt);
130- } else if (auto ref = node->get <clang::DeclRefExpr>()) {
131- return hover (unit, ref->getDecl (), opt);
132- } else if (auto typeloc = node->get <clang::TypeLoc>()) {
133- return hover (unit, typeloc, opt);
134- }
135-
136- clice::logging::warn (" Not selected" );
137-
138- node->data .dump (llvm::errs (), unit.context ());
139-
140- // / FIXME: ...
141- // / - param var pointed at var decl, no param info
142-
143- // / TODO: add ....
144- // / not captured:
145- // / - TypeLocs
146- // / - Fields inside template
147- // / - NestedNameSpecifierLoc
148- // / - Template specification
242+ return hover (unit, node, opt);
149243 } else {
150244 clice::logging::warn (" Not an ast node" );
151245 }
152246
153247 return std::nullopt ;
154248}
155249
250+ std::optional<std::string> Hover::get_item_content (HoverItem::HoverKind kind) {
251+ for (auto & item: this ->items ) {
252+ if (item.kind == kind) {
253+ return item.value ;
254+ }
255+ }
256+ return std::nullopt ;
257+ }
258+
259+ std::optional<std::string> Hover::display (config::HoverOptions opt) {
260+ std::string content;
261+ llvm::raw_string_ostream os (content);
262+ os << std::format (" {}: {}\n " , this ->kind .name (), this ->name );
263+ os << std::format (" Contains {} items\n " , this ->items .size ());
264+ for (auto & hi: this ->items ) {
265+ os << std::format (" - {}: {}\n " , clice::refl::enum_name (hi.kind ), hi.value );
266+ }
267+ os << " ---\n " ;
268+ os << " Document:\n ```text\n " << this ->document << " \n ```\n " ;
269+ os << " ---\n " ;
270+ os << " Source code:\n ```cpp\n " << this ->source << " \n ```\n " ;
271+ return os.str ();
272+ }
273+
156274} // namespace clice::feature
0 commit comments