aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/hover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/hover.rs')
-rw-r--r--crates/ra_ide/src/hover.rs120
1 files changed, 53 insertions, 67 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 91167e30a..f703af434 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,8 +1,10 @@
1use std::iter::once; 1use std::iter::once;
2use std::path::PathBuf;
3use std::sync::Arc;
2 4
3use hir::{ 5use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 6 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
5 ModuleSource, Semantics, Module, Documentation, AttrDef, Crate 7 ModuleSource, Semantics, Documentation, AttrDef, Crate
6}; 8};
7use itertools::Itertools; 9use itertools::Itertools;
8use ra_db::SourceDatabase; 10use ra_db::SourceDatabase;
@@ -12,8 +14,12 @@ use ra_ide_db::{
12}; 14};
13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 15use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
14use ra_project_model::ProjectWorkspace; 16use ra_project_model::ProjectWorkspace;
15use ra_hir_def::{item_scope::ItemInNs, db::DefDatabase, ModuleDefId}; 17use ra_hir_def::{item_scope::ItemInNs, db::DefDatabase};
16use ra_tt::{Literal, Ident, Punct, TokenTree, Leaf, Subtree, SmolStr}; 18use ra_tt::{Literal, Ident, Punct, TokenTree, Leaf};
19
20use comrak::{parse_document,format_commonmark, ComrakOptions, Arena};
21use comrak::nodes::NodeValue;
22use url::Url;
17 23
18use crate::{ 24use crate::{
19 display::{ 25 display::{
@@ -68,13 +74,6 @@ pub struct HoverGotoTypeData {
68 pub nav: NavigationTarget, 74 pub nav: NavigationTarget,
69} 75}
70 76
71use std::path::{Path, PathBuf};
72use std::sync::Arc;
73use comrak::{parse_document,format_commonmark, ComrakOptions, Arena};
74use comrak::nodes::NodeValue;
75use url::Url;
76use ra_ide_db::imports_locator::ImportsLocator;
77
78/// Contains the results when hovering over an item 77/// Contains the results when hovering over an item
79#[derive(Debug, Default)] 78#[derive(Debug, Default)]
80pub struct HoverResult { 79pub struct HoverResult {
@@ -392,50 +391,42 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
392 391
393/// Rewrite documentation links in markdown to point to local documentation/docs.rs 392/// Rewrite documentation links in markdown to point to local documentation/docs.rs
394fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition, workspaces: Arc<Vec<ProjectWorkspace>>) -> Option<String> { 393fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition, workspaces: Arc<Vec<ProjectWorkspace>>) -> Option<String> {
395 // FIXME: Fail early 394 let arena = Arena::new();
396 if let (Some(name), Some(module)) = (definition.name(db), definition.module(db)) { 395 let doc = parse_document(&arena, markdown, &ComrakOptions::default());
397 let krate_name = module.krate().display_name(db)?; 396 let doc_target_dirs = workspaces
398 let arena = Arena::new(); 397 .iter()
399 let doc = parse_document(&arena, markdown, &ComrakOptions::default()); 398 .filter_map(|workspace| if let ProjectWorkspace::Cargo{cargo: cargo_workspace, ..} = workspace {Some(cargo_workspace)} else {None})
400 let path = module.path_to_root(db); 399 .map(|workspace| workspace.workspace_root())
401 let mut doc_target_dirs = workspaces 400 // TODO: `target` is configurable in cargo config, we should respect it
402 .iter() 401 .map(|root| root.join("target/doc"));
403 .filter_map(|workspace| if let ProjectWorkspace::Cargo{cargo: cargo_workspace, ..} = workspace {Some(cargo_workspace)} else {None}) 402
404 .map(|workspace| workspace.workspace_root()) 403 iter_nodes(doc, &|node| {
405 // TODO: `target` is configurable in cargo config, we should respect it 404 match &mut node.data.borrow_mut().value {
406 .map(|root| root.join("target/doc")); 405 &mut NodeValue::Link(ref mut link) => {
407 406 match Url::parse(&String::from_utf8(link.url.clone()).unwrap()) {
408 iter_nodes(doc, &|node| { 407 // If this is a valid absolute URL don't touch it
409 match &mut node.data.borrow_mut().value { 408 Ok(_) => (),
410 &mut NodeValue::Link(ref mut link) => { 409 // If contains .html file-based link to new page
411 match Url::parse(&String::from_utf8(link.url.clone()).unwrap()) { 410 // If starts with #fragment file-based link to fragment on current page
412 // If this is a valid absolute URL don't touch it 411 // If contains :: module-based link
413 Ok(_) => (), 412 Err(_) => {
414 // If contains .html file-based link to new page 413 let link_str = String::from_utf8(link.url.clone()).unwrap();
415 // If starts with #fragment file-based link to fragment on current page 414 let resolved = try_resolve_path(db, &mut doc_target_dirs.clone(), definition, &link_str, UrlMode::Url)
416 // If contains :: module-based link 415 .or_else(|| try_resolve_intra(db, &mut doc_target_dirs.clone(), definition, &link_str));
417 Err(_) => { 416
418 let link_str = String::from_utf8(link.url.clone()).unwrap(); 417 if let Some(resolved) = resolved {
419 let resolved = try_resolve_path(db, &mut doc_target_dirs.clone(), definition, &link_str, UrlMode::Url) 418 link.url = resolved.as_bytes().to_vec();
420 .or_else(|| try_resolve_intra(db, &mut doc_target_dirs.clone(), definition, &link_str));
421
422 if let Some(resolved) = resolved {
423 link.url = resolved.as_bytes().to_vec();
424 }
425
426 } 419 }
420
427 } 421 }
428 }, 422 }
429 _ => () 423 },
430 } 424 _ => ()
431 }); 425 }
432 let mut out = Vec::new(); 426 });
433 format_commonmark(doc, &ComrakOptions::default(), &mut out); 427 let mut out = Vec::new();
434 Some(String::from_utf8(out).unwrap()) 428 format_commonmark(doc, &ComrakOptions::default(), &mut out).ok()?;
435 } else { 429 Some(String::from_utf8(out).unwrap())
436 // eprintln!("WARN: Unable to determine name or module for hover; link rewriting disabled.");
437 None
438 }
439} 430}
440 431
441/// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`) 432/// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`)
@@ -467,13 +458,10 @@ fn try_resolve_path(db: &RootDatabase, doc_target_dirs: impl Iterator<Item = Pat
467 UrlMode::Url => { 458 UrlMode::Url => {
468 let root = get_doc_url(db, &krate); 459 let root = get_doc_url(db, &krate);
469 let mut base = base.join("/"); 460 let mut base = base.join("/");
470 if let Some(url) = root { 461 if link.starts_with("#") {
471 eprintln!("root: {:?} base: {:?} link: {} root&base: {} root&base&link: {}", url, &base, link, url.join(&base).unwrap(), url.join(&base).unwrap().join(link).unwrap()); 462 base = base + "/"
472 if link.starts_with("#") { 463 };
473 base = base + "/" 464 root.and_then(|url| url.join(&base).ok()).and_then(|url| url.join(link).ok()).map(|url| url.into_string())
474 };
475 Some(url.join(&base)?.join(link)?.into_string())
476 } else {None}
477 }, 465 },
478 UrlMode::File => { 466 UrlMode::File => {
479 let base = base.collect::<PathBuf>(); 467 let base = base.collect::<PathBuf>();
@@ -491,7 +479,7 @@ fn try_resolve_path(db: &RootDatabase, doc_target_dirs: impl Iterator<Item = Pat
491 479
492/// Try to get the root URL of the documentation of a crate. 480/// Try to get the root URL of the documentation of a crate.
493fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> { 481fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
494 // Look for #![doc(html_root_url = "https://docs.rs/...")] 482 // Look for #![doc(html_root_url = "...")]
495 let attrs = db.attrs(AttrDef::from(krate.root_module(db)?).into()); 483 let attrs = db.attrs(AttrDef::from(krate.root_module(db)?).into());
496 let doc_attr_q = attrs.by_key("doc"); 484 let doc_attr_q = attrs.by_key("doc");
497 let doc_url = if doc_attr_q.exists() { 485 let doc_url = if doc_attr_q.exists() {
@@ -500,20 +488,18 @@ fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
500 TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident_text, ..})), 488 TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident_text, ..})),
501 TokenTree::Leaf(Leaf::Punct(Punct{r#char: '=', ..})), 489 TokenTree::Leaf(Leaf::Punct(Punct{r#char: '=', ..})),
502 TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..})) 490 TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))
503 ] if ident_text == "html_root_url" => Some(text), 491 ] if ident_text == "html_root_url" => Some(text.to_string()),
504 _ => { 492 _ => {
505 None 493 None
506 } 494 }
507 }).next() 495 }).next()
508 } else { 496 } else {
509 None 497 // Fallback to docs.rs
498 // TODO: Specify an exact version here (from Cargo.lock)
499 Some(format!("https://docs.rs/{}/*", krate.display_name(db)?))
510 }; 500 };
511 eprintln!("url {:?}", doc_url);
512 501
513 // TODO: It should be possible to fallback to `format!("https://docs.rs/{}/*", crate_name, *)` 502 doc_url.map(|s| s.trim_matches('"').to_owned() + "/").and_then(|s| Url::parse(&s).ok())
514 let url = doc_url.map(|s| s.trim_matches('"').to_owned() + "/").and_then(|s| Url::parse(&s).ok());
515 eprintln!("url {:?}", url);
516 url
517} 503}
518 504
519fn iter_nodes<'a, F>(node: &'a comrak::nodes::AstNode<'a>, f: &F) 505fn iter_nodes<'a, F>(node: &'a comrak::nodes::AstNode<'a>, f: &F)