diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/hover.rs | 276 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/rename.rs | 46 | ||||
-rw-r--r-- | crates/ide/src/typing.rs | 36 |
4 files changed, 205 insertions, 161 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 05a2b1293..c6d6bb74a 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,8 +1,5 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::{ | 2 | use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; |
3 | AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module, | ||
4 | ModuleDef, Semantics, | ||
5 | }; | ||
6 | use ide_db::{ | 3 | use ide_db::{ |
7 | base_db::SourceDatabase, | 4 | base_db::SourceDatabase, |
8 | defs::{Definition, NameClass, NameRefClass}, | 5 | defs::{Definition, NameClass, NameRefClass}, |
@@ -33,37 +30,20 @@ use crate::{ | |||
33 | 30 | ||
34 | #[derive(Clone, Debug, PartialEq, Eq)] | 31 | #[derive(Clone, Debug, PartialEq, Eq)] |
35 | pub struct HoverConfig { | 32 | pub struct HoverConfig { |
36 | pub implementations: bool, | ||
37 | pub references: bool, | ||
38 | pub run: bool, | ||
39 | pub debug: bool, | ||
40 | pub goto_type_def: bool, | ||
41 | pub links_in_hover: bool, | 33 | pub links_in_hover: bool, |
42 | pub markdown: bool, | 34 | pub documentation: Option<HoverDocFormat>, |
43 | } | 35 | } |
44 | 36 | ||
45 | impl HoverConfig { | 37 | impl HoverConfig { |
46 | pub const NO_ACTIONS: Self = Self { | 38 | fn markdown(&self) -> bool { |
47 | implementations: false, | 39 | matches!(self.documentation, Some(HoverDocFormat::Markdown)) |
48 | references: false, | ||
49 | run: false, | ||
50 | debug: false, | ||
51 | goto_type_def: false, | ||
52 | links_in_hover: true, | ||
53 | markdown: true, | ||
54 | }; | ||
55 | |||
56 | pub fn any(&self) -> bool { | ||
57 | self.implementations || self.references || self.runnable() || self.goto_type_def | ||
58 | } | ||
59 | |||
60 | pub fn none(&self) -> bool { | ||
61 | !self.any() | ||
62 | } | 40 | } |
41 | } | ||
63 | 42 | ||
64 | pub fn runnable(&self) -> bool { | 43 | #[derive(Clone, Debug, PartialEq, Eq)] |
65 | self.run || self.debug | 44 | pub enum HoverDocFormat { |
66 | } | 45 | Markdown, |
46 | PlainText, | ||
67 | } | 47 | } |
68 | 48 | ||
69 | #[derive(Debug, Clone)] | 49 | #[derive(Debug, Clone)] |
@@ -96,10 +76,9 @@ pub struct HoverResult { | |||
96 | pub(crate) fn hover( | 76 | pub(crate) fn hover( |
97 | db: &RootDatabase, | 77 | db: &RootDatabase, |
98 | position: FilePosition, | 78 | position: FilePosition, |
99 | links_in_hover: bool, | 79 | config: &HoverConfig, |
100 | markdown: bool, | ||
101 | ) -> Option<RangeInfo<HoverResult>> { | 80 | ) -> Option<RangeInfo<HoverResult>> { |
102 | let sema = Semantics::new(db); | 81 | let sema = hir::Semantics::new(db); |
103 | let file = sema.parse(position.file_id).syntax().clone(); | 82 | let file = sema.parse(position.file_id).syntax().clone(); |
104 | let token = pick_best(file.token_at_offset(position.offset))?; | 83 | let token = pick_best(file.token_at_offset(position.offset))?; |
105 | let token = sema.descend_into_macros(token); | 84 | let token = sema.descend_into_macros(token); |
@@ -131,7 +110,7 @@ pub(crate) fn hover( | |||
131 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 110 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
132 | let (idl_range, link, ns) = | 111 | let (idl_range, link, ns) = |
133 | extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { | 112 | extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { |
134 | let InFile { file_id, value: range } = doc_mapping.map(range)?; | 113 | let hir::InFile { file_id, value: range } = doc_mapping.map(range)?; |
135 | if file_id == position.file_id.into() && range.contains(position.offset) { | 114 | if file_id == position.file_id.into() && range.contains(position.offset) { |
136 | Some((range, link, ns)) | 115 | Some((range, link, ns)) |
137 | } else { | 116 | } else { |
@@ -151,13 +130,13 @@ pub(crate) fn hover( | |||
151 | 130 | ||
152 | if let Some(definition) = definition { | 131 | if let Some(definition) = definition { |
153 | let famous_defs = match &definition { | 132 | let famous_defs = match &definition { |
154 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => { | 133 | Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => { |
155 | Some(FamousDefs(&sema, sema.scope(&node).krate())) | 134 | Some(FamousDefs(&sema, sema.scope(&node).krate())) |
156 | } | 135 | } |
157 | _ => None, | 136 | _ => None, |
158 | }; | 137 | }; |
159 | if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) { | 138 | if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref(), config) { |
160 | res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown); | 139 | res.markup = process_markup(sema.db, definition, &markup, config); |
161 | if let Some(action) = show_implementations_action(db, definition) { | 140 | if let Some(action) = show_implementations_action(db, definition) { |
162 | res.actions.push(action); | 141 | res.actions.push(action); |
163 | } | 142 | } |
@@ -179,7 +158,7 @@ pub(crate) fn hover( | |||
179 | } | 158 | } |
180 | } | 159 | } |
181 | 160 | ||
182 | if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { | 161 | if let res @ Some(_) = hover_for_keyword(&sema, config, &token) { |
183 | return res; | 162 | return res; |
184 | } | 163 | } |
185 | 164 | ||
@@ -198,7 +177,7 @@ pub(crate) fn hover( | |||
198 | } | 177 | } |
199 | }; | 178 | }; |
200 | 179 | ||
201 | res.markup = if markdown { | 180 | res.markup = if config.markdown() { |
202 | Markup::fenced_block(&ty.display(db)) | 181 | Markup::fenced_block(&ty.display(db)) |
203 | } else { | 182 | } else { |
204 | ty.display(db).to_string().into() | 183 | ty.display(db).to_string().into() |
@@ -261,8 +240,10 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov | |||
261 | } | 240 | } |
262 | 241 | ||
263 | let adt = match def { | 242 | let adt = match def { |
264 | Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action), | 243 | Definition::ModuleDef(hir::ModuleDef::Trait(it)) => { |
265 | Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it), | 244 | return it.try_to_nav(db).map(to_action) |
245 | } | ||
246 | Definition::ModuleDef(hir::ModuleDef::Adt(it)) => Some(it), | ||
266 | Definition::SelfType(it) => it.self_ty(db).as_adt(), | 247 | Definition::SelfType(it) => it.self_ty(db).as_adt(), |
267 | _ => None, | 248 | _ => None, |
268 | }?; | 249 | }?; |
@@ -271,25 +252,27 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov | |||
271 | 252 | ||
272 | fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | 253 | fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { |
273 | match def { | 254 | match def { |
274 | Definition::ModuleDef(ModuleDef::Function(it)) => it.try_to_nav(db).map(|nav_target| { | 255 | Definition::ModuleDef(hir::ModuleDef::Function(it)) => { |
275 | HoverAction::Reference(FilePosition { | 256 | it.try_to_nav(db).map(|nav_target| { |
276 | file_id: nav_target.file_id, | 257 | HoverAction::Reference(FilePosition { |
277 | offset: nav_target.focus_or_full_range().start(), | 258 | file_id: nav_target.file_id, |
259 | offset: nav_target.focus_or_full_range().start(), | ||
260 | }) | ||
278 | }) | 261 | }) |
279 | }), | 262 | } |
280 | _ => None, | 263 | _ => None, |
281 | } | 264 | } |
282 | } | 265 | } |
283 | 266 | ||
284 | fn runnable_action( | 267 | fn runnable_action( |
285 | sema: &Semantics<RootDatabase>, | 268 | sema: &hir::Semantics<RootDatabase>, |
286 | def: Definition, | 269 | def: Definition, |
287 | file_id: FileId, | 270 | file_id: FileId, |
288 | ) -> Option<HoverAction> { | 271 | ) -> Option<HoverAction> { |
289 | match def { | 272 | match def { |
290 | Definition::ModuleDef(it) => match it { | 273 | Definition::ModuleDef(it) => match it { |
291 | ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), | 274 | hir::ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), |
292 | ModuleDef::Function(func) => { | 275 | hir::ModuleDef::Function(func) => { |
293 | let src = func.source(sema.db)?; | 276 | let src = func.source(sema.db)?; |
294 | if src.file_id != file_id.into() { | 277 | if src.file_id != file_id.into() { |
295 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); | 278 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); |
@@ -306,19 +289,19 @@ fn runnable_action( | |||
306 | } | 289 | } |
307 | 290 | ||
308 | fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | 291 | fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { |
309 | let mut targets: Vec<ModuleDef> = Vec::new(); | 292 | let mut targets: Vec<hir::ModuleDef> = Vec::new(); |
310 | let mut push_new_def = |item: ModuleDef| { | 293 | let mut push_new_def = |item: hir::ModuleDef| { |
311 | if !targets.contains(&item) { | 294 | if !targets.contains(&item) { |
312 | targets.push(item); | 295 | targets.push(item); |
313 | } | 296 | } |
314 | }; | 297 | }; |
315 | 298 | ||
316 | if let Definition::GenericParam(GenericParam::TypeParam(it)) = def { | 299 | if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def { |
317 | it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); | 300 | it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); |
318 | } else { | 301 | } else { |
319 | let ty = match def { | 302 | let ty = match def { |
320 | Definition::Local(it) => it.ty(db), | 303 | Definition::Local(it) => it.ty(db), |
321 | Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db), | 304 | Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db), |
322 | _ => return None, | 305 | _ => return None, |
323 | }; | 306 | }; |
324 | 307 | ||
@@ -348,42 +331,32 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | |||
348 | Some(HoverAction::GoToType(targets)) | 331 | Some(HoverAction::GoToType(targets)) |
349 | } | 332 | } |
350 | 333 | ||
351 | fn hover_markup( | 334 | fn hover_markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> { |
352 | docs: Option<String>, | 335 | let mut buf = String::new(); |
353 | desc: Option<String>, | ||
354 | mod_path: Option<String>, | ||
355 | ) -> Option<Markup> { | ||
356 | match desc { | ||
357 | Some(desc) => { | ||
358 | let mut buf = String::new(); | ||
359 | |||
360 | if let Some(mod_path) = mod_path { | ||
361 | if !mod_path.is_empty() { | ||
362 | format_to!(buf, "```rust\n{}\n```\n\n", mod_path); | ||
363 | } | ||
364 | } | ||
365 | format_to!(buf, "```rust\n{}\n```", desc); | ||
366 | 336 | ||
367 | if let Some(doc) = docs { | 337 | if let Some(mod_path) = mod_path { |
368 | format_to!(buf, "\n___\n\n{}", doc); | 338 | if !mod_path.is_empty() { |
369 | } | 339 | format_to!(buf, "```rust\n{}\n```\n\n", mod_path); |
370 | Some(buf.into()) | ||
371 | } | 340 | } |
372 | None => docs.map(Markup::from), | ||
373 | } | 341 | } |
342 | format_to!(buf, "```rust\n{}\n```", desc); | ||
343 | |||
344 | if let Some(doc) = docs { | ||
345 | format_to!(buf, "\n___\n\n{}", doc); | ||
346 | } | ||
347 | Some(buf.into()) | ||
374 | } | 348 | } |
375 | 349 | ||
376 | fn process_markup( | 350 | fn process_markup( |
377 | db: &RootDatabase, | 351 | db: &RootDatabase, |
378 | def: Definition, | 352 | def: Definition, |
379 | markup: &Markup, | 353 | markup: &Markup, |
380 | links_in_hover: bool, | 354 | config: &HoverConfig, |
381 | markdown: bool, | ||
382 | ) -> Markup { | 355 | ) -> Markup { |
383 | let markup = markup.as_str(); | 356 | let markup = markup.as_str(); |
384 | let markup = if !markdown { | 357 | let markup = if !config.markdown() { |
385 | remove_markdown(markup) | 358 | remove_markdown(markup) |
386 | } else if links_in_hover { | 359 | } else if config.links_in_hover { |
387 | rewrite_links(db, markup, &def) | 360 | rewrite_links(db, markup, &def) |
388 | } else { | 361 | } else { |
389 | remove_links(markup) | 362 | remove_links(markup) |
@@ -396,11 +369,11 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> | |||
396 | Definition::Field(f) => Some(f.parent_def(db).name(db)), | 369 | Definition::Field(f) => Some(f.parent_def(db).name(db)), |
397 | Definition::Local(l) => l.parent(db).name(db), | 370 | Definition::Local(l) => l.parent(db).name(db), |
398 | Definition::ModuleDef(md) => match md { | 371 | Definition::ModuleDef(md) => match md { |
399 | ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { | 372 | hir::ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { |
400 | AssocItemContainer::Trait(t) => Some(t.name(db)), | 373 | hir::AssocItemContainer::Trait(t) => Some(t.name(db)), |
401 | AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)), | 374 | hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)), |
402 | }, | 375 | }, |
403 | ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)), | 376 | hir::ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)), |
404 | _ => None, | 377 | _ => None, |
405 | }, | 378 | }, |
406 | _ => None, | 379 | _ => None, |
@@ -408,7 +381,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> | |||
408 | .map(|name| name.to_string()) | 381 | .map(|name| name.to_string()) |
409 | } | 382 | } |
410 | 383 | ||
411 | fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String { | 384 | fn render_path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String { |
412 | let crate_name = | 385 | let crate_name = |
413 | db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string()); | 386 | db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string()); |
414 | let module_path = module | 387 | let module_path = module |
@@ -420,6 +393,9 @@ fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> | |||
420 | } | 393 | } |
421 | 394 | ||
422 | fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { | 395 | fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { |
396 | if let Definition::GenericParam(_) = def { | ||
397 | return None; | ||
398 | } | ||
423 | def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) | 399 | def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) |
424 | } | 400 | } |
425 | 401 | ||
@@ -427,60 +403,57 @@ fn hover_for_definition( | |||
427 | db: &RootDatabase, | 403 | db: &RootDatabase, |
428 | def: Definition, | 404 | def: Definition, |
429 | famous_defs: Option<&FamousDefs>, | 405 | famous_defs: Option<&FamousDefs>, |
406 | config: &HoverConfig, | ||
430 | ) -> Option<Markup> { | 407 | ) -> Option<Markup> { |
431 | let mod_path = definition_mod_path(db, &def); | 408 | let mod_path = definition_mod_path(db, &def); |
432 | return match def { | 409 | let (label, docs) = match def { |
433 | Definition::Macro(it) => match &it.source(db)?.value { | 410 | Definition::Macro(it) => match &it.source(db)?.value { |
434 | Either::Left(mac) => { | 411 | Either::Left(mac) => { |
435 | let label = macro_label(mac); | 412 | let label = macro_label(mac); |
436 | from_def_source_labeled(db, it, Some(label), mod_path) | 413 | (label, it.attrs(db).docs()) |
437 | } | 414 | } |
438 | Either::Right(_) => { | 415 | Either::Right(_) => { |
439 | // FIXME | 416 | // FIXME |
440 | None | 417 | return None; |
441 | } | 418 | } |
442 | }, | 419 | }, |
443 | Definition::Field(def) => from_hir_fmt(db, def, mod_path), | 420 | Definition::Field(def) => label_and_docs(db, def), |
444 | Definition::ModuleDef(it) => match it { | 421 | Definition::ModuleDef(it) => match it { |
445 | ModuleDef::Module(it) => from_hir_fmt(db, it, mod_path), | 422 | hir::ModuleDef::Module(it) => label_and_docs(db, it), |
446 | ModuleDef::Function(it) => from_hir_fmt(db, it, mod_path), | 423 | hir::ModuleDef::Function(it) => label_and_docs(db, it), |
447 | ModuleDef::Adt(it) => from_hir_fmt(db, it, mod_path), | 424 | hir::ModuleDef::Adt(it) => label_and_docs(db, it), |
448 | ModuleDef::Variant(it) => from_hir_fmt(db, it, mod_path), | 425 | hir::ModuleDef::Variant(it) => label_and_docs(db, it), |
449 | ModuleDef::Const(it) => from_hir_fmt(db, it, mod_path), | 426 | hir::ModuleDef::Const(it) => label_and_docs(db, it), |
450 | ModuleDef::Static(it) => from_hir_fmt(db, it, mod_path), | 427 | hir::ModuleDef::Static(it) => label_and_docs(db, it), |
451 | ModuleDef::Trait(it) => from_hir_fmt(db, it, mod_path), | 428 | hir::ModuleDef::Trait(it) => label_and_docs(db, it), |
452 | ModuleDef::TypeAlias(it) => from_hir_fmt(db, it, mod_path), | 429 | hir::ModuleDef::TypeAlias(it) => label_and_docs(db, it), |
453 | ModuleDef::BuiltinType(it) => famous_defs | 430 | hir::ModuleDef::BuiltinType(it) => { |
454 | .and_then(|fd| hover_for_builtin(fd, it)) | 431 | return famous_defs |
455 | .or_else(|| Some(Markup::fenced_block(&it.name()))), | 432 | .and_then(|fd| hover_for_builtin(fd, it)) |
433 | .or_else(|| Some(Markup::fenced_block(&it.name()))) | ||
434 | } | ||
456 | }, | 435 | }, |
457 | Definition::Local(it) => hover_for_local(it, db), | 436 | Definition::Local(it) => return hover_for_local(it, db), |
458 | Definition::SelfType(impl_def) => { | 437 | Definition::SelfType(impl_def) => { |
459 | impl_def.self_ty(db).as_adt().and_then(|adt| from_hir_fmt(db, adt, mod_path)) | 438 | impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? |
460 | } | 439 | } |
461 | Definition::GenericParam(it) => from_hir_fmt(db, it, None), | 440 | Definition::GenericParam(it) => label_and_docs(db, it), |
462 | Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), | 441 | Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))), |
463 | }; | 442 | }; |
464 | 443 | ||
465 | fn from_hir_fmt<D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> | 444 | return hover_markup( |
445 | docs.filter(|_| config.documentation.is_some()).map(Into::into), | ||
446 | label, | ||
447 | mod_path, | ||
448 | ); | ||
449 | |||
450 | fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>) | ||
466 | where | 451 | where |
467 | D: HasAttrs + HirDisplay, | 452 | D: HasAttrs + HirDisplay, |
468 | { | 453 | { |
469 | let label = def.display(db).to_string(); | 454 | let label = def.display(db).to_string(); |
470 | from_def_source_labeled(db, def, Some(label), mod_path) | 455 | let docs = def.attrs(db).docs(); |
471 | } | 456 | (label, docs) |
472 | |||
473 | fn from_def_source_labeled<D>( | ||
474 | db: &RootDatabase, | ||
475 | def: D, | ||
476 | short_label: Option<String>, | ||
477 | mod_path: Option<String>, | ||
478 | ) -> Option<Markup> | ||
479 | where | ||
480 | D: HasAttrs, | ||
481 | { | ||
482 | let docs = def.attrs(db).docs().map(Into::into); | ||
483 | hover_markup(docs, short_label, mod_path) | ||
484 | } | 457 | } |
485 | } | 458 | } |
486 | 459 | ||
@@ -504,16 +477,15 @@ fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> { | |||
504 | } | 477 | } |
505 | Either::Right(_) => format!("{}self: {}", is_mut, ty), | 478 | Either::Right(_) => format!("{}self: {}", is_mut, ty), |
506 | }; | 479 | }; |
507 | hover_markup(None, Some(desc), None) | 480 | hover_markup(None, desc, None) |
508 | } | 481 | } |
509 | 482 | ||
510 | fn hover_for_keyword( | 483 | fn hover_for_keyword( |
511 | sema: &Semantics<RootDatabase>, | 484 | sema: &Semantics<RootDatabase>, |
512 | links_in_hover: bool, | 485 | config: &HoverConfig, |
513 | markdown: bool, | ||
514 | token: &SyntaxToken, | 486 | token: &SyntaxToken, |
515 | ) -> Option<RangeInfo<HoverResult>> { | 487 | ) -> Option<RangeInfo<HoverResult>> { |
516 | if !token.kind().is_keyword() { | 488 | if !token.kind().is_keyword() || !config.documentation.is_some() { |
517 | return None; | 489 | return None; |
518 | } | 490 | } |
519 | let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate()); | 491 | let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate()); |
@@ -524,9 +496,8 @@ fn hover_for_keyword( | |||
524 | let markup = process_markup( | 496 | let markup = process_markup( |
525 | sema.db, | 497 | sema.db, |
526 | Definition::ModuleDef(doc_owner.into()), | 498 | Definition::ModuleDef(doc_owner.into()), |
527 | &hover_markup(Some(docs.into()), Some(token.text().into()), None)?, | 499 | &hover_markup(Some(docs.into()), token.text().into(), None)?, |
528 | links_in_hover, | 500 | config, |
529 | markdown, | ||
530 | ); | 501 | ); |
531 | Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() })) | 502 | Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() })) |
532 | } | 503 | } |
@@ -536,7 +507,7 @@ fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Opt | |||
536 | let primitive_mod = format!("prim_{}", builtin.name()); | 507 | let primitive_mod = format!("prim_{}", builtin.name()); |
537 | let doc_owner = find_std_module(famous_defs, &primitive_mod)?; | 508 | let doc_owner = find_std_module(famous_defs, &primitive_mod)?; |
538 | let docs = doc_owner.attrs(famous_defs.0.db).docs()?; | 509 | let docs = doc_owner.attrs(famous_defs.0.db).docs()?; |
539 | hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None) | 510 | hover_markup(Some(docs.into()), builtin.name().to_string(), None) |
540 | } | 511 | } |
541 | 512 | ||
542 | fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> { | 513 | fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> { |
@@ -566,16 +537,34 @@ mod tests { | |||
566 | use expect_test::{expect, Expect}; | 537 | use expect_test::{expect, Expect}; |
567 | use ide_db::base_db::FileLoader; | 538 | use ide_db::base_db::FileLoader; |
568 | 539 | ||
569 | use crate::fixture; | 540 | use crate::{fixture, hover::HoverDocFormat, HoverConfig}; |
570 | 541 | ||
571 | fn check_hover_no_result(ra_fixture: &str) { | 542 | fn check_hover_no_result(ra_fixture: &str) { |
572 | let (analysis, position) = fixture::position(ra_fixture); | 543 | let (analysis, position) = fixture::position(ra_fixture); |
573 | assert!(analysis.hover(position, true, true).unwrap().is_none()); | 544 | assert!(analysis |
545 | .hover( | ||
546 | position, | ||
547 | &HoverConfig { | ||
548 | links_in_hover: true, | ||
549 | documentation: Some(HoverDocFormat::Markdown) | ||
550 | } | ||
551 | ) | ||
552 | .unwrap() | ||
553 | .is_none()); | ||
574 | } | 554 | } |
575 | 555 | ||
576 | fn check(ra_fixture: &str, expect: Expect) { | 556 | fn check(ra_fixture: &str, expect: Expect) { |
577 | let (analysis, position) = fixture::position(ra_fixture); | 557 | let (analysis, position) = fixture::position(ra_fixture); |
578 | let hover = analysis.hover(position, true, true).unwrap().unwrap(); | 558 | let hover = analysis |
559 | .hover( | ||
560 | position, | ||
561 | &HoverConfig { | ||
562 | links_in_hover: true, | ||
563 | documentation: Some(HoverDocFormat::Markdown), | ||
564 | }, | ||
565 | ) | ||
566 | .unwrap() | ||
567 | .unwrap(); | ||
579 | 568 | ||
580 | let content = analysis.db.file_text(position.file_id); | 569 | let content = analysis.db.file_text(position.file_id); |
581 | let hovered_element = &content[hover.range]; | 570 | let hovered_element = &content[hover.range]; |
@@ -586,7 +575,16 @@ mod tests { | |||
586 | 575 | ||
587 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { | 576 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { |
588 | let (analysis, position) = fixture::position(ra_fixture); | 577 | let (analysis, position) = fixture::position(ra_fixture); |
589 | let hover = analysis.hover(position, false, true).unwrap().unwrap(); | 578 | let hover = analysis |
579 | .hover( | ||
580 | position, | ||
581 | &HoverConfig { | ||
582 | links_in_hover: false, | ||
583 | documentation: Some(HoverDocFormat::Markdown), | ||
584 | }, | ||
585 | ) | ||
586 | .unwrap() | ||
587 | .unwrap(); | ||
590 | 588 | ||
591 | let content = analysis.db.file_text(position.file_id); | 589 | let content = analysis.db.file_text(position.file_id); |
592 | let hovered_element = &content[hover.range]; | 590 | let hovered_element = &content[hover.range]; |
@@ -597,7 +595,16 @@ mod tests { | |||
597 | 595 | ||
598 | fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { | 596 | fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { |
599 | let (analysis, position) = fixture::position(ra_fixture); | 597 | let (analysis, position) = fixture::position(ra_fixture); |
600 | let hover = analysis.hover(position, true, false).unwrap().unwrap(); | 598 | let hover = analysis |
599 | .hover( | ||
600 | position, | ||
601 | &HoverConfig { | ||
602 | links_in_hover: true, | ||
603 | documentation: Some(HoverDocFormat::PlainText), | ||
604 | }, | ||
605 | ) | ||
606 | .unwrap() | ||
607 | .unwrap(); | ||
601 | 608 | ||
602 | let content = analysis.db.file_text(position.file_id); | 609 | let content = analysis.db.file_text(position.file_id); |
603 | let hovered_element = &content[hover.range]; | 610 | let hovered_element = &content[hover.range]; |
@@ -608,7 +615,16 @@ mod tests { | |||
608 | 615 | ||
609 | fn check_actions(ra_fixture: &str, expect: Expect) { | 616 | fn check_actions(ra_fixture: &str, expect: Expect) { |
610 | let (analysis, position) = fixture::position(ra_fixture); | 617 | let (analysis, position) = fixture::position(ra_fixture); |
611 | let hover = analysis.hover(position, true, true).unwrap().unwrap(); | 618 | let hover = analysis |
619 | .hover( | ||
620 | position, | ||
621 | &HoverConfig { | ||
622 | links_in_hover: true, | ||
623 | documentation: Some(HoverDocFormat::Markdown), | ||
624 | }, | ||
625 | ) | ||
626 | .unwrap() | ||
627 | .unwrap(); | ||
612 | expect.assert_debug_eq(&hover.info.actions) | 628 | expect.assert_debug_eq(&hover.info.actions) |
613 | } | 629 | } |
614 | 630 | ||
@@ -2258,7 +2274,7 @@ pub fn fo$0o() {} | |||
2258 | case 13. collapsed link: foo | 2274 | case 13. collapsed link: foo |
2259 | case 14. shortcut link: foo | 2275 | case 14. shortcut link: foo |
2260 | case 15. inline without URL: foo | 2276 | case 15. inline without URL: foo |
2261 | case 16. just escaped text: \[foo] | 2277 | case 16. just escaped text: \[foo\] |
2262 | case 17. inline link: Foo | 2278 | case 17. inline link: Foo |
2263 | 2279 | ||
2264 | [^example]: https://www.example.com/ | 2280 | [^example]: https://www.example.com/ |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 4bd073cc3..b978e36af 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -75,7 +75,7 @@ pub use crate::{ | |||
75 | expand_macro::ExpandedMacro, | 75 | expand_macro::ExpandedMacro, |
76 | file_structure::{StructureNode, StructureNodeKind}, | 76 | file_structure::{StructureNode, StructureNodeKind}, |
77 | folding_ranges::{Fold, FoldKind}, | 77 | folding_ranges::{Fold, FoldKind}, |
78 | hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult}, | 78 | hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, |
79 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, | 79 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, |
80 | markup::Markup, | 80 | markup::Markup, |
81 | move_item::Direction, | 81 | move_item::Direction, |
@@ -217,6 +217,7 @@ impl Analysis { | |||
217 | file_id, | 217 | file_id, |
218 | Edition::Edition2018, | 218 | Edition::Edition2018, |
219 | None, | 219 | None, |
220 | cfg_options.clone(), | ||
220 | cfg_options, | 221 | cfg_options, |
221 | Env::default(), | 222 | Env::default(), |
222 | Default::default(), | 223 | Default::default(), |
@@ -407,10 +408,9 @@ impl Analysis { | |||
407 | pub fn hover( | 408 | pub fn hover( |
408 | &self, | 409 | &self, |
409 | position: FilePosition, | 410 | position: FilePosition, |
410 | links_in_hover: bool, | 411 | config: &HoverConfig, |
411 | markdown: bool, | ||
412 | ) -> Cancellable<Option<RangeInfo<HoverResult>>> { | 412 | ) -> Cancellable<Option<RangeInfo<HoverResult>>> { |
413 | self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) | 413 | self.with_db(|db| hover::hover(db, position, config)) |
414 | } | 414 | } |
415 | 415 | ||
416 | /// Return URL(s) for the documentation of the symbol under the cursor. | 416 | /// Return URL(s) for the documentation of the symbol under the cursor. |
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 8096dfa0e..96bd07708 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs | |||
@@ -10,7 +10,7 @@ use ide_db::{ | |||
10 | rename::{bail, format_err, source_edit_from_references, IdentifierKind}, | 10 | rename::{bail, format_err, source_edit_from_references, IdentifierKind}, |
11 | RootDatabase, | 11 | RootDatabase, |
12 | }; | 12 | }; |
13 | use stdx::never; | 13 | use stdx::{always, never}; |
14 | use syntax::{ast, AstNode, SyntaxNode}; | 14 | use syntax::{ast, AstNode, SyntaxNode}; |
15 | 15 | ||
16 | use text_edit::TextEdit; | 16 | use text_edit::TextEdit; |
@@ -31,10 +31,13 @@ pub(crate) fn prepare_rename( | |||
31 | let source_file = sema.parse(position.file_id); | 31 | let source_file = sema.parse(position.file_id); |
32 | let syntax = source_file.syntax(); | 32 | let syntax = source_file.syntax(); |
33 | 33 | ||
34 | let def = find_definition(&sema, syntax, position)?; | 34 | let (name_like, def) = find_definition(&sema, syntax, position)?; |
35 | let frange = def | 35 | if def.range_for_rename(&sema).is_none() { |
36 | .range_for_rename(&sema) | 36 | bail!("No references found at position") |
37 | .ok_or_else(|| format_err!("No references found at position"))?; | 37 | } |
38 | |||
39 | let frange = sema.original_range(name_like.syntax()); | ||
40 | always!(frange.range.contains_inclusive(position.offset) && frange.file_id == position.file_id); | ||
38 | Ok(RangeInfo::new(frange.range, ())) | 41 | Ok(RangeInfo::new(frange.range, ())) |
39 | } | 42 | } |
40 | 43 | ||
@@ -55,31 +58,23 @@ pub(crate) fn rename( | |||
55 | new_name: &str, | 58 | new_name: &str, |
56 | ) -> RenameResult<SourceChange> { | 59 | ) -> RenameResult<SourceChange> { |
57 | let sema = Semantics::new(db); | 60 | let sema = Semantics::new(db); |
58 | rename_with_semantics(&sema, position, new_name) | ||
59 | } | ||
60 | |||
61 | pub(crate) fn rename_with_semantics( | ||
62 | sema: &Semantics<RootDatabase>, | ||
63 | position: FilePosition, | ||
64 | new_name: &str, | ||
65 | ) -> RenameResult<SourceChange> { | ||
66 | let source_file = sema.parse(position.file_id); | 61 | let source_file = sema.parse(position.file_id); |
67 | let syntax = source_file.syntax(); | 62 | let syntax = source_file.syntax(); |
68 | 63 | ||
69 | let def = find_definition(sema, syntax, position)?; | 64 | let (_name_like, def) = find_definition(&sema, syntax, position)?; |
70 | 65 | ||
71 | if let Definition::Local(local) = def { | 66 | if let Definition::Local(local) = def { |
72 | if let Some(self_param) = local.as_self_param(sema.db) { | 67 | if let Some(self_param) = local.as_self_param(sema.db) { |
73 | cov_mark::hit!(rename_self_to_param); | 68 | cov_mark::hit!(rename_self_to_param); |
74 | return rename_self_to_param(sema, local, self_param, new_name); | 69 | return rename_self_to_param(&sema, local, self_param, new_name); |
75 | } | 70 | } |
76 | if new_name == "self" { | 71 | if new_name == "self" { |
77 | cov_mark::hit!(rename_to_self); | 72 | cov_mark::hit!(rename_to_self); |
78 | return rename_to_self(sema, local); | 73 | return rename_to_self(&sema, local); |
79 | } | 74 | } |
80 | } | 75 | } |
81 | 76 | ||
82 | def.rename(sema, new_name) | 77 | def.rename(&sema, new_name) |
83 | } | 78 | } |
84 | 79 | ||
85 | /// Called by the client when it is about to rename a file. | 80 | /// Called by the client when it is about to rename a file. |
@@ -100,11 +95,12 @@ fn find_definition( | |||
100 | sema: &Semantics<RootDatabase>, | 95 | sema: &Semantics<RootDatabase>, |
101 | syntax: &SyntaxNode, | 96 | syntax: &SyntaxNode, |
102 | position: FilePosition, | 97 | position: FilePosition, |
103 | ) -> RenameResult<Definition> { | 98 | ) -> RenameResult<(ast::NameLike, Definition)> { |
104 | match sema | 99 | let name_like = sema |
105 | .find_node_at_offset_with_descend(syntax, position.offset) | 100 | .find_node_at_offset_with_descend::<ast::NameLike>(syntax, position.offset) |
106 | .ok_or_else(|| format_err!("No references found at position"))? | 101 | .ok_or_else(|| format_err!("No references found at position"))?; |
107 | { | 102 | |
103 | let def = match &name_like { | ||
108 | // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet | 104 | // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet |
109 | ast::NameLike::Name(name) | 105 | ast::NameLike::Name(name) |
110 | if name.syntax().parent().map_or(false, |it| ast::Rename::can_cast(it.kind())) => | 106 | if name.syntax().parent().map_or(false, |it| ast::Rename::can_cast(it.kind())) => |
@@ -134,7 +130,9 @@ fn find_definition( | |||
134 | .map(|it| it.referenced_or_defined(sema.db)) | 130 | .map(|it| it.referenced_or_defined(sema.db)) |
135 | }), | 131 | }), |
136 | } | 132 | } |
137 | .ok_or_else(|| format_err!("No references found at position")) | 133 | .ok_or_else(|| format_err!("No references found at position"))?; |
134 | |||
135 | Ok((name_like, def)) | ||
138 | } | 136 | } |
139 | 137 | ||
140 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { | 138 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { |
@@ -328,7 +326,7 @@ mod tests { | |||
328 | fn test_prepare_rename_namelikes() { | 326 | fn test_prepare_rename_namelikes() { |
329 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); | 327 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); |
330 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); | 328 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); |
331 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"3..7: name"#]]); | 329 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]); |
332 | } | 330 | } |
333 | 331 | ||
334 | #[test] | 332 | #[test] |
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 4ad49eca0..37ae92350 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs | |||
@@ -23,7 +23,7 @@ use syntax::{ | |||
23 | algo::find_node_at_offset, | 23 | algo::find_node_at_offset, |
24 | ast::{self, edit::IndentLevel, AstToken}, | 24 | ast::{self, edit::IndentLevel, AstToken}, |
25 | AstNode, Parse, SourceFile, | 25 | AstNode, Parse, SourceFile, |
26 | SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR}, | 26 | SyntaxKind::{self, FIELD_EXPR, METHOD_CALL_EXPR}, |
27 | TextRange, TextSize, | 27 | TextRange, TextSize, |
28 | }; | 28 | }; |
29 | 29 | ||
@@ -95,9 +95,16 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option< | |||
95 | } | 95 | } |
96 | 96 | ||
97 | let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?; | 97 | let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?; |
98 | if brace_token.kind() != SyntaxKind::L_CURLY { | ||
99 | return None; | ||
100 | } | ||
98 | 101 | ||
99 | // Remove the `{` to get a better parse tree, and reparse | 102 | // Remove the `{` to get a better parse tree, and reparse. |
100 | let file = file.reparse(&Indel::delete(brace_token.text_range())); | 103 | let range = brace_token.text_range(); |
104 | if !stdx::always!(range.len() == TextSize::of('{')) { | ||
105 | return None; | ||
106 | } | ||
107 | let file = file.reparse(&Indel::delete(range)); | ||
101 | 108 | ||
102 | if let Some(edit) = brace_expr(&file.tree(), offset) { | 109 | if let Some(edit) = brace_expr(&file.tree(), offset) { |
103 | return Some(edit); | 110 | return Some(edit); |
@@ -550,6 +557,29 @@ fn f() { | |||
550 | } | 557 | } |
551 | 558 | ||
552 | #[test] | 559 | #[test] |
560 | fn noop_in_string_literal() { | ||
561 | // Regression test for #9351 | ||
562 | type_char_noop( | ||
563 | '{', | ||
564 | r##" | ||
565 | fn check_with(ra_fixture: &str, expect: Expect) { | ||
566 | let base = r#" | ||
567 | enum E { T(), R$0, C } | ||
568 | use self::E::X; | ||
569 | const Z: E = E::C; | ||
570 | mod m {} | ||
571 | asdasdasdasdasdasda | ||
572 | sdasdasdasdasdasda | ||
573 | sdasdasdasdasd | ||
574 | "#; | ||
575 | let actual = completion_list(&format!("{}\n{}", base, ra_fixture)); | ||
576 | expect.assert_eq(&actual) | ||
577 | } | ||
578 | "##, | ||
579 | ); | ||
580 | } | ||
581 | |||
582 | #[test] | ||
553 | fn adds_closing_brace_for_use_tree() { | 583 | fn adds_closing_brace_for_use_tree() { |
554 | type_char( | 584 | type_char( |
555 | '{', | 585 | '{', |