From 867e6c329d5ab09d1a8ab1dde5283322a53fd0dc Mon Sep 17 00:00:00 2001 From: makspll Date: Sun, 23 Feb 2025 02:21:36 +0000 Subject: [PATCH 1/6] feat: improve generics behaviour and fix links --- .../mdbook_lad_preprocessor/src/lib.rs | 183 ++++++--- .../mdbook_lad_preprocessor/src/sections.rs | 362 ++++++++++-------- .../tests/book_integration_tests.rs | 2 +- .../books/example_ladfile/expected/parent.md | 1 + .../expected/{test.lad.md => parent/lad.md} | 0 .../expected/{ => parent}/lad/functions.md | 0 .../{ => parent}/lad/functions/hello_world.md | 0 .../expected/{ => parent}/lad/globals.md | 0 .../expected/{ => parent}/lad/types.md | 0 .../books/example_ladfile/src/SUMMARY.md | 3 +- .../tests/books/example_ladfile/src/parent.md | 1 + docs/src/Generated/generated-intro.md | 1 + docs/src/SUMMARY.md | 3 +- 13 files changed, 352 insertions(+), 204 deletions(-) create mode 100644 crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md rename crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/{test.lad.md => parent/lad.md} (100%) rename crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/{ => parent}/lad/functions.md (100%) rename crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/{ => parent}/lad/functions/hello_world.md (100%) rename crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/{ => parent}/lad/globals.md (100%) rename crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/{ => parent}/lad/types.md (100%) create mode 100644 crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md create mode 100644 docs/src/Generated/generated-intro.md diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs index 8ca2b16c81..610a70eb56 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs @@ -1,7 +1,8 @@ //! The library crate for the mdbook LAD preprocessor. #![allow(missing_docs)] -use mdbook::{errors::Error, preprocess::Preprocessor}; +use mdbook::{errors::Error, preprocess::Preprocessor, BookItem}; +use sections::Section; mod argument_visitor; mod markdown; mod sections; @@ -23,59 +24,143 @@ impl Preprocessor for LADPreprocessor { let mut errors = Vec::default(); book.for_each_mut(|item| { - if let mdbook::BookItem::Chapter(chapter) = item { - let is_lad_chapter = chapter - .source_path - .as_ref() - .and_then(|a| a.file_name()) - .is_some_and(|a| a.to_string_lossy().ends_with(LAD_EXTENSION)); - - if !is_lad_chapter { - log::debug!("Skipping non-LAD chapter: {:?}", chapter.source_path); - log::trace!( - "Non-LAD chapter: {}", - serde_json::to_string_pretty(&chapter).unwrap_or_default() - ); - return; + if let mdbook::BookItem::Chapter(parent) = item { + let mut replacements = Vec::default(); + for (ladfile_idx, child) in parent.sub_items.iter().enumerate() { + if let BookItem::Chapter(child) = child { + let is_lad_file = child + .source_path + .as_ref() + .and_then(|a| a.file_name()) + .is_some_and(|a| a.to_string_lossy().ends_with(LAD_EXTENSION)); + + if !is_lad_file { + continue; + } + + let chapter_title = + child.name.clone().trim_end_matches(".lad.json").to_owned(); + + let ladfile = match ladfile::parse_lad_file(&child.content) { + Ok(lad) => lad, + Err(e) => { + log::debug!("Failed to parse LAD file: {:?}", e); + errors.push(Error::new(e).context("Failed to parse LAD file")); + continue; + } + }; + + log::debug!( + "Parsed LAD file: {}", + serde_json::to_string_pretty(&ladfile).unwrap_or_default() + ); + + let new_chapter = Section::Summary { + ladfile: &ladfile, + title: Some(chapter_title), + } + .into_chapter(Some(parent), ladfile_idx); + + log::debug!( + "New chapter: {}", + serde_json::to_string_pretty(&new_chapter).unwrap_or_default() + ); + + // replace + replacements.push((ladfile_idx, BookItem::Chapter(new_chapter))); + } } - let chapter_title = chapter.name.clone(); - - let lad = match ladfile::parse_lad_file(&chapter.content) { - Ok(lad) => lad, - Err(e) => { - log::debug!("Failed to parse LAD file: {:?}", e); - errors.push(Error::new(e).context("Failed to parse LAD file")); - return; - } - }; - - log::debug!( - "Parsed LAD file: {}", - serde_json::to_string_pretty(&lad).unwrap_or_default() - ); - - let sections = sections::lad_file_to_sections(&lad, Some(chapter_title)); - - let new_chapter = sections::section_to_chapter( - sections, - Some(chapter), - chapter.parent_names.clone(), - chapter.number.clone(), - None, - None, - ); - - // serialize chapter to json - log::debug!( - "New chapter: {}", - serde_json::to_string_pretty(&new_chapter).unwrap_or_default() - ); - - *chapter = new_chapter; + for (idx, replacement) in replacements { + log::debug!( + "Replacing chapter at index {}. With : \n{}", + idx, + serde_json::to_string_pretty(&replacement).unwrap_or_default() + ); + parent.sub_items[idx] = replacement; + } } }); + // book.for_each_mut(|item| { + // if let mdbook::BookItem::Chapter(chapter) = item { + // let is_lad_chapter = chapter + // .source_path + // .as_ref() + // .and_then(|a| a.file_name()) + // .is_some_and(|a| a.to_string_lossy().ends_with(LAD_EXTENSION)); + + // if !is_lad_chapter { + // log::debug!("Skipping non-LAD chapter: {:?}", chapter.source_path); + // log::trace!( + // "Non-LAD chapter: {}", + // serde_json::to_string_pretty(&chapter).unwrap_or_default() + // ); + // return; + // } + + // let chapter_title = chapter + // .name + // .clone() + // .trim_end_matches(".lad.json") + // .to_owned(); + + // let lad = match ladfile::parse_lad_file(&chapter.content) { + // Ok(lad) => lad, + // Err(e) => { + // log::debug!("Failed to parse LAD file: {:?}", e); + // errors.push(Error::new(e).context("Failed to parse LAD file")); + // return; + // } + // }; + + // log::debug!( + // "Parsed LAD file: {}", + // serde_json::to_string_pretty(&lad).unwrap_or_default() + // ); + + // let mut new_chapter = Section::Summary { + // ladfile: &lad, + // title: Some(chapter_title), + // } + // .into_chapter(None, 0); + + // new_chapter.path = new_chapter + // .path + // .map(|m| chapter.path.as_ref().cloned().unwrap_or_default().join(m)); + + // new_chapter.source_path = new_chapter.source_path.map(|m| { + // chapter + // .source_path + // .as_ref() + // .cloned() + // .unwrap_or_default() + // .join(m) + // }); + + // new_chapter.parent_names = chapter.parent_names.clone(); + + // // let sections = sections::lad_file_to_sections(&lad, Some(chapter_title)); + + // // let new_chapter = sections::section_to_chapter( + // // sections, + // // Some(chapter), + // // chapter.parent_names.clone(), + // // chapter.number.clone(), + // // None, + // // None, + // // ); + + // // serialize chapter to json + // log::debug!( + // "New chapter: {}", + // serde_json::to_string_pretty(&new_chapter).unwrap_or_default() + // ); + + // *chapter = new_chapter; + // } + // }); + if !errors.is_empty() { // return on first error for error in errors { diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs index 5828b849e9..2b6b7c6ce4 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs @@ -4,153 +4,154 @@ use crate::{ markdown_vec, }; use ladfile::{ - ArgumentVisitor, LadArgument, LadFile, LadFunction, LadInstance, LadType, LadTypeLayout, + ArgumentVisitor, LadArgument, LadFile, LadFunction, LadInstance, LadType, LadTypeId, + LadTypeLayout, }; -use mdbook::book::{Chapter, SectionNumber}; -use std::{borrow::Cow, collections::HashSet, path::PathBuf}; - -pub(crate) fn section_to_chapter( - section: SectionAndChildren, - original_chapter: Option<&Chapter>, - parent_names: Vec, - number: Option, - root_path: Option, - root_source_path: Option, -) -> Chapter { - let mut parent_builder = MarkdownBuilder::new(); - section.section.to_markdown(&mut parent_builder); - - // important to reset the extension of the parent, since when we're nesting - // we add the filename with .md, but if the parent is being emitted as markdown, then when - // we create the child, we will create the `parent.md` file as a folder, then when we emit - // the parent itself, the file (directory) will already exist - let new_path = root_path - .unwrap_or_default() - .with_extension("") - .join(section.section.file_name()); - - let new_source_path = root_source_path - .unwrap_or_default() - .with_extension("") - .join(section.section.file_name()); - - let current_number = number.clone().unwrap_or_default(); - - let children_chapters = section - .children - .into_iter() - .enumerate() - .map(|(index, child)| { - let mut new_number = current_number.clone(); - new_number.push(index as u32); - section_to_chapter( - child, - None, - vec![section.section.title()], - Some(new_number), - Some(new_path.clone()), - Some(new_source_path.clone()), - ) - }) - .map(mdbook::BookItem::Chapter) - .collect(); - - if let Some(original) = original_chapter { - // override content only - log::debug!( - "Setting .md extension for chapter paths: {:?}, {:?}.", - original.path, - original.source_path - ); - - Chapter { - content: parent_builder.build(), - sub_items: children_chapters, - path: original.path.as_ref().map(|p| p.with_extension("md")), - source_path: original - .source_path - .as_ref() - .map(|p| p.with_extension("md")), - ..original.clone() - } - } else { - Chapter { - name: section.section.title(), - content: parent_builder.build(), - number, - sub_items: children_chapters, - path: Some(new_path), - source_path: Some(new_source_path), - parent_names, - } - } -} - -fn section_to_section_and_children(section: Section<'_>) -> SectionAndChildren<'_> { - let children = section - .children() - .into_iter() - .map(section_to_section_and_children) - .collect(); - - SectionAndChildren { children, section } -} - -pub(crate) fn lad_file_to_sections( - ladfile: &ladfile::LadFile, - title: Option, -) -> SectionAndChildren<'_> { - section_to_section_and_children(Section::Summary { ladfile, title }) - // build a hierarchy as follows: - // - Summary - // - Instances - // - Functions - // - Global Function Detail 1 - // - Types - // - Type1 - // - Type detail 1 - // - Function detail 1 - // - Function detail 2 - // let mut types_children = ladfile - // .types - // .iter() - // .map(|(_, lad_type)| (lad_type, Section::TypeDetail { lad_type, ladfile })) - // .map(|(lad_type, section)| SectionAndChildren { - // section, - // children: lad_type - // .associated_functions - // .iter() - // .filter_map(|f| { - // let function = ladfile.functions.get(f)?; - // Some(SectionAndChildren { - // section: Section::FunctionDetail { function, ladfile }, - // children: vec![], - // }) - // }) - // .collect(), - // }) - // .collect(); - - // // now add a `functions` subsection before all types, for global functions - - // SectionAndChildren { - // section: summary, - // children: vec![ - // SectionAndChildren { - // section: Section::TypeSummary { ladfile }, - // children: types_children, - // }, - // SectionAndChildren { - // section: Section::FunctionSummary { ladfile }, - // children: vec![], - // }, - // ], - // } -} -pub(crate) struct SectionAndChildren<'a> { - section: Section<'a>, - children: Vec>, -} +use mdbook::book::Chapter; +use std::{borrow::Cow, collections::HashSet}; + +// pub(crate) fn section_to_chapter( +// section: SectionAndChildren, +// original_chapter: Option<&Chapter>, +// parent_names: Vec, +// number: Option, +// root_path: Option, +// root_source_path: Option, +// ) -> Chapter { +// let mut parent_builder = MarkdownBuilder::new(); +// section.section.to_markdown(&mut parent_builder); + +// // important to reset the extension of the parent, since when we're nesting +// // we add the filename with .md, but if the parent is being emitted as markdown, then when +// // we create the child, we will create the `parent.md` file as a folder, then when we emit +// // the parent itself, the file (directory) will already exist +// let new_path = root_path +// .unwrap_or_default() +// .with_extension("") +// .join(section.section.file_name()); + +// let new_source_path = root_source_path +// .unwrap_or_default() +// .with_extension("") +// .join(section.section.file_name()); + +// let current_number = number.clone().unwrap_or_default(); + +// let children_chapters = section +// .children +// .into_iter() +// .enumerate() +// .map(|(index, child)| { +// let mut new_number = current_number.clone(); +// new_number.push(index as u32); +// section_to_chapter( +// child, +// None, +// vec![section.section.title()], +// Some(new_number), +// Some(new_path.clone()), +// Some(new_source_path.clone()), +// ) +// }) +// .map(mdbook::BookItem::Chapter) +// .collect(); + +// if let Some(original) = original_chapter { +// // override content only +// log::debug!( +// "Setting .md extension for chapter paths: {:?}, {:?}.", +// original.path, +// original.source_path +// ); + +// Chapter { +// content: parent_builder.build(), +// sub_items: children_chapters, +// path: original.path.as_ref().map(|p| p.with_extension("md")), +// source_path: original +// .source_path +// .as_ref() +// .map(|p| p.with_extension("md")), +// ..original.clone() +// } +// } else { +// Chapter { +// name: section.section.title(), +// content: parent_builder.build(), +// number, +// sub_items: children_chapters, +// path: Some(new_path), +// source_path: Some(new_source_path), +// parent_names, +// } +// } +// } + +// fn section_to_section_and_children(section: Section<'_>) -> SectionAndChildren<'_> { +// let children = section +// .children() +// .into_iter() +// .map(section_to_section_and_children) +// .collect(); + +// SectionAndChildren { children, section } +// } + +// pub(crate) fn lad_file_to_sections( +// ladfile: &ladfile::LadFile, +// title: Option, +// ) -> SectionAndChildren<'_> { +// section_to_section_and_children(Section::Summary { ladfile, title }) +// build a hierarchy as follows: +// - Summary +// - Instances +// - Functions +// - Global Function Detail 1 +// - Types +// - Type1 +// - Type detail 1 +// - Function detail 1 +// - Function detail 2 +// let mut types_children = ladfile +// .types +// .iter() +// .map(|(_, lad_type)| (lad_type, Section::TypeDetail { lad_type, ladfile })) +// .map(|(lad_type, section)| SectionAndChildren { +// section, +// children: lad_type +// .associated_functions +// .iter() +// .filter_map(|f| { +// let function = ladfile.functions.get(f)?; +// Some(SectionAndChildren { +// section: Section::FunctionDetail { function, ladfile }, +// children: vec![], +// }) +// }) +// .collect(), +// }) +// .collect(); + +// // now add a `functions` subsection before all types, for global functions + +// SectionAndChildren { +// section: summary, +// children: vec![ +// SectionAndChildren { +// section: Section::TypeSummary { ladfile }, +// children: types_children, +// }, +// SectionAndChildren { +// section: Section::FunctionSummary { ladfile }, +// children: vec![], +// }, +// ], +// } +// } +// pub(crate) struct SectionAndChildren<'a> { +// section: Section<'a>, +// children: Vec>, +// } /// Sections which convert to single markdown files pub(crate) enum Section<'a> { @@ -165,6 +166,7 @@ pub(crate) enum Section<'a> { /// A link directory to all global instances within the ladfile InstancesSummary { ladfile: &'a ladfile::LadFile }, TypeDetail { + lad_type_id: &'a LadTypeId, lad_type: &'a LadType, ladfile: &'a ladfile::LadFile, }, @@ -180,6 +182,48 @@ pub fn linkify_filename(name: impl Into) -> String { } impl<'a> Section<'a> { + /// convert into a chapter, including children + pub(crate) fn into_chapter(self, parent: Option<&Chapter>, index: usize) -> Chapter { + let mut builder = MarkdownBuilder::new(); + self.to_markdown(&mut builder); + + let default_chapter = Chapter::default(); + let parent = match parent { + Some(parent) => parent, + None => &default_chapter, + }; + + let parent_path = parent.path.clone().unwrap_or_default().with_extension(""); + let parent_source_path = parent + .source_path + .clone() + .unwrap_or_default() + .with_extension(""); + + let mut current_number = parent.number.clone().unwrap_or_default(); + current_number.push(index as u32); + + let mut chapter = Chapter { + name: self.title(), + content: builder.build(), + parent_names: vec![parent.name.clone()], + path: Some(parent_path.join(self.file_name())), + source_path: Some(parent_source_path.join(self.file_name())), + number: Some(current_number), + sub_items: vec![], + }; + + chapter.sub_items = self + .children() + .into_iter() + .enumerate() + .map(|(i, c)| c.into_chapter(Some(&chapter), i)) + .map(mdbook::BookItem::Chapter) + .collect(); + + chapter + } + pub(crate) fn title(&self) -> String { match self { Section::Summary { title, .. } => { @@ -189,8 +233,14 @@ impl<'a> Section<'a> { Section::FunctionSummary { .. } => "Functions".to_owned(), Section::InstancesSummary { .. } => "Globals".to_owned(), Section::TypeDetail { - lad_type: type_id, .. - } => type_id.identifier.clone(), + ladfile, + lad_type_id, + .. + } => { + let mut visitor = MarkdownArgumentVisitor::new(ladfile); + visitor.visit_lad_type_id(lad_type_id); + visitor.build() + } Section::FunctionDetail { function, .. } => function.identifier.to_string(), } } @@ -211,7 +261,11 @@ impl<'a> Section<'a> { Section::TypeSummary { ladfile } => ladfile .types .iter() - .map(|(_, lad_type)| Section::TypeDetail { lad_type, ladfile }) + .map(|(lad_type_id, lad_type)| Section::TypeDetail { + lad_type, + ladfile, + lad_type_id, + }) .collect(), Section::FunctionSummary { ladfile } => { @@ -233,7 +287,9 @@ impl<'a> Section<'a> { Section::InstancesSummary { .. } => { vec![] } - Section::TypeDetail { lad_type, ladfile } => lad_type + Section::TypeDetail { + lad_type, ladfile, .. + } => lad_type .associated_functions .iter() .filter_map(|f| { @@ -313,7 +369,9 @@ impl<'a> Section<'a> { functions_directory: "functions".to_owned(), }] } - Section::TypeDetail { lad_type, ladfile } => { + Section::TypeDetail { + lad_type, ladfile, .. + } => { let functions = lad_type .associated_functions .iter() @@ -327,7 +385,7 @@ impl<'a> Section<'a> { SectionItem::Description { lad_type }, SectionItem::FunctionsSummary { functions, - functions_directory: linkify_filename(&lad_type.identifier), + functions_directory: linkify_filename(self.title()), }, ] } diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/book_integration_tests.rs b/crates/lad_backends/mdbook_lad_preprocessor/tests/book_integration_tests.rs index 1a75ca6052..c643821cb3 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/tests/book_integration_tests.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/book_integration_tests.rs @@ -13,7 +13,7 @@ fn add_executable_dir_to_path() { .expect("failed to get parent directory"); let mut paths = std::env::split_paths(&std::env::var("PATH").expect("failed to get PATH")) .collect::>(); - paths.push(dir.to_owned()); + paths.insert(0, dir.to_owned()); std::env::set_var( "PATH", std::env::join_paths(paths).expect("failed to join paths"), diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md new file mode 100644 index 0000000000..ffddbd4bd5 --- /dev/null +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md @@ -0,0 +1 @@ +Parent \ No newline at end of file diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/test.lad.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad.md similarity index 100% rename from crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/test.lad.md rename to crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad.md diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/functions.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/functions.md similarity index 100% rename from crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/functions.md rename to crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/functions.md diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/functions/hello_world.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/functions/hello_world.md similarity index 100% rename from crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/functions/hello_world.md rename to crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/functions/hello_world.md diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/globals.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/globals.md similarity index 100% rename from crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/globals.md rename to crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/globals.md diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/types.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/types.md similarity index 100% rename from crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/lad/types.md rename to crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/types.md diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/SUMMARY.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/SUMMARY.md index 41b8c69c1e..87583d92d7 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/SUMMARY.md +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/SUMMARY.md @@ -1 +1,2 @@ -- [LAD](test.lad.json) +- [parent](parent.md) + - [LAD](test.lad.json) diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md new file mode 100644 index 0000000000..ffddbd4bd5 --- /dev/null +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md @@ -0,0 +1 @@ +Parent \ No newline at end of file diff --git a/docs/src/Generated/generated-intro.md b/docs/src/Generated/generated-intro.md new file mode 100644 index 0000000000..1a1aebed6a --- /dev/null +++ b/docs/src/Generated/generated-intro.md @@ -0,0 +1 @@ +# WIP \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 438602bbbc..c449f35ccf 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -34,4 +34,5 @@ # WIP -- [Generated Docs](./ladfiles/bindings.lad.json) +- [WIP](./Generated/generated-intro.md) + - [Generated Docs](./ladfiles/bindings.lad.json) From 536861954a41084a7e77274196bc5662d3fedf36 Mon Sep 17 00:00:00 2001 From: makspll Date: Sun, 23 Feb 2025 15:24:12 +0000 Subject: [PATCH 2/6] improve formatting of generics, refactor code --- .../src/argument_visitor.rs | 14 +- .../mdbook_lad_preprocessor/src/lib.rs | 217 ++++++++---------- .../books/example_ladfile/expected/parent.md | 1 - .../tests/books/example_ladfile/src/parent.md | 1 - docs/book.toml | 2 + 5 files changed, 97 insertions(+), 138 deletions(-) diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs index db7eefa738..c86cfd2b5c 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/argument_visitor.rs @@ -25,22 +25,18 @@ impl<'a> MarkdownArgumentVisitor<'a> { impl ArgumentVisitor for MarkdownArgumentVisitor<'_> { fn visit_lad_type_id(&mut self, type_id: &ladfile::LadTypeId) { - let mut buffer = String::new(); - // Write identifier - buffer.push_str(&self.ladfile.get_type_identifier(type_id)); + self.buffer.text(self.ladfile.get_type_identifier(type_id)); if let Some(generics) = self.ladfile.get_type_generics(type_id) { - buffer.push('<'); + self.buffer.text('<'); for (i, generic) in generics.iter().enumerate() { + self.visit_lad_type_id(&generic.type_id); if i > 0 { - buffer.push_str(", "); + self.buffer.text(", "); } - buffer.push_str(&self.ladfile.get_type_identifier(&generic.type_id)); } - buffer.push('>'); + self.buffer.text('>'); } - - self.buffer.text(buffer); } fn walk_option(&mut self, inner: &ladfile::LadArgumentKind) { diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs index 610a70eb56..9912e05376 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs @@ -1,7 +1,7 @@ //! The library crate for the mdbook LAD preprocessor. #![allow(missing_docs)] -use mdbook::{errors::Error, preprocess::Preprocessor, BookItem}; +use mdbook::{errors::Error, preprocess::Preprocessor}; use sections::Section; mod argument_visitor; mod markdown; @@ -11,6 +11,46 @@ const LAD_EXTENSION: &str = "lad.json"; pub struct LADPreprocessor; +impl LADPreprocessor { + /// Checks if a chapter is a LAD file. + fn is_lad_file(chapter: &mdbook::book::Chapter) -> bool { + chapter + .source_path + .as_ref() + .and_then(|a| a.file_name()) + .map(|s| s.to_string_lossy().ends_with(LAD_EXTENSION)) + .unwrap_or(false) + } + + /// Process a chapter that is a LAD file. + /// + /// `parent` is the optional parent chapter reference, + /// and `chapter_index` is the index of the chapter among its siblings. + fn process_lad_chapter( + chapter: &mdbook::book::Chapter, + parent: Option<&mdbook::book::Chapter>, + chapter_index: usize, + ) -> Result { + let chapter_title = chapter.name.trim_end_matches(".lad.json").to_owned(); + let ladfile = ladfile::parse_lad_file(&chapter.content) + .map_err(|e| Error::new(e).context("Failed to parse LAD file"))?; + log::debug!( + "Parsed LAD file: {}", + serde_json::to_string_pretty(&ladfile).unwrap_or_default() + ); + let new_chapter = Section::Summary { + ladfile: &ladfile, + title: Some(chapter_title), + } + .into_chapter(parent, chapter_index); + log::debug!( + "New chapter: {}", + serde_json::to_string_pretty(&new_chapter).unwrap_or_default() + ); + Ok(new_chapter) + } +} + impl Preprocessor for LADPreprocessor { fn name(&self) -> &str { "lad-preprocessor" @@ -21,145 +61,68 @@ impl Preprocessor for LADPreprocessor { _ctx: &mdbook::preprocess::PreprocessorContext, mut book: mdbook::book::Book, ) -> mdbook::errors::Result { - let mut errors = Vec::default(); + let mut errors = Vec::new(); + // first replace children in parents book.for_each_mut(|item| { if let mdbook::BookItem::Chapter(parent) = item { - let mut replacements = Vec::default(); - for (ladfile_idx, child) in parent.sub_items.iter().enumerate() { - if let BookItem::Chapter(child) = child { - let is_lad_file = child - .source_path - .as_ref() - .and_then(|a| a.file_name()) - .is_some_and(|a| a.to_string_lossy().ends_with(LAD_EXTENSION)); - - if !is_lad_file { - continue; - } - - let chapter_title = - child.name.clone().trim_end_matches(".lad.json").to_owned(); - - let ladfile = match ladfile::parse_lad_file(&child.content) { - Ok(lad) => lad, - Err(e) => { - log::debug!("Failed to parse LAD file: {:?}", e); - errors.push(Error::new(e).context("Failed to parse LAD file")); - continue; + // First, collect the indices and new chapters for LAD file chapters. + let replacements: Vec<(usize, mdbook::book::Chapter)> = parent + .sub_items + .iter() + .enumerate() + .filter_map(|(idx, item)| { + if let mdbook::BookItem::Chapter(chapter) = item { + if LADPreprocessor::is_lad_file(chapter) { + match LADPreprocessor::process_lad_chapter( + chapter, + Some(parent), + idx, + ) { + Ok(new_chapter) => return Some((idx, new_chapter)), + Err(e) => { + errors.push(e); + return None; + } + } } - }; - - log::debug!( - "Parsed LAD file: {}", - serde_json::to_string_pretty(&ladfile).unwrap_or_default() - ); - - let new_chapter = Section::Summary { - ladfile: &ladfile, - title: Some(chapter_title), } - .into_chapter(Some(parent), ladfile_idx); - - log::debug!( - "New chapter: {}", - serde_json::to_string_pretty(&new_chapter).unwrap_or_default() - ); - - // replace - replacements.push((ladfile_idx, BookItem::Chapter(new_chapter))); + None + }) + .collect(); + + // Then, apply the replacements. + for (idx, new_chapter) in replacements { + if let mdbook::BookItem::Chapter(chapter) = &mut parent.sub_items[idx] { + *chapter = new_chapter; } } - - for (idx, replacement) in replacements { - log::debug!( - "Replacing chapter at index {}. With : \n{}", - idx, - serde_json::to_string_pretty(&replacement).unwrap_or_default() - ); - parent.sub_items[idx] = replacement; - } } }); - // book.for_each_mut(|item| { - // if let mdbook::BookItem::Chapter(chapter) = item { - // let is_lad_chapter = chapter - // .source_path - // .as_ref() - // .and_then(|a| a.file_name()) - // .is_some_and(|a| a.to_string_lossy().ends_with(LAD_EXTENSION)); - - // if !is_lad_chapter { - // log::debug!("Skipping non-LAD chapter: {:?}", chapter.source_path); - // log::trace!( - // "Non-LAD chapter: {}", - // serde_json::to_string_pretty(&chapter).unwrap_or_default() - // ); - // return; - // } - - // let chapter_title = chapter - // .name - // .clone() - // .trim_end_matches(".lad.json") - // .to_owned(); - - // let lad = match ladfile::parse_lad_file(&chapter.content) { - // Ok(lad) => lad, - // Err(e) => { - // log::debug!("Failed to parse LAD file: {:?}", e); - // errors.push(Error::new(e).context("Failed to parse LAD file")); - // return; - // } - // }; - - // log::debug!( - // "Parsed LAD file: {}", - // serde_json::to_string_pretty(&lad).unwrap_or_default() - // ); - - // let mut new_chapter = Section::Summary { - // ladfile: &lad, - // title: Some(chapter_title), - // } - // .into_chapter(None, 0); - - // new_chapter.path = new_chapter - // .path - // .map(|m| chapter.path.as_ref().cloned().unwrap_or_default().join(m)); - - // new_chapter.source_path = new_chapter.source_path.map(|m| { - // chapter - // .source_path - // .as_ref() - // .cloned() - // .unwrap_or_default() - // .join(m) - // }); - - // new_chapter.parent_names = chapter.parent_names.clone(); - - // // let sections = sections::lad_file_to_sections(&lad, Some(chapter_title)); + // then try match items themselves + book.for_each_mut(|item| { + if let mdbook::BookItem::Chapter(chapter) = item { + if !LADPreprocessor::is_lad_file(chapter) { + return; + } - // // let new_chapter = sections::section_to_chapter( - // // sections, - // // Some(chapter), - // // chapter.parent_names.clone(), - // // chapter.number.clone(), - // // None, - // // None, - // // ); + let new_chapter = match LADPreprocessor::process_lad_chapter(chapter, None, 0) { + Ok(new_chapter) => new_chapter, + Err(e) => { + errors.push(e); + return; + } + }; - // // serialize chapter to json - // log::debug!( - // "New chapter: {}", - // serde_json::to_string_pretty(&new_chapter).unwrap_or_default() - // ); + *chapter = new_chapter; + } + }); - // *chapter = new_chapter; - // } - // }); + log::debug!( + "Book after LAD processing: {}", + serde_json::to_string_pretty(&book).unwrap_or_default() + ); if !errors.is_empty() { // return on first error diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md index ffddbd4bd5..e69de29bb2 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent.md @@ -1 +0,0 @@ -Parent \ No newline at end of file diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md index ffddbd4bd5..e69de29bb2 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/src/parent.md @@ -1 +0,0 @@ -Parent \ No newline at end of file diff --git a/docs/book.toml b/docs/book.toml index bace71420b..9cf55445be 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -12,3 +12,5 @@ git-repository-url = "https://github.com/makspll/bevy_mod_scripting" edit-url-template = "https://github.com/makspll/bevy_mod_scripting/edit/main/docs/{path}" [preprocessor.lad-preprocessor] + +[output.markdown] From f5e733fa0b8e586920e5d0f508161431a23d57f5 Mon Sep 17 00:00:00 2001 From: Maksymilian Mozolewski Date: Sun, 23 Feb 2025 15:35:27 +0000 Subject: [PATCH 3/6] Update SUMMARY.md --- docs/src/SUMMARY.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index c449f35ccf..438602bbbc 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -34,5 +34,4 @@ # WIP -- [WIP](./Generated/generated-intro.md) - - [Generated Docs](./ladfiles/bindings.lad.json) +- [Generated Docs](./ladfiles/bindings.lad.json) From eb6082ee916eb5231fd65a30580668e2d2a07741 Mon Sep 17 00:00:00 2001 From: makspll Date: Sun, 23 Feb 2025 16:32:05 +0000 Subject: [PATCH 4/6] escape more characters in filenames --- crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs index 2b6b7c6ce4..180f67cb22 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs @@ -178,7 +178,11 @@ pub(crate) enum Section<'a> { /// Makes a filename safe to put in links pub fn linkify_filename(name: impl Into) -> String { - name.into().to_lowercase().replace(" ", "_") + name.into() + .to_lowercase() + .replace(" ", "_") + .replace("<", "") + .replace(">", "") } impl<'a> Section<'a> { From c87df70d702930036282a5afe94556c56d29c525 Mon Sep 17 00:00:00 2001 From: makspll Date: Sun, 23 Feb 2025 16:50:48 +0000 Subject: [PATCH 5/6] fix more broken links and escape markdown better --- .../mdbook_lad_preprocessor/src/markdown.rs | 2 +- .../mdbook_lad_preprocessor/src/sections.rs | 187 ++---------------- crates/ladfile/src/lib.rs | 13 ++ 3 files changed, 35 insertions(+), 167 deletions(-) diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs index 68a388c023..51c099cb5f 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/markdown.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -/// Takes the first n characters from the markdown, without splitting any formatting +/// Takes the first n characters from the markdown, without splitting any formatting. pub(crate) fn markdown_substring(markdown: &str, length: usize) -> &str { if markdown.len() <= length { return markdown; diff --git a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs index 180f67cb22..3843518c2d 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs +++ b/crates/lad_backends/mdbook_lad_preprocessor/src/sections.rs @@ -10,148 +10,11 @@ use ladfile::{ use mdbook::book::Chapter; use std::{borrow::Cow, collections::HashSet}; -// pub(crate) fn section_to_chapter( -// section: SectionAndChildren, -// original_chapter: Option<&Chapter>, -// parent_names: Vec, -// number: Option, -// root_path: Option, -// root_source_path: Option, -// ) -> Chapter { -// let mut parent_builder = MarkdownBuilder::new(); -// section.section.to_markdown(&mut parent_builder); - -// // important to reset the extension of the parent, since when we're nesting -// // we add the filename with .md, but if the parent is being emitted as markdown, then when -// // we create the child, we will create the `parent.md` file as a folder, then when we emit -// // the parent itself, the file (directory) will already exist -// let new_path = root_path -// .unwrap_or_default() -// .with_extension("") -// .join(section.section.file_name()); - -// let new_source_path = root_source_path -// .unwrap_or_default() -// .with_extension("") -// .join(section.section.file_name()); - -// let current_number = number.clone().unwrap_or_default(); - -// let children_chapters = section -// .children -// .into_iter() -// .enumerate() -// .map(|(index, child)| { -// let mut new_number = current_number.clone(); -// new_number.push(index as u32); -// section_to_chapter( -// child, -// None, -// vec![section.section.title()], -// Some(new_number), -// Some(new_path.clone()), -// Some(new_source_path.clone()), -// ) -// }) -// .map(mdbook::BookItem::Chapter) -// .collect(); - -// if let Some(original) = original_chapter { -// // override content only -// log::debug!( -// "Setting .md extension for chapter paths: {:?}, {:?}.", -// original.path, -// original.source_path -// ); - -// Chapter { -// content: parent_builder.build(), -// sub_items: children_chapters, -// path: original.path.as_ref().map(|p| p.with_extension("md")), -// source_path: original -// .source_path -// .as_ref() -// .map(|p| p.with_extension("md")), -// ..original.clone() -// } -// } else { -// Chapter { -// name: section.section.title(), -// content: parent_builder.build(), -// number, -// sub_items: children_chapters, -// path: Some(new_path), -// source_path: Some(new_source_path), -// parent_names, -// } -// } -// } - -// fn section_to_section_and_children(section: Section<'_>) -> SectionAndChildren<'_> { -// let children = section -// .children() -// .into_iter() -// .map(section_to_section_and_children) -// .collect(); - -// SectionAndChildren { children, section } -// } - -// pub(crate) fn lad_file_to_sections( -// ladfile: &ladfile::LadFile, -// title: Option, -// ) -> SectionAndChildren<'_> { -// section_to_section_and_children(Section::Summary { ladfile, title }) -// build a hierarchy as follows: -// - Summary -// - Instances -// - Functions -// - Global Function Detail 1 -// - Types -// - Type1 -// - Type detail 1 -// - Function detail 1 -// - Function detail 2 -// let mut types_children = ladfile -// .types -// .iter() -// .map(|(_, lad_type)| (lad_type, Section::TypeDetail { lad_type, ladfile })) -// .map(|(lad_type, section)| SectionAndChildren { -// section, -// children: lad_type -// .associated_functions -// .iter() -// .filter_map(|f| { -// let function = ladfile.functions.get(f)?; -// Some(SectionAndChildren { -// section: Section::FunctionDetail { function, ladfile }, -// children: vec![], -// }) -// }) -// .collect(), -// }) -// .collect(); - -// // now add a `functions` subsection before all types, for global functions - -// SectionAndChildren { -// section: summary, -// children: vec![ -// SectionAndChildren { -// section: Section::TypeSummary { ladfile }, -// children: types_children, -// }, -// SectionAndChildren { -// section: Section::FunctionSummary { ladfile }, -// children: vec![], -// }, -// ], -// } -// } -// pub(crate) struct SectionAndChildren<'a> { -// section: Section<'a>, -// children: Vec>, -// } +fn print_type(ladfile: &LadFile, type_: &LadTypeId) -> String { + let mut visitor = MarkdownArgumentVisitor::new(ladfile); + visitor.visit_lad_type_id(type_); + visitor.build() +} /// Sections which convert to single markdown files pub(crate) enum Section<'a> { @@ -240,11 +103,7 @@ impl<'a> Section<'a> { ladfile, lad_type_id, .. - } => { - let mut visitor = MarkdownArgumentVisitor::new(ladfile); - visitor.visit_lad_type_id(lad_type_id); - visitor.build() - } + } => print_type(ladfile, lad_type_id), Section::FunctionDetail { function, .. } => function.identifier.to_string(), } } @@ -349,10 +208,11 @@ impl<'a> Section<'a> { vec![SectionItem::InstancesSummary { instances }] } Section::TypeSummary { ladfile } => { - let types = ladfile.types.values().collect::>(); + let types = ladfile.types.keys().collect::>(); vec![SectionItem::TypesSummary { types, types_directory: linkify_filename(self.title()), + ladfile, }] } Section::FunctionSummary { ladfile } => { @@ -432,8 +292,9 @@ pub enum SectionItem<'a> { ladfile: &'a ladfile::LadFile, }, TypesSummary { - types: Vec<&'a LadType>, + types: Vec<&'a LadTypeId>, types_directory: String, + ladfile: &'a ladfile::LadFile, }, InstancesSummary { instances: Vec<(&'a Cow<'static, str>, &'a LadInstance)>, @@ -529,7 +390,7 @@ impl IntoMarkdown for SectionItem<'_> { builder.row(markdown_vec![ Markdown::new_paragraph(first_col).code(), Markdown::Link { - text: second_col.to_owned(), + text: second_col.to_owned().replace("\n", " "), url: format!("./{}/{}.md", functions_path, function.identifier), anchor: false } @@ -540,6 +401,7 @@ impl IntoMarkdown for SectionItem<'_> { SectionItem::TypesSummary { types, types_directory, + ladfile, } => { builder.heading(2, "Types"); @@ -547,29 +409,22 @@ impl IntoMarkdown for SectionItem<'_> { builder.table(|builder| { builder.headers(vec!["Type", "Summary"]); for type_ in types.iter() { - let first_col = type_.identifier.to_string(); + let printed_type = print_type(ladfile, type_); + + let documentation = ladfile.get_type_documentation(type_); // first line with content from documentation trimmed to 100 chars - let second_col = type_ - .documentation - .as_deref() - .map(|doc| { - let doc = doc.trim(); - if doc.len() > 100 { - format!("{}...", &doc[..100]) - } else { - doc.to_owned() - } - }) - .unwrap_or_else(|| NO_DOCS_STRING.to_owned()); + let second_col = documentation + .map(|doc| markdown_substring(doc, 100)) + .unwrap_or_else(|| NO_DOCS_STRING); builder.row(markdown_vec![ - Markdown::new_paragraph(first_col).code(), + Markdown::new_paragraph(printed_type.clone()).code(), Markdown::Link { - text: second_col, + text: second_col.to_owned().replace("\n", " "), url: format!( "./{types_directory}/{}.md", - linkify_filename(&type_.identifier) + linkify_filename(printed_type) ), anchor: false } diff --git a/crates/ladfile/src/lib.rs b/crates/ladfile/src/lib.rs index 66b86f44c3..a702a7f94e 100644 --- a/crates/ladfile/src/lib.rs +++ b/crates/ladfile/src/lib.rs @@ -69,6 +69,19 @@ impl LadFile { .get(type_id) .and_then(|t| (!t.generics.is_empty()).then_some(t.generics.as_slice())) } + + /// Retrieves the documentation of a type id if it is a defined type and has documentation. + pub fn get_type_documentation(&self, type_id: &LadTypeId) -> Option<&str> { + self.types + .get(type_id) + .and_then(|t| t.documentation.as_deref()) + // try primitives + .or_else(|| { + self.primitives + .get(type_id) + .map(|p| p.documentation.as_ref()) + }) + } } impl Default for LadFile { From d4a8df2804a3d8950cc9a9ab4ac0098f7f2136d5 Mon Sep 17 00:00:00 2001 From: makspll Date: Sun, 23 Feb 2025 17:17:50 +0000 Subject: [PATCH 6/6] fix test --- .../books/example_ladfile/expected/parent/lad/types.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/types.md b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/types.md index 1592d341ed..a591a784da 100644 --- a/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/types.md +++ b/crates/lad_backends/mdbook_lad_preprocessor/tests/books/example_ladfile/expected/parent/lad/types.md @@ -5,6 +5,6 @@ | Type | Summary | | --- | --- | | `EnumType` | [No Documentation 🚧](./types/enumtype.md) | -| `StructType` | [I am a struct](./types/structtype.md) | -| `TupleStructType` | [I am a tuple test type](./types/tuplestructtype.md) | -| `UnitType` | [I am a unit test type](./types/unittype.md) | \ No newline at end of file +| `StructType` | [ I am a struct](./types/structtypeusize.md) | +| `TupleStructType` | [ I am a tuple test type](./types/tuplestructtype.md) | +| `UnitType` | [ I am a unit test type](./types/unittype.md) | \ No newline at end of file