aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/code_model.rs48
-rw-r--r--crates/ide/src/doc_links.rs99
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/stdx/src/macros.rs8
-rw-r--r--editors/code/src/commands.ts6
-rw-r--r--editors/code/src/lsp_ext.ts2
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};
40use rustc_hash::FxHashSet; 40use rustc_hash::FxHashSet;
41use stdx::impl_from; 41use stdx::impl_from;
@@ -186,6 +186,25 @@ impl_from!(
186 for ModuleDef 186 for ModuleDef
187); 187);
188 188
189impl 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
198impl 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
189impl ModuleDef { 208impl 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)]
796pub enum MethodOwner {
797 Trait(Trait),
798 Adt(Adt),
799}
800
801impl_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.
758pub enum Access { 804pub 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
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.
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]
23macro_rules! impl_from { 29macro_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
422export function openDocs(ctx: Ctx): Cmd { 422export 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
122export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, String | void, void>('experimental/externalDocs'); 122export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs');