WIP Navigation sorting

This commit is contained in:
ef3d0c3e 2024-08-03 18:43:54 +02:00
parent 90cf691737
commit 0982527944
5 changed files with 131 additions and 20 deletions

View file

@ -25,3 +25,8 @@ function make_doc(categories, title, page_title)
nml.variable.insert("compiler.output", page_title .. ".html")
end
>@
@@style.section = {
"link_pos": "Before",
"link": ["", "🔗 ", " "]
}

View file

@ -7,7 +7,7 @@ use super::compiler::Target;
#[derive(Debug, Default)]
pub struct NavEntry {
pub(self) entries: Vec<(String, String)>,
pub(self) entries: Vec<(String, String, Option<String>)>,
pub(self) children: HashMap<String, NavEntry>,
}
@ -34,7 +34,7 @@ impl NavEntry {
depth: usize,
) {
// Orphans = Links
for (title, path) in &entry.entries {
for (title, path, _) in &entry.entries {
result.push_str(
format!(
r#"<li><a href="{}">{}</a></li>"#,
@ -75,6 +75,36 @@ impl NavEntry {
}
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> {
@ -83,12 +113,16 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
children: HashMap::new(),
};
// All paths (for duplicate checking)
let mut all_paths = HashMap::new();
for doc in docs {
let cat = doc.get_variable("nav.category");
let subcat = doc.get_variable("nav.subcategory");
let title = doc
.get_variable("nav.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 (title, path) = match (title, path) {
@ -142,8 +176,67 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
&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)
}
#[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
);
}
}

View file

@ -81,7 +81,7 @@ impl ReferenceRule {
),
);
Self {
re: [Regex::new(r"§\{(.*)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
re: [Regex::new(r"§\{(.*?)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
properties: PropertyParser{ properties: props },
}
}

View file

@ -46,16 +46,17 @@ impl Element for Section {
match compiler.target() {
Target::HTML => {
// 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 number = " ".to_string()
+ numbering
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(".")
.as_str();
number
let mut result = String::new();
for num in numbering.iter()
{
result = result + num.to_string().as_str() + ".";
}
result += " ";
result
} else {
String::new()
};
@ -71,8 +72,10 @@ impl Element for Section {
let refname = Compiler::refname(compiler.target(), self.title.as_str());
let link = format!(
"<a class=\"section-link\" href=\"#{refname}\">{}</a>",
Compiler::sanitize(compiler.target(), self.style.link.as_str())
"{}<a class=\"section-link\" href=\"#{refname}\">{}</a>{}",
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 {
@ -123,7 +126,7 @@ impl ReferenceableElement for Section {
);
Ok(format!(
"<a class=\"section-ref\" href=\"#{}\">{caption}</a>",
"<a class=\"section-reference\" href=\"#{}\">{caption}</a>",
Compiler::refname(compiler.target(), self.title.as_str())
))
}
@ -390,14 +393,14 @@ mod section_style {
#[derive(Debug, Serialize, Deserialize)]
pub struct SectionStyle {
pub link_pos: SectionLinkPos,
pub link: String,
pub link: [String; 3],
}
impl Default for SectionStyle {
fn default() -> Self {
Self {
link_pos: SectionLinkPos::After,
link: "🔗".to_string(),
link_pos: SectionLinkPos::Before,
link: ["".into(), "🔗".into(), " ".into()],
}
}
}

View file

@ -1,6 +1,6 @@
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)]
pub struct LineCursor
@ -146,3 +146,13 @@ impl KernelHolder for LsParser
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!()
}
}