aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-08-31 09:26:55 +0100
committerZac Pullar-Strecker <[email protected]>2020-10-08 02:59:31 +0100
commita06d736b77770e4c1e738086c81b4fd60fcfcb23 (patch)
treee70d61afec0cb7cf71c4ad2636a7adb8186a7a2f
parentbfda0d25834250a3adbcd0d26953a1cdc6662e7f (diff)
Add support for struct & trait methods
-rw-r--r--crates/ide/src/link_rewrite.rs90
1 files changed, 83 insertions, 7 deletions
diff --git a/crates/ide/src/link_rewrite.rs b/crates/ide/src/link_rewrite.rs
index 80005c06e..1e102997f 100644
--- a/crates/ide/src/link_rewrite.rs
+++ b/crates/ide/src/link_rewrite.rs
@@ -91,7 +91,6 @@ pub fn remove_links(markdown: &str) -> String {
91} 91}
92 92
93pub fn get_doc_link<T: Resolvable + Clone>(db: &dyn HirDatabase, definition: &T) -> Option<String> { 93pub fn get_doc_link<T: Resolvable + Clone>(db: &dyn HirDatabase, definition: &T) -> Option<String> {
94 eprintln!("hir::doc_links::get_doc_link");
95 let module_def = definition.clone().try_into_module_def()?; 94 let module_def = definition.clone().try_into_module_def()?;
96 95
97 get_doc_link_impl(db, &module_def) 96 get_doc_link_impl(db, &module_def)
@@ -105,8 +104,31 @@ pub fn get_doc_link<T: Resolvable + Clone>(db: &dyn HirDatabase, definition: &T)
105// BUG: For methods 104// BUG: For methods
106// import_map.path_of(ns) fails, is not designed to resolve methods 105// import_map.path_of(ns) fails, is not designed to resolve methods
107fn get_doc_link_impl(db: &dyn HirDatabase, moddef: &ModuleDef) -> Option<String> { 106fn get_doc_link_impl(db: &dyn HirDatabase, moddef: &ModuleDef) -> Option<String> {
108 eprintln!("get_doc_link_impl: {:#?}", moddef); 107 // Get the outermost definition for the moduledef. This is used to resolve the public path to the type,
109 let ns = ItemInNs::Types(moddef.clone().into()); 108 // then we can join the method, field, etc onto it if required.
109 let target_def: ModuleDef = match moddef {
110 ModuleDef::Function(f) => match f.as_assoc_item(db).map(|assoc| assoc.container(db)) {
111 Some(AssocItemContainer::Trait(t)) => t.into(),
112 Some(AssocItemContainer::ImplDef(imp)) => {
113 let resolver = ModuleId::from(imp.module(db)).resolver(db.upcast());
114 let ctx = TyLoweringContext::new(db, &resolver);
115 Adt::from(
116 Ty::from_hir(
117 &ctx,
118 &imp.target_trait(db).unwrap_or_else(|| imp.target_type(db)),
119 )
120 .as_adt()
121 .map(|t| t.0)
122 .unwrap(),
123 )
124 .into()
125 }
126 None => ModuleDef::Function(*f),
127 },
128 moddef => *moddef,
129 };
130
131 let ns = ItemInNs::Types(target_def.clone().into());
110 132
111 let module = moddef.module(db)?; 133 let module = moddef.module(db)?;
112 let krate = module.krate(); 134 let krate = module.krate();
@@ -117,7 +139,28 @@ fn get_doc_link_impl(db: &dyn HirDatabase, moddef: &ModuleDef) -> Option<String>
117 139
118 get_doc_url(db, &krate) 140 get_doc_url(db, &krate)
119 .and_then(|url| url.join(&base).ok()) 141 .and_then(|url| url.join(&base).ok())
120 .and_then(|url| get_symbol_filename(db, &moddef).as_deref().and_then(|f| url.join(f).ok())) 142 .and_then(|url| {
143 get_symbol_filename(db, &target_def).as_deref().and_then(|f| url.join(f).ok())
144 })
145 .and_then(|url| match moddef {
146 ModuleDef::Function(f) => {
147 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(*f)))
148 .as_deref()
149 .and_then(|f| url.join(f).ok())
150 }
151 ModuleDef::Const(c) => {
152 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Const(*c)))
153 .as_deref()
154 .and_then(|f| url.join(f).ok())
155 }
156 ModuleDef::TypeAlias(ty) => {
157 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::TypeAlias(*ty)))
158 .as_deref()
159 .and_then(|f| url.join(f).ok())
160 }
161 // TODO: Field <- this requires passing in a definition or something
162 _ => Some(url),
163 })
121 .map(|url| url.into_string()) 164 .map(|url| url.into_string())
122} 165}
123 166
@@ -307,6 +350,12 @@ fn ns_from_intra_spec(s: &str) -> Option<hir::Namespace> {
307 .next() 350 .next()
308} 351}
309 352
353/// Get the root URL for the documentation of a crate.
354///
355/// ```
356/// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
357/// ^^^^^^^^^^^^^^^^^^^^^^^^^^
358/// ```
310fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> { 359fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
311 krate 360 krate
312 .get_html_root_url(db) 361 .get_html_root_url(db)
@@ -323,8 +372,11 @@ fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
323 372
324/// Get the filename and extension generated for a symbol by rustdoc. 373/// Get the filename and extension generated for a symbol by rustdoc.
325/// 374///
326/// Example: `struct.Shard.html` 375/// ```
327fn get_symbol_filename(db: &RootDatabase, definition: &ModuleDef) -> Option<String> { 376/// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
377/// ^^^^^^^^^^^^^^^^^^^
378/// ```
379fn get_symbol_filename(db: &dyn HirDatabase, definition: &ModuleDef) -> Option<String> {
328 Some(match definition { 380 Some(match definition {
329 ModuleDef::Adt(adt) => match adt { 381 ModuleDef::Adt(adt) => match adt {
330 Adt::Struct(s) => format!("struct.{}.html", s.name(db)), 382 Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
@@ -334,7 +386,7 @@ fn get_symbol_filename(db: &RootDatabase, definition: &ModuleDef) -> Option<Stri
334 ModuleDef::Module(_) => "index.html".to_string(), 386 ModuleDef::Module(_) => "index.html".to_string(),
335 ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)), 387 ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)),
336 ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)), 388 ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)),
337 ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t), 389 ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.as_name()),
338 ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)), 390 ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)),
339 ModuleDef::EnumVariant(ev) => { 391 ModuleDef::EnumVariant(ev) => {
340 format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) 392 format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
@@ -344,6 +396,30 @@ fn get_symbol_filename(db: &RootDatabase, definition: &ModuleDef) -> Option<Stri
344 }) 396 })
345} 397}
346 398
399enum FieldOrAssocItem {
400 Field(Field),
401 AssocItem(AssocItem),
402}
403
404/// Get the fragment required to link to a specific field, method, associated type, or associated constant.
405///
406/// ```
407/// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
408/// ^^^^^^^^^^^^^^
409/// ```
410fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem) -> Option<String> {
411 Some(match field_or_assoc {
412 FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)),
413 FieldOrAssocItem::AssocItem(assoc) => match assoc {
414 // TODO: Rustdoc sometimes uses tymethod instead of method. This case needs to be investigated.
415 AssocItem::Function(function) => format!("#method.{}", function.name(db)),
416 // TODO: This might be the old method for documenting associated constants, i32::MAX uses a separate page...
417 AssocItem::Const(constant) => format!("#associatedconstant.{}", constant.name(db)?),
418 AssocItem::TypeAlias(ty) => format!("#associatedtype.{}", ty.name(db)),
419 },
420 })
421}
422
347fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 423fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
348 return tokens.max_by_key(priority); 424 return tokens.max_by_key(priority);
349 fn priority(n: &SyntaxToken) -> usize { 425 fn priority(n: &SyntaxToken) -> usize {