aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-03-23 19:56:38 +0000
committerLukas Wirth <[email protected]>2021-03-23 19:56:38 +0000
commitcaaeb92882a082bf1ac8b7d74c09ca0295c2ed10 (patch)
treee01edb95326d04eb7e6d525a6c0c7c986eba670e /crates/ide/src
parent8b0d0bd9c7488fedfd0bdd34a0a9cb04da3f143a (diff)
Cleanup intra_doc_link parsing
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/doc_links.rs133
1 files changed, 51 insertions, 82 deletions
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index f12c9d442..0cee741ac 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -114,7 +114,7 @@ pub(crate) fn extract_definitions_from_markdown(
114 for (event, range) in doc.into_offset_iter() { 114 for (event, range) in doc.into_offset_iter() {
115 if let Event::Start(Tag::Link(_, target, title)) = event { 115 if let Event::Start(Tag::Link(_, target, title)) = event {
116 let link = if target.is_empty() { title } else { target }; 116 let link = if target.is_empty() { title } else { target };
117 let (link, ns) = parse_link(&link); 117 let (link, ns) = parse_intra_doc_link(&link);
118 res.push((range, link.to_string(), ns)); 118 res.push((range, link.to_string(), ns));
119 } 119 }
120 } 120 }
@@ -276,26 +276,8 @@ fn rewrite_intra_doc_link(
276 title: &str, 276 title: &str,
277) -> Option<(String, String)> { 277) -> Option<(String, String)> {
278 let link = if target.is_empty() { title } else { target }; 278 let link = if target.is_empty() { title } else { target };
279 let (link, ns) = parse_link(link); 279 let (link, ns) = parse_intra_doc_link(link);
280 let resolved = match def { 280 let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
281 Definition::ModuleDef(def) => match def {
282 ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns),
283 ModuleDef::Function(it) => it.resolve_doc_path(db, link, ns),
284 ModuleDef::Adt(it) => it.resolve_doc_path(db, link, ns),
285 ModuleDef::Variant(it) => it.resolve_doc_path(db, link, ns),
286 ModuleDef::Const(it) => it.resolve_doc_path(db, link, ns),
287 ModuleDef::Static(it) => it.resolve_doc_path(db, link, ns),
288 ModuleDef::Trait(it) => it.resolve_doc_path(db, link, ns),
289 ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
290 ModuleDef::BuiltinType(_) => return None,
291 },
292 Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
293 Definition::Field(it) => it.resolve_doc_path(db, link, ns),
294 Definition::SelfType(_)
295 | Definition::Local(_)
296 | Definition::GenericParam(_)
297 | Definition::Label(_) => return None,
298 }?;
299 let krate = resolved.module(db)?.krate(); 281 let krate = resolved.module(db)?.krate();
300 let canonical_path = resolved.canonical_path(db)?; 282 let canonical_path = resolved.canonical_path(db)?;
301 let mut new_url = get_doc_url(db, &krate)? 283 let mut new_url = get_doc_url(db, &krate)?
@@ -307,24 +289,23 @@ fn rewrite_intra_doc_link(
307 .ok()?; 289 .ok()?;
308 290
309 if let ModuleDef::Trait(t) = resolved { 291 if let ModuleDef::Trait(t) = resolved {
310 let items = t.items(db); 292 if let Some(assoc_item) = t.items(db).into_iter().find_map(|assoc_item| {
311 if let Some(field_or_assoc_item) = items.iter().find_map(|assoc_item| {
312 if let Some(name) = assoc_item.name(db) { 293 if let Some(name) = assoc_item.name(db) {
313 if *link == format!("{}::{}", canonical_path, name) { 294 if *link == format!("{}::{}", canonical_path, name) {
314 return Some(FieldOrAssocItem::AssocItem(*assoc_item)); 295 return Some(assoc_item);
315 } 296 }
316 } 297 }
317 None 298 None
318 }) { 299 }) {
319 if let Some(fragment) = get_symbol_fragment(db, &field_or_assoc_item) { 300 if let Some(fragment) =
301 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(assoc_item))
302 {
320 new_url = new_url.join(&fragment).ok()?; 303 new_url = new_url.join(&fragment).ok()?;
321 } 304 }
322 }; 305 };
323 } 306 }
324 307
325 let new_target = new_url.into_string(); 308 Some((new_url.into_string(), strip_prefixes_suffixes(title).to_string()))
326 let new_title = strip_prefixes_suffixes(title);
327 Some((new_target, new_title.to_string()))
328} 309}
329 310
330/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). 311/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
@@ -401,73 +382,61 @@ fn map_links<'e>(
401 }) 382 })
402} 383}
403 384
404fn parse_link(s: &str) -> (&str, Option<hir::Namespace>) { 385const TYPES: ([&str; 9], [&str; 0]) =
405 let path = strip_prefixes_suffixes(s); 386 (["type", "struct", "enum", "mod", "trait", "union", "module", "prim", "primitive"], []);
406 let ns = ns_from_intra_spec(s); 387const VALUES: ([&str; 8], [&str; 1]) =
407 (path, ns)
408}
409
410/// Strip prefixes, suffixes, and inline code marks from the given string.
411fn strip_prefixes_suffixes(mut s: &str) -> &str {
412 s = s.trim_matches('`');
413
414 [
415 (TYPES.0.iter(), TYPES.1.iter()),
416 (VALUES.0.iter(), VALUES.1.iter()),
417 (MACROS.0.iter(), MACROS.1.iter()),
418 ]
419 .iter()
420 .for_each(|(prefixes, suffixes)| {
421 prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix));
422 suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix));
423 });
424 s.trim_start_matches('@').trim()
425}
426
427static TYPES: ([&str; 7], [&str; 0]) =
428 (["type", "struct", "enum", "mod", "trait", "union", "module"], []);
429static VALUES: ([&str; 8], [&str; 1]) =
430 (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]); 388 (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]);
431static MACROS: ([&str; 1], [&str; 1]) = (["macro"], ["!"]); 389const MACROS: ([&str; 2], [&str; 1]) = (["macro", "derive"], ["!"]);
432 390
433/// Extract the specified namespace from an intra-doc-link if one exists. 391/// Extract the specified namespace from an intra-doc-link if one exists.
434/// 392///
435/// # Examples 393/// # Examples
436/// 394///
437/// * `struct MyStruct` -> `Namespace::Types` 395/// * `struct MyStruct` -> ("MyStruct", `Namespace::Types`)
438/// * `panic!` -> `Namespace::Macros` 396/// * `panic!` -> ("panic", `Namespace::Macros`)
439/// * `fn@from_intra_spec` -> `Namespace::Values` 397/// * `fn@from_intra_spec` -> ("from_intra_spec", `Namespace::Values`)
440fn ns_from_intra_spec(s: &str) -> Option<hir::Namespace> { 398fn parse_intra_doc_link(s: &str) -> (&str, Option<hir::Namespace>) {
399 let s = s.trim_matches('`');
400
441 [ 401 [
442 (hir::Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())), 402 (hir::Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())),
443 (hir::Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())), 403 (hir::Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())),
444 (hir::Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())), 404 (hir::Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())),
445 ] 405 ]
446 .iter() 406 .iter()
447 .filter(|(_ns, (prefixes, suffixes))| { 407 .cloned()
448 prefixes 408 .find_map(|(ns, (mut prefixes, mut suffixes))| {
449 .clone() 409 if let Some(prefix) = prefixes.find(|&&prefix| {
450 .map(|prefix| { 410 s.starts_with(prefix)
451 s.starts_with(*prefix) 411 && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ')
452 && s.chars() 412 }) {
453 .nth(prefix.len() + 1) 413 Some((&s[prefix.len() + 1..], ns))
454 .map(|c| c == '@' || c == ' ') 414 } else {
455 .unwrap_or(false) 415 suffixes.find_map(|&suffix| s.strip_suffix(suffix).zip(Some(ns)))
456 }) 416 }
457 .any(|cond| cond) 417 })
458 || suffixes 418 .map_or((s, None), |(s, ns)| (s, Some(ns)))
459 .clone() 419}
460 .map(|suffix| { 420
461 s.starts_with(*suffix) 421fn strip_prefixes_suffixes(s: &str) -> &str {
462 && s.chars() 422 [
463 .nth(suffix.len() + 1) 423 (TYPES.0.iter(), TYPES.1.iter()),
464 .map(|c| c == '@' || c == ' ') 424 (VALUES.0.iter(), VALUES.1.iter()),
465 .unwrap_or(false) 425 (MACROS.0.iter(), MACROS.1.iter()),
466 }) 426 ]
467 .any(|cond| cond) 427 .iter()
428 .cloned()
429 .find_map(|(mut prefixes, mut suffixes)| {
430 if let Some(prefix) = prefixes.find(|&&prefix| {
431 s.starts_with(prefix)
432 && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ')
433 }) {
434 Some(&s[prefix.len() + 1..])
435 } else {
436 suffixes.find_map(|&suffix| s.strip_suffix(suffix))
437 }
468 }) 438 })
469 .map(|(ns, (_, _))| *ns) 439 .unwrap_or(s)
470 .next()
471} 440}
472 441
473/// Get the root URL for the documentation of a crate. 442/// Get the root URL for the documentation of a crate.