Skip to content

Commit eb727c7

Browse files
bors[bot]Veykril
andauthored
Merge #10319
10319: internal: Cleanup hover a bit r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Wirth <[email protected]>
2 parents 88a214f + 42eb4ef commit eb727c7

File tree

7 files changed

+145
-173
lines changed

7 files changed

+145
-173
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir_def/src/attr.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ fn inner_attributes(
547547
Some((attrs, docs))
548548
}
549549

550+
#[derive(Debug)]
550551
pub struct AttrSourceMap {
551552
attrs: Vec<InFile<ast::Attr>>,
552553
doc_comments: Vec<InFile<ast::Comment>>,
@@ -599,6 +600,7 @@ impl AttrSourceMap {
599600
}
600601

601602
/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
603+
#[derive(Debug)]
602604
pub struct DocsRangeMap {
603605
source_map: AttrSourceMap,
604606
// (docstring-line-range, attr_index, attr-string-range)

crates/ide/src/goto_definition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub(crate) fn goto_definition(
6464
}
6565
}
6666
Some(
67-
Definition::from_node(&sema, &token)
67+
Definition::from_token(&sema, &token)
6868
.into_iter()
6969
.flat_map(|def| {
7070
try_find_trait_item_definition(sema.db, &def)

crates/ide/src/hover.rs

Lines changed: 67 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::HashSet, ops::ControlFlow};
1+
use std::iter;
22

33
use either::Either;
44
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
@@ -15,7 +15,7 @@ use itertools::Itertools;
1515
use stdx::format_to;
1616
use syntax::{
1717
algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, Direction, SyntaxKind::*,
18-
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
18+
SyntaxNode, SyntaxToken, T,
1919
};
2020

2121
use crate::{
@@ -99,7 +99,7 @@ pub(crate) fn hover(
9999
FileRange { file_id, range }: FileRange,
100100
config: &HoverConfig,
101101
) -> Option<RangeInfo<HoverResult>> {
102-
let sema = hir::Semantics::new(db);
102+
let sema = &hir::Semantics::new(db);
103103
let file = sema.parse(file_id).syntax().clone();
104104

105105
if !range.is_empty() {
@@ -114,145 +114,82 @@ pub(crate) fn hover(
114114
_ => 1,
115115
})?;
116116

117-
let mut seen = HashSet::default();
117+
let descended = sema.descend_into_macros_many(original_token.clone());
118118

119-
let mut fallback = None;
120-
// attributes, require special machinery as they are mere ident tokens
121-
122-
let descend_macros = sema.descend_into_macros_many(original_token.clone());
123-
124-
for token in &descend_macros {
125-
if token.kind() != COMMENT {
126-
if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
127-
// lints
128-
if let Some(res) = try_hover_for_lint(&attr, &token) {
129-
return Some(res);
130-
}
131-
}
132-
}
133-
}
134-
135-
descend_macros
136-
.iter()
137-
.filter_map(|token| match token.parent() {
138-
Some(node) => {
139-
match find_hover_result(
140-
&sema,
141-
file_id,
142-
offset,
143-
config,
144-
&original_token,
145-
token,
146-
&node,
147-
&mut seen,
148-
) {
149-
Some(res) => match res {
150-
ControlFlow::Break(inner) => Some(inner),
151-
ControlFlow::Continue(_) => {
152-
if fallback.is_none() {
153-
// FIXME we're only taking the first fallback into account that's not `None`
154-
fallback = hover_for_keyword(&sema, config, &token)
155-
.or(type_hover(&sema, config, &token));
156-
}
157-
None
158-
}
159-
},
160-
None => None,
161-
}
162-
}
163-
None => None,
164-
})
165-
// reduce all descends into a single `RangeInfo`
166-
// that spans from the earliest start to the latest end (fishy/FIXME),
167-
// concatenates all `Markup`s with `\n---\n`,
168-
// and accumulates all actions into its `actions` vector.
169-
.reduce(|mut acc, RangeInfo { range, mut info }| {
170-
let start = acc.range.start().min(range.start());
171-
let end = acc.range.end().max(range.end());
172-
173-
acc.range = TextRange::new(start, end);
174-
acc.info.actions.append(&mut info.actions);
175-
acc.info.markup = Markup::from(format!("{}\n---\n{}", acc.info.markup, info.markup));
176-
acc
177-
})
178-
.or(fallback)
179-
}
180-
181-
fn find_hover_result(
182-
sema: &Semantics<RootDatabase>,
183-
file_id: FileId,
184-
offset: TextSize,
185-
config: &HoverConfig,
186-
original_token: &SyntaxToken,
187-
token: &SyntaxToken,
188-
node: &SyntaxNode,
189-
seen: &mut HashSet<Definition>,
190-
) -> Option<ControlFlow<RangeInfo<HoverResult>>> {
191-
let mut range_override = None;
192-
193-
// intra-doc links and attributes are special cased
194-
// so don't add them to the `seen` duplicate check
195-
let mut add_to_seen_definitions = true;
196-
197-
let definition = Definition::from_node(sema, token).into_iter().next().or_else(|| {
119+
// FIXME handle doc attributes? TokenMap currently doesn't work with comments
120+
if original_token.kind() == COMMENT {
121+
let relative_comment_offset = offset - original_token.text_range().start();
198122
// intra-doc links
199-
// FIXME: move comment + attribute special cases somewhere else to simplify control flow,
200-
// hopefully simplifying the return type of this function in the process
201-
// (the `Break`/`Continue` distinction is needed to decide whether to use fallback hovers)
202-
//
203-
// FIXME: hovering the intra doc link to `Foo` not working:
204-
//
205-
// #[identity]
206-
// trait Foo {
207-
// /// [`Foo`]
208-
// fn foo() {}
209-
if token.kind() == COMMENT {
210-
add_to_seen_definitions = false;
211-
cov_mark::hit!(no_highlight_on_comment_hover);
212-
let (attributes, def) = doc_attributes(sema, node)?;
123+
cov_mark::hit!(no_highlight_on_comment_hover);
124+
return descended.iter().find_map(|t| {
125+
match t.kind() {
126+
COMMENT => (),
127+
TOKEN_TREE => {}
128+
_ => return None,
129+
}
130+
let node = t.parent()?;
131+
let absolute_comment_offset = t.text_range().start() + relative_comment_offset;
132+
let (attributes, def) = doc_attributes(sema, &node)?;
213133
let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?;
214134
let (idl_range, link, ns) = extract_definitions_from_docs(&docs).into_iter().find_map(
215135
|(range, link, ns)| {
216136
let mapped = doc_mapping.map(range)?;
217-
(mapped.file_id == file_id.into() && mapped.value.contains(offset))
218-
.then(|| (mapped.value, link, ns))
137+
(mapped.file_id == file_id.into()
138+
&& mapped.value.contains(absolute_comment_offset))
139+
.then(|| (mapped.value, link, ns))
219140
},
220141
)?;
221-
range_override = Some(idl_range);
222-
Some(match resolve_doc_path_for_def(sema.db, def, &link, ns)? {
142+
let def = match resolve_doc_path_for_def(sema.db, def, &link, ns)? {
223143
Either::Left(it) => Definition::ModuleDef(it),
224144
Either::Right(it) => Definition::Macro(it),
225-
})
226-
} else {
227-
None
228-
}
229-
});
145+
};
146+
let res = hover_for_definition(sema, file_id, def, &node, config)?;
147+
Some(RangeInfo::new(idl_range, res))
148+
});
149+
}
230150

231-
if let Some(definition) = definition {
232-
// skip duplicates
233-
if seen.contains(&definition) {
234-
return None;
235-
}
236-
if add_to_seen_definitions {
237-
seen.insert(definition);
151+
// attributes, require special machinery as they are mere ident tokens
152+
153+
// FIXME: Definition should include known lints and the like instead of having this special case here
154+
if let res @ Some(_) = descended.iter().find_map(|token| {
155+
let attr = token.ancestors().find_map(ast::Attr::cast)?;
156+
try_hover_for_lint(&attr, &token)
157+
}) {
158+
return res;
159+
}
160+
161+
let result = descended
162+
.iter()
163+
.filter_map(|token| {
164+
let node = token.parent()?;
165+
let defs = Definition::from_token(sema, token);
166+
Some(defs.into_iter().zip(iter::once(node).cycle()))
167+
})
168+
.flatten()
169+
.unique_by(|&(def, _)| def)
170+
.filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
171+
.reduce(|mut acc, HoverResult { markup, actions }| {
172+
acc.actions.extend(actions);
173+
acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
174+
acc
175+
});
176+
if result.is_none() {
177+
// fallbacks, show keywords or types
178+
if let res @ Some(_) = hover_for_keyword(sema, config, &original_token) {
179+
return res;
238180
}
239-
if let Some(res) = hover_for_definition(sema, file_id, definition, &node, config) {
240-
let range = range_override.unwrap_or_else(|| original_token.text_range());
241-
return Some(ControlFlow::Break(RangeInfo::new(range, res)));
181+
if let res @ Some(_) = descended.iter().find_map(|token| type_hover(sema, config, token)) {
182+
return res;
242183
}
243184
}
244-
245-
Some(ControlFlow::Continue(()))
185+
result.map(|res| RangeInfo::new(original_token.text_range(), res))
246186
}
247187

248188
fn type_hover(
249189
sema: &Semantics<RootDatabase>,
250190
config: &HoverConfig,
251191
token: &SyntaxToken,
252192
) -> Option<RangeInfo<HoverResult>> {
253-
if token.kind() == COMMENT {
254-
return None;
255-
}
256193
let node = token
257194
.ancestors()
258195
.take_while(|it| !ast::Item::can_cast(it.kind()))
@@ -3626,6 +3563,15 @@ fn main() {
36263563
```rust
36273564
f: &i32
36283565
```
3566+
---
3567+
3568+
```rust
3569+
test::S
3570+
```
3571+
3572+
```rust
3573+
f: i32
3574+
```
36293575
"#]],
36303576
);
36313577
}

crates/ide_db/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ rustc-hash = "1.1.0"
1717
once_cell = "1.3.1"
1818
either = "1.6.1"
1919
itertools = "0.10.0"
20+
arrayvec = "0.7"
2021

2122
stdx = { path = "../stdx", version = "0.0.0" }
2223
syntax = { path = "../syntax", version = "0.0.0" }

0 commit comments

Comments
 (0)