From a71e95bf4f07e044eb3f335929d2adea74247a9e Mon Sep 17 00:00:00 2001 From: Minijackson Date: Sun, 22 Dec 2019 18:10:39 +0100 Subject: Fix TOC links for inline documentation --- src/generator/pandoc.rs | 64 +++++++++++++++++++++++++++++--------------- src/parser/clang/entities.rs | 61 +++++++++++++++++++---------------------- src/types.rs | 4 ++- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/generator/pandoc.rs b/src/generator/pandoc.rs index 035acab..fe2e2f0 100644 --- a/src/generator/pandoc.rs +++ b/src/generator/pandoc.rs @@ -35,13 +35,7 @@ pub(crate) fn into_pandoc(entity: Entity, config: &Config) -> (Pandoc, BTreeMap< let entity_kind = entity.kind; for (children_kind, children) in entity.children { - if config - .inlines - .get(&entity.language) - .and_then(|lang_inlines| lang_inlines.get(&entity_kind)) - .map(|children_inlines| children_inlines.contains(&children_kind)) - == Some(true) - { + if is_inline(config, entity.language, &entity_kind, &children_kind) { inline_children.insert(children_kind, children); } else { // By default, do not inline @@ -50,7 +44,7 @@ pub(crate) fn into_pandoc(entity: Entity, config: &Config) -> (Pandoc, BTreeMap< } for (section_name, children) in &separate_children { - if let Some(members_list) = member_list(children) { + if let Some(members_list) = member_list(children, LinkType::External) { content.push(Block::Header( 2, Attr::null(), @@ -61,10 +55,10 @@ pub(crate) fn into_pandoc(entity: Entity, config: &Config) -> (Pandoc, BTreeMap< } } - let mut embedded_documentation = Vec::new(); + let mut inline_documentation = Vec::new(); for (section_name, children) in inline_children { - if let Some(members_list) = member_list(&children) { + if let Some(members_list) = member_list(&children, LinkType::Anchor) { content.push(Block::Header( 2, Attr::null(), @@ -73,20 +67,20 @@ pub(crate) fn into_pandoc(entity: Entity, config: &Config) -> (Pandoc, BTreeMap< content.push(members_list); - embedded_documentation.push(Block::Header( + inline_documentation.push(Block::Header( 2, Attr::null(), vec![Inline::Str(section_name.0 + " Documentation")], )); - for (_id, child) in children { - embedded_documentation.push(Block::Header( + for (id, child) in children { + inline_documentation.push(Block::Header( 3, - Attr::null(), + Attr(id.0, vec![], vec![]), vec![Inline::Code(Attr::null(), child.name)], )); - embedded_documentation.push(Block::Div( + inline_documentation.push(Block::Div( Attr(String::new(), vec![String::from("doc")], vec![]), vec![raw_markdown(child.documentation)], )); @@ -94,7 +88,7 @@ pub(crate) fn into_pandoc(entity: Entity, config: &Config) -> (Pandoc, BTreeMap< } } - content.append(&mut embedded_documentation); + content.append(&mut inline_documentation); let leftovers = separate_children .into_iter() @@ -105,11 +99,25 @@ pub(crate) fn into_pandoc(entity: Entity, config: &Config) -> (Pandoc, BTreeMap< (Pandoc(meta, content), leftovers) } +fn is_inline( + config: &Config, + language: Language, + parent_kind: &EntityKind, + children_kind: &ChildrenKind, +) -> bool { + config + .inlines + .get(&language) + .and_then(|lang_inlines| lang_inlines.get(parent_kind)) + .map(|children_inlines| children_inlines.contains(children_kind)) + == Some(true) +} + fn str_block(content: String) -> Block { Block::Plain(vec![Inline::Str(content)]) } -fn entity_link(id: &EntityId, name: String) -> Inline { +fn entity_link(id: &EntityId, name: String, link_type: LinkType) -> Inline { use pandoc_types::definition::Target; use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; @@ -131,9 +139,12 @@ fn entity_link(id: &EntityId, name: String) -> Inline { Attr::null(), vec![Inline::Code(Attr::null(), name)], Target( - once("./") - .chain(utf8_percent_encode(&id.0, FRAGMENT)) - .collect(), + once(match link_type { + LinkType::External => "./", + LinkType::Anchor => "#", + }) + .chain(utf8_percent_encode(&id.0, FRAGMENT)) + .collect(), String::new(), ), ) @@ -144,12 +155,21 @@ fn raw_markdown(text: String) -> Block { Block::RawBlock(Format(String::from("markdown")), text) } -fn member_list<'a>(members: impl IntoIterator) -> Option { +#[derive(Debug, Clone, Copy)] +enum LinkType { + Anchor, + External, +} + +fn member_list<'a>( + members: impl IntoIterator, + link_type: LinkType, +) -> Option { let definitions: Vec<(Vec, Vec>)> = members .into_iter() .map(|(id, entity)| { ( - vec![entity_link(id, entity.name.clone())], + vec![entity_link(id, entity.name.clone(), link_type)], vec![vec![str_block(entity.brief_description.clone())]], ) }) diff --git a/src/parser/clang/entities.rs b/src/parser/clang/entities.rs index 41fe061..cbc17b7 100644 --- a/src/parser/clang/entities.rs +++ b/src/parser/clang/entities.rs @@ -1,4 +1,4 @@ -use crate::types::*; +use crate::types::{self, *}; use clang::{EntityKind, Usr}; use thiserror::Error; @@ -169,55 +169,48 @@ where } } +fn entity_id(usr: Usr) -> EntityId { + // This is a somewhat ugly workaround, because Pandoc parses markdown identifiers as alphanum + // or one of "-_:.", which means Pandoc can't parse libclang's USRs in title ids and related. + // + // + EntityId(usr.0.replace("@", "::").replace("#", ".").replace("$", "-")) +} + +fn append_children( + acc: &mut types::Children, + to_insert: BTreeMap>, +) { + for (usr, child) in to_insert { + acc.entry(ChildrenKind(String::from( + child.entity.kind().to_str_plural(), + ))) + .or_default() + .insert(entity_id(usr), child.into()); + } +} + impl From> for Entity { fn from(entity: Described) -> Self { - let mut children: BTreeMap> = BTreeMap::new(); + let mut children: types::Children = BTreeMap::new(); let kind = EntityKind(String::from(entity.entity.kind().to_str_singular())); let clang_children = entity.entity.into_children(); if let Some(namespaces) = clang_children.namespaces { - for (usr, namespace) in namespaces { - children - .entry(ChildrenKind(String::from( - namespace.entity.kind().to_str_plural(), - ))) - .or_default() - .insert(EntityId(usr.0), namespace.into()); - } + append_children(&mut children, namespaces); } if let Some(variables) = clang_children.variables { - for (usr, variable) in variables { - children - .entry(ChildrenKind(String::from( - variable.entity.kind().to_str_plural(), - ))) - .or_default() - .insert(EntityId(usr.0), variable.into()); - } + append_children(&mut children, variables); } if let Some(structs) = clang_children.structs { - for (usr, r#struct) in structs { - children - .entry(ChildrenKind(String::from( - r#struct.entity.kind().to_str_plural(), - ))) - .or_default() - .insert(EntityId(usr.0), r#struct.into()); - } + append_children(&mut children, structs); } if let Some(functions) = clang_children.functions { - for (usr, function) in functions { - children - .entry(ChildrenKind(String::from( - function.entity.kind().to_str_plural(), - ))) - .or_default() - .insert(EntityId(usr.0), function.into()); - } + append_children(&mut children, functions); } Entity { diff --git a/src/types.rs b/src/types.rs index f378f8b..d192ab5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -18,9 +18,11 @@ pub struct Entity { pub kind: EntityKind, pub brief_description: String, pub documentation: String, - pub children: BTreeMap>, + pub children: Children, } +pub type Children = BTreeMap>; + // TODO: use newtype for entity kind, entity ID #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -- cgit v1.2.3