aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/hover.rs276
-rw-r--r--crates/ide/src/lib.rs8
-rw-r--r--crates/ide/src/rename.rs46
-rw-r--r--crates/ide/src/typing.rs36
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 @@
1use either::Either; 1use either::Either;
2use hir::{ 2use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
3 AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module,
4 ModuleDef, Semantics,
5};
6use ide_db::{ 3use 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)]
35pub struct HoverConfig { 32pub 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
45impl HoverConfig { 37impl 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 44pub 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 {
96pub(crate) fn hover( 76pub(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
272fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 253fn 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
284fn runnable_action( 267fn 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
308fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 291fn 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
351fn hover_markup( 334fn 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
376fn process_markup( 350fn 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
411fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String { 384fn 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
422fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 395fn 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
510fn hover_for_keyword( 483fn 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
542fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> { 513fn 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};
13use stdx::never; 13use stdx::{always, never};
14use syntax::{ast, AstNode, SyntaxNode}; 14use syntax::{ast, AstNode, SyntaxNode};
15 15
16use text_edit::TextEdit; 16use 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
61pub(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
140fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { 138fn 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##"
565fn check_with(ra_fixture: &str, expect: Expect) {
566 let base = r#"
567enum E { T(), R$0, C }
568use self::E::X;
569const Z: E = E::C;
570mod m {}
571asdasdasdasdasdasda
572sdasdasdasdasdasda
573sdasdasdasdasd
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 '{',