Auto merge of #13638 - DesmondWillowbrook:hover-rest-pat-mvp, r=Veykril
feat: adds hover hint to ".." in record pattern Hovering on the "rest" pattern in struct destructuring, ```rust struct Baz { a: u32, b: u32, c: u32, d: u32 } let Baz { a, b, ..$0} = Baz { a: 1, b: 2, c: 3, d: 4 }; ``` shows: ``` .., c: u32, d: u32 ``` Currently only works with struct patterns. ![image](https://user-images.githubusercontent.com/51814158/202837115-f424cc26-c2d7-4027-8eea-eeb7749ad146.png)
This commit is contained in:
commit
6918009fea
@ -127,6 +127,7 @@ pub(crate) fn hover(
|
||||
original_token.parent().and_then(ast::TokenTree::cast),
|
||||
Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))
|
||||
);
|
||||
|
||||
// prefer descending the same token kind in attribute expansions, in normal macros text
|
||||
// equivalency is more important
|
||||
let descended = if in_attr {
|
||||
@ -135,16 +136,17 @@ pub(crate) fn hover(
|
||||
sema.descend_into_macros_with_same_text(original_token.clone())
|
||||
};
|
||||
|
||||
// try lint hover
|
||||
let result = descended
|
||||
.iter()
|
||||
.find_map(|token| {
|
||||
// FIXME: Definition should include known lints and the like instead of having this special case here
|
||||
let hovered_lint = descended.iter().find_map(|token| {
|
||||
let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
|
||||
render::try_for_lint(&attr, token)
|
||||
});
|
||||
if let Some(res) = hovered_lint {
|
||||
return Some(RangeInfo::new(original_token.text_range(), res));
|
||||
}
|
||||
|
||||
let result = descended
|
||||
})
|
||||
// try item definitions
|
||||
.or_else(|| {
|
||||
descended
|
||||
.iter()
|
||||
.filter_map(|token| {
|
||||
let node = token.parent()?;
|
||||
@ -163,26 +165,38 @@ pub(crate) fn hover(
|
||||
acc.actions.extend(actions);
|
||||
acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
|
||||
acc
|
||||
})
|
||||
})
|
||||
// try keywords
|
||||
.or_else(|| descended.iter().find_map(|token| render::keyword(sema, config, token)))
|
||||
// try rest item hover
|
||||
.or_else(|| {
|
||||
descended.iter().find_map(|token| {
|
||||
if token.kind() != DOT2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rest_pat = token.parent().and_then(ast::RestPat::cast)?;
|
||||
let record_pat_field_list =
|
||||
rest_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast)?;
|
||||
|
||||
let record_pat =
|
||||
record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?;
|
||||
|
||||
Some(render::struct_rest_pat(sema, config, &record_pat))
|
||||
})
|
||||
});
|
||||
|
||||
if result.is_none() {
|
||||
// fallbacks, show keywords or types
|
||||
|
||||
let res = descended.iter().find_map(|token| render::keyword(sema, config, token));
|
||||
if let Some(res) = res {
|
||||
return Some(RangeInfo::new(original_token.text_range(), res));
|
||||
}
|
||||
let res = descended
|
||||
.iter()
|
||||
.find_map(|token| hover_type_fallback(sema, config, token, &original_token));
|
||||
if let Some(_) = res {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
result.map(|mut res: HoverResult| {
|
||||
result
|
||||
.map(|mut res: HoverResult| {
|
||||
res.actions = dedupe_or_merge_hover_actions(res.actions);
|
||||
RangeInfo::new(original_token.text_range(), res)
|
||||
})
|
||||
// fallback to type hover if there aren't any other suggestions
|
||||
// this finds its own range instead of using the closest token's range
|
||||
.or_else(|| {
|
||||
descended.iter().find_map(|token| hover_type_fallback(sema, config, token, &token))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn hover_for_definition(
|
||||
@ -269,6 +283,7 @@ fn hover_type_fallback(
|
||||
};
|
||||
|
||||
let res = render::type_info(sema, config, &expr_or_pat)?;
|
||||
|
||||
let range = sema
|
||||
.original_range_opt(&node)
|
||||
.map(|frange| frange.range)
|
||||
|
@ -14,7 +14,9 @@ use ide_db::{
|
||||
use itertools::Itertools;
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
algo, ast, match_ast, AstNode, Direction,
|
||||
algo,
|
||||
ast::{self, RecordPat},
|
||||
match_ast, AstNode, Direction,
|
||||
SyntaxKind::{LET_EXPR, LET_STMT},
|
||||
SyntaxToken, T,
|
||||
};
|
||||
@ -250,6 +252,50 @@ pub(super) fn keyword(
|
||||
Some(HoverResult { markup, actions })
|
||||
}
|
||||
|
||||
/// Returns missing types in a record pattern.
|
||||
/// Only makes sense when there's a rest pattern in the record pattern.
|
||||
/// i.e. `let S {a, ..} = S {a: 1, b: 2}`
|
||||
pub(super) fn struct_rest_pat(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &HoverConfig,
|
||||
pattern: &RecordPat,
|
||||
) -> HoverResult {
|
||||
let missing_fields = sema.record_pattern_missing_fields(pattern);
|
||||
|
||||
// if there are no missing fields, the end result is a hover that shows ".."
|
||||
// should be left in to indicate that there are no more fields in the pattern
|
||||
// example, S {a: 1, b: 2, ..} when struct S {a: u32, b: u32}
|
||||
|
||||
let mut res = HoverResult::default();
|
||||
let mut targets: Vec<hir::ModuleDef> = Vec::new();
|
||||
let mut push_new_def = |item: hir::ModuleDef| {
|
||||
if !targets.contains(&item) {
|
||||
targets.push(item);
|
||||
}
|
||||
};
|
||||
for (_, t) in &missing_fields {
|
||||
walk_and_push_ty(sema.db, &t, &mut push_new_def);
|
||||
}
|
||||
|
||||
res.markup = {
|
||||
let mut s = String::from(".., ");
|
||||
for (f, _) in &missing_fields {
|
||||
s += f.display(sema.db).to_string().as_ref();
|
||||
s += ", ";
|
||||
}
|
||||
// get rid of trailing comma
|
||||
s.truncate(s.len() - 2);
|
||||
|
||||
if config.markdown() {
|
||||
Markup::fenced_block(&s)
|
||||
} else {
|
||||
s.into()
|
||||
}
|
||||
};
|
||||
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
|
||||
res
|
||||
}
|
||||
|
||||
pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<HoverResult> {
|
||||
let (path, tt) = attr.as_simple_call()?;
|
||||
if !tt.syntax().text_range().contains(token.text_range().start()) {
|
||||
|
@ -5307,3 +5307,38 @@ fn main() { $0V; }
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_rest_pat() {
|
||||
check(
|
||||
r#"
|
||||
struct Struct {a: u32, b: u32, c: u8, d: u16};
|
||||
|
||||
fn main() {
|
||||
let Struct {a, c, .$0.} = Struct {a: 1, b: 2, c: 3, d: 4};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*..*
|
||||
```rust
|
||||
.., b: u32, d: u16
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
|
||||
check(
|
||||
r#"
|
||||
struct Struct {a: u32, b: u32, c: u8, d: u16};
|
||||
|
||||
fn main() {
|
||||
let Struct {a, b, c, d, .$0.} = Struct {a: 1, b: 2, c: 3, d: 4};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
*..*
|
||||
```rust
|
||||
..
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user