diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/hover.rs | 145 |
1 files changed, 133 insertions, 12 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 455a27e48..ed4f18e1f 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -6,12 +6,18 @@ use hir::{ | |||
6 | use ide_db::{ | 6 | use ide_db::{ |
7 | base_db::SourceDatabase, | 7 | base_db::SourceDatabase, |
8 | defs::{Definition, NameClass, NameRefClass}, | 8 | defs::{Definition, NameClass, NameRefClass}, |
9 | helpers::FamousDefs, | 9 | helpers::{ |
10 | generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, | ||
11 | FamousDefs, | ||
12 | }, | ||
10 | RootDatabase, | 13 | RootDatabase, |
11 | }; | 14 | }; |
12 | use itertools::Itertools; | 15 | use itertools::Itertools; |
13 | use stdx::format_to; | 16 | use stdx::format_to; |
14 | use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 17 | use syntax::{ |
18 | algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, TokenAtOffset, | ||
19 | T, | ||
20 | }; | ||
15 | 21 | ||
16 | use crate::{ | 22 | use crate::{ |
17 | display::{macro_label, TryToNav}, | 23 | display::{macro_label, TryToNav}, |
@@ -118,8 +124,9 @@ pub(crate) fn hover( | |||
118 | |d| d.defined(db), | 124 | |d| d.defined(db), |
119 | ), | 125 | ), |
120 | 126 | ||
121 | _ => ast::Comment::cast(token.clone()) | 127 | _ => { |
122 | .and_then(|_| { | 128 | if ast::Comment::cast(token.clone()).is_some() { |
129 | cov_mark::hit!(no_highlight_on_comment_hover); | ||
123 | let (attributes, def) = doc_attributes(&sema, &node)?; | 130 | let (attributes, def) = doc_attributes(&sema, &node)?; |
124 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 131 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
125 | let (idl_range, link, ns) = | 132 | let (idl_range, link, ns) = |
@@ -132,9 +139,13 @@ pub(crate) fn hover( | |||
132 | } | 139 | } |
133 | })?; | 140 | })?; |
134 | range = Some(idl_range); | 141 | range = Some(idl_range); |
135 | resolve_doc_path_for_def(db, def, &link, ns) | 142 | resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef) |
136 | }) | 143 | } else if let res@Some(_) = try_hover_for_attribute(&token) { |
137 | .map(Definition::ModuleDef), | 144 | return res; |
145 | } else { | ||
146 | None | ||
147 | } | ||
148 | }, | ||
138 | } | 149 | } |
139 | }; | 150 | }; |
140 | 151 | ||
@@ -168,11 +179,6 @@ pub(crate) fn hover( | |||
168 | } | 179 | } |
169 | } | 180 | } |
170 | 181 | ||
171 | if token.kind() == syntax::SyntaxKind::COMMENT { | ||
172 | cov_mark::hit!(no_highlight_on_comment_hover); | ||
173 | return None; | ||
174 | } | ||
175 | |||
176 | if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { | 182 | if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { |
177 | return res; | 183 | return res; |
178 | } | 184 | } |
@@ -201,6 +207,51 @@ pub(crate) fn hover( | |||
201 | Some(RangeInfo::new(range, res)) | 207 | Some(RangeInfo::new(range, res)) |
202 | } | 208 | } |
203 | 209 | ||
210 | fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> { | ||
211 | let attr = token.ancestors().nth(1).and_then(ast::Attr::cast)?; | ||
212 | let (path, tt) = attr.as_simple_call()?; | ||
213 | if !tt.syntax().text_range().contains(token.text_range().start()) { | ||
214 | return None; | ||
215 | } | ||
216 | let (is_clippy, lints) = match &*path { | ||
217 | "feature" => (false, FEATURES), | ||
218 | "allow" | "deny" | "forbid" | "warn" => { | ||
219 | let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev) | ||
220 | .filter(|t| t.kind() == T![:]) | ||
221 | .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) | ||
222 | .filter(|t| t.kind() == T![:]) | ||
223 | .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) | ||
224 | .map_or(false, |t| { | ||
225 | t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy") | ||
226 | }); | ||
227 | if is_clippy { | ||
228 | (true, CLIPPY_LINTS) | ||
229 | } else { | ||
230 | (false, DEFAULT_LINTS) | ||
231 | } | ||
232 | } | ||
233 | _ => return None, | ||
234 | }; | ||
235 | |||
236 | let tmp; | ||
237 | let needle = if is_clippy { | ||
238 | tmp = format!("clippy::{}", token.text()); | ||
239 | &tmp | ||
240 | } else { | ||
241 | &*token.text() | ||
242 | }; | ||
243 | |||
244 | let lint = | ||
245 | lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?; | ||
246 | Some(RangeInfo::new( | ||
247 | token.text_range(), | ||
248 | HoverResult { | ||
249 | markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)), | ||
250 | ..Default::default() | ||
251 | }, | ||
252 | )) | ||
253 | } | ||
254 | |||
204 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | 255 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { |
205 | fn to_action(nav_target: NavigationTarget) -> HoverAction { | 256 | fn to_action(nav_target: NavigationTarget) -> HoverAction { |
206 | HoverAction::Implementation(FilePosition { | 257 | HoverAction::Implementation(FilePosition { |
@@ -4004,4 +4055,74 @@ pub fn foo() {} | |||
4004 | "#]], | 4055 | "#]], |
4005 | ) | 4056 | ) |
4006 | } | 4057 | } |
4058 | |||
4059 | #[test] | ||
4060 | fn hover_feature() { | ||
4061 | check( | ||
4062 | r#"#![feature(box_syntax$0)]"#, | ||
4063 | expect![[r##" | ||
4064 | *box_syntax* | ||
4065 | ``` | ||
4066 | box_syntax | ||
4067 | ``` | ||
4068 | ___ | ||
4069 | |||
4070 | # `box_syntax` | ||
4071 | |||
4072 | The tracking issue for this feature is: [#49733] | ||
4073 | |||
4074 | [#49733]: https://github.com/rust-lang/rust/issues/49733 | ||
4075 | |||
4076 | See also [`box_patterns`](box-patterns.md) | ||
4077 | |||
4078 | ------------------------ | ||
4079 | |||
4080 | Currently the only stable way to create a `Box` is via the `Box::new` method. | ||
4081 | Also it is not possible in stable Rust to destructure a `Box` in a match | ||
4082 | pattern. The unstable `box` keyword can be used to create a `Box`. An example | ||
4083 | usage would be: | ||
4084 | |||
4085 | ```rust | ||
4086 | #![feature(box_syntax)] | ||
4087 | |||
4088 | fn main() { | ||
4089 | let b = box 5; | ||
4090 | } | ||
4091 | ``` | ||
4092 | |||
4093 | "##]], | ||
4094 | ) | ||
4095 | } | ||
4096 | |||
4097 | #[test] | ||
4098 | fn hover_lint() { | ||
4099 | check( | ||
4100 | r#"#![allow(arithmetic_overflow$0)]"#, | ||
4101 | expect![[r#" | ||
4102 | *arithmetic_overflow* | ||
4103 | ``` | ||
4104 | arithmetic_overflow | ||
4105 | ``` | ||
4106 | ___ | ||
4107 | |||
4108 | arithmetic operation overflows | ||
4109 | "#]], | ||
4110 | ) | ||
4111 | } | ||
4112 | |||
4113 | #[test] | ||
4114 | fn hover_clippy_lint() { | ||
4115 | check( | ||
4116 | r#"#![allow(clippy::almost_swapped$0)]"#, | ||
4117 | expect![[r#" | ||
4118 | *almost_swapped* | ||
4119 | ``` | ||
4120 | clippy::almost_swapped | ||
4121 | ``` | ||
4122 | ___ | ||
4123 | |||
4124 | Checks for `foo = bar; bar = foo` sequences. | ||
4125 | "#]], | ||
4126 | ) | ||
4127 | } | ||
4007 | } | 4128 | } |