1- use std:: { collections :: HashSet , ops :: ControlFlow } ;
1+ use std:: iter ;
22
33use either:: Either ;
44use hir:: { AsAssocItem , HasAttrs , HasSource , HirDisplay , Semantics , TypeInfo } ;
@@ -15,7 +15,7 @@ use itertools::Itertools;
1515use stdx:: format_to;
1616use 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
2121use 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
248188fn 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 }
0 commit comments