diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/hir/src/code_model.rs | 103 | ||||
-rw-r--r-- | crates/hir/src/doc_links.rs | 233 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ide/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 597 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ide/src/link_rewrite.rs | 81 | ||||
-rw-r--r-- | crates/ide/src/mock_analysis.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/rust-analyzer/main.rs | 2 |
10 files changed, 969 insertions, 61 deletions
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 60a48170e..72f941c46 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml | |||
@@ -15,6 +15,7 @@ rustc-hash = "1.1.0" | |||
15 | either = "1.5.3" | 15 | either = "1.5.3" |
16 | arrayvec = "0.5.1" | 16 | arrayvec = "0.5.1" |
17 | itertools = "0.9.0" | 17 | itertools = "0.9.0" |
18 | url = "2.1.1" | ||
18 | 19 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 20 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 21 | syntax = { path = "../syntax", version = "0.0.0" } |
@@ -23,3 +24,4 @@ profile = { path = "../profile", version = "0.0.0" } | |||
23 | hir_expand = { path = "../hir_expand", version = "0.0.0" } | 24 | hir_expand = { path = "../hir_expand", version = "0.0.0" } |
24 | hir_def = { path = "../hir_def", version = "0.0.0" } | 25 | hir_def = { path = "../hir_def", version = "0.0.0" } |
25 | hir_ty = { path = "../hir_ty", version = "0.0.0" } | 26 | hir_ty = { path = "../hir_ty", version = "0.0.0" } |
27 | tt = { path = "../tt", version = "0.0.0" } | ||
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 3d92d0c0d..94dd7f6f5 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -20,7 +20,7 @@ use hir_def::{ | |||
20 | type_ref::{Mutability, TypeRef}, | 20 | type_ref::{Mutability, TypeRef}, |
21 | AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, | 21 | AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, |
22 | ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, | 22 | ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, |
23 | TraitId, TypeAliasId, TypeParamId, UnionId, | 23 | TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, |
24 | }; | 24 | }; |
25 | use hir_expand::{ | 25 | use hir_expand::{ |
26 | diagnostics::DiagnosticSink, | 26 | diagnostics::DiagnosticSink, |
@@ -39,9 +39,11 @@ use syntax::{ | |||
39 | ast::{self, AttrsOwner, NameOwner}, | 39 | ast::{self, AttrsOwner, NameOwner}, |
40 | AstNode, SmolStr, | 40 | AstNode, SmolStr, |
41 | }; | 41 | }; |
42 | use tt::{Ident, Leaf, Literal, TokenTree}; | ||
42 | 43 | ||
43 | use crate::{ | 44 | use crate::{ |
44 | db::{DefDatabase, HirDatabase}, | 45 | db::{DefDatabase, HirDatabase}, |
46 | doc_links::Resolvable, | ||
45 | has_source::HasSource, | 47 | has_source::HasSource, |
46 | HirDisplay, InFile, Name, | 48 | HirDisplay, InFile, Name, |
47 | }; | 49 | }; |
@@ -122,6 +124,31 @@ impl Crate { | |||
122 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | 124 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { |
123 | db.crate_graph().iter().map(|id| Crate { id }).collect() | 125 | db.crate_graph().iter().map(|id| Crate { id }).collect() |
124 | } | 126 | } |
127 | |||
128 | /// Try to get the root URL of the documentation of a crate. | ||
129 | pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> { | ||
130 | // Look for #![doc(html_root_url = "...")] | ||
131 | let attrs = db.attrs(AttrDef::from(self.root_module(db)).into()); | ||
132 | let doc_attr_q = attrs.by_key("doc"); | ||
133 | |||
134 | if !doc_attr_q.exists() { | ||
135 | return None; | ||
136 | } | ||
137 | |||
138 | let doc_url = doc_attr_q.tt_values().map(|tt| { | ||
139 | let name = tt.token_trees.iter() | ||
140 | .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url")) | ||
141 | .skip(2) | ||
142 | .next(); | ||
143 | |||
144 | match name { | ||
145 | Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), | ||
146 | _ => None | ||
147 | } | ||
148 | }).flat_map(|t| t).next(); | ||
149 | |||
150 | doc_url.map(|s| s.trim_matches('"').trim_end_matches("/").to_owned() + "/") | ||
151 | } | ||
125 | } | 152 | } |
126 | 153 | ||
127 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 154 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -198,7 +225,6 @@ impl ModuleDef { | |||
198 | ModuleDef::Function(it) => Some(it.name(db)), | 225 | ModuleDef::Function(it) => Some(it.name(db)), |
199 | ModuleDef::EnumVariant(it) => Some(it.name(db)), | 226 | ModuleDef::EnumVariant(it) => Some(it.name(db)), |
200 | ModuleDef::TypeAlias(it) => Some(it.name(db)), | 227 | ModuleDef::TypeAlias(it) => Some(it.name(db)), |
201 | |||
202 | ModuleDef::Module(it) => it.name(db), | 228 | ModuleDef::Module(it) => it.name(db), |
203 | ModuleDef::Const(it) => it.name(db), | 229 | ModuleDef::Const(it) => it.name(db), |
204 | ModuleDef::Static(it) => it.name(db), | 230 | ModuleDef::Static(it) => it.name(db), |
@@ -1771,3 +1797,76 @@ pub trait HasVisibility { | |||
1771 | vis.is_visible_from(db.upcast(), module.id) | 1797 | vis.is_visible_from(db.upcast(), module.id) |
1772 | } | 1798 | } |
1773 | } | 1799 | } |
1800 | |||
1801 | impl Resolvable for ModuleDef { | ||
1802 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1803 | Some(match self { | ||
1804 | ModuleDef::Module(m) => ModuleId::from(m.clone()).resolver(db), | ||
1805 | ModuleDef::Function(f) => FunctionId::from(f.clone()).resolver(db), | ||
1806 | ModuleDef::Adt(adt) => AdtId::from(adt.clone()).resolver(db), | ||
1807 | ModuleDef::EnumVariant(ev) => { | ||
1808 | GenericDefId::from(GenericDef::from(ev.clone())).resolver(db) | ||
1809 | } | ||
1810 | ModuleDef::Const(c) => GenericDefId::from(GenericDef::from(c.clone())).resolver(db), | ||
1811 | ModuleDef::Static(s) => StaticId::from(s.clone()).resolver(db), | ||
1812 | ModuleDef::Trait(t) => TraitId::from(t.clone()).resolver(db), | ||
1813 | ModuleDef::TypeAlias(t) => ModuleId::from(t.module(db)).resolver(db), | ||
1814 | // FIXME: This should be a resolver relative to `std/core` | ||
1815 | ModuleDef::BuiltinType(_t) => None?, | ||
1816 | }) | ||
1817 | } | ||
1818 | |||
1819 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1820 | Some(self) | ||
1821 | } | ||
1822 | } | ||
1823 | |||
1824 | impl Resolvable for TypeParam { | ||
1825 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1826 | Some(Into::<ModuleId>::into(self.module(db)).resolver(db)) | ||
1827 | } | ||
1828 | |||
1829 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1830 | None | ||
1831 | } | ||
1832 | } | ||
1833 | |||
1834 | impl Resolvable for MacroDef { | ||
1835 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1836 | Some(Into::<ModuleId>::into(self.module(db)?).resolver(db)) | ||
1837 | } | ||
1838 | |||
1839 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1840 | None | ||
1841 | } | ||
1842 | } | ||
1843 | |||
1844 | impl Resolvable for Field { | ||
1845 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1846 | Some(Into::<VariantId>::into(Into::<VariantDef>::into(self.parent_def(db))).resolver(db)) | ||
1847 | } | ||
1848 | |||
1849 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1850 | None | ||
1851 | } | ||
1852 | } | ||
1853 | |||
1854 | impl Resolvable for ImplDef { | ||
1855 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1856 | Some(Into::<ModuleId>::into(self.module(db)).resolver(db)) | ||
1857 | } | ||
1858 | |||
1859 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1860 | None | ||
1861 | } | ||
1862 | } | ||
1863 | |||
1864 | impl Resolvable for Local { | ||
1865 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1866 | Some(Into::<ModuleId>::into(self.module(db)).resolver(db)) | ||
1867 | } | ||
1868 | |||
1869 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1870 | None | ||
1871 | } | ||
1872 | } | ||
diff --git a/crates/hir/src/doc_links.rs b/crates/hir/src/doc_links.rs new file mode 100644 index 000000000..dd2379bfc --- /dev/null +++ b/crates/hir/src/doc_links.rs | |||
@@ -0,0 +1,233 @@ | |||
1 | //! Resolves links in markdown documentation. | ||
2 | |||
3 | use std::iter::once; | ||
4 | |||
5 | use itertools::Itertools; | ||
6 | use url::Url; | ||
7 | |||
8 | use crate::{db::HirDatabase, Adt, AsName, Crate, Hygiene, ItemInNs, ModPath, ModuleDef}; | ||
9 | use hir_def::{db::DefDatabase, resolver::Resolver}; | ||
10 | use syntax::ast::Path; | ||
11 | |||
12 | pub fn resolve_doc_link<T: Resolvable + Clone, D: DefDatabase + HirDatabase>( | ||
13 | db: &D, | ||
14 | definition: &T, | ||
15 | link_text: &str, | ||
16 | link_target: &str, | ||
17 | ) -> Option<(String, String)> { | ||
18 | try_resolve_intra(db, definition, link_text, &link_target).or_else(|| { | ||
19 | if let Some(definition) = definition.clone().try_into_module_def() { | ||
20 | try_resolve_path(db, &definition, &link_target) | ||
21 | .map(|target| (target, link_text.to_string())) | ||
22 | } else { | ||
23 | None | ||
24 | } | ||
25 | }) | ||
26 | } | ||
27 | |||
28 | /// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`). | ||
29 | /// | ||
30 | /// See [RFC1946](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md). | ||
31 | fn try_resolve_intra<T: Resolvable, D: DefDatabase + HirDatabase>( | ||
32 | db: &D, | ||
33 | definition: &T, | ||
34 | link_text: &str, | ||
35 | link_target: &str, | ||
36 | ) -> Option<(String, String)> { | ||
37 | // Set link_target for implied shortlinks | ||
38 | let link_target = | ||
39 | if link_target.is_empty() { link_text.trim_matches('`') } else { link_target }; | ||
40 | |||
41 | let doclink = IntraDocLink::from(link_target); | ||
42 | |||
43 | // Parse link as a module path | ||
44 | let path = Path::parse(doclink.path).ok()?; | ||
45 | let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap(); | ||
46 | |||
47 | // Resolve it relative to symbol's location (according to the RFC this should consider small scopes) | ||
48 | let resolver = definition.resolver(db)?; | ||
49 | |||
50 | let resolved = resolver.resolve_module_path_in_items(db, &modpath); | ||
51 | let (defid, namespace) = match doclink.namespace { | ||
52 | // FIXME: .or(resolved.macros) | ||
53 | None => resolved | ||
54 | .types | ||
55 | .map(|t| (t.0, Namespace::Types)) | ||
56 | .or(resolved.values.map(|t| (t.0, Namespace::Values)))?, | ||
57 | Some(ns @ Namespace::Types) => (resolved.types?.0, ns), | ||
58 | Some(ns @ Namespace::Values) => (resolved.values?.0, ns), | ||
59 | // FIXME: | ||
60 | Some(Namespace::Macros) => None?, | ||
61 | }; | ||
62 | |||
63 | // Get the filepath of the final symbol | ||
64 | let def: ModuleDef = defid.into(); | ||
65 | let module = def.module(db)?; | ||
66 | let krate = module.krate(); | ||
67 | let ns = match namespace { | ||
68 | Namespace::Types => ItemInNs::Types(defid), | ||
69 | Namespace::Values => ItemInNs::Values(defid), | ||
70 | // FIXME: | ||
71 | Namespace::Macros => None?, | ||
72 | }; | ||
73 | let import_map = db.import_map(krate.into()); | ||
74 | let path = import_map.path_of(ns)?; | ||
75 | |||
76 | Some(( | ||
77 | get_doc_url(db, &krate)? | ||
78 | .join(&format!("{}/", krate.display_name(db)?)) | ||
79 | .ok()? | ||
80 | .join(&path.segments.iter().map(|name| name.to_string()).join("/")) | ||
81 | .ok()? | ||
82 | .join(&get_symbol_filename(db, &def)?) | ||
83 | .ok()? | ||
84 | .into_string(), | ||
85 | strip_prefixes_suffixes(link_text).to_string(), | ||
86 | )) | ||
87 | } | ||
88 | |||
89 | /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). | ||
90 | fn try_resolve_path(db: &dyn HirDatabase, moddef: &ModuleDef, link_target: &str) -> Option<String> { | ||
91 | if !link_target.contains("#") && !link_target.contains(".html") { | ||
92 | return None; | ||
93 | } | ||
94 | let ns = ItemInNs::Types(moddef.clone().into()); | ||
95 | |||
96 | let module = moddef.module(db)?; | ||
97 | let krate = module.krate(); | ||
98 | let import_map = db.import_map(krate.into()); | ||
99 | let base = once(format!("{}", krate.display_name(db)?)) | ||
100 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| format!("{}", name))) | ||
101 | .join("/"); | ||
102 | |||
103 | get_doc_url(db, &krate) | ||
104 | .and_then(|url| url.join(&base).ok()) | ||
105 | .and_then(|url| { | ||
106 | get_symbol_filename(db, moddef).as_deref().map(|f| url.join(f).ok()).flatten() | ||
107 | }) | ||
108 | .and_then(|url| url.join(link_target).ok()) | ||
109 | .map(|url| url.into_string()) | ||
110 | } | ||
111 | |||
112 | /// Strip prefixes, suffixes, and inline code marks from the given string. | ||
113 | fn strip_prefixes_suffixes(mut s: &str) -> &str { | ||
114 | s = s.trim_matches('`'); | ||
115 | |||
116 | [ | ||
117 | (TYPES.0.iter(), TYPES.1.iter()), | ||
118 | (VALUES.0.iter(), VALUES.1.iter()), | ||
119 | (MACROS.0.iter(), MACROS.1.iter()), | ||
120 | ] | ||
121 | .iter() | ||
122 | .for_each(|(prefixes, suffixes)| { | ||
123 | prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix)); | ||
124 | suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix)); | ||
125 | }); | ||
126 | let s = s.trim_start_matches("@").trim(); | ||
127 | s | ||
128 | } | ||
129 | |||
130 | fn get_doc_url(db: &dyn HirDatabase, krate: &Crate) -> Option<Url> { | ||
131 | krate | ||
132 | .get_html_root_url(db) | ||
133 | .or_else(|| | ||
134 | // Fallback to docs.rs | ||
135 | // FIXME: Specify an exact version here. This may be difficult, as multiple versions of the same crate could exist. | ||
136 | Some(format!("https://docs.rs/{}/*/", krate.display_name(db)?))) | ||
137 | .and_then(|s| Url::parse(&s).ok()) | ||
138 | } | ||
139 | |||
140 | /// Get the filename and extension generated for a symbol by rustdoc. | ||
141 | /// | ||
142 | /// Example: `struct.Shard.html` | ||
143 | fn get_symbol_filename(db: &dyn HirDatabase, definition: &ModuleDef) -> Option<String> { | ||
144 | Some(match definition { | ||
145 | ModuleDef::Adt(adt) => match adt { | ||
146 | Adt::Struct(s) => format!("struct.{}.html", s.name(db)), | ||
147 | Adt::Enum(e) => format!("enum.{}.html", e.name(db)), | ||
148 | Adt::Union(u) => format!("union.{}.html", u.name(db)), | ||
149 | }, | ||
150 | ModuleDef::Module(_) => "index.html".to_string(), | ||
151 | ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)), | ||
152 | ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)), | ||
153 | ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.as_name()), | ||
154 | ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)), | ||
155 | ModuleDef::EnumVariant(ev) => { | ||
156 | format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) | ||
157 | } | ||
158 | ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?), | ||
159 | ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?), | ||
160 | }) | ||
161 | } | ||
162 | |||
163 | struct IntraDocLink<'s> { | ||
164 | path: &'s str, | ||
165 | namespace: Option<Namespace>, | ||
166 | } | ||
167 | |||
168 | impl<'s> From<&'s str> for IntraDocLink<'s> { | ||
169 | fn from(s: &'s str) -> Self { | ||
170 | Self { path: strip_prefixes_suffixes(s), namespace: Namespace::from_intra_spec(s) } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] | ||
175 | enum Namespace { | ||
176 | Types, | ||
177 | Values, | ||
178 | Macros, | ||
179 | } | ||
180 | |||
181 | static TYPES: ([&str; 7], [&str; 0]) = | ||
182 | (["type", "struct", "enum", "mod", "trait", "union", "module"], []); | ||
183 | static VALUES: ([&str; 8], [&str; 1]) = | ||
184 | (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]); | ||
185 | static MACROS: ([&str; 1], [&str; 1]) = (["macro"], ["!"]); | ||
186 | |||
187 | impl Namespace { | ||
188 | /// Extract the specified namespace from an intra-doc-link if one exists. | ||
189 | /// | ||
190 | /// # Examples | ||
191 | /// | ||
192 | /// * `struct MyStruct` -> `Namespace::Types` | ||
193 | /// * `panic!` -> `Namespace::Macros` | ||
194 | /// * `fn@from_intra_spec` -> `Namespace::Values` | ||
195 | fn from_intra_spec(s: &str) -> Option<Self> { | ||
196 | [ | ||
197 | (Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())), | ||
198 | (Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())), | ||
199 | (Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())), | ||
200 | ] | ||
201 | .iter() | ||
202 | .filter(|(_ns, (prefixes, suffixes))| { | ||
203 | prefixes | ||
204 | .clone() | ||
205 | .map(|prefix| { | ||
206 | s.starts_with(*prefix) | ||
207 | && s.chars() | ||
208 | .nth(prefix.len() + 1) | ||
209 | .map(|c| c == '@' || c == ' ') | ||
210 | .unwrap_or(false) | ||
211 | }) | ||
212 | .any(|cond| cond) | ||
213 | || suffixes | ||
214 | .clone() | ||
215 | .map(|suffix| { | ||
216 | s.starts_with(*suffix) | ||
217 | && s.chars() | ||
218 | .nth(suffix.len() + 1) | ||
219 | .map(|c| c == '@' || c == ' ') | ||
220 | .unwrap_or(false) | ||
221 | }) | ||
222 | .any(|cond| cond) | ||
223 | }) | ||
224 | .map(|(ns, (_, _))| *ns) | ||
225 | .next() | ||
226 | } | ||
227 | } | ||
228 | |||
229 | /// Sealed trait used solely for the generic bound on [`resolve_doc_link`]. | ||
230 | pub trait Resolvable { | ||
231 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver>; | ||
232 | fn try_into_module_def(self) -> Option<ModuleDef>; | ||
233 | } | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 78d8651cb..d1f4d7813 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -27,6 +27,7 @@ pub mod diagnostics; | |||
27 | 27 | ||
28 | mod from_id; | 28 | mod from_id; |
29 | mod code_model; | 29 | mod code_model; |
30 | mod doc_links; | ||
30 | 31 | ||
31 | mod has_source; | 32 | mod has_source; |
32 | 33 | ||
@@ -37,6 +38,7 @@ pub use crate::{ | |||
37 | Function, GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, | 38 | Function, GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, |
38 | ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, | 39 | ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, |
39 | }, | 40 | }, |
41 | doc_links::resolve_doc_link, | ||
40 | has_source::HasSource, | 42 | has_source::HasSource, |
41 | semantics::{original_range, PathResolution, Semantics, SemanticsScope}, | 43 | semantics::{original_range, PathResolution, Semantics, SemanticsScope}, |
42 | }; | 44 | }; |
@@ -47,13 +49,14 @@ pub use hir_def::{ | |||
47 | body::scope::ExprScopes, | 49 | body::scope::ExprScopes, |
48 | builtin_type::BuiltinType, | 50 | builtin_type::BuiltinType, |
49 | docs::Documentation, | 51 | docs::Documentation, |
52 | item_scope::ItemInNs, | ||
50 | nameres::ModuleSource, | 53 | nameres::ModuleSource, |
51 | path::ModPath, | 54 | path::ModPath, |
52 | type_ref::{Mutability, TypeRef}, | 55 | type_ref::{Mutability, TypeRef}, |
53 | }; | 56 | }; |
54 | pub use hir_expand::{ | 57 | pub use hir_expand::{ |
55 | name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId, | 58 | name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, |
56 | MacroFile, Origin, | 59 | /* FIXME */ MacroDefId, MacroFile, Origin, |
57 | }; | 60 | }; |
58 | pub use hir_ty::display::HirDisplay; | 61 | pub use hir_ty::display::HirDisplay; |
59 | 62 | ||
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 336e9d2aa..e61c276df 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml | |||
@@ -16,6 +16,8 @@ itertools = "0.9.0" | |||
16 | log = "0.4.8" | 16 | log = "0.4.8" |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | oorandom = "11.1.2" | 18 | oorandom = "11.1.2" |
19 | pulldown-cmark-to-cmark = "5.0.0" | ||
20 | pulldown-cmark = {version = "0.7.2", default-features = false} | ||
19 | 21 | ||
20 | stdx = { path = "../stdx", version = "0.0.0" } | 22 | stdx = { path = "../stdx", version = "0.0.0" } |
21 | syntax = { path = "../syntax", version = "0.0.0" } | 23 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index c75b2a510..b012e4900 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -14,6 +14,7 @@ use test_utils::mark; | |||
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | display::{macro_label, ShortLabel, ToNav, TryToNav}, | 16 | display::{macro_label, ShortLabel, ToNav, TryToNav}, |
17 | link_rewrite::rewrite_links, | ||
17 | markup::Markup, | 18 | markup::Markup, |
18 | runnables::runnable, | 19 | runnables::runnable, |
19 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, | 20 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, |
@@ -92,7 +93,8 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
92 | }; | 93 | }; |
93 | if let Some(definition) = definition { | 94 | if let Some(definition) = definition { |
94 | if let Some(markup) = hover_for_definition(db, definition) { | 95 | if let Some(markup) = hover_for_definition(db, definition) { |
95 | res.markup = markup; | 96 | let markup = rewrite_links(db, &markup.as_str(), &definition); |
97 | res.markup = Markup::from(markup); | ||
96 | if let Some(action) = show_implementations_action(db, definition) { | 98 | if let Some(action) = show_implementations_action(db, definition) { |
97 | res.actions.push(action); | 99 | res.actions.push(action); |
98 | } | 100 | } |
@@ -425,6 +427,7 @@ fn main() { | |||
425 | "#, | 427 | "#, |
426 | expect![[r#" | 428 | expect![[r#" |
427 | *iter* | 429 | *iter* |
430 | |||
428 | ```rust | 431 | ```rust |
429 | Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> | 432 | Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> |
430 | ``` | 433 | ``` |
@@ -443,6 +446,11 @@ fn main() { let foo_test = fo<|>o(); } | |||
443 | "#, | 446 | "#, |
444 | expect![[r#" | 447 | expect![[r#" |
445 | *foo* | 448 | *foo* |
449 | |||
450 | ```rust | ||
451 | test | ||
452 | ``` | ||
453 | |||
446 | ```rust | 454 | ```rust |
447 | pub fn foo() -> u32 | 455 | pub fn foo() -> u32 |
448 | ``` | 456 | ``` |
@@ -487,6 +495,11 @@ fn main() { let foo_test = fo<|>o(); } | |||
487 | "#, | 495 | "#, |
488 | expect![[r#" | 496 | expect![[r#" |
489 | *foo* | 497 | *foo* |
498 | |||
499 | ```rust | ||
500 | test | ||
501 | ``` | ||
502 | |||
490 | ```rust | 503 | ```rust |
491 | pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str | 504 | pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str |
492 | ``` | 505 | ``` |
@@ -504,6 +517,11 @@ fn main() { } | |||
504 | "#, | 517 | "#, |
505 | expect![[r#" | 518 | expect![[r#" |
506 | *foo* | 519 | *foo* |
520 | |||
521 | ```rust | ||
522 | test | ||
523 | ``` | ||
524 | |||
507 | ```rust | 525 | ```rust |
508 | pub fn foo(a: u32, b: u32) -> u32 | 526 | pub fn foo(a: u32, b: u32) -> u32 |
509 | ``` | 527 | ``` |
@@ -525,20 +543,27 @@ pub fn foo<|>(_: &Path) {} | |||
525 | 543 | ||
526 | fn main() { } | 544 | fn main() { } |
527 | "#, | 545 | "#, |
528 | expect![[r#" | 546 | expect![[r##" |
529 | *foo* | 547 | *foo* |
548 | |||
549 | ```rust | ||
550 | test | ||
551 | ``` | ||
552 | |||
530 | ```rust | 553 | ```rust |
531 | pub fn foo(_: &Path) | 554 | pub fn foo(_: &Path) |
532 | ``` | 555 | ``` |
533 | ___ | 556 | |
557 | --- | ||
534 | 558 | ||
535 | # Example | 559 | # Example |
560 | |||
536 | ``` | 561 | ``` |
537 | # use std::path::Path; | 562 | # use std::path::Path; |
538 | # | 563 | # |
539 | foo(Path::new("hello, world!")) | 564 | foo(Path::new("hello, world!")) |
540 | ``` | 565 | ``` |
541 | "#]], | 566 | "##]], |
542 | ); | 567 | ); |
543 | } | 568 | } |
544 | 569 | ||
@@ -555,8 +580,9 @@ fn main() { | |||
555 | "#, | 580 | "#, |
556 | expect![[r#" | 581 | expect![[r#" |
557 | *field_a* | 582 | *field_a* |
583 | |||
558 | ```rust | 584 | ```rust |
559 | Foo | 585 | test::Foo |
560 | ``` | 586 | ``` |
561 | 587 | ||
562 | ```rust | 588 | ```rust |
@@ -576,8 +602,9 @@ fn main() { | |||
576 | "#, | 602 | "#, |
577 | expect![[r#" | 603 | expect![[r#" |
578 | *field_a* | 604 | *field_a* |
605 | |||
579 | ```rust | 606 | ```rust |
580 | Foo | 607 | test::Foo |
581 | ``` | 608 | ``` |
582 | 609 | ||
583 | ```rust | 610 | ```rust |
@@ -593,6 +620,11 @@ fn main() { | |||
593 | r#"const foo<|>: u32 = 123;"#, | 620 | r#"const foo<|>: u32 = 123;"#, |
594 | expect![[r#" | 621 | expect![[r#" |
595 | *foo* | 622 | *foo* |
623 | |||
624 | ```rust | ||
625 | test | ||
626 | ``` | ||
627 | |||
596 | ```rust | 628 | ```rust |
597 | const foo: u32 = 123 | 629 | const foo: u32 = 123 |
598 | ``` | 630 | ``` |
@@ -602,6 +634,11 @@ fn main() { | |||
602 | r#"static foo<|>: u32 = 456;"#, | 634 | r#"static foo<|>: u32 = 456;"#, |
603 | expect![[r#" | 635 | expect![[r#" |
604 | *foo* | 636 | *foo* |
637 | |||
638 | ```rust | ||
639 | test | ||
640 | ``` | ||
641 | |||
605 | ```rust | 642 | ```rust |
606 | static foo: u32 | 643 | static foo: u32 |
607 | ``` | 644 | ``` |
@@ -620,6 +657,7 @@ fn main() { | |||
620 | }"#, | 657 | }"#, |
621 | expect![[r#" | 658 | expect![[r#" |
622 | *zz* | 659 | *zz* |
660 | |||
623 | ```rust | 661 | ```rust |
624 | Test<i32, u8> | 662 | Test<i32, u8> |
625 | ``` | 663 | ``` |
@@ -638,8 +676,9 @@ fn main() { So<|>me(12); } | |||
638 | "#, | 676 | "#, |
639 | expect![[r#" | 677 | expect![[r#" |
640 | *Some* | 678 | *Some* |
679 | |||
641 | ```rust | 680 | ```rust |
642 | Option | 681 | test::Option |
643 | ``` | 682 | ``` |
644 | 683 | ||
645 | ```rust | 684 | ```rust |
@@ -657,6 +696,7 @@ fn main() { let b<|>ar = Some(12); } | |||
657 | "#, | 696 | "#, |
658 | expect![[r#" | 697 | expect![[r#" |
659 | *bar* | 698 | *bar* |
699 | |||
660 | ```rust | 700 | ```rust |
661 | Option<i32> | 701 | Option<i32> |
662 | ``` | 702 | ``` |
@@ -675,14 +715,16 @@ enum Option<T> { | |||
675 | "#, | 715 | "#, |
676 | expect![[r#" | 716 | expect![[r#" |
677 | *None* | 717 | *None* |
718 | |||
678 | ```rust | 719 | ```rust |
679 | Option | 720 | test::Option |
680 | ``` | 721 | ``` |
681 | 722 | ||
682 | ```rust | 723 | ```rust |
683 | None | 724 | None |
684 | ``` | 725 | ``` |
685 | ___ | 726 | |
727 | --- | ||
686 | 728 | ||
687 | The None variant | 729 | The None variant |
688 | "#]], | 730 | "#]], |
@@ -700,14 +742,16 @@ fn main() { | |||
700 | "#, | 742 | "#, |
701 | expect![[r#" | 743 | expect![[r#" |
702 | *Some* | 744 | *Some* |
745 | |||
703 | ```rust | 746 | ```rust |
704 | Option | 747 | test::Option |
705 | ``` | 748 | ``` |
706 | 749 | ||
707 | ```rust | 750 | ```rust |
708 | Some | 751 | Some |
709 | ``` | 752 | ``` |
710 | ___ | 753 | |
754 | --- | ||
711 | 755 | ||
712 | The Some variant | 756 | The Some variant |
713 | "#]], | 757 | "#]], |
@@ -720,6 +764,7 @@ fn main() { | |||
720 | r#"fn func(foo: i32) { fo<|>o; }"#, | 764 | r#"fn func(foo: i32) { fo<|>o; }"#, |
721 | expect![[r#" | 765 | expect![[r#" |
722 | *foo* | 766 | *foo* |
767 | |||
723 | ```rust | 768 | ```rust |
724 | i32 | 769 | i32 |
725 | ``` | 770 | ``` |
@@ -733,6 +778,7 @@ fn main() { | |||
733 | r#"fn func(fo<|>o: i32) {}"#, | 778 | r#"fn func(fo<|>o: i32) {}"#, |
734 | expect![[r#" | 779 | expect![[r#" |
735 | *foo* | 780 | *foo* |
781 | |||
736 | ```rust | 782 | ```rust |
737 | i32 | 783 | i32 |
738 | ``` | 784 | ``` |
@@ -746,6 +792,7 @@ fn main() { | |||
746 | r#"fn func(foo: i32) { if true { <|>foo; }; }"#, | 792 | r#"fn func(foo: i32) { if true { <|>foo; }; }"#, |
747 | expect![[r#" | 793 | expect![[r#" |
748 | *foo* | 794 | *foo* |
795 | |||
749 | ```rust | 796 | ```rust |
750 | i32 | 797 | i32 |
751 | ``` | 798 | ``` |
@@ -759,6 +806,7 @@ fn main() { | |||
759 | r#"fn func(<|>foo: i32) {}"#, | 806 | r#"fn func(<|>foo: i32) {}"#, |
760 | expect![[r#" | 807 | expect![[r#" |
761 | *foo* | 808 | *foo* |
809 | |||
762 | ```rust | 810 | ```rust |
763 | i32 | 811 | i32 |
764 | ``` | 812 | ``` |
@@ -778,6 +826,7 @@ fn main() { | |||
778 | fn f(_x<|>: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#, | 826 | fn f(_x<|>: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#, |
779 | expect![[r#" | 827 | expect![[r#" |
780 | *_x* | 828 | *_x* |
829 | |||
781 | ```rust | 830 | ```rust |
782 | impl Deref<Target = u8> + DerefMut<Target = u8> | 831 | impl Deref<Target = u8> + DerefMut<Target = u8> |
783 | ``` | 832 | ``` |
@@ -799,6 +848,7 @@ fn main() { let foo_<|>test = Thing::new(); } | |||
799 | "#, | 848 | "#, |
800 | expect![[r#" | 849 | expect![[r#" |
801 | *foo_test* | 850 | *foo_test* |
851 | |||
802 | ```rust | 852 | ```rust |
803 | Thing | 853 | Thing |
804 | ``` | 854 | ``` |
@@ -822,8 +872,9 @@ fn main() { let foo_test = wrapper::Thing::new<|>(); } | |||
822 | "#, | 872 | "#, |
823 | expect![[r#" | 873 | expect![[r#" |
824 | *new* | 874 | *new* |
875 | |||
825 | ```rust | 876 | ```rust |
826 | wrapper::Thing | 877 | test::wrapper::Thing |
827 | ``` | 878 | ``` |
828 | 879 | ||
829 | ```rust | 880 | ```rust |
@@ -852,6 +903,11 @@ fn main() { | |||
852 | "#, | 903 | "#, |
853 | expect![[r#" | 904 | expect![[r#" |
854 | *C* | 905 | *C* |
906 | |||
907 | ```rust | ||
908 | test | ||
909 | ``` | ||
910 | |||
855 | ```rust | 911 | ```rust |
856 | const C: u32 = 1 | 912 | const C: u32 = 1 |
857 | ``` | 913 | ``` |
@@ -929,6 +985,7 @@ fn y() { | |||
929 | "#, | 985 | "#, |
930 | expect![[r#" | 986 | expect![[r#" |
931 | *x* | 987 | *x* |
988 | |||
932 | ```rust | 989 | ```rust |
933 | i32 | 990 | i32 |
934 | ``` | 991 | ``` |
@@ -946,6 +1003,11 @@ fn f() { fo<|>o!(); } | |||
946 | "#, | 1003 | "#, |
947 | expect![[r#" | 1004 | expect![[r#" |
948 | *foo* | 1005 | *foo* |
1006 | |||
1007 | ```rust | ||
1008 | test | ||
1009 | ``` | ||
1010 | |||
949 | ```rust | 1011 | ```rust |
950 | macro_rules! foo | 1012 | macro_rules! foo |
951 | ``` | 1013 | ``` |
@@ -976,6 +1038,11 @@ id! { | |||
976 | "#, | 1038 | "#, |
977 | expect![[r#" | 1039 | expect![[r#" |
978 | *foo* | 1040 | *foo* |
1041 | |||
1042 | ```rust | ||
1043 | test | ||
1044 | ``` | ||
1045 | |||
979 | ```rust | 1046 | ```rust |
980 | fn foo() | 1047 | fn foo() |
981 | ``` | 1048 | ``` |
@@ -992,6 +1059,7 @@ fn foo(bar:u32) { let a = id!(ba<|>r); } | |||
992 | "#, | 1059 | "#, |
993 | expect![[r#" | 1060 | expect![[r#" |
994 | *bar* | 1061 | *bar* |
1062 | |||
995 | ```rust | 1063 | ```rust |
996 | u32 | 1064 | u32 |
997 | ``` | 1065 | ``` |
@@ -1009,6 +1077,7 @@ fn foo(bar:u32) { let a = id!(ba<|>r); } | |||
1009 | "#, | 1077 | "#, |
1010 | expect![[r#" | 1078 | expect![[r#" |
1011 | *bar* | 1079 | *bar* |
1080 | |||
1012 | ```rust | 1081 | ```rust |
1013 | u32 | 1082 | u32 |
1014 | ``` | 1083 | ``` |
@@ -1067,6 +1136,11 @@ fn foo() { | |||
1067 | "#, | 1136 | "#, |
1068 | expect![[r#" | 1137 | expect![[r#" |
1069 | *bar* | 1138 | *bar* |
1139 | |||
1140 | ```rust | ||
1141 | test | ||
1142 | ``` | ||
1143 | |||
1070 | ```rust | 1144 | ```rust |
1071 | fn bar() -> bool | 1145 | fn bar() -> bool |
1072 | ``` | 1146 | ``` |
@@ -1099,12 +1173,18 @@ fn bar() { fo<|>o(); } | |||
1099 | ", | 1173 | ", |
1100 | expect![[r#" | 1174 | expect![[r#" |
1101 | *foo* | 1175 | *foo* |
1176 | |||
1177 | ```rust | ||
1178 | test | ||
1179 | ``` | ||
1180 | |||
1102 | ```rust | 1181 | ```rust |
1103 | fn foo() | 1182 | fn foo() |
1104 | ``` | 1183 | ``` |
1105 | ___ | ||
1106 | 1184 | ||
1107 | <- ` ` here | 1185 | --- |
1186 | |||
1187 | \<- ` ` here | ||
1108 | "#]], | 1188 | "#]], |
1109 | ); | 1189 | ); |
1110 | } | 1190 | } |
@@ -1115,6 +1195,11 @@ fn bar() { fo<|>o(); } | |||
1115 | r#"async fn foo<|>() {}"#, | 1195 | r#"async fn foo<|>() {}"#, |
1116 | expect![[r#" | 1196 | expect![[r#" |
1117 | *foo* | 1197 | *foo* |
1198 | |||
1199 | ```rust | ||
1200 | test | ||
1201 | ``` | ||
1202 | |||
1118 | ```rust | 1203 | ```rust |
1119 | async fn foo() | 1204 | async fn foo() |
1120 | ``` | 1205 | ``` |
@@ -1124,6 +1209,11 @@ fn bar() { fo<|>o(); } | |||
1124 | r#"pub const unsafe fn foo<|>() {}"#, | 1209 | r#"pub const unsafe fn foo<|>() {}"#, |
1125 | expect![[r#" | 1210 | expect![[r#" |
1126 | *foo* | 1211 | *foo* |
1212 | |||
1213 | ```rust | ||
1214 | test | ||
1215 | ``` | ||
1216 | |||
1127 | ```rust | 1217 | ```rust |
1128 | pub const unsafe fn foo() | 1218 | pub const unsafe fn foo() |
1129 | ``` | 1219 | ``` |
@@ -1133,6 +1223,11 @@ fn bar() { fo<|>o(); } | |||
1133 | r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#, | 1223 | r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#, |
1134 | expect![[r#" | 1224 | expect![[r#" |
1135 | *foo* | 1225 | *foo* |
1226 | |||
1227 | ```rust | ||
1228 | test | ||
1229 | ``` | ||
1230 | |||
1136 | ```rust | 1231 | ```rust |
1137 | pub(crate) async unsafe extern "C" fn foo() | 1232 | pub(crate) async unsafe extern "C" fn foo() |
1138 | ``` | 1233 | ``` |
@@ -1210,6 +1305,11 @@ fn my() {} | |||
1210 | "#, | 1305 | "#, |
1211 | expect![[r#" | 1306 | expect![[r#" |
1212 | *my* | 1307 | *my* |
1308 | |||
1309 | ```rust | ||
1310 | test | ||
1311 | ``` | ||
1312 | |||
1213 | ```rust | 1313 | ```rust |
1214 | mod my | 1314 | mod my |
1215 | ``` | 1315 | ``` |
@@ -1228,10 +1328,16 @@ fn foo() { let bar = Ba<|>r; } | |||
1228 | "#, | 1328 | "#, |
1229 | expect![[r#" | 1329 | expect![[r#" |
1230 | *Bar* | 1330 | *Bar* |
1331 | |||
1332 | ```rust | ||
1333 | test | ||
1334 | ``` | ||
1335 | |||
1231 | ```rust | 1336 | ```rust |
1232 | struct Bar | 1337 | struct Bar |
1233 | ``` | 1338 | ``` |
1234 | ___ | 1339 | |
1340 | --- | ||
1235 | 1341 | ||
1236 | bar docs | 1342 | bar docs |
1237 | "#]], | 1343 | "#]], |
@@ -1249,10 +1355,16 @@ fn foo() { let bar = Ba<|>r; } | |||
1249 | "#, | 1355 | "#, |
1250 | expect![[r#" | 1356 | expect![[r#" |
1251 | *Bar* | 1357 | *Bar* |
1358 | |||
1359 | ```rust | ||
1360 | test | ||
1361 | ``` | ||
1362 | |||
1252 | ```rust | 1363 | ```rust |
1253 | struct Bar | 1364 | struct Bar |
1254 | ``` | 1365 | ``` |
1255 | ___ | 1366 | |
1367 | --- | ||
1256 | 1368 | ||
1257 | bar docs | 1369 | bar docs |
1258 | "#]], | 1370 | "#]], |
@@ -1272,10 +1384,16 @@ fn foo() { let bar = Ba<|>r; } | |||
1272 | "#, | 1384 | "#, |
1273 | expect![[r#" | 1385 | expect![[r#" |
1274 | *Bar* | 1386 | *Bar* |
1387 | |||
1388 | ```rust | ||
1389 | test | ||
1390 | ``` | ||
1391 | |||
1275 | ```rust | 1392 | ```rust |
1276 | struct Bar | 1393 | struct Bar |
1277 | ``` | 1394 | ``` |
1278 | ___ | 1395 | |
1396 | --- | ||
1279 | 1397 | ||
1280 | bar docs 0 | 1398 | bar docs 0 |
1281 | 1399 | ||
@@ -1287,6 +1405,371 @@ fn foo() { let bar = Ba<|>r; } | |||
1287 | } | 1405 | } |
1288 | 1406 | ||
1289 | #[test] | 1407 | #[test] |
1408 | fn test_hover_path_link() { | ||
1409 | check( | ||
1410 | r" | ||
1411 | //- /lib.rs | ||
1412 | pub struct Foo; | ||
1413 | /// [Foo](struct.Foo.html) | ||
1414 | pub struct B<|>ar | ||
1415 | ", | ||
1416 | expect![[r#" | ||
1417 | *Bar* | ||
1418 | |||
1419 | ```rust | ||
1420 | test | ||
1421 | ``` | ||
1422 | |||
1423 | ```rust | ||
1424 | pub struct Bar | ||
1425 | ``` | ||
1426 | |||
1427 | --- | ||
1428 | |||
1429 | [Foo](https://docs.rs/test/*/test/struct.Foo.html) | ||
1430 | "#]], | ||
1431 | ); | ||
1432 | } | ||
1433 | |||
1434 | #[test] | ||
1435 | fn test_hover_path_link_no_strip() { | ||
1436 | check( | ||
1437 | r" | ||
1438 | //- /lib.rs | ||
1439 | pub struct Foo; | ||
1440 | /// [struct Foo](struct.Foo.html) | ||
1441 | pub struct B<|>ar | ||
1442 | ", | ||
1443 | expect![[r#" | ||
1444 | *Bar* | ||
1445 | |||
1446 | ```rust | ||
1447 | test | ||
1448 | ``` | ||
1449 | |||
1450 | ```rust | ||
1451 | pub struct Bar | ||
1452 | ``` | ||
1453 | |||
1454 | --- | ||
1455 | |||
1456 | [struct Foo](https://docs.rs/test/*/test/struct.Foo.html) | ||
1457 | "#]], | ||
1458 | ); | ||
1459 | } | ||
1460 | |||
1461 | #[ignore = "path based links currently only support documentation on ModuleDef items"] | ||
1462 | #[test] | ||
1463 | fn test_hover_path_link_field() { | ||
1464 | check( | ||
1465 | r" | ||
1466 | //- /lib.rs | ||
1467 | pub struct Foo; | ||
1468 | pub struct Bar { | ||
1469 | /// [Foo](struct.Foo.html) | ||
1470 | fie<|>ld: () | ||
1471 | } | ||
1472 | ", | ||
1473 | expect![[r#" | ||
1474 | *field* | ||
1475 | |||
1476 | ```rust | ||
1477 | test::Bar | ||
1478 | ``` | ||
1479 | |||
1480 | ```rust | ||
1481 | field: () | ||
1482 | ``` | ||
1483 | |||
1484 | --- | ||
1485 | |||
1486 | [Foo](https://docs.rs/test/*/test/struct.Foo.html) | ||
1487 | "#]], | ||
1488 | ); | ||
1489 | } | ||
1490 | |||
1491 | #[test] | ||
1492 | fn test_hover_intra_link() { | ||
1493 | check( | ||
1494 | r" | ||
1495 | //- /lib.rs | ||
1496 | pub mod foo { | ||
1497 | pub struct Foo; | ||
1498 | } | ||
1499 | /// [Foo](foo::Foo) | ||
1500 | pub struct B<|>ar | ||
1501 | ", | ||
1502 | expect![[r#" | ||
1503 | *Bar* | ||
1504 | |||
1505 | ```rust | ||
1506 | test | ||
1507 | ``` | ||
1508 | |||
1509 | ```rust | ||
1510 | pub struct Bar | ||
1511 | ``` | ||
1512 | |||
1513 | --- | ||
1514 | |||
1515 | [Foo](https://docs.rs/test/*/test/foo/struct.Foo.html) | ||
1516 | "#]], | ||
1517 | ); | ||
1518 | } | ||
1519 | |||
1520 | #[test] | ||
1521 | fn test_hover_intra_link_html_root_url() { | ||
1522 | check( | ||
1523 | r#" | ||
1524 | //- /lib.rs | ||
1525 | |||
1526 | #![doc(arbitrary_attribute = "test", html_root_url = "https:/example.com", arbitrary_attribute2)] | ||
1527 | |||
1528 | pub mod foo { | ||
1529 | pub struct Foo; | ||
1530 | } | ||
1531 | /// [Foo](foo::Foo) | ||
1532 | pub struct B<|>ar | ||
1533 | "#, | ||
1534 | expect![[r#" | ||
1535 | *Bar* | ||
1536 | |||
1537 | ```rust | ||
1538 | test | ||
1539 | ``` | ||
1540 | |||
1541 | ```rust | ||
1542 | pub struct Bar | ||
1543 | ``` | ||
1544 | |||
1545 | --- | ||
1546 | |||
1547 | [Foo](https://example.com/test/foo/struct.Foo.html) | ||
1548 | "#]], | ||
1549 | ); | ||
1550 | } | ||
1551 | |||
1552 | #[test] | ||
1553 | fn test_hover_intra_link_shortlink() { | ||
1554 | check( | ||
1555 | r" | ||
1556 | //- /lib.rs | ||
1557 | pub struct Foo; | ||
1558 | /// [Foo] | ||
1559 | pub struct B<|>ar | ||
1560 | ", | ||
1561 | expect![[r#" | ||
1562 | *Bar* | ||
1563 | |||
1564 | ```rust | ||
1565 | test | ||
1566 | ``` | ||
1567 | |||
1568 | ```rust | ||
1569 | pub struct Bar | ||
1570 | ``` | ||
1571 | |||
1572 | --- | ||
1573 | |||
1574 | [Foo](https://docs.rs/test/*/test/struct.Foo.html) | ||
1575 | "#]], | ||
1576 | ); | ||
1577 | } | ||
1578 | |||
1579 | #[test] | ||
1580 | fn test_hover_intra_link_shortlink_code() { | ||
1581 | check( | ||
1582 | r" | ||
1583 | //- /lib.rs | ||
1584 | pub struct Foo; | ||
1585 | /// [`Foo`] | ||
1586 | pub struct B<|>ar | ||
1587 | ", | ||
1588 | expect![[r#" | ||
1589 | *Bar* | ||
1590 | |||
1591 | ```rust | ||
1592 | test | ||
1593 | ``` | ||
1594 | |||
1595 | ```rust | ||
1596 | pub struct Bar | ||
1597 | ``` | ||
1598 | |||
1599 | --- | ||
1600 | |||
1601 | [`Foo`](https://docs.rs/test/*/test/struct.Foo.html) | ||
1602 | "#]], | ||
1603 | ); | ||
1604 | } | ||
1605 | |||
1606 | #[test] | ||
1607 | fn test_hover_intra_link_namespaced() { | ||
1608 | check( | ||
1609 | r" | ||
1610 | //- /lib.rs | ||
1611 | pub struct Foo; | ||
1612 | fn Foo() {} | ||
1613 | /// [Foo()] | ||
1614 | pub struct B<|>ar | ||
1615 | ", | ||
1616 | expect![[r#" | ||
1617 | *Bar* | ||
1618 | |||
1619 | ```rust | ||
1620 | test | ||
1621 | ``` | ||
1622 | |||
1623 | ```rust | ||
1624 | pub struct Bar | ||
1625 | ``` | ||
1626 | |||
1627 | --- | ||
1628 | |||
1629 | [Foo](https://docs.rs/test/*/test/struct.Foo.html) | ||
1630 | "#]], | ||
1631 | ); | ||
1632 | } | ||
1633 | |||
1634 | #[test] | ||
1635 | fn test_hover_intra_link_shortlink_namspaced_code() { | ||
1636 | check( | ||
1637 | r" | ||
1638 | //- /lib.rs | ||
1639 | pub struct Foo; | ||
1640 | /// [`struct Foo`] | ||
1641 | pub struct B<|>ar | ||
1642 | ", | ||
1643 | expect![[r#" | ||
1644 | *Bar* | ||
1645 | |||
1646 | ```rust | ||
1647 | test | ||
1648 | ``` | ||
1649 | |||
1650 | ```rust | ||
1651 | pub struct Bar | ||
1652 | ``` | ||
1653 | |||
1654 | --- | ||
1655 | |||
1656 | [`Foo`](https://docs.rs/test/*/test/struct.Foo.html) | ||
1657 | "#]], | ||
1658 | ); | ||
1659 | } | ||
1660 | |||
1661 | #[test] | ||
1662 | fn test_hover_intra_link_shortlink_namspaced_code_with_at() { | ||
1663 | check( | ||
1664 | r" | ||
1665 | //- /lib.rs | ||
1666 | pub struct Foo; | ||
1667 | /// [`struct@Foo`] | ||
1668 | pub struct B<|>ar | ||
1669 | ", | ||
1670 | expect![[r#" | ||
1671 | *Bar* | ||
1672 | |||
1673 | ```rust | ||
1674 | test | ||
1675 | ``` | ||
1676 | |||
1677 | ```rust | ||
1678 | pub struct Bar | ||
1679 | ``` | ||
1680 | |||
1681 | --- | ||
1682 | |||
1683 | [`Foo`](https://docs.rs/test/*/test/struct.Foo.html) | ||
1684 | "#]], | ||
1685 | ); | ||
1686 | } | ||
1687 | |||
1688 | #[test] | ||
1689 | fn test_hover_intra_link_reference() { | ||
1690 | check( | ||
1691 | r" | ||
1692 | //- /lib.rs | ||
1693 | pub struct Foo; | ||
1694 | /// [my Foo][foo] | ||
1695 | /// | ||
1696 | /// [foo]: Foo | ||
1697 | pub struct B<|>ar | ||
1698 | ", | ||
1699 | expect![[r#" | ||
1700 | *Bar* | ||
1701 | |||
1702 | ```rust | ||
1703 | test | ||
1704 | ``` | ||
1705 | |||
1706 | ```rust | ||
1707 | pub struct Bar | ||
1708 | ``` | ||
1709 | |||
1710 | --- | ||
1711 | |||
1712 | [my Foo](https://docs.rs/test/*/test/struct.Foo.html) | ||
1713 | "#]], | ||
1714 | ); | ||
1715 | } | ||
1716 | |||
1717 | #[test] | ||
1718 | fn test_hover_external_url() { | ||
1719 | check( | ||
1720 | r" | ||
1721 | //- /lib.rs | ||
1722 | pub struct Foo; | ||
1723 | /// [external](https://www.google.com) | ||
1724 | pub struct B<|>ar | ||
1725 | ", | ||
1726 | expect![[r#" | ||
1727 | *Bar* | ||
1728 | |||
1729 | ```rust | ||
1730 | test | ||
1731 | ``` | ||
1732 | |||
1733 | ```rust | ||
1734 | pub struct Bar | ||
1735 | ``` | ||
1736 | |||
1737 | --- | ||
1738 | |||
1739 | [external](https://www.google.com) | ||
1740 | "#]], | ||
1741 | ); | ||
1742 | } | ||
1743 | |||
1744 | // Check that we don't rewrite links which we can't identify | ||
1745 | #[test] | ||
1746 | fn test_hover_unknown_target() { | ||
1747 | check( | ||
1748 | r" | ||
1749 | //- /lib.rs | ||
1750 | pub struct Foo; | ||
1751 | /// [baz](Baz) | ||
1752 | pub struct B<|>ar | ||
1753 | ", | ||
1754 | expect![[r#" | ||
1755 | *Bar* | ||
1756 | |||
1757 | ```rust | ||
1758 | test | ||
1759 | ``` | ||
1760 | |||
1761 | ```rust | ||
1762 | pub struct Bar | ||
1763 | ``` | ||
1764 | |||
1765 | --- | ||
1766 | |||
1767 | [baz](Baz) | ||
1768 | "#]], | ||
1769 | ); | ||
1770 | } | ||
1771 | |||
1772 | #[test] | ||
1290 | fn test_hover_macro_generated_struct_fn_doc_comment() { | 1773 | fn test_hover_macro_generated_struct_fn_doc_comment() { |
1291 | mark::check!(hover_macro_generated_struct_fn_doc_comment); | 1774 | mark::check!(hover_macro_generated_struct_fn_doc_comment); |
1292 | 1775 | ||
@@ -1308,16 +1791,18 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
1308 | "#, | 1791 | "#, |
1309 | expect![[r#" | 1792 | expect![[r#" |
1310 | *foo* | 1793 | *foo* |
1794 | |||
1311 | ```rust | 1795 | ```rust |
1312 | Bar | 1796 | test::Bar |
1313 | ``` | 1797 | ``` |
1314 | 1798 | ||
1315 | ```rust | 1799 | ```rust |
1316 | fn foo(&self) | 1800 | fn foo(&self) |
1317 | ``` | 1801 | ``` |
1318 | ___ | ||
1319 | 1802 | ||
1320 | Do the foo | 1803 | --- |
1804 | |||
1805 | Do the foo | ||
1321 | "#]], | 1806 | "#]], |
1322 | ); | 1807 | ); |
1323 | } | 1808 | } |
@@ -1344,14 +1829,16 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
1344 | "#, | 1829 | "#, |
1345 | expect![[r#" | 1830 | expect![[r#" |
1346 | *foo* | 1831 | *foo* |
1832 | |||
1347 | ```rust | 1833 | ```rust |
1348 | Bar | 1834 | test::Bar |
1349 | ``` | 1835 | ``` |
1350 | 1836 | ||
1351 | ```rust | 1837 | ```rust |
1352 | fn foo(&self) | 1838 | fn foo(&self) |
1353 | ``` | 1839 | ``` |
1354 | ___ | 1840 | |
1841 | --- | ||
1355 | 1842 | ||
1356 | Do the foo | 1843 | Do the foo |
1357 | "#]], | 1844 | "#]], |
@@ -1526,7 +2013,7 @@ fn main() { let s<|>t = S{ f1:0 }; } | |||
1526 | GoToType( | 2013 | GoToType( |
1527 | [ | 2014 | [ |
1528 | HoverGotoTypeData { | 2015 | HoverGotoTypeData { |
1529 | mod_path: "S", | 2016 | mod_path: "test::S", |
1530 | nav: NavigationTarget { | 2017 | nav: NavigationTarget { |
1531 | file_id: FileId( | 2018 | file_id: FileId( |
1532 | 1, | 2019 | 1, |
@@ -1565,7 +2052,7 @@ fn main() { let s<|>t = S{ f1:Arg(0) }; } | |||
1565 | GoToType( | 2052 | GoToType( |
1566 | [ | 2053 | [ |
1567 | HoverGotoTypeData { | 2054 | HoverGotoTypeData { |
1568 | mod_path: "S", | 2055 | mod_path: "test::S", |
1569 | nav: NavigationTarget { | 2056 | nav: NavigationTarget { |
1570 | file_id: FileId( | 2057 | file_id: FileId( |
1571 | 1, | 2058 | 1, |
@@ -1584,7 +2071,7 @@ fn main() { let s<|>t = S{ f1:Arg(0) }; } | |||
1584 | }, | 2071 | }, |
1585 | }, | 2072 | }, |
1586 | HoverGotoTypeData { | 2073 | HoverGotoTypeData { |
1587 | mod_path: "Arg", | 2074 | mod_path: "test::Arg", |
1588 | nav: NavigationTarget { | 2075 | nav: NavigationTarget { |
1589 | file_id: FileId( | 2076 | file_id: FileId( |
1590 | 1, | 2077 | 1, |
@@ -1623,7 +2110,7 @@ fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; } | |||
1623 | GoToType( | 2110 | GoToType( |
1624 | [ | 2111 | [ |
1625 | HoverGotoTypeData { | 2112 | HoverGotoTypeData { |
1626 | mod_path: "S", | 2113 | mod_path: "test::S", |
1627 | nav: NavigationTarget { | 2114 | nav: NavigationTarget { |
1628 | file_id: FileId( | 2115 | file_id: FileId( |
1629 | 1, | 2116 | 1, |
@@ -1642,7 +2129,7 @@ fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; } | |||
1642 | }, | 2129 | }, |
1643 | }, | 2130 | }, |
1644 | HoverGotoTypeData { | 2131 | HoverGotoTypeData { |
1645 | mod_path: "Arg", | 2132 | mod_path: "test::Arg", |
1646 | nav: NavigationTarget { | 2133 | nav: NavigationTarget { |
1647 | file_id: FileId( | 2134 | file_id: FileId( |
1648 | 1, | 2135 | 1, |
@@ -1684,7 +2171,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
1684 | GoToType( | 2171 | GoToType( |
1685 | [ | 2172 | [ |
1686 | HoverGotoTypeData { | 2173 | HoverGotoTypeData { |
1687 | mod_path: "A", | 2174 | mod_path: "test::A", |
1688 | nav: NavigationTarget { | 2175 | nav: NavigationTarget { |
1689 | file_id: FileId( | 2176 | file_id: FileId( |
1690 | 1, | 2177 | 1, |
@@ -1703,7 +2190,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
1703 | }, | 2190 | }, |
1704 | }, | 2191 | }, |
1705 | HoverGotoTypeData { | 2192 | HoverGotoTypeData { |
1706 | mod_path: "B", | 2193 | mod_path: "test::B", |
1707 | nav: NavigationTarget { | 2194 | nav: NavigationTarget { |
1708 | file_id: FileId( | 2195 | file_id: FileId( |
1709 | 1, | 2196 | 1, |
@@ -1722,7 +2209,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
1722 | }, | 2209 | }, |
1723 | }, | 2210 | }, |
1724 | HoverGotoTypeData { | 2211 | HoverGotoTypeData { |
1725 | mod_path: "M::C", | 2212 | mod_path: "test::M::C", |
1726 | nav: NavigationTarget { | 2213 | nav: NavigationTarget { |
1727 | file_id: FileId( | 2214 | file_id: FileId( |
1728 | 1, | 2215 | 1, |
@@ -1761,7 +2248,7 @@ fn main() { let s<|>t = foo(); } | |||
1761 | GoToType( | 2248 | GoToType( |
1762 | [ | 2249 | [ |
1763 | HoverGotoTypeData { | 2250 | HoverGotoTypeData { |
1764 | mod_path: "Foo", | 2251 | mod_path: "test::Foo", |
1765 | nav: NavigationTarget { | 2252 | nav: NavigationTarget { |
1766 | file_id: FileId( | 2253 | file_id: FileId( |
1767 | 1, | 2254 | 1, |
@@ -1801,7 +2288,7 @@ fn main() { let s<|>t = foo(); } | |||
1801 | GoToType( | 2288 | GoToType( |
1802 | [ | 2289 | [ |
1803 | HoverGotoTypeData { | 2290 | HoverGotoTypeData { |
1804 | mod_path: "Foo", | 2291 | mod_path: "test::Foo", |
1805 | nav: NavigationTarget { | 2292 | nav: NavigationTarget { |
1806 | file_id: FileId( | 2293 | file_id: FileId( |
1807 | 1, | 2294 | 1, |
@@ -1820,7 +2307,7 @@ fn main() { let s<|>t = foo(); } | |||
1820 | }, | 2307 | }, |
1821 | }, | 2308 | }, |
1822 | HoverGotoTypeData { | 2309 | HoverGotoTypeData { |
1823 | mod_path: "S", | 2310 | mod_path: "test::S", |
1824 | nav: NavigationTarget { | 2311 | nav: NavigationTarget { |
1825 | file_id: FileId( | 2312 | file_id: FileId( |
1826 | 1, | 2313 | 1, |
@@ -1860,7 +2347,7 @@ fn main() { let s<|>t = foo(); } | |||
1860 | GoToType( | 2347 | GoToType( |
1861 | [ | 2348 | [ |
1862 | HoverGotoTypeData { | 2349 | HoverGotoTypeData { |
1863 | mod_path: "Foo", | 2350 | mod_path: "test::Foo", |
1864 | nav: NavigationTarget { | 2351 | nav: NavigationTarget { |
1865 | file_id: FileId( | 2352 | file_id: FileId( |
1866 | 1, | 2353 | 1, |
@@ -1879,7 +2366,7 @@ fn main() { let s<|>t = foo(); } | |||
1879 | }, | 2366 | }, |
1880 | }, | 2367 | }, |
1881 | HoverGotoTypeData { | 2368 | HoverGotoTypeData { |
1882 | mod_path: "Bar", | 2369 | mod_path: "test::Bar", |
1883 | nav: NavigationTarget { | 2370 | nav: NavigationTarget { |
1884 | file_id: FileId( | 2371 | file_id: FileId( |
1885 | 1, | 2372 | 1, |
@@ -1922,7 +2409,7 @@ fn main() { let s<|>t = foo(); } | |||
1922 | GoToType( | 2409 | GoToType( |
1923 | [ | 2410 | [ |
1924 | HoverGotoTypeData { | 2411 | HoverGotoTypeData { |
1925 | mod_path: "Foo", | 2412 | mod_path: "test::Foo", |
1926 | nav: NavigationTarget { | 2413 | nav: NavigationTarget { |
1927 | file_id: FileId( | 2414 | file_id: FileId( |
1928 | 1, | 2415 | 1, |
@@ -1941,7 +2428,7 @@ fn main() { let s<|>t = foo(); } | |||
1941 | }, | 2428 | }, |
1942 | }, | 2429 | }, |
1943 | HoverGotoTypeData { | 2430 | HoverGotoTypeData { |
1944 | mod_path: "Bar", | 2431 | mod_path: "test::Bar", |
1945 | nav: NavigationTarget { | 2432 | nav: NavigationTarget { |
1946 | file_id: FileId( | 2433 | file_id: FileId( |
1947 | 1, | 2434 | 1, |
@@ -1960,7 +2447,7 @@ fn main() { let s<|>t = foo(); } | |||
1960 | }, | 2447 | }, |
1961 | }, | 2448 | }, |
1962 | HoverGotoTypeData { | 2449 | HoverGotoTypeData { |
1963 | mod_path: "S1", | 2450 | mod_path: "test::S1", |
1964 | nav: NavigationTarget { | 2451 | nav: NavigationTarget { |
1965 | file_id: FileId( | 2452 | file_id: FileId( |
1966 | 1, | 2453 | 1, |
@@ -1979,7 +2466,7 @@ fn main() { let s<|>t = foo(); } | |||
1979 | }, | 2466 | }, |
1980 | }, | 2467 | }, |
1981 | HoverGotoTypeData { | 2468 | HoverGotoTypeData { |
1982 | mod_path: "S2", | 2469 | mod_path: "test::S2", |
1983 | nav: NavigationTarget { | 2470 | nav: NavigationTarget { |
1984 | file_id: FileId( | 2471 | file_id: FileId( |
1985 | 1, | 2472 | 1, |
@@ -2016,7 +2503,7 @@ fn foo(ar<|>g: &impl Foo) {} | |||
2016 | GoToType( | 2503 | GoToType( |
2017 | [ | 2504 | [ |
2018 | HoverGotoTypeData { | 2505 | HoverGotoTypeData { |
2019 | mod_path: "Foo", | 2506 | mod_path: "test::Foo", |
2020 | nav: NavigationTarget { | 2507 | nav: NavigationTarget { |
2021 | file_id: FileId( | 2508 | file_id: FileId( |
2022 | 1, | 2509 | 1, |
@@ -2056,7 +2543,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2056 | GoToType( | 2543 | GoToType( |
2057 | [ | 2544 | [ |
2058 | HoverGotoTypeData { | 2545 | HoverGotoTypeData { |
2059 | mod_path: "Foo", | 2546 | mod_path: "test::Foo", |
2060 | nav: NavigationTarget { | 2547 | nav: NavigationTarget { |
2061 | file_id: FileId( | 2548 | file_id: FileId( |
2062 | 1, | 2549 | 1, |
@@ -2075,7 +2562,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2075 | }, | 2562 | }, |
2076 | }, | 2563 | }, |
2077 | HoverGotoTypeData { | 2564 | HoverGotoTypeData { |
2078 | mod_path: "Bar", | 2565 | mod_path: "test::Bar", |
2079 | nav: NavigationTarget { | 2566 | nav: NavigationTarget { |
2080 | file_id: FileId( | 2567 | file_id: FileId( |
2081 | 1, | 2568 | 1, |
@@ -2094,7 +2581,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2094 | }, | 2581 | }, |
2095 | }, | 2582 | }, |
2096 | HoverGotoTypeData { | 2583 | HoverGotoTypeData { |
2097 | mod_path: "S", | 2584 | mod_path: "test::S", |
2098 | nav: NavigationTarget { | 2585 | nav: NavigationTarget { |
2099 | file_id: FileId( | 2586 | file_id: FileId( |
2100 | 1, | 2587 | 1, |
@@ -2132,7 +2619,7 @@ fn foo(ar<|>g: &impl Foo<S>) {} | |||
2132 | GoToType( | 2619 | GoToType( |
2133 | [ | 2620 | [ |
2134 | HoverGotoTypeData { | 2621 | HoverGotoTypeData { |
2135 | mod_path: "Foo", | 2622 | mod_path: "test::Foo", |
2136 | nav: NavigationTarget { | 2623 | nav: NavigationTarget { |
2137 | file_id: FileId( | 2624 | file_id: FileId( |
2138 | 1, | 2625 | 1, |
@@ -2151,7 +2638,7 @@ fn foo(ar<|>g: &impl Foo<S>) {} | |||
2151 | }, | 2638 | }, |
2152 | }, | 2639 | }, |
2153 | HoverGotoTypeData { | 2640 | HoverGotoTypeData { |
2154 | mod_path: "S", | 2641 | mod_path: "test::S", |
2155 | nav: NavigationTarget { | 2642 | nav: NavigationTarget { |
2156 | file_id: FileId( | 2643 | file_id: FileId( |
2157 | 1, | 2644 | 1, |
@@ -2194,7 +2681,7 @@ fn main() { let s<|>t = foo(); } | |||
2194 | GoToType( | 2681 | GoToType( |
2195 | [ | 2682 | [ |
2196 | HoverGotoTypeData { | 2683 | HoverGotoTypeData { |
2197 | mod_path: "B", | 2684 | mod_path: "test::B", |
2198 | nav: NavigationTarget { | 2685 | nav: NavigationTarget { |
2199 | file_id: FileId( | 2686 | file_id: FileId( |
2200 | 1, | 2687 | 1, |
@@ -2213,7 +2700,7 @@ fn main() { let s<|>t = foo(); } | |||
2213 | }, | 2700 | }, |
2214 | }, | 2701 | }, |
2215 | HoverGotoTypeData { | 2702 | HoverGotoTypeData { |
2216 | mod_path: "Foo", | 2703 | mod_path: "test::Foo", |
2217 | nav: NavigationTarget { | 2704 | nav: NavigationTarget { |
2218 | file_id: FileId( | 2705 | file_id: FileId( |
2219 | 1, | 2706 | 1, |
@@ -2250,7 +2737,7 @@ fn foo(ar<|>g: &dyn Foo) {} | |||
2250 | GoToType( | 2737 | GoToType( |
2251 | [ | 2738 | [ |
2252 | HoverGotoTypeData { | 2739 | HoverGotoTypeData { |
2253 | mod_path: "Foo", | 2740 | mod_path: "test::Foo", |
2254 | nav: NavigationTarget { | 2741 | nav: NavigationTarget { |
2255 | file_id: FileId( | 2742 | file_id: FileId( |
2256 | 1, | 2743 | 1, |
@@ -2288,7 +2775,7 @@ fn foo(ar<|>g: &dyn Foo<S>) {} | |||
2288 | GoToType( | 2775 | GoToType( |
2289 | [ | 2776 | [ |
2290 | HoverGotoTypeData { | 2777 | HoverGotoTypeData { |
2291 | mod_path: "Foo", | 2778 | mod_path: "test::Foo", |
2292 | nav: NavigationTarget { | 2779 | nav: NavigationTarget { |
2293 | file_id: FileId( | 2780 | file_id: FileId( |
2294 | 1, | 2781 | 1, |
@@ -2307,7 +2794,7 @@ fn foo(ar<|>g: &dyn Foo<S>) {} | |||
2307 | }, | 2794 | }, |
2308 | }, | 2795 | }, |
2309 | HoverGotoTypeData { | 2796 | HoverGotoTypeData { |
2310 | mod_path: "S", | 2797 | mod_path: "test::S", |
2311 | nav: NavigationTarget { | 2798 | nav: NavigationTarget { |
2312 | file_id: FileId( | 2799 | file_id: FileId( |
2313 | 1, | 2800 | 1, |
@@ -2348,7 +2835,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
2348 | GoToType( | 2835 | GoToType( |
2349 | [ | 2836 | [ |
2350 | HoverGotoTypeData { | 2837 | HoverGotoTypeData { |
2351 | mod_path: "ImplTrait", | 2838 | mod_path: "test::ImplTrait", |
2352 | nav: NavigationTarget { | 2839 | nav: NavigationTarget { |
2353 | file_id: FileId( | 2840 | file_id: FileId( |
2354 | 1, | 2841 | 1, |
@@ -2367,7 +2854,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
2367 | }, | 2854 | }, |
2368 | }, | 2855 | }, |
2369 | HoverGotoTypeData { | 2856 | HoverGotoTypeData { |
2370 | mod_path: "B", | 2857 | mod_path: "test::B", |
2371 | nav: NavigationTarget { | 2858 | nav: NavigationTarget { |
2372 | file_id: FileId( | 2859 | file_id: FileId( |
2373 | 1, | 2860 | 1, |
@@ -2386,7 +2873,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
2386 | }, | 2873 | }, |
2387 | }, | 2874 | }, |
2388 | HoverGotoTypeData { | 2875 | HoverGotoTypeData { |
2389 | mod_path: "DynTrait", | 2876 | mod_path: "test::DynTrait", |
2390 | nav: NavigationTarget { | 2877 | nav: NavigationTarget { |
2391 | file_id: FileId( | 2878 | file_id: FileId( |
2392 | 1, | 2879 | 1, |
@@ -2405,7 +2892,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
2405 | }, | 2892 | }, |
2406 | }, | 2893 | }, |
2407 | HoverGotoTypeData { | 2894 | HoverGotoTypeData { |
2408 | mod_path: "S", | 2895 | mod_path: "test::S", |
2409 | nav: NavigationTarget { | 2896 | nav: NavigationTarget { |
2410 | file_id: FileId( | 2897 | file_id: FileId( |
2411 | 1, | 2898 | 1, |
@@ -2453,7 +2940,7 @@ fn main() { let s<|>t = test().get(); } | |||
2453 | GoToType( | 2940 | GoToType( |
2454 | [ | 2941 | [ |
2455 | HoverGotoTypeData { | 2942 | HoverGotoTypeData { |
2456 | mod_path: "Foo", | 2943 | mod_path: "test::Foo", |
2457 | nav: NavigationTarget { | 2944 | nav: NavigationTarget { |
2458 | file_id: FileId( | 2945 | file_id: FileId( |
2459 | 1, | 2946 | 1, |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index e3af6d5bc..570790384 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -43,6 +43,7 @@ mod status; | |||
43 | mod syntax_highlighting; | 43 | mod syntax_highlighting; |
44 | mod syntax_tree; | 44 | mod syntax_tree; |
45 | mod typing; | 45 | mod typing; |
46 | mod link_rewrite; | ||
46 | 47 | ||
47 | use std::sync::Arc; | 48 | use std::sync::Arc; |
48 | 49 | ||
diff --git a/crates/ide/src/link_rewrite.rs b/crates/ide/src/link_rewrite.rs new file mode 100644 index 000000000..ff3200eef --- /dev/null +++ b/crates/ide/src/link_rewrite.rs | |||
@@ -0,0 +1,81 @@ | |||
1 | //! Resolves and rewrites links in markdown documentation. | ||
2 | //! | ||
3 | //! Most of the implementation can be found in [`hir::doc_links`]. | ||
4 | |||
5 | use pulldown_cmark::{CowStr, Event, Options, Parser, Tag}; | ||
6 | use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; | ||
7 | |||
8 | use hir::resolve_doc_link; | ||
9 | use ide_db::{defs::Definition, RootDatabase}; | ||
10 | |||
11 | /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) | ||
12 | pub fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String { | ||
13 | let doc = Parser::new_with_broken_link_callback( | ||
14 | markdown, | ||
15 | Options::empty(), | ||
16 | Some(&|label, _| Some((/*url*/ label.to_string(), /*title*/ label.to_string()))), | ||
17 | ); | ||
18 | |||
19 | let doc = map_links(doc, |target, title: &str| { | ||
20 | // This check is imperfect, there's some overlap between valid intra-doc links | ||
21 | // and valid URLs so we choose to be too eager to try to resolve what might be | ||
22 | // a URL. | ||
23 | if target.contains("://") { | ||
24 | (target.to_string(), title.to_string()) | ||
25 | } else { | ||
26 | // Two posibilities: | ||
27 | // * path-based links: `../../module/struct.MyStruct.html` | ||
28 | // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` | ||
29 | let resolved = match definition { | ||
30 | Definition::ModuleDef(t) => resolve_doc_link(db, t, title, target), | ||
31 | Definition::Macro(t) => resolve_doc_link(db, t, title, target), | ||
32 | Definition::Field(t) => resolve_doc_link(db, t, title, target), | ||
33 | Definition::SelfType(t) => resolve_doc_link(db, t, title, target), | ||
34 | Definition::Local(t) => resolve_doc_link(db, t, title, target), | ||
35 | Definition::TypeParam(t) => resolve_doc_link(db, t, title, target), | ||
36 | }; | ||
37 | |||
38 | match resolved { | ||
39 | Some((target, title)) => (target, title), | ||
40 | None => (target.to_string(), title.to_string()), | ||
41 | } | ||
42 | } | ||
43 | }); | ||
44 | let mut out = String::new(); | ||
45 | let mut options = CmarkOptions::default(); | ||
46 | options.code_block_backticks = 3; | ||
47 | cmark_with_options(doc, &mut out, None, options).ok(); | ||
48 | out | ||
49 | } | ||
50 | |||
51 | // Rewrites a markdown document, resolving links using `callback` and additionally striping prefixes/suffixes on link titles. | ||
52 | fn map_links<'e>( | ||
53 | events: impl Iterator<Item = Event<'e>>, | ||
54 | callback: impl Fn(&str, &str) -> (String, String), | ||
55 | ) -> impl Iterator<Item = Event<'e>> { | ||
56 | let mut in_link = false; | ||
57 | let mut link_target: Option<CowStr> = None; | ||
58 | |||
59 | events.map(move |evt| match evt { | ||
60 | Event::Start(Tag::Link(_link_type, ref target, _)) => { | ||
61 | in_link = true; | ||
62 | link_target = Some(target.clone()); | ||
63 | evt | ||
64 | } | ||
65 | Event::End(Tag::Link(link_type, _target, _)) => { | ||
66 | in_link = false; | ||
67 | Event::End(Tag::Link(link_type, link_target.take().unwrap(), CowStr::Borrowed(""))) | ||
68 | } | ||
69 | Event::Text(s) if in_link => { | ||
70 | let (link_target_s, link_name) = callback(&link_target.take().unwrap(), &s); | ||
71 | link_target = Some(CowStr::Boxed(link_target_s.into())); | ||
72 | Event::Text(CowStr::Boxed(link_name.into())) | ||
73 | } | ||
74 | Event::Code(s) if in_link => { | ||
75 | let (link_target_s, link_name) = callback(&link_target.take().unwrap(), &s); | ||
76 | link_target = Some(CowStr::Boxed(link_target_s.into())); | ||
77 | Event::Code(CowStr::Boxed(link_name.into())) | ||
78 | } | ||
79 | _ => evt, | ||
80 | }) | ||
81 | } | ||
diff --git a/crates/ide/src/mock_analysis.rs b/crates/ide/src/mock_analysis.rs index 363e6d27e..235796dbc 100644 --- a/crates/ide/src/mock_analysis.rs +++ b/crates/ide/src/mock_analysis.rs | |||
@@ -115,7 +115,7 @@ impl MockAnalysis { | |||
115 | root_crate = Some(crate_graph.add_crate_root( | 115 | root_crate = Some(crate_graph.add_crate_root( |
116 | file_id, | 116 | file_id, |
117 | edition, | 117 | edition, |
118 | None, | 118 | Some("test".to_string()), |
119 | cfg, | 119 | cfg, |
120 | env, | 120 | env, |
121 | Default::default(), | 121 | Default::default(), |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs index fa315ff8e..e97784c47 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/main.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs | |||
@@ -690,5 +690,5 @@ pub fn foo(_input: TokenStream) -> TokenStream { | |||
690 | }); | 690 | }); |
691 | 691 | ||
692 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); | 692 | let value = res.get("contents").unwrap().get("value").unwrap().to_string(); |
693 | assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) | 693 | assert_eq!(value, r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) |
694 | } | 694 | } |