aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/doc_links.rs
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-09-01 09:26:10 +0100
committerZac Pullar-Strecker <[email protected]>2020-10-08 03:04:20 +0100
commit974518fde7975b839ed4ccd4c5ce1d48cd6db3c7 (patch)
treecf25ef47c5531ca9934842a963afaabe78fc525f /crates/ide/src/doc_links.rs
parenta14194b428efdb09cc45f9862ec34bef0038cd35 (diff)
Code reorganisation and field support
Diffstat (limited to 'crates/ide/src/doc_links.rs')
-rw-r--r--crates/ide/src/doc_links.rs99
1 files changed, 46 insertions, 53 deletions
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 7fe88577d..512c42c4d 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -2,20 +2,27 @@
2//! 2//!
3//! Most of the implementation can be found in [`hir::doc_links`]. 3//! Most of the implementation can be found in [`hir::doc_links`].
4 4
5use hir::{Adt, Crate, HasAttrs, ModuleDef}; 5use std::iter::once;
6use ide_db::{defs::Definition, RootDatabase}; 6
7use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag}; 7use itertools::Itertools;
8use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; 8use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions};
9use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag};
9use url::Url; 10use url::Url;
10 11
11use crate::{FilePosition, Semantics}; 12use ide_db::{defs::Definition, RootDatabase};
12use hir::{get_doc_link, resolve_doc_link}; 13
14use hir::{
15 db::{DefDatabase, HirDatabase},
16 Adt, AsName, AssocItem, Crate, Field, HasAttrs, ItemInNs, ModuleDef,
17};
13use ide_db::{ 18use ide_db::{
14 defs::{classify_name, classify_name_ref, Definition}, 19 defs::{classify_name, classify_name_ref, Definition},
15 RootDatabase, 20 RootDatabase,
16}; 21};
17use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 22use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
18 23
24use crate::{FilePosition, Semantics};
25
19pub type DocumentationLink = String; 26pub type DocumentationLink = String;
20 27
21/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) 28/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
@@ -100,64 +107,58 @@ pub fn get_doc_link<T: Resolvable + Clone>(db: &dyn HirDatabase, definition: &T)
100// BUG: For Option 107// BUG: For Option
101// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some 108// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some
102// Instead of https://doc.rust-lang.org/nightly/core/option/enum.Option.html 109// Instead of https://doc.rust-lang.org/nightly/core/option/enum.Option.html
103fn get_doc_link_impl(db: &dyn HirDatabase, moddef: &ModuleDef) -> Option<String> { 110// This could be worked around by turning the `EnumVariant` into `Enum` before attempting resolution,
111// but it's really just working around the problem. Ideally we need to implement a slightly different
112// version of import map which follows the same process as rustdoc. Otherwise there'll always be some
113// edge cases where we select the wrong import path.
114fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
104 // Get the outermost definition for the moduledef. This is used to resolve the public path to the type, 115 // Get the outermost definition for the moduledef. This is used to resolve the public path to the type,
105 // then we can join the method, field, etc onto it if required. 116 // then we can join the method, field, etc onto it if required.
106 let target_def: ModuleDef = match moddef { 117 let target_def: ModuleDef = match definition {
107 ModuleDef::Function(f) => match f.as_assoc_item(db).map(|assoc| assoc.container(db)) { 118 Definition::ModuleDef(moddef) => match moddef {
108 Some(AssocItemContainer::Trait(t)) => t.into(), 119 ModuleDef::Function(f) => {
109 Some(AssocItemContainer::ImplDef(imp)) => { 120 f.parent_def(db).map(|mowner| mowner.into()).unwrap_or_else(|| f.clone().into())
110 let resolver = ModuleId::from(imp.module(db)).resolver(db.upcast());
111 let ctx = TyLoweringContext::new(db, &resolver);
112 Adt::from(
113 Ty::from_hir(
114 &ctx,
115 &imp.target_trait(db).unwrap_or_else(|| imp.target_type(db)),
116 )
117 .as_adt()
118 .map(|t| t.0)
119 .unwrap(),
120 )
121 .into()
122 } 121 }
123 None => ModuleDef::Function(*f), 122 moddef => moddef,
124 }, 123 },
125 moddef => *moddef, 124 Definition::Field(f) => f.parent_def(db).into(),
125 // FIXME: Handle macros
126 _ => return None,
126 }; 127 };
127 128
128 let ns = ItemInNs::Types(target_def.clone().into()); 129 let ns = ItemInNs::Types(target_def.clone().into());
129 130
130 let module = moddef.module(db)?; 131 let module = definition.module(db)?;
131 let krate = module.krate(); 132 let krate = module.krate();
132 let import_map = db.import_map(krate.into()); 133 let import_map = db.import_map(krate.into());
133 let base = once(krate.display_name(db).unwrap()) 134 let base = once(krate.display_name(db).unwrap())
134 .chain(import_map.path_of(ns).unwrap().segments.iter().map(|name| format!("{}", name))) 135 .chain(import_map.path_of(ns).unwrap().segments.iter().map(|name| format!("{}", name)))
135 .join("/"); 136 .join("/");
136 137
137 get_doc_url(db, &krate) 138 let filename = get_symbol_filename(db, &target_def);
138 .and_then(|url| url.join(&base).ok()) 139 let fragment = match definition {
139 .and_then(|url| { 140 Definition::ModuleDef(moddef) => match moddef {
140 get_symbol_filename(db, &target_def).as_deref().and_then(|f| url.join(f).ok())
141 })
142 .and_then(|url| match moddef {
143 ModuleDef::Function(f) => { 141 ModuleDef::Function(f) => {
144 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(*f))) 142 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f)))
145 .as_deref()
146 .and_then(|f| url.join(f).ok())
147 } 143 }
148 ModuleDef::Const(c) => { 144 ModuleDef::Const(c) => {
149 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Const(*c))) 145 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Const(c)))
150 .as_deref()
151 .and_then(|f| url.join(f).ok())
152 } 146 }
153 ModuleDef::TypeAlias(ty) => { 147 ModuleDef::TypeAlias(ty) => {
154 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::TypeAlias(*ty))) 148 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::TypeAlias(ty)))
155 .as_deref()
156 .and_then(|f| url.join(f).ok())
157 } 149 }
158 // TODO: Field <- this requires passing in a definition or something 150 _ => None,
159 _ => Some(url), 151 },
160 }) 152 Definition::Field(field) => get_symbol_fragment(db, &FieldOrAssocItem::Field(field)),
153 _ => None,
154 };
155
156 get_doc_url(db, &krate)
157 .and_then(|url| url.join(&base).ok())
158 .and_then(|url| filename.as_deref().and_then(|f| url.join(f).ok()))
159 .and_then(
160 |url| if let Some(fragment) = fragment { url.join(&fragment).ok() } else { Some(url) },
161 )
161 .map(|url| url.into_string()) 162 .map(|url| url.into_string())
162} 163}
163 164
@@ -219,9 +220,8 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
219 .map(|url| url.into_string()) 220 .map(|url| url.into_string())
220} 221}
221 222
222// FIXME: This should either be moved, or the module should be renamed.
223/// Retrieve a link to documentation for the given symbol. 223/// Retrieve a link to documentation for the given symbol.
224pub fn get_doc_url(db: &RootDatabase, position: &FilePosition) -> Option<DocumentationLink> { 224pub fn external_docs(db: &RootDatabase, position: &FilePosition) -> Option<DocumentationLink> {
225 let sema = Semantics::new(db); 225 let sema = Semantics::new(db);
226 let file = sema.parse(position.file_id).syntax().clone(); 226 let file = sema.parse(position.file_id).syntax().clone();
227 let token = pick_best(file.token_at_offset(position.offset))?; 227 let token = pick_best(file.token_at_offset(position.offset))?;
@@ -236,14 +236,7 @@ pub fn get_doc_url(db: &RootDatabase, position: &FilePosition) -> Option<Documen
236 } 236 }
237 }; 237 };
238 238
239 match definition? { 239 get_doc_link(db, definition?)
240 Definition::Macro(t) => get_doc_link(db, &t),
241 Definition::Field(t) => get_doc_link(db, &t),
242 Definition::ModuleDef(t) => get_doc_link(db, &t),
243 Definition::SelfType(t) => get_doc_link(db, &t),
244 Definition::Local(t) => get_doc_link(db, &t),
245 Definition::TypeParam(t) => get_doc_link(db, &t),
246 }
247} 240}
248 241
249/// Rewrites a markdown document, applying 'callback' to each link. 242/// Rewrites a markdown document, applying 'callback' to each link.