WIP Navigation sorting
This commit is contained in:
parent
90cf691737
commit
0982527944
5 changed files with 131 additions and 20 deletions
|
@ -25,3 +25,8 @@ function make_doc(categories, title, page_title)
|
||||||
nml.variable.insert("compiler.output", page_title .. ".html")
|
nml.variable.insert("compiler.output", page_title .. ".html")
|
||||||
end
|
end
|
||||||
>@
|
>@
|
||||||
|
|
||||||
|
@@style.section = {
|
||||||
|
"link_pos": "Before",
|
||||||
|
"link": ["", "🔗 ", " "]
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use super::compiler::Target;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct NavEntry {
|
pub struct NavEntry {
|
||||||
pub(self) entries: Vec<(String, String)>,
|
pub(self) entries: Vec<(String, String, Option<String>)>,
|
||||||
pub(self) children: HashMap<String, NavEntry>,
|
pub(self) children: HashMap<String, NavEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ impl NavEntry {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) {
|
) {
|
||||||
// Orphans = Links
|
// Orphans = Links
|
||||||
for (title, path) in &entry.entries {
|
for (title, path, _) in &entry.entries {
|
||||||
result.push_str(
|
result.push_str(
|
||||||
format!(
|
format!(
|
||||||
r#"<li><a href="{}">{}</a></li>"#,
|
r#"<li><a href="{}">{}</a></li>"#,
|
||||||
|
@ -75,6 +75,36 @@ impl NavEntry {
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the insert index of the entry inside an already sorted entry list
|
||||||
|
fn sort_entry(
|
||||||
|
title: &String,
|
||||||
|
previous: &Option<String>,
|
||||||
|
entries: &Vec<(String, String, Option<String>)>,
|
||||||
|
) -> usize {
|
||||||
|
let mut insert_at = 0;
|
||||||
|
if let Some(previous) = &previous
|
||||||
|
// Using sort key
|
||||||
|
{
|
||||||
|
for (i, (ent_title, _, _)) in entries.iter().enumerate() {
|
||||||
|
if ent_title == previous {
|
||||||
|
insert_at = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then sort alphabetically
|
||||||
|
for (ent_title, _, ent_previous) in entries.iter().skip(insert_at) {
|
||||||
|
if (previous.is_some() && ent_previous != previous) || ent_title > title {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_at += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_at
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, String> {
|
pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, String> {
|
||||||
|
@ -83,12 +113,16 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
|
||||||
children: HashMap::new(),
|
children: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// All paths (for duplicate checking)
|
||||||
|
let mut all_paths = HashMap::new();
|
||||||
|
|
||||||
for doc in docs {
|
for doc in docs {
|
||||||
let cat = doc.get_variable("nav.category");
|
let cat = doc.get_variable("nav.category");
|
||||||
let subcat = doc.get_variable("nav.subcategory");
|
let subcat = doc.get_variable("nav.subcategory");
|
||||||
let title = doc
|
let title = doc
|
||||||
.get_variable("nav.title")
|
.get_variable("nav.title")
|
||||||
.or(doc.get_variable("doc.title"));
|
.or(doc.get_variable("doc.title"));
|
||||||
|
let previous = doc.get_variable("nav.previous").map(|s| s.clone());
|
||||||
let path = doc.get_variable("compiler.output");
|
let path = doc.get_variable("compiler.output");
|
||||||
|
|
||||||
let (title, path) = match (title, path) {
|
let (title, path) = match (title, path) {
|
||||||
|
@ -142,8 +176,67 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
|
||||||
&mut nav
|
&mut nav
|
||||||
};
|
};
|
||||||
|
|
||||||
pent.entries.push((title.clone(), path.clone()))
|
// Find duplicates titles in current parent
|
||||||
|
for (ent_title, _, _) in &pent.entries {
|
||||||
|
if ent_title == title {
|
||||||
|
return Err(format!(
|
||||||
|
"Conflicting entry title `{title}` for entries with the same parent: ({})",
|
||||||
|
pent.entries
|
||||||
|
.iter()
|
||||||
|
.map(|(title, _, _)| title.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find duplicate paths
|
||||||
|
if let Some(dup_title) = all_paths.get(path) {
|
||||||
|
return Err(format!("Conflicting paths: `{path}`. Previously used for entry: `{dup_title}`, conflicting use in `{title}`"));
|
||||||
|
}
|
||||||
|
all_paths.insert(path.clone(), title.clone());
|
||||||
|
|
||||||
|
pent.entries.insert(
|
||||||
|
NavEntry::sort_entry(title, &previous, &pent.entries),
|
||||||
|
(title.clone(), path.clone(), previous),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(nav)
|
Ok(nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sort() {
|
||||||
|
let entries: Vec<(String, String, Option<String>)> = vec![
|
||||||
|
("Root".into(), "".into(), None),
|
||||||
|
("First".into(), "".into(), Some("Root".into())),
|
||||||
|
("1".into(), "".into(), Some("First".into())),
|
||||||
|
("2".into(), "".into(), Some("First".into())),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
NavEntry::sort_entry(&"E".into(), &Some("Root".into()), &entries),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
NavEntry::sort_entry(&"G".into(), &Some("Root".into()), &entries),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
// Orphans
|
||||||
|
assert_eq!(NavEntry::sort_entry(&"Q".into(), &None, &entries), 0);
|
||||||
|
assert_eq!(NavEntry::sort_entry(&"S".into(), &None, &entries), 4);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
NavEntry::sort_entry(&"1.1".into(), &Some("First".into()), &entries),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
NavEntry::sort_entry(&"2.1".into(), &Some("First".into()), &entries),
|
||||||
|
4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl ReferenceRule {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Self {
|
Self {
|
||||||
re: [Regex::new(r"§\{(.*)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
|
re: [Regex::new(r"§\{(.*?)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
|
||||||
properties: PropertyParser{ properties: props },
|
properties: PropertyParser{ properties: props },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,16 +46,17 @@ impl Element for Section {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
// Section numbering
|
// Section numbering
|
||||||
let number = if (self.kind & section_kind::NO_NUMBER) == section_kind::NO_NUMBER {
|
let number = if (self.kind & section_kind::NO_NUMBER) != section_kind::NO_NUMBER {
|
||||||
let numbering = compiler.section_counter(self.depth);
|
let numbering = compiler.section_counter(self.depth);
|
||||||
let number = " ".to_string()
|
|
||||||
+ numbering
|
let mut result = String::new();
|
||||||
.iter()
|
for num in numbering.iter()
|
||||||
.map(|n| n.to_string())
|
{
|
||||||
.collect::<Vec<_>>()
|
result = result + num.to_string().as_str() + ".";
|
||||||
.join(".")
|
}
|
||||||
.as_str();
|
result += " ";
|
||||||
number
|
|
||||||
|
result
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
|
@ -71,8 +72,10 @@ impl Element for Section {
|
||||||
|
|
||||||
let refname = Compiler::refname(compiler.target(), self.title.as_str());
|
let refname = Compiler::refname(compiler.target(), self.title.as_str());
|
||||||
let link = format!(
|
let link = format!(
|
||||||
"<a class=\"section-link\" href=\"#{refname}\">{}</a>",
|
"{}<a class=\"section-link\" href=\"#{refname}\">{}</a>{}",
|
||||||
Compiler::sanitize(compiler.target(), self.style.link.as_str())
|
Compiler::sanitize(compiler.target(), self.style.link[0].as_str()),
|
||||||
|
Compiler::sanitize(compiler.target(), self.style.link[1].as_str()),
|
||||||
|
Compiler::sanitize(compiler.target(), self.style.link[2].as_str())
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.style.link_pos == SectionLinkPos::After {
|
if self.style.link_pos == SectionLinkPos::After {
|
||||||
|
@ -123,7 +126,7 @@ impl ReferenceableElement for Section {
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"<a class=\"section-ref\" href=\"#{}\">{caption}</a>",
|
"<a class=\"section-reference\" href=\"#{}\">{caption}</a>",
|
||||||
Compiler::refname(compiler.target(), self.title.as_str())
|
Compiler::refname(compiler.target(), self.title.as_str())
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -390,14 +393,14 @@ mod section_style {
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct SectionStyle {
|
pub struct SectionStyle {
|
||||||
pub link_pos: SectionLinkPos,
|
pub link_pos: SectionLinkPos,
|
||||||
pub link: String,
|
pub link: [String; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SectionStyle {
|
impl Default for SectionStyle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
link_pos: SectionLinkPos::After,
|
link_pos: SectionLinkPos::Before,
|
||||||
link: "🔗".to_string(),
|
link: ["".into(), "🔗".into(), " ".into()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{cell::{RefCell, RefMut}, collections::HashMap, rc::Rc};
|
use std::{cell::{RefCell, RefMut}, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use crate::{document::{document::Document, element::Element}, lua::kernel::{Kernel, KernelHolder}, parser::{parser::{Parser, ReportColors}, rule::Rule, source::{Cursor, Source}, state::StateHolder}};
|
use crate::{document::{document::Document, element::Element, style::{ElementStyle, StyleHolder}}, lua::kernel::{Kernel, KernelHolder}, parser::{parser::{Parser, ReportColors}, rule::Rule, source::{Cursor, Source}, state::StateHolder}};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LineCursor
|
pub struct LineCursor
|
||||||
|
@ -146,3 +146,13 @@ impl KernelHolder for LsParser
|
||||||
self.get_kernel(name.as_str()).unwrap()
|
self.get_kernel(name.as_str()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StyleHolder for LsParser {
|
||||||
|
fn styles(&self) -> std::cell::Ref<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue