Skip to content

Commit 5045bce

Browse files
committed
Refactor hover
1 parent c5f0002 commit 5045bce

File tree

4 files changed

+370
-78
lines changed

4 files changed

+370
-78
lines changed

include/Feature/Hover.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "AST/SymbolKind.h"
44
#include "AST/SourceCode.h"
55
#include "Index/Shared.h"
6+
#include "Protocol/Basic.h"
67

78
namespace clice::config {
89

@@ -34,8 +35,8 @@ struct HoverItem {
3435
BitWidth,
3536
/// The index of a field in a class/struct.
3637
FieldIndex,
37-
/// The value of an enum item.
38-
EnumValue,
38+
/// The value of variable(on initialization / constant) | enum item
39+
Value,
3940
};
4041

4142
using enum HoverKind;
@@ -63,6 +64,14 @@ struct Hover {
6364

6465
/// The source code of the declaration.
6566
std::string source;
67+
68+
/// Highlight range
69+
std::optional<proto::Range> hl_range;
70+
71+
std::optional<std::string> get_item_content(HoverItem::HoverKind kind);
72+
73+
/// Return the markdown string of hover info
74+
std::optional<std::string> display(config::HoverOptions opt);
6675
};
6776

6877
// /// Generate the hover information for the given declaration(for test).

src/Feature/Hover.cpp

Lines changed: 191 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,79 +7,111 @@
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

1114
namespace clice::feature {
1215

1316
namespace {
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

85117
static 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+
106200
std::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

src/Server/Feature.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,19 @@ auto Server::on_hover(proto::HoverParams params) -> Result {
6363
.show_aka = true};
6464

6565
co_return co_await async::submit([kind = this->kind, offset, &ast, &opt] {
66-
auto hover = feature::hover(*ast, offset, opt);
67-
// TODO: Join comment with ast info, build structed text
6866
proto::Hover result;
6967
result.contents.kind = "markdown";
70-
result.contents.value = std::format("{}: {}", hover->kind.name(), hover->name);
68+
if(auto hover = feature::hover(*ast, offset, opt); hover) {
69+
if(auto info = hover->display(opt); info) {
70+
result.contents.value = *info;
71+
} else {
72+
clice::logging::warn("Cannot display hover info");
73+
result.contents.value = "Cannot display hover info";
74+
}
75+
} else {
76+
clice::logging::warn("Cannot get hover info");
77+
result.contents.value = "Cannot get hover info";
78+
}
7179
return json::serialize(result);
7280
});
7381
}

0 commit comments

Comments
 (0)