summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers+gitlab@digital-scurf.org>2023-05-12 12:08:46 +0000
committerDaniel Silverstone <dsilvers+gitlab@digital-scurf.org>2023-05-12 12:08:46 +0000
commit5b30f07a5989769397de7441ac9440e379a53488 (patch)
treef6884f4b6c3ee43c9c736f7f91e12d8a5ec98c49
parent37c46ae17cb3f75a4b34c9e2104110c20fb2cb59 (diff)
parentace211d2c2e9c775872c4a23087763afff5a5bb2 (diff)
downloadsubplot-5b30f07a5989769397de7441ac9440e379a53488.tar.gz
Merge branch 'liw/muldti-md' into 'main'
support multiple markdown files per subplot document Closes #318 See merge request subplot/subplot!328
-rw-r--r--src/bin/subplot.rs14
-rw-r--r--src/doc.rs113
-rw-r--r--src/md.rs62
-rw-r--r--src/metadata.rs23
-rw-r--r--subplot-build/src/lib.rs4
-rw-r--r--subplot.md38
6 files changed, 166 insertions, 88 deletions
diff --git a/src/bin/subplot.rs b/src/bin/subplot.rs
index 41d8894..8da6c2b 100644
--- a/src/bin/subplot.rs
+++ b/src/bin/subplot.rs
@@ -264,8 +264,18 @@ impl Docgen {
} else if let Some(date) = doc.meta().date() {
date.to_string()
} else {
- let filename = doc.meta().basedir().join(doc.meta().markdown_filename());
- Self::mtime_formatted(Self::mtime(&filename)?)
+ let mut newest = None;
+ for filename in doc.meta().markdown_filenames() {
+ let mtime = Self::mtime(filename)?;
+ if let Some(so_far) = newest {
+ if mtime > so_far {
+ newest = Some(mtime);
+ }
+ } else {
+ newest = Some(mtime);
+ }
+ }
+ Self::mtime_formatted(newest.unwrap())
};
if Self::need_output(&mut doc, self.template.as_deref(), &self.output) {
diff --git a/src/doc.rs b/src/doc.rs
index 459b0e7..640968c 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -1,8 +1,8 @@
use crate::bindings::CaptureType;
use crate::generate_test_program;
use crate::get_basedir_from;
-use crate::html::Element;
use crate::html::HtmlPage;
+use crate::html::{Content, Element, ElementTag};
use crate::md::Markdown;
use crate::EmbeddedFile;
use crate::EmbeddedFiles;
@@ -66,7 +66,7 @@ static KNOWN_BLOCK_CLASSES: &[&str] = &["numberLines", "noNumberLines"];
#[derive(Debug)]
pub struct Document {
subplot: PathBuf,
- md: Markdown,
+ markdowns: Vec<Markdown>,
meta: Metadata,
files: EmbeddedFiles,
style: Style,
@@ -75,14 +75,14 @@ pub struct Document {
impl Document {
fn new(
subplot: PathBuf,
- md: Markdown,
+ markdowns: Vec<Markdown>,
meta: Metadata,
files: EmbeddedFiles,
style: Style,
) -> Document {
let doc = Document {
subplot,
- md,
+ markdowns,
meta,
files,
style,
@@ -95,7 +95,7 @@ impl Document {
basedir: P,
subplot: PathBuf,
yamlmeta: &YamlMetadata,
- md: Markdown,
+ markdowns: Vec<Markdown>,
style: Style,
template: Option<&str>,
) -> Result<Document, SubplotError>
@@ -104,12 +104,22 @@ impl Document {
{
let meta = Metadata::from_yaml_metadata(basedir, yamlmeta, template)?;
trace!("metadata from YAML: {:#?}", meta);
- let files = md.embedded_files();
- let doc = Document::new(subplot, md, meta, files, style);
+ let files = Self::all_files(&markdowns);
+ let doc = Document::new(subplot, markdowns, meta, files, style);
trace!("Loaded from JSON OK");
Ok(doc)
}
+ fn all_files(markdowns: &[Markdown]) -> EmbeddedFiles {
+ let mut files = EmbeddedFiles::default();
+ for md in markdowns {
+ for file in md.embedded_files().files() {
+ files.push(file.clone());
+ }
+ }
+ files
+ }
+
/// Construct a Document from a named file.
pub fn from_file(
basedir: &Path,
@@ -126,12 +136,13 @@ impl Document {
let meta = load_metadata_from_yaml_file(filename)?;
trace!("metadata from YAML file: {:#?}", meta);
- let mdfile = meta.markdown();
- let mdfile = basedir.join(mdfile);
- let mut md = Markdown::load_file(mdfile.as_path())?;
- md.set_metadata(&meta);
+ let mut markdowns = vec![];
+ for filename in meta.markdowns() {
+ let filename = basedir.join(filename);
+ markdowns.push(Markdown::load_file(&filename)?);
+ }
- let doc = Self::from_ast(basedir, filename.into(), &meta, md, style, template)?;
+ let doc = Self::from_ast(basedir, filename.into(), &meta, markdowns, style, template)?;
trace!("Loaded document OK");
Ok(doc)
@@ -144,12 +155,54 @@ impl Document {
title.push_child(crate::html::Content::Text(self.meta().title().into()));
head.push_child(crate::html::Content::Elt(title));
- self.md.set_date(date.into());
+ self.meta.set_date(date.into());
- let page = HtmlPage::new(head, self.md.to_html());
+ let mut body = self.typeset_meta();
+ for md in self.markdowns.iter() {
+ body.push_child(Content::Elt(md.root_element().clone()));
+ }
+ let page = HtmlPage::new(head, body);
page.serialize().unwrap() // FIXME
}
+ fn typeset_meta(&self) -> Element {
+ let mut div = Element::new(ElementTag::Div);
+ div.push_child(Content::Elt(Self::title(self.meta.title())));
+ if let Some(authors) = self.meta.authors() {
+ div.push_child(Content::Elt(Self::authors(authors)));
+ }
+ if let Some(date) = self.meta.date() {
+ div.push_child(Content::Elt(Self::date(date)));
+ }
+ div
+ }
+
+ fn title(title: &str) -> Element {
+ let mut e = Element::new(ElementTag::H1);
+ e.push_child(Content::Text(title.into()));
+ e
+ }
+
+ fn authors(authors: &[String]) -> Element {
+ let mut list = Element::new(ElementTag::P);
+ list.push_child(Content::Text("By: ".into()));
+ let mut first = true;
+ for a in authors {
+ if !first {
+ list.push_child(Content::Text(", ".into()));
+ }
+ list.push_child(Content::Text(a.into()));
+ first = false;
+ }
+ list
+ }
+
+ fn date(date: &str) -> Element {
+ let mut e = Element::new(ElementTag::P);
+ e.push_child(Content::Text(date.into()));
+ e
+ }
+
/// Return the document's metadata.
pub fn meta(&self) -> &Metadata {
&self.meta
@@ -191,10 +244,14 @@ impl Document {
names.push(PathBuf::from(x))
}
- names.push(self.meta().markdown_filename().to_path_buf());
+ for name in self.meta().markdown_filenames() {
+ names.push(name.into());
+ }
- let mut images = self.md.images();
- names.append(&mut images);
+ for md in self.markdowns.iter() {
+ let mut images = md.images();
+ names.append(&mut images);
+ }
names
}
@@ -237,7 +294,7 @@ impl Document {
/// Check that all the block classes in the document are known
fn check_block_classes(&self) -> Result<(), SubplotError> {
- let classes_in_doc = self.md.block_classes();
+ let classes_in_doc = self.all_block_classes();
// Build the set of known good classes
let mut known_classes: HashSet<String> = HashSet::new();
@@ -260,6 +317,16 @@ impl Document {
}
}
+ fn all_block_classes(&self) -> HashSet<String> {
+ let mut set = HashSet::new();
+ for md in self.markdowns.iter() {
+ for class in md.block_classes() {
+ set.insert(class);
+ }
+ }
+ set
+ }
+
/// Check that all named files (in matched steps) are actually present in the
/// document.
pub fn check_named_files_exist(
@@ -363,12 +430,18 @@ impl Document {
/// Typeset a Subplot document.
pub fn typeset(&mut self, warnings: &mut Warnings) {
- warnings.push_all(self.md.typeset(self.style.clone(), self.meta.bindings()));
+ for md in self.markdowns.iter_mut() {
+ warnings.push_all(md.typeset(self.style.clone(), self.meta.bindings()));
+ }
}
/// Return all scenarios in a document.
pub fn scenarios(&self) -> Result<Vec<Scenario>, SubplotError> {
- self.md.scenarios()
+ let mut scenarios = vec![];
+ for md in self.markdowns.iter() {
+ scenarios.append(&mut md.scenarios()?);
+ }
+ Ok(scenarios)
}
/// Return matched scenarios in a document.
diff --git a/src/md.rs b/src/md.rs
index 9ad30bc..5e56198 100644
--- a/src/md.rs
+++ b/src/md.rs
@@ -3,7 +3,7 @@
use crate::{
html::{parse, Attribute, Content, Element, ElementTag},
parse_scenario_snippet, Bindings, EmbeddedFile, EmbeddedFiles, Scenario, ScenarioStep, Style,
- SubplotError, Warnings, YamlMetadata,
+ SubplotError, Warnings,
};
use log::trace;
use std::collections::HashSet;
@@ -13,7 +13,6 @@ use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct Markdown {
html: Element,
- meta: Option<YamlMetadata>,
}
impl Markdown {
@@ -32,63 +31,12 @@ impl Markdown {
}
fn new(html: Element) -> Self {
- Self { html, meta: None }
+ Self { html }
}
- /// Set document metadata from subplot.
- pub fn set_metadata(&mut self, meta: &YamlMetadata) {
- self.meta = Some(meta.clone());
- }
-
- /// Set date.
- pub fn set_date(&mut self, date: String) {
- if let Some(meta) = &mut self.meta {
- meta.set_date(date);
- }
- }
-
- /// Return parsed HTML of the markdown.
- pub fn to_html(&self) -> Element {
- if let Some(meta) = &self.meta {
- let mut div = Element::new(ElementTag::Div);
- div.push_child(Content::Elt(Self::title(meta.title())));
- if let Some(authors) = meta.authors() {
- div.push_child(Content::Elt(Self::authors(authors)));
- }
- if let Some(date) = meta.date() {
- div.push_child(Content::Elt(Self::date(date)));
- }
- div.push_child(Content::Elt(self.html.clone()));
- div
- } else {
- self.html.clone()
- }
- }
-
- fn title(title: &str) -> Element {
- let mut e = Element::new(ElementTag::H1);
- e.push_child(Content::Text(title.into()));
- e
- }
-
- fn authors(authors: &[String]) -> Element {
- let mut list = Element::new(ElementTag::P);
- list.push_child(Content::Text("By: ".into()));
- let mut first = true;
- for a in authors {
- if !first {
- list.push_child(Content::Text(", ".into()));
- }
- list.push_child(Content::Text(a.into()));
- first = false;
- }
- list
- }
-
- fn date(date: &str) -> Element {
- let mut e = Element::new(ElementTag::P);
- e.push_child(Content::Text(date.into()));
- e
+ /// Return root element of markdown.
+ pub fn root_element(&self) -> &Element {
+ &self.html
}
/// Find included images.
diff --git a/src/metadata.rs b/src/metadata.rs
index 9f25621..e4d3d53 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -63,9 +63,9 @@ impl YamlMetadata {
Ok(meta)
}
- /// Name of file with the Markdown for the subplot document.
- pub fn markdown(&self) -> &Path {
- &self.markdowns[0]
+ /// Names of files with the Markdown for the subplot document.
+ pub fn markdowns(&self) -> &[PathBuf] {
+ &self.markdowns
}
/// Title.
@@ -176,7 +176,8 @@ pub struct Metadata {
basedir: PathBuf,
title: String,
date: Option<String>,
- markdown_filename: PathBuf,
+ authors: Option<Vec<String>>,
+ markdown_filenames: Vec<PathBuf>,
bindings_filenames: Vec<PathBuf>,
bindings: Bindings,
impls: HashMap<String, DocumentImpl>,
@@ -234,7 +235,8 @@ impl Metadata {
basedir: basedir.as_ref().to_path_buf(),
title: yaml.title().into(),
date: yaml.date().map(|s| s.into()),
- markdown_filename: yaml.markdown().into(),
+ authors: yaml.authors().map(|a| a.into()),
+ markdown_filenames: yaml.markdowns().into(),
bindings_filenames,
bindings,
impls,
@@ -261,14 +263,19 @@ impl Metadata {
self.date = Some(date);
}
+ /// Authors.
+ pub fn authors(&self) -> Option<&[String]> {
+ self.authors.as_deref()
+ }
+
/// Return base dir for all relative filenames.
pub fn basedir(&self) -> &Path {
&self.basedir
}
- /// Return filename of the markdown file.
- pub fn markdown_filename(&self) -> &Path {
- &self.markdown_filename
+ /// Return filenames of the markdown files.
+ pub fn markdown_filenames(&self) -> &[PathBuf] {
+ &self.markdown_filenames
}
/// Return filename where bindings are specified.
diff --git a/subplot-build/src/lib.rs b/subplot-build/src/lib.rs
index 7f194c5..20d3e08 100644
--- a/subplot-build/src/lib.rs
+++ b/subplot-build/src/lib.rs
@@ -54,7 +54,9 @@ where
// re-running.
let base_path = get_basedir_from(filename);
let meta = output.doc.meta();
- buildrs_deps(&base_path, Some(meta.markdown_filename()));
+ for filename in meta.markdown_filenames() {
+ buildrs_deps(&base_path, Some(filename.as_path()));
+ }
buildrs_deps(&base_path, meta.bindings_filenames());
let docimpl = output
.doc
diff --git a/subplot.md b/subplot.md
index 05edeae..8556afd 100644
--- a/subplot.md
+++ b/subplot.md
@@ -2685,6 +2685,44 @@ bibliography: [foo.bib, bar.bib]
~~~
+### Multiple markdown files
+
+This scenario tests that the `markdowns` field in metadata can specify
+more than one markdown file.
+
+~~~scenario
+given file multimd.subplot
+given file md1.md
+given file md2.md
+given an installed subplot
+when I run subplot docgen multimd.subplot -o multimd.html
+when I run cat multimd.html
+then file multimd.html exists
+and file multimd.html contains "<title>The Fabulous Title</title>"
+and file multimd.html contains "First markdown file."
+and file multimd.html contains "Second markdown file."
+~~~
+
+~~~{#multimd.subplot .file .yaml .numberLines}
+title: The Fabulous Title
+authors:
+- Alfred Pennyworth
+- Geoffrey Butler
+date: WIP
+markdowns:
+- md1.md
+- md2.md
+~~~
+
+~~~{#md1.md .file .markdown .numberLines}
+First markdown file.
+~~~
+
+~~~{#md2.md .file .markdown .numberLines}
+Second markdown file.
+~~~
+
+
## Embedded files
Subplot allows data files to be embedded in the input document. This