Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-db/src/documentation.rs')
| -rw-r--r-- | crates/ide-db/src/documentation.rs | 83 |
1 files changed, 72 insertions, 11 deletions
diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs index ef2c83992c..30c355f8b3 100644 --- a/crates/ide-db/src/documentation.rs +++ b/crates/ide-db/src/documentation.rs @@ -34,11 +34,13 @@ impl From<Documentation> for String { pub trait HasDocs: HasAttrs { fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>; + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)>; fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, ns: Option<hir::Namespace>, + is_inner_doc: bool, ) -> Option<hir::DocLinkDef>; } /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. @@ -53,7 +55,7 @@ pub struct DocsRangeMap { impl DocsRangeMap { /// Maps a [`TextRange`] relative to the documentation string back to its AST range - pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { + pub fn map(&self, range: TextRange) -> Option<(InFile<TextRange>, AttrId)> { let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; let (line_docs_range, idx, original_line_src_range) = self.mapping[found]; if !line_docs_range.contains_range(range) { @@ -71,7 +73,7 @@ impl DocsRangeMap { text_range.end() + original_line_src_range.start() + relative_range.start(), string.syntax().text_range().len().min(range.len()), ); - Some(InFile { file_id, value: range }) + Some((InFile { file_id, value: range }, idx)) } Either::Right(comment) => { let text_range = comment.syntax().text_range(); @@ -82,10 +84,22 @@ impl DocsRangeMap { + relative_range.start(), text_range.len().min(range.len()), ); - Some(InFile { file_id, value: range }) + Some((InFile { file_id, value: range }, idx)) } } } + + pub fn shift_docstring_line_range(self, offset: TextSize) -> DocsRangeMap { + let mapping = self + .mapping + .into_iter() + .map(|(buf_offset, id, base_offset)| { + let buf_offset = buf_offset.checked_add(offset).unwrap(); + (buf_offset, id, base_offset) + }) + .collect_vec(); + DocsRangeMap { source_map: self.source_map, mapping } + } } pub fn docs_with_rangemap( @@ -161,13 +175,20 @@ macro_rules! impl_has_docs { fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { docs_from_attrs(&self.attrs(db)).map(Documentation) } + fn docs_with_rangemap( + self, + db: &dyn HirDatabase, + ) -> Option<(Documentation, DocsRangeMap)> { + docs_with_rangemap(db, &self.attrs(db)) + } fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, - ns: Option<hir::Namespace> + ns: Option<hir::Namespace>, + is_inner_doc: bool, ) -> Option<hir::DocLinkDef> { - resolve_doc_path_on(db, self, link, ns) + resolve_doc_path_on(db, self, link, ns, is_inner_doc) } } )*}; @@ -184,13 +205,21 @@ macro_rules! impl_has_docs_enum { fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { hir::$enum::$variant(self).docs(db) } + + fn docs_with_rangemap( + self, + db: &dyn HirDatabase, + ) -> Option<(Documentation, DocsRangeMap)> { + hir::$enum::$variant(self).docs_with_rangemap(db) + } fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, - ns: Option<hir::Namespace> + ns: Option<hir::Namespace>, + is_inner_doc: bool, ) -> Option<hir::DocLinkDef> { - hir::$enum::$variant(self).resolve_doc_path(db, link, ns) + hir::$enum::$variant(self).resolve_doc_path(db, link, ns, is_inner_doc) } } )*}; @@ -207,16 +236,25 @@ impl HasDocs for hir::AssocItem { } } + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> { + match self { + hir::AssocItem::Function(it) => it.docs_with_rangemap(db), + hir::AssocItem::Const(it) => it.docs_with_rangemap(db), + hir::AssocItem::TypeAlias(it) => it.docs_with_rangemap(db), + } + } + fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, ns: Option<hir::Namespace>, + is_inner_doc: bool, ) -> Option<hir::DocLinkDef> { match self { - hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), - hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), - hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns), + hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), + hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), } } } @@ -238,13 +276,36 @@ impl HasDocs for hir::ExternCrateDecl { } .map(Documentation::new) } + + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> { + let crate_docs = docs_with_rangemap(db, &self.resolved_crate(db)?.root_module().attrs(db)); + let decl_docs = docs_with_rangemap(db, &self.attrs(db)); + match (decl_docs, crate_docs) { + (None, None) => None, + (Some(decl_docs), None) => Some(decl_docs), + (None, Some(crate_docs)) => Some(crate_docs), + ( + Some((Documentation(mut decl_docs), mut decl_range_map)), + Some((Documentation(crate_docs), crate_range_map)), + ) => { + decl_docs.push('\n'); + decl_docs.push('\n'); + let offset = TextSize::new(decl_docs.len() as u32); + decl_docs += &crate_docs; + let crate_range_map = crate_range_map.shift_docstring_line_range(offset); + decl_range_map.mapping.extend(crate_range_map.mapping); + Some((Documentation(decl_docs), decl_range_map)) + } + } + } fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, ns: Option<hir::Namespace>, + is_inner_doc: bool, ) -> Option<hir::DocLinkDef> { - resolve_doc_path_on(db, self, link, ns) + resolve_doc_path_on(db, self, link, ns, is_inner_doc) } } |