Skip to content

Commit 974518f

Browse files
committed
Code reorganisation and field support
1 parent a14194b commit 974518f

File tree

6 files changed

+105
-60
lines changed

6 files changed

+105
-60
lines changed

crates/hir/src/code_model.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use hir_ty::{
3535
traits::SolutionVariables,
3636
ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate,
3737
InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty,
38-
TyDefId, TyKind, TypeCtor,
38+
TyDefId, TyKind, TypeCtor, TyLoweringContext, TypeCtor,
3939
};
4040
use rustc_hash::FxHashSet;
4141
use stdx::impl_from;
@@ -186,6 +186,25 @@ impl_from!(
186186
for ModuleDef
187187
);
188188

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+
189208
impl ModuleDef {
190209
pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
191210
match self {
@@ -752,8 +771,35 @@ impl Function {
752771
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
753772
hir_ty::diagnostics::validate_body(db, self.id.into(), sink)
754773
}
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+
}
755793
}
756794

795+
#[derive(Debug)]
796+
pub enum MethodOwner {
797+
Trait(Trait),
798+
Adt(Adt),
799+
}
800+
801+
impl_from!(Trait, Adt for MethodOwner);
802+
757803
// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
758804
pub enum Access {
759805
Shared,

crates/ide/src/doc_links.rs

Lines changed: 46 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,27 @@
22
//!
33
//! Most of the implementation can be found in [`hir::doc_links`].
44
5-
use hir::{Adt, Crate, HasAttrs, ModuleDef};
6-
use ide_db::{defs::Definition, RootDatabase};
7-
use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag};
5+
use std::iter::once;
6+
7+
use itertools::Itertools;
88
use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions};
9+
use pulldown_cmark::{CowStr, Event, LinkType, Options, Parser, Tag};
910
use url::Url;
1011

11-
use crate::{FilePosition, Semantics};
12-
use hir::{get_doc_link, resolve_doc_link};
12+
use ide_db::{defs::Definition, RootDatabase};
13+
14+
use hir::{
15+
db::{DefDatabase, HirDatabase},
16+
Adt, AsName, AssocItem, Crate, Field, HasAttrs, ItemInNs, ModuleDef,
17+
};
1318
use ide_db::{
1419
defs::{classify_name, classify_name_ref, Definition},
1520
RootDatabase,
1621
};
1722
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
1823

24+
use crate::{FilePosition, Semantics};
25+
1926
pub type DocumentationLink = String;
2027

2128
/// 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)
100107
// BUG: For Option
101108
// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some
102109
// 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> {
104115
// Get the outermost definition for the moduledef. This is used to resolve the public path to the type,
105116
// then we can join the method, field, etc onto it if required.
106-
let target_def: ModuleDef = match moddef {
107-
ModuleDef::Function(f) => match f.as_assoc_item(db).map(|assoc| assoc.container(db)) {
108-
Some(AssocItemContainer::Trait(t)) => t.into(),
109-
Some(AssocItemContainer::ImplDef(imp)) => {
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()
117+
let target_def: ModuleDef = match definition {
118+
Definition::ModuleDef(moddef) => match moddef {
119+
ModuleDef::Function(f) => {
120+
f.parent_def(db).map(|mowner| mowner.into()).unwrap_or_else(|| f.clone().into())
122121
}
123-
None => ModuleDef::Function(*f),
122+
moddef => moddef,
124123
},
125-
moddef => *moddef,
124+
Definition::Field(f) => f.parent_def(db).into(),
125+
// FIXME: Handle macros
126+
_ => return None,
126127
};
127128

128129
let ns = ItemInNs::Types(target_def.clone().into());
129130

130-
let module = moddef.module(db)?;
131+
let module = definition.module(db)?;
131132
let krate = module.krate();
132133
let import_map = db.import_map(krate.into());
133134
let base = once(krate.display_name(db).unwrap())
134135
.chain(import_map.path_of(ns).unwrap().segments.iter().map(|name| format!("{}", name)))
135136
.join("/");
136137

137-
get_doc_url(db, &krate)
138-
.and_then(|url| url.join(&base).ok())
139-
.and_then(|url| {
140-
get_symbol_filename(db, &target_def).as_deref().and_then(|f| url.join(f).ok())
141-
})
142-
.and_then(|url| match moddef {
138+
let filename = get_symbol_filename(db, &target_def);
139+
let fragment = match definition {
140+
Definition::ModuleDef(moddef) => match moddef {
143141
ModuleDef::Function(f) => {
144-
get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(*f)))
145-
.as_deref()
146-
.and_then(|f| url.join(f).ok())
142+
get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f)))
147143
}
148144
ModuleDef::Const(c) => {
149-
get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Const(*c)))
150-
.as_deref()
151-
.and_then(|f| url.join(f).ok())
145+
get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Const(c)))
152146
}
153147
ModuleDef::TypeAlias(ty) => {
154-
get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::TypeAlias(*ty)))
155-
.as_deref()
156-
.and_then(|f| url.join(f).ok())
148+
get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::TypeAlias(ty)))
157149
}
158-
// TODO: Field <- this requires passing in a definition or something
159-
_ => Some(url),
160-
})
150+
_ => None,
151+
},
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+
)
161162
.map(|url| url.into_string())
162163
}
163164

@@ -219,9 +220,8 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
219220
.map(|url| url.into_string())
220221
}
221222

222-
// FIXME: This should either be moved, or the module should be renamed.
223223
/// 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> {
225225
let sema = Semantics::new(db);
226226
let file = sema.parse(position.file_id).syntax().clone();
227227
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
236236
}
237237
};
238238

239-
match 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-
}
239+
get_doc_link(db, definition?)
247240
}
248241

249242
/// Rewrites a markdown document, applying 'callback' to each link.

crates/ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ impl Analysis {
387387
&self,
388388
position: FilePosition,
389389
) -> 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))
391391
}
392392

393393
/// Computes parameter information for the given call expression.

crates/stdx/src/macros.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ macro_rules! format_to {
1818
};
1919
}
2020

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+
/// ```
2228
#[macro_export]
2329
macro_rules! impl_from {
2430
($($variant:ident $(($($sub_variant:ident),*))?),* for $enum:ident) => {

editors/code/src/commands.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,12 +421,10 @@ export function gotoLocation(ctx: Ctx): Cmd {
421421

422422
export function openDocs(ctx: Ctx): Cmd {
423423
return async () => {
424-
console.log("running openDocs");
425424

426425
const client = ctx.client;
427426
const editor = vscode.window.activeTextEditor;
428427
if (!editor || !client) {
429-
console.log("not yet ready");
430428
return
431429
};
432430

@@ -435,7 +433,9 @@ export function openDocs(ctx: Ctx): Cmd {
435433

436434
const doclink = await client.sendRequest(ra.openDocs, { position, textDocument });
437435

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+
}
439439
};
440440

441441
}

editors/code/src/lsp_ext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,4 @@ export interface CommandLinkGroup {
119119
commands: CommandLink[];
120120
}
121121

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');

0 commit comments

Comments
 (0)