diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-12-08 13:23:12 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-12-08 13:23:12 +0000 |
commit | 2aa7f2ece517a5202421f7a4a7cdd99bd1862ac8 (patch) | |
tree | fcdf682d2175735262a7d733043bb510fd86526e | |
parent | 4d4f11925f793c45560c45c088d4b3139c2c171c (diff) | |
parent | 3174e941dbb7d91bad011ba51a9b55736996b36c (diff) |
Merge #6750
6750: Remove documentation query, move doc handling to attributes r=matklad a=Veykril
Fixes #3182
Removes the documentation query in favor of `Attrs::docs`. Attrs already handlded doc comments partially but the alloc saving check was wrong so it only worked when other attributes existed as well. Unfortunately the `new` constructor has to do an intermediate allocation now because we need to keep the order of mixed doc attributes and doc comments.
I've also partially adjusted the `hover` module to have its tests check the changes, it still has some `HasSource` trait usage due to the `ShortLabel` trait usage, as that is only implemented on the Ast parts and not the Hir, should this ideally be implemented for the Hir types as well?(would be a follow up PR of course)
Co-authored-by: Lukas Wirth <[email protected]>
-rw-r--r-- | crates/hir/src/attrs.rs | 7 | ||||
-rw-r--r-- | crates/hir/src/db.rs | 12 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/hir_def/src/attr.rs | 68 | ||||
-rw-r--r-- | crates/hir_def/src/db.rs | 6 | ||||
-rw-r--r-- | crates/hir_def/src/docs.rs | 121 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/mod_resolution.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 64 | ||||
-rw-r--r-- | crates/ide_db/src/apply_change.rs | 1 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 27 | ||||
-rw-r--r-- | crates/syntax/src/ast/traits.rs | 38 |
12 files changed, 137 insertions, 213 deletions
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index c3e820d89..1f2ee2580 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! Attributes & documentation for hir types. | 1 | //! Attributes & documentation for hir types. |
2 | use hir_def::{ | 2 | use hir_def::{ |
3 | attr::Attrs, docs::Documentation, path::ModPath, resolver::HasResolver, AttrDefId, ModuleDefId, | 3 | attr::{Attrs, Documentation}, |
4 | path::ModPath, | ||
5 | resolver::HasResolver, | ||
6 | AttrDefId, ModuleDefId, | ||
4 | }; | 7 | }; |
5 | use hir_expand::hygiene::Hygiene; | 8 | use hir_expand::hygiene::Hygiene; |
6 | use hir_ty::db::HirDatabase; | 9 | use hir_ty::db::HirDatabase; |
@@ -38,7 +41,7 @@ macro_rules! impl_has_attrs { | |||
38 | } | 41 | } |
39 | fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { | 42 | fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { |
40 | let def = AttrDefId::$def_id(self.into()); | 43 | let def = AttrDefId::$def_id(self.into()); |
41 | db.documentation(def) | 44 | db.attrs(def).docs() |
42 | } | 45 | } |
43 | fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { | 46 | fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { |
44 | let def = AttrDefId::$def_id(self.into()); | 47 | let def = AttrDefId::$def_id(self.into()); |
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index 8c767b249..8d949b264 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs | |||
@@ -2,12 +2,12 @@ | |||
2 | 2 | ||
3 | pub use hir_def::db::{ | 3 | pub use hir_def::db::{ |
4 | AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, | 4 | AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, |
5 | CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, | 5 | CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, ExprScopesQuery, |
6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, | 6 | FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, InternConstQuery, |
7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, | 7 | InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, |
8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, | 8 | InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, |
9 | InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery, | 9 | ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery, StructDataQuery, |
10 | StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, | 10 | TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 93bdb4472..c7c7377d7 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -44,10 +44,9 @@ pub use crate::{ | |||
44 | 44 | ||
45 | pub use hir_def::{ | 45 | pub use hir_def::{ |
46 | adt::StructKind, | 46 | adt::StructKind, |
47 | attr::Attrs, | 47 | attr::{Attrs, Documentation}, |
48 | body::scope::ExprScopes, | 48 | body::scope::ExprScopes, |
49 | builtin_type::BuiltinType, | 49 | builtin_type::BuiltinType, |
50 | docs::Documentation, | ||
51 | find_path::PrefixKind, | 50 | find_path::PrefixKind, |
52 | import_map, | 51 | import_map, |
53 | item_scope::ItemInNs, | 52 | item_scope::ItemInNs, |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index b2ce7ca3c..12f4b02e2 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -5,10 +5,11 @@ use std::{ops, sync::Arc}; | |||
5 | use cfg::{CfgExpr, CfgOptions}; | 5 | use cfg::{CfgExpr, CfgOptions}; |
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; | 7 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; |
8 | use itertools::Itertools; | ||
8 | use mbe::ast_to_token_tree; | 9 | use mbe::ast_to_token_tree; |
9 | use syntax::{ | 10 | use syntax::{ |
10 | ast::{self, AstNode, AttrsOwner}, | 11 | ast::{self, AstNode, AttrsOwner}, |
11 | SmolStr, | 12 | AstToken, SmolStr, |
12 | }; | 13 | }; |
13 | use tt::Subtree; | 14 | use tt::Subtree; |
14 | 15 | ||
@@ -21,6 +22,22 @@ use crate::{ | |||
21 | AdtId, AttrDefId, Lookup, | 22 | AdtId, AttrDefId, Lookup, |
22 | }; | 23 | }; |
23 | 24 | ||
25 | /// Holds documentation | ||
26 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
27 | pub struct Documentation(String); | ||
28 | |||
29 | impl Documentation { | ||
30 | pub fn as_str(&self) -> &str { | ||
31 | &self.0 | ||
32 | } | ||
33 | } | ||
34 | |||
35 | impl Into<String> for Documentation { | ||
36 | fn into(self) -> String { | ||
37 | self.0 | ||
38 | } | ||
39 | } | ||
40 | |||
24 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | 41 | #[derive(Default, Debug, Clone, PartialEq, Eq)] |
25 | pub struct Attrs { | 42 | pub struct Attrs { |
26 | entries: Option<Arc<[Attr]>>, | 43 | entries: Option<Arc<[Attr]>>, |
@@ -93,18 +110,25 @@ impl Attrs { | |||
93 | } | 110 | } |
94 | 111 | ||
95 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | 112 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { |
96 | let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map( | 113 | let docs = ast::CommentIter::from_syntax_node(owner.syntax()).map(|docs_text| { |
97 | |docs_text| Attr { | 114 | ( |
98 | input: Some(AttrInput::Literal(SmolStr::new(docs_text))), | 115 | docs_text.syntax().text_range().start(), |
99 | path: ModPath::from(hir_expand::name!(doc)), | 116 | docs_text.doc_comment().map(|doc| Attr { |
100 | }, | 117 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
101 | ); | 118 | path: ModPath::from(hir_expand::name!(doc)), |
102 | let mut attrs = owner.attrs().peekable(); | 119 | }), |
103 | let entries = if attrs.peek().is_none() { | 120 | ) |
121 | }); | ||
122 | let attrs = owner | ||
123 | .attrs() | ||
124 | .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene))); | ||
125 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | ||
126 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | ||
127 | let entries = if attrs.is_empty() { | ||
104 | // Avoid heap allocation | 128 | // Avoid heap allocation |
105 | None | 129 | None |
106 | } else { | 130 | } else { |
107 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect()) | 131 | Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect()) |
108 | }; | 132 | }; |
109 | Attrs { entries } | 133 | Attrs { entries } |
110 | } | 134 | } |
@@ -140,6 +164,24 @@ impl Attrs { | |||
140 | Some(cfg) => cfg_options.check(&cfg) != Some(false), | 164 | Some(cfg) => cfg_options.check(&cfg) != Some(false), |
141 | } | 165 | } |
142 | } | 166 | } |
167 | |||
168 | pub fn docs(&self) -> Option<Documentation> { | ||
169 | let docs = self | ||
170 | .by_key("doc") | ||
171 | .attrs() | ||
172 | .flat_map(|attr| match attr.input.as_ref()? { | ||
173 | AttrInput::Literal(s) => Some(s), | ||
174 | AttrInput::TokenTree(_) => None, | ||
175 | }) | ||
176 | .intersperse(&SmolStr::new_inline("\n")) | ||
177 | .map(|it| it.as_str()) | ||
178 | .collect::<String>(); | ||
179 | if docs.is_empty() { | ||
180 | None | ||
181 | } else { | ||
182 | Some(Documentation(docs.into())) | ||
183 | } | ||
184 | } | ||
143 | } | 185 | } |
144 | 186 | ||
145 | #[derive(Debug, Clone, PartialEq, Eq)] | 187 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -160,8 +202,10 @@ impl Attr { | |||
160 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { | 202 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { |
161 | let path = ModPath::from_src(ast.path()?, hygiene)?; | 203 | let path = ModPath::from_src(ast.path()?, hygiene)?; |
162 | let input = if let Some(lit) = ast.literal() { | 204 | let input = if let Some(lit) = ast.literal() { |
163 | // FIXME: escape? raw string? | 205 | let value = match lit.kind() { |
164 | let value = lit.syntax().first_token()?.text().trim_matches('"').into(); | 206 | ast::LiteralKind::String(string) => string.value()?.into(), |
207 | _ => lit.syntax().first_token()?.text().trim_matches('"').into(), | ||
208 | }; | ||
165 | Some(AttrInput::Literal(value)) | 209 | Some(AttrInput::Literal(value)) |
166 | } else if let Some(tt) = ast.token_tree() { | 210 | } else if let Some(tt) = ast.token_tree() { |
167 | Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) | 211 | Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0)) |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 6d694de11..7f250da33 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -10,7 +10,6 @@ use crate::{ | |||
10 | attr::Attrs, | 10 | attr::Attrs, |
11 | body::{scope::ExprScopes, Body, BodySourceMap}, | 11 | body::{scope::ExprScopes, Body, BodySourceMap}, |
12 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, | 12 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
13 | docs::Documentation, | ||
14 | generics::GenericParams, | 13 | generics::GenericParams, |
15 | import_map::ImportMap, | 14 | import_map::ImportMap, |
16 | item_tree::ItemTree, | 15 | item_tree::ItemTree, |
@@ -105,11 +104,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
105 | #[salsa::invoke(LangItems::lang_item_query)] | 104 | #[salsa::invoke(LangItems::lang_item_query)] |
106 | fn lang_item(&self, start_crate: CrateId, item: SmolStr) -> Option<LangItemTarget>; | 105 | fn lang_item(&self, start_crate: CrateId, item: SmolStr) -> Option<LangItemTarget>; |
107 | 106 | ||
108 | // FIXME(https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102) | ||
109 | // Remove this query completely, in favor of `Attrs::docs` method | ||
110 | #[salsa::invoke(Documentation::documentation_query)] | ||
111 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; | ||
112 | |||
113 | #[salsa::invoke(ImportMap::import_map_query)] | 107 | #[salsa::invoke(ImportMap::import_map_query)] |
114 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; | 108 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; |
115 | } | 109 | } |
diff --git a/crates/hir_def/src/docs.rs b/crates/hir_def/src/docs.rs deleted file mode 100644 index 3e59a8f47..000000000 --- a/crates/hir_def/src/docs.rs +++ /dev/null | |||
@@ -1,121 +0,0 @@ | |||
1 | //! Defines hir documentation. | ||
2 | //! | ||
3 | //! This really shouldn't exist, instead, we should deshugar doc comments into attributes, see | ||
4 | //! https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102 | ||
5 | |||
6 | use std::sync::Arc; | ||
7 | |||
8 | use either::Either; | ||
9 | use itertools::Itertools; | ||
10 | use syntax::{ast, SmolStr}; | ||
11 | |||
12 | use crate::{ | ||
13 | db::DefDatabase, | ||
14 | src::{HasChildSource, HasSource}, | ||
15 | AdtId, AttrDefId, Lookup, | ||
16 | }; | ||
17 | |||
18 | /// Holds documentation | ||
19 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
20 | pub struct Documentation(Arc<str>); | ||
21 | |||
22 | impl Into<String> for Documentation { | ||
23 | fn into(self) -> String { | ||
24 | self.as_str().to_owned() | ||
25 | } | ||
26 | } | ||
27 | |||
28 | impl Documentation { | ||
29 | fn new(s: &str) -> Documentation { | ||
30 | Documentation(s.into()) | ||
31 | } | ||
32 | |||
33 | pub fn from_ast<N>(node: &N) -> Option<Documentation> | ||
34 | where | ||
35 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
36 | { | ||
37 | docs_from_ast(node) | ||
38 | } | ||
39 | |||
40 | pub fn as_str(&self) -> &str { | ||
41 | &*self.0 | ||
42 | } | ||
43 | |||
44 | pub(crate) fn documentation_query( | ||
45 | db: &dyn DefDatabase, | ||
46 | def: AttrDefId, | ||
47 | ) -> Option<Documentation> { | ||
48 | match def { | ||
49 | AttrDefId::ModuleId(module) => { | ||
50 | let def_map = db.crate_def_map(module.krate); | ||
51 | let src = def_map[module.local_id].declaration_source(db)?; | ||
52 | docs_from_ast(&src.value) | ||
53 | } | ||
54 | AttrDefId::FieldId(it) => { | ||
55 | let src = it.parent.child_source(db); | ||
56 | match &src.value[it.local_id] { | ||
57 | Either::Left(_tuple) => None, | ||
58 | Either::Right(record) => docs_from_ast(record), | ||
59 | } | ||
60 | } | ||
61 | AttrDefId::AdtId(it) => match it { | ||
62 | AdtId::StructId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
63 | AdtId::EnumId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
64 | AdtId::UnionId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
65 | }, | ||
66 | AttrDefId::EnumVariantId(it) => { | ||
67 | let src = it.parent.child_source(db); | ||
68 | docs_from_ast(&src.value[it.local_id]) | ||
69 | } | ||
70 | AttrDefId::TraitId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
71 | AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db.upcast())), | ||
72 | AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
73 | AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
74 | AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
75 | AttrDefId::TypeAliasId(it) => docs_from_ast(&it.lookup(db).source(db).value), | ||
76 | AttrDefId::ImplId(_) => None, | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation> | ||
82 | where | ||
83 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
84 | { | ||
85 | let doc_comment_text = node.doc_comment_text(); | ||
86 | let doc_attr_text = expand_doc_attrs(node); | ||
87 | let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); | ||
88 | docs.map(|it| Documentation::new(&it)) | ||
89 | } | ||
90 | |||
91 | fn merge_doc_comments_and_attrs( | ||
92 | doc_comment_text: Option<String>, | ||
93 | doc_attr_text: Option<String>, | ||
94 | ) -> Option<String> { | ||
95 | match (doc_comment_text, doc_attr_text) { | ||
96 | (Some(mut comment_text), Some(attr_text)) => { | ||
97 | comment_text.push_str("\n"); | ||
98 | comment_text.push_str(&attr_text); | ||
99 | Some(comment_text) | ||
100 | } | ||
101 | (Some(comment_text), None) => Some(comment_text), | ||
102 | (None, Some(attr_text)) => Some(attr_text), | ||
103 | (None, None) => None, | ||
104 | } | ||
105 | } | ||
106 | |||
107 | fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> { | ||
108 | let mut docs = String::new(); | ||
109 | owner | ||
110 | .attrs() | ||
111 | .filter_map(|attr| attr.as_simple_key_value().filter(|(key, _)| key == "doc")) | ||
112 | .map(|(_, value)| value) | ||
113 | .intersperse(SmolStr::new_inline("\n")) | ||
114 | // No FromIterator<SmolStr> for String | ||
115 | .for_each(|s| docs.push_str(s.as_str())); | ||
116 | if docs.is_empty() { | ||
117 | None | ||
118 | } else { | ||
119 | Some(docs) | ||
120 | } | ||
121 | } | ||
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index b41c5acb2..02ed30e4d 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -31,7 +31,6 @@ pub mod adt; | |||
31 | pub mod data; | 31 | pub mod data; |
32 | pub mod generics; | 32 | pub mod generics; |
33 | pub mod lang_item; | 33 | pub mod lang_item; |
34 | pub mod docs; | ||
35 | 34 | ||
36 | pub mod expr; | 35 | pub mod expr; |
37 | pub mod body; | 36 | pub mod body; |
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index ef6f85e15..e80b593aa 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -372,7 +372,7 @@ fn module_resolution_explicit_path_mod_rs_with_win_separator() { | |||
372 | check( | 372 | check( |
373 | r#" | 373 | r#" |
374 | //- /main.rs | 374 | //- /main.rs |
375 | #[path = "module\bar\mod.rs"] | 375 | #[path = r"module\bar\mod.rs"] |
376 | mod foo; | 376 | mod foo; |
377 | 377 | ||
378 | //- /module/bar/mod.rs | 378 | //- /module/bar/mod.rs |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index dc9621f46..1b6ff6d21 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use hir::{ | 1 | use hir::{ |
2 | Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, | 2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, HasAttrs, HasSource, HirDisplay, Module, |
3 | Module, ModuleDef, ModuleSource, Semantics, | 3 | ModuleDef, ModuleSource, Semantics, |
4 | }; | 4 | }; |
5 | use ide_db::base_db::SourceDatabase; | 5 | use ide_db::base_db::SourceDatabase; |
6 | use ide_db::{ | 6 | use ide_db::{ |
@@ -319,31 +319,27 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
319 | let mod_path = definition_mod_path(db, &def); | 319 | let mod_path = definition_mod_path(db, &def); |
320 | return match def { | 320 | return match def { |
321 | Definition::Macro(it) => { | 321 | Definition::Macro(it) => { |
322 | let src = it.source(db); | 322 | let label = macro_label(&it.source(db).value); |
323 | let docs = Documentation::from_ast(&src.value).map(Into::into); | 323 | from_def_source_labeled(db, it, Some(label), mod_path) |
324 | hover_markup(docs, Some(macro_label(&src.value)), mod_path) | ||
325 | } | 324 | } |
326 | Definition::Field(it) => { | 325 | Definition::Field(def) => { |
327 | let src = it.source(db); | 326 | let src = def.source(db).value; |
328 | match src.value { | 327 | if let FieldSource::Named(it) = src { |
329 | FieldSource::Named(it) => { | 328 | from_def_source_labeled(db, def, it.short_label(), mod_path) |
330 | let docs = Documentation::from_ast(&it).map(Into::into); | 329 | } else { |
331 | hover_markup(docs, it.short_label(), mod_path) | 330 | None |
332 | } | ||
333 | _ => None, | ||
334 | } | 331 | } |
335 | } | 332 | } |
336 | Definition::ModuleDef(it) => match it { | 333 | Definition::ModuleDef(it) => match it { |
337 | ModuleDef::Module(it) => match it.definition_source(db).value { | 334 | ModuleDef::Module(it) => from_def_source_labeled( |
338 | ModuleSource::Module(it) => { | 335 | db, |
339 | let docs = Documentation::from_ast(&it).map(Into::into); | 336 | it, |
340 | hover_markup(docs, it.short_label(), mod_path) | 337 | match it.definition_source(db).value { |
341 | } | 338 | ModuleSource::Module(it) => it.short_label(), |
342 | ModuleSource::SourceFile(it) => { | 339 | ModuleSource::SourceFile(it) => it.short_label(), |
343 | let docs = Documentation::from_ast(&it).map(Into::into); | 340 | }, |
344 | hover_markup(docs, it.short_label(), mod_path) | 341 | mod_path, |
345 | } | 342 | ), |
346 | }, | ||
347 | ModuleDef::Function(it) => from_def_source(db, it, mod_path), | 343 | ModuleDef::Function(it) => from_def_source(db, it, mod_path), |
348 | ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path), | 344 | ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path), |
349 | ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path), | 345 | ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path), |
@@ -371,12 +367,24 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
371 | 367 | ||
372 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> | 368 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> |
373 | where | 369 | where |
374 | D: HasSource<Ast = A>, | 370 | D: HasSource<Ast = A> + HasAttrs + Copy, |
375 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner, | 371 | A: ShortLabel, |
372 | { | ||
373 | let short_label = def.source(db).value.short_label(); | ||
374 | from_def_source_labeled(db, def, short_label, mod_path) | ||
375 | } | ||
376 | |||
377 | fn from_def_source_labeled<D>( | ||
378 | db: &RootDatabase, | ||
379 | def: D, | ||
380 | short_label: Option<String>, | ||
381 | mod_path: Option<String>, | ||
382 | ) -> Option<Markup> | ||
383 | where | ||
384 | D: HasAttrs, | ||
376 | { | 385 | { |
377 | let src = def.source(db); | 386 | let docs = def.attrs(db).docs().map(Into::into); |
378 | let docs = Documentation::from_ast(&src.value).map(Into::into); | 387 | hover_markup(docs, short_label, mod_path) |
379 | hover_markup(docs, src.value.short_label(), mod_path) | ||
380 | } | 388 | } |
381 | } | 389 | } |
382 | 390 | ||
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index 987191fe3..e2251f2b7 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -166,7 +166,6 @@ impl RootDatabase { | |||
166 | hir::db::ModuleLangItemsQuery | 166 | hir::db::ModuleLangItemsQuery |
167 | hir::db::CrateLangItemsQuery | 167 | hir::db::CrateLangItemsQuery |
168 | hir::db::LangItemQuery | 168 | hir::db::LangItemQuery |
169 | hir::db::DocumentationQuery | ||
170 | hir::db::ImportMapQuery | 169 | hir::db::ImportMapQuery |
171 | 170 | ||
172 | // HirDatabase | 171 | // HirDatabase |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index fa40e64e8..a10b14778 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -24,6 +24,28 @@ impl ast::Comment { | |||
24 | .unwrap(); | 24 | .unwrap(); |
25 | prefix | 25 | prefix |
26 | } | 26 | } |
27 | |||
28 | /// Returns the textual content of a doc comment block as a single string. | ||
29 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | ||
30 | /// trailing `*/`, trailing whitespace and then joins the lines. | ||
31 | pub fn doc_comment(&self) -> Option<&str> { | ||
32 | let kind = self.kind(); | ||
33 | match kind { | ||
34 | CommentKind { shape, doc: Some(_) } => { | ||
35 | let prefix = kind.prefix(); | ||
36 | let text = &self.text().as_str()[prefix.len()..]; | ||
37 | let ws = text.chars().next().filter(|c| c.is_whitespace()); | ||
38 | let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]); | ||
39 | match shape { | ||
40 | CommentShape::Block if text.ends_with("*/") => { | ||
41 | Some(&text[..text.len() - "*/".len()]) | ||
42 | } | ||
43 | _ => Some(text), | ||
44 | } | ||
45 | } | ||
46 | _ => None, | ||
47 | } | ||
48 | } | ||
27 | } | 49 | } |
28 | 50 | ||
29 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 51 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
@@ -73,6 +95,11 @@ impl CommentKind { | |||
73 | .unwrap(); | 95 | .unwrap(); |
74 | kind | 96 | kind |
75 | } | 97 | } |
98 | |||
99 | fn prefix(&self) -> &'static str { | ||
100 | let &(prefix, _) = CommentKind::BY_PREFIX.iter().find(|(_, kind)| kind == self).unwrap(); | ||
101 | prefix | ||
102 | } | ||
76 | } | 103 | } |
77 | 104 | ||
78 | impl ast::Whitespace { | 105 | impl ast::Whitespace { |
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs index 0bdc22d95..13a769d51 100644 --- a/crates/syntax/src/ast/traits.rs +++ b/crates/syntax/src/ast/traits.rs | |||
@@ -91,40 +91,12 @@ impl CommentIter { | |||
91 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | 91 | /// That is, strips leading `///` (+ optional 1 character of whitespace), |
92 | /// trailing `*/`, trailing whitespace and then joins the lines. | 92 | /// trailing `*/`, trailing whitespace and then joins the lines. |
93 | pub fn doc_comment_text(self) -> Option<String> { | 93 | pub fn doc_comment_text(self) -> Option<String> { |
94 | let mut has_comments = false; | 94 | let docs = |
95 | let docs = self | 95 | self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)).join("\n"); |
96 | .filter(|comment| comment.kind().doc.is_some()) | 96 | if docs.is_empty() { |
97 | .map(|comment| { | ||
98 | has_comments = true; | ||
99 | let prefix_len = comment.prefix().len(); | ||
100 | |||
101 | let line: &str = comment.text().as_str(); | ||
102 | |||
103 | // Determine if the prefix or prefix + 1 char is stripped | ||
104 | let pos = | ||
105 | if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) { | ||
106 | prefix_len + ws.len_utf8() | ||
107 | } else { | ||
108 | prefix_len | ||
109 | }; | ||
110 | |||
111 | let end = if comment.kind().shape.is_block() && line.ends_with("*/") { | ||
112 | line.len() - 2 | ||
113 | } else { | ||
114 | line.len() | ||
115 | }; | ||
116 | |||
117 | // Note that we do not trim the end of the line here | ||
118 | // since whitespace can have special meaning at the end | ||
119 | // of a line in markdown. | ||
120 | line[pos..end].to_owned() | ||
121 | }) | ||
122 | .join("\n"); | ||
123 | |||
124 | if has_comments { | ||
125 | Some(docs) | ||
126 | } else { | ||
127 | None | 97 | None |
98 | } else { | ||
99 | Some(docs) | ||
128 | } | 100 | } |
129 | } | 101 | } |
130 | } | 102 | } |