diff options
-rw-r--r-- | crates/hir/src/code_model.rs | 48 | ||||
-rw-r--r-- | crates/ide/src/doc_links.rs | 99 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/stdx/src/macros.rs | 8 | ||||
-rw-r--r-- | editors/code/src/commands.ts | 6 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 2 |
6 files changed, 105 insertions, 60 deletions
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 031c91ccf..1dd6d73f3 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -35,7 +35,7 @@ use hir_ty::{ | |||
35 | traits::SolutionVariables, | 35 | traits::SolutionVariables, |
36 | ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, | 36 | ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, |
37 | InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty, | 37 | InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty, |
38 | TyDefId, TyKind, TypeCtor, | 38 | TyDefId, TyKind, TypeCtor, TyLoweringContext, TypeCtor, |
39 | }; | 39 | }; |
40 | use rustc_hash::FxHashSet; | 40 | use rustc_hash::FxHashSet; |
41 | use stdx::impl_from; | 41 | use stdx::impl_from; |
@@ -186,6 +186,25 @@ impl_from!( | |||
186 | for ModuleDef | 186 | for ModuleDef |
187 | ); | 187 | ); |
188 | 188 | ||
189 | impl From<MethodOwner> for ModuleDef { | ||
190 | fn from(mowner: MethodOwner) -> Self { | ||
191 | match mowner { | ||
192 | MethodOwner::Trait(t) => t.into(), | ||
193 | MethodOwner::Adt(t) => t.into(), | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | |||
198 | impl From<VariantDef> for ModuleDef { | ||
199 | fn from(var: VariantDef) -> Self { | ||
200 | match var { | ||
201 | VariantDef::Struct(t) => Adt::from(t).into(), | ||
202 | VariantDef::Union(t) => Adt::from(t).into(), | ||
203 | VariantDef::EnumVariant(t) => t.into(), | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
189 | impl ModuleDef { | 208 | impl ModuleDef { |
190 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | 209 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { |
191 | match self { | 210 | match self { |
@@ -752,8 +771,35 @@ impl Function { | |||
752 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 771 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { |
753 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink) | 772 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink) |
754 | } | 773 | } |
774 | |||
775 | pub fn parent_def(self, db: &dyn HirDatabase) -> Option<MethodOwner> { | ||
776 | match self.as_assoc_item(db).map(|assoc| assoc.container(db)) { | ||
777 | Some(AssocItemContainer::Trait(t)) => Some(t.into()), | ||
778 | Some(AssocItemContainer::ImplDef(imp)) => { | ||
779 | let resolver = ModuleId::from(imp.module(db)).resolver(db.upcast()); | ||
780 | let ctx = TyLoweringContext::new(db, &resolver); | ||
781 | let adt = Ty::from_hir( | ||
782 | &ctx, | ||
783 | &imp.target_trait(db).unwrap_or_else(|| imp.target_type(db)), | ||
784 | ) | ||
785 | .as_adt() | ||
786 | .map(|t| t.0) | ||
787 | .unwrap(); | ||
788 | Some(Adt::from(adt).into()) | ||
789 | } | ||
790 | None => None, | ||
791 | } | ||
792 | } | ||
755 | } | 793 | } |
756 | 794 | ||
795 | #[derive(Debug)] | ||
796 | pub enum MethodOwner { | ||
797 | Trait(Trait), | ||
798 | Adt(Adt), | ||
799 | } | ||
800 | |||
801 | impl_from!(Trait, Adt for MethodOwner); | ||
802 | |||
757 | // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. | 803 | // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. |
758 | pub enum Access { | 804 | pub enum Access { |
759 | Shared, | 805 | Shared, |
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 | ||
5 | use hir::{Adt, Crate, HasAttrs, ModuleDef}; | 5 | use std::iter::once; |
6 | use ide_db::{defs::Definition, RootDatabase}; | 6 | |
7 | use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag}; | 7 | use itertools::Itertools; |
8 | use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; | 8 | use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions}; |
9 | use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag}; | ||
9 | use url::Url; | 10 | use url::Url; |
10 | 11 | ||
11 | use crate::{FilePosition, Semantics}; | 12 | use ide_db::{defs::Definition, RootDatabase}; |
12 | use hir::{get_doc_link, resolve_doc_link}; | 13 | |
14 | use hir::{ | ||
15 | db::{DefDatabase, HirDatabase}, | ||
16 | Adt, AsName, AssocItem, Crate, Field, HasAttrs, ItemInNs, ModuleDef, | ||
17 | }; | ||
13 | use ide_db::{ | 18 | use ide_db::{ |
14 | defs::{classify_name, classify_name_ref, Definition}, | 19 | defs::{classify_name, classify_name_ref, Definition}, |
15 | RootDatabase, | 20 | RootDatabase, |
16 | }; | 21 | }; |
17 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 22 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; |
18 | 23 | ||
24 | use crate::{FilePosition, Semantics}; | ||
25 | |||
19 | pub type DocumentationLink = String; | 26 | pub 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 |
103 | fn 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. | ||
114 | fn 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. |
224 | pub fn get_doc_url(db: &RootDatabase, position: &FilePosition) -> Option<DocumentationLink> { | 224 | pub 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. |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 0580d2979..5db6e1311 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -387,7 +387,7 @@ impl Analysis { | |||
387 | &self, | 387 | &self, |
388 | position: FilePosition, | 388 | position: FilePosition, |
389 | ) -> Cancelable<Option<doc_links::DocumentationLink>> { | 389 | ) -> Cancelable<Option<doc_links::DocumentationLink>> { |
390 | self.with_db(|db| doc_links::get_doc_url(db, &position)) | 390 | self.with_db(|db| doc_links::external_docs(db, &position)) |
391 | } | 391 | } |
392 | 392 | ||
393 | /// Computes parameter information for the given call expression. | 393 | /// Computes parameter information for the given call expression. |
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs index bf298460f..f5ee3484b 100644 --- a/crates/stdx/src/macros.rs +++ b/crates/stdx/src/macros.rs | |||
@@ -18,7 +18,13 @@ macro_rules! format_to { | |||
18 | }; | 18 | }; |
19 | } | 19 | } |
20 | 20 | ||
21 | // Generates `From` impls for `Enum E { Foo(Foo), Bar(Bar) }` enums | 21 | /// Generates `From` impls for `Enum E { Foo(Foo), Bar(Bar) }` enums |
22 | /// | ||
23 | /// # Example | ||
24 | /// | ||
25 | /// ```rust | ||
26 | /// impl_from!(Struct, Union, Enum for Adt); | ||
27 | /// ``` | ||
22 | #[macro_export] | 28 | #[macro_export] |
23 | macro_rules! impl_from { | 29 | macro_rules! impl_from { |
24 | ($($variant:ident $(($($sub_variant:ident),*))?),* for $enum:ident) => { | 30 | ($($variant:ident $(($($sub_variant:ident),*))?),* for $enum:ident) => { |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index b22cd450b..24c2e196d 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -421,12 +421,10 @@ export function gotoLocation(ctx: Ctx): Cmd { | |||
421 | 421 | ||
422 | export function openDocs(ctx: Ctx): Cmd { | 422 | export function openDocs(ctx: Ctx): Cmd { |
423 | return async () => { | 423 | return async () => { |
424 | console.log("running openDocs"); | ||
425 | 424 | ||
426 | const client = ctx.client; | 425 | const client = ctx.client; |
427 | const editor = vscode.window.activeTextEditor; | 426 | const editor = vscode.window.activeTextEditor; |
428 | if (!editor || !client) { | 427 | if (!editor || !client) { |
429 | console.log("not yet ready"); | ||
430 | return | 428 | return |
431 | }; | 429 | }; |
432 | 430 | ||
@@ -435,7 +433,9 @@ export function openDocs(ctx: Ctx): Cmd { | |||
435 | 433 | ||
436 | const doclink = await client.sendRequest(ra.openDocs, { position, textDocument }); | 434 | const doclink = await client.sendRequest(ra.openDocs, { position, textDocument }); |
437 | 435 | ||
438 | vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink.remote)); | 436 | if (doclink != null) { |
437 | vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink)); | ||
438 | } | ||
439 | }; | 439 | }; |
440 | 440 | ||
441 | } | 441 | } |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 562804715..fc8e120b3 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -119,4 +119,4 @@ export interface CommandLinkGroup { | |||
119 | commands: CommandLink[]; | 119 | commands: CommandLink[]; |
120 | } | 120 | } |
121 | 121 | ||
122 | export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, String | void, void>('experimental/externalDocs'); | 122 | export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs'); |