Skip to content

Commit a542bd4

Browse files
committed
Split out some hover functions
1 parent c5b1e3f commit a542bd4

File tree

3 files changed

+230
-80
lines changed

3 files changed

+230
-80
lines changed

crates/ide/src/hover.rs

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -86,30 +86,38 @@ pub struct HoverResult {
8686
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
8787
pub(crate) fn hover(
8888
db: &RootDatabase,
89-
file_range: FileRange,
89+
frange @ FileRange { file_id, range }: FileRange,
9090
config: &HoverConfig,
9191
) -> Option<RangeInfo<HoverResult>> {
9292
let sema = &hir::Semantics::new(db);
93-
let mut res = hover_impl(sema, file_range, config)?;
93+
let file = sema.parse(file_id).syntax().clone();
94+
let mut res = if range.is_empty() {
95+
hover_simple(sema, FilePosition { file_id, offset: range.start() }, file, config)
96+
} else {
97+
hover_ranged(sema, frange, file, config)
98+
}?;
99+
94100
if let HoverDocFormat::PlainText = config.format {
95101
res.info.markup = remove_markdown(res.info.markup.as_str()).into();
96102
}
97103
Some(res)
98104
}
99105

100-
fn hover_impl(
106+
fn hover_simple(
101107
sema: &Semantics<'_, RootDatabase>,
102-
FileRange { file_id, range }: FileRange,
108+
FilePosition { file_id, offset }: FilePosition,
109+
file: SyntaxNode,
103110
config: &HoverConfig,
104111
) -> Option<RangeInfo<HoverResult>> {
105-
let file = sema.parse(file_id).syntax().clone();
106-
if !range.is_empty() {
107-
return hover_ranged(&file, range, sema, config);
108-
}
109-
let offset = range.start();
110-
111112
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
112-
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 4,
113+
IDENT
114+
| INT_NUMBER
115+
| LIFETIME_IDENT
116+
| T![self]
117+
| T![super]
118+
| T![crate]
119+
| T![Self]
120+
| T![_] => 4,
113121
// index and prefix ops
114122
T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
115123
kind if kind.is_keyword() => 2,
@@ -142,19 +150,18 @@ fn hover_impl(
142150
} else {
143151
sema.descend_into_macros_with_same_text(original_token.clone())
144152
};
153+
let descended = || descended.iter();
145154

146-
// try lint hover
147-
let result = descended
148-
.iter()
155+
let result = descended()
156+
// try lint hover
149157
.find_map(|token| {
150158
// FIXME: Definition should include known lints and the like instead of having this special case here
151159
let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
152160
render::try_for_lint(&attr, token)
153161
})
154-
// try item definitions
162+
// try definitions
155163
.or_else(|| {
156-
descended
157-
.iter()
164+
descended()
158165
.filter_map(|token| {
159166
let node = token.parent()?;
160167
let class = IdentClass::classify_token(sema, token)?;
@@ -175,10 +182,12 @@ fn hover_impl(
175182
})
176183
})
177184
// try keywords
178-
.or_else(|| descended.iter().find_map(|token| render::keyword(sema, config, token)))
179-
// try rest item hover
185+
.or_else(|| descended().find_map(|token| render::keyword(sema, config, token)))
186+
// try _ hovers
187+
.or_else(|| descended().find_map(|token| render::underscore(sema, config, token)))
188+
// try rest pattern hover
180189
.or_else(|| {
181-
descended.iter().find_map(|token| {
190+
descended().find_map(|token| {
182191
if token.kind() != DOT2 {
183192
return None;
184193
}
@@ -201,39 +210,13 @@ fn hover_impl(
201210
})
202211
// fallback to type hover if there aren't any other suggestions
203212
// this finds its own range instead of using the closest token's range
204-
.or_else(|| {
205-
descended.iter().find_map(|token| hover_type_fallback(sema, config, token, token))
206-
})
207-
}
208-
209-
pub(crate) fn hover_for_definition(
210-
sema: &Semantics<'_, RootDatabase>,
211-
file_id: FileId,
212-
definition: Definition,
213-
node: &SyntaxNode,
214-
config: &HoverConfig,
215-
) -> Option<HoverResult> {
216-
let famous_defs = match &definition {
217-
Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
218-
_ => None,
219-
};
220-
render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
221-
HoverResult {
222-
markup: render::process_markup(sema.db, definition, &markup, config),
223-
actions: show_implementations_action(sema.db, definition)
224-
.into_iter()
225-
.chain(show_fn_references_action(sema.db, definition))
226-
.chain(runnable_action(sema, definition, file_id))
227-
.chain(goto_type_action_for_def(sema.db, definition))
228-
.collect(),
229-
}
230-
})
213+
.or_else(|| descended().find_map(|token| hover_type_fallback(sema, config, token, token)))
231214
}
232215

233216
fn hover_ranged(
234-
file: &SyntaxNode,
235-
range: syntax::TextRange,
236217
sema: &Semantics<'_, RootDatabase>,
218+
FileRange { range, .. }: FileRange,
219+
file: SyntaxNode,
237220
config: &HoverConfig,
238221
) -> Option<RangeInfo<HoverResult>> {
239222
// FIXME: make this work in attributes
@@ -248,7 +231,7 @@ fn hover_ranged(
248231
}
249232
_ => None,
250233
};
251-
let res = res.or_else(|| render::type_info(sema, config, &expr_or_pat));
234+
let res = res.or_else(|| render::type_info_of(sema, config, &expr_or_pat));
252235
res.map(|it| {
253236
let range = match expr_or_pat {
254237
Either::Left(it) => it.syntax().text_range(),
@@ -258,6 +241,33 @@ fn hover_ranged(
258241
})
259242
}
260243

244+
pub(crate) fn hover_for_definition(
245+
sema: &Semantics<'_, RootDatabase>,
246+
file_id: FileId,
247+
definition: Definition,
248+
node: &SyntaxNode,
249+
config: &HoverConfig,
250+
) -> Option<HoverResult> {
251+
let famous_defs = match &definition {
252+
Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
253+
_ => None,
254+
};
255+
render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
256+
HoverResult {
257+
markup: render::process_markup(sema.db, definition, &markup, config),
258+
actions: [
259+
show_implementations_action(sema.db, definition),
260+
show_fn_references_action(sema.db, definition),
261+
runnable_action(sema, definition, file_id),
262+
goto_type_action_for_def(sema.db, definition),
263+
]
264+
.into_iter()
265+
.flatten()
266+
.collect(),
267+
}
268+
})
269+
}
270+
261271
fn hover_type_fallback(
262272
sema: &Semantics<'_, RootDatabase>,
263273
config: &HoverConfig,
@@ -282,7 +292,7 @@ fn hover_type_fallback(
282292
}
283293
};
284294

285-
let res = render::type_info(sema, config, &expr_or_pat)?;
295+
let res = render::type_info_of(sema, config, &expr_or_pat)?;
286296

287297
let range = sema
288298
.original_range_opt(&node)

crates/ide/src/hover/render.rs

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::{
2929
HoverAction, HoverConfig, HoverResult, Markup,
3030
};
3131

32-
pub(super) fn type_info(
32+
pub(super) fn type_info_of(
3333
sema: &Semantics<'_, RootDatabase>,
3434
_config: &HoverConfig,
3535
expr_or_pat: &Either<ast::Expr, ast::Pat>,
@@ -38,34 +38,7 @@ pub(super) fn type_info(
3838
Either::Left(expr) => sema.type_of_expr(expr)?,
3939
Either::Right(pat) => sema.type_of_pat(pat)?,
4040
};
41-
42-
let mut res = HoverResult::default();
43-
let mut targets: Vec<hir::ModuleDef> = Vec::new();
44-
let mut push_new_def = |item: hir::ModuleDef| {
45-
if !targets.contains(&item) {
46-
targets.push(item);
47-
}
48-
};
49-
walk_and_push_ty(sema.db, &original, &mut push_new_def);
50-
51-
res.markup = if let Some(adjusted_ty) = adjusted {
52-
walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
53-
let original = original.display(sema.db).to_string();
54-
let adjusted = adjusted_ty.display(sema.db).to_string();
55-
let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
56-
format!(
57-
"```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
58-
original,
59-
adjusted,
60-
apad = static_text_diff_len + adjusted.len().max(original.len()),
61-
opad = original.len(),
62-
)
63-
.into()
64-
} else {
65-
Markup::fenced_block(&original.display(sema.db))
66-
};
67-
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
68-
Some(res)
41+
type_info(sema, _config, original, adjusted)
6942
}
7043

7144
pub(super) fn try_expr(
@@ -217,6 +190,48 @@ pub(super) fn deref_expr(
217190
Some(res)
218191
}
219192

193+
pub(super) fn underscore(
194+
sema: &Semantics<'_, RootDatabase>,
195+
config: &HoverConfig,
196+
token: &SyntaxToken,
197+
) -> Option<HoverResult> {
198+
if token.kind() != T![_] {
199+
return None;
200+
}
201+
let parent = token.parent()?;
202+
let _it = match_ast! {
203+
match parent {
204+
ast::InferType(it) => it,
205+
ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it))),
206+
ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it))),
207+
_ => return None,
208+
}
209+
};
210+
// let it = infer_type.syntax().parent()?;
211+
// match_ast! {
212+
// match it {
213+
// ast::LetStmt(_it) => (),
214+
// ast::Param(_it) => (),
215+
// ast::RetType(_it) => (),
216+
// ast::TypeArg(_it) => (),
217+
218+
// ast::CastExpr(_it) => (),
219+
// ast::ParenType(_it) => (),
220+
// ast::TupleType(_it) => (),
221+
// ast::PtrType(_it) => (),
222+
// ast::RefType(_it) => (),
223+
// ast::ArrayType(_it) => (),
224+
// ast::SliceType(_it) => (),
225+
// ast::ForType(_it) => (),
226+
// _ => return None,
227+
// }
228+
// }
229+
230+
// FIXME: https://github.com/rust-lang/rust-analyzer/issues/11762, this currently always returns Unknown
231+
// type_info(sema, config, sema.resolve_type(&ast::Type::InferType(it))?, None)
232+
None
233+
}
234+
220235
pub(super) fn keyword(
221236
sema: &Semantics<'_, RootDatabase>,
222237
config: &HoverConfig,
@@ -458,6 +473,41 @@ pub(super) fn definition(
458473
markup(docs, label, mod_path)
459474
}
460475

476+
fn type_info(
477+
sema: &Semantics<'_, RootDatabase>,
478+
_config: &HoverConfig,
479+
original: hir::Type,
480+
adjusted: Option<hir::Type>,
481+
) -> Option<HoverResult> {
482+
let mut res = HoverResult::default();
483+
let mut targets: Vec<hir::ModuleDef> = Vec::new();
484+
let mut push_new_def = |item: hir::ModuleDef| {
485+
if !targets.contains(&item) {
486+
targets.push(item);
487+
}
488+
};
489+
walk_and_push_ty(sema.db, &original, &mut push_new_def);
490+
491+
res.markup = if let Some(adjusted_ty) = adjusted {
492+
walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
493+
let original = original.display(sema.db).to_string();
494+
let adjusted = adjusted_ty.display(sema.db).to_string();
495+
let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
496+
format!(
497+
"```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
498+
original,
499+
adjusted,
500+
apad = static_text_diff_len + adjusted.len().max(original.len()),
501+
opad = original.len(),
502+
)
503+
.into()
504+
} else {
505+
Markup::fenced_block(&original.display(sema.db))
506+
};
507+
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
508+
Some(res)
509+
}
510+
461511
fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
462512
let name = attr.name(db);
463513
let desc = format!("#[{name}]");

0 commit comments

Comments
 (0)