From cc7bc7169c168d43a25316c4527ae6f252dbd2bb Mon Sep 17 00:00:00 2001
From: ef3d0c3e <ef3d0c3e@pundalik.org>
Date: Sun, 4 Aug 2024 08:32:15 +0200
Subject: [PATCH] Better sorting

---
 src/compiler/navigation.rs | 98 ++++++++++++++++++--------------------
 src/elements/section.rs    | 37 ++++++++++++++
 src/main.rs                |  1 -
 3 files changed, 84 insertions(+), 52 deletions(-)

diff --git a/src/compiler/navigation.rs b/src/compiler/navigation.rs
index 7d92f8d..becffaa 100644
--- a/src/compiler/navigation.rs
+++ b/src/compiler/navigation.rs
@@ -76,34 +76,28 @@ 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;
+		left: &(String, String, Option<String>),
+		right: &(String, String, Option<String>),
+	) -> std::cmp::Ordering {
+		match (&left.2, &right.2) {
+			(Some(_), Some(_)) => left.0.cmp(&right.0),
+			(Some(lp), None) => {
+				if &right.0 == lp {
+					std::cmp::Ordering::Greater
+				} else {
+					left.0.cmp(&right.0)
 				}
 			}
-		}
-
-		// 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;
+			(None, Some(rp)) => {
+				if &left.0 == rp {
+					std::cmp::Ordering::Less
+				} else {
+					left.0.cmp(&right.0)
+				}
 			}
-
-			insert_at += 1;
+			(None, None) => left.0.cmp(&right.0),
 		}
-
-		insert_at
 	}
 }
 
@@ -196,47 +190,49 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
 		}
 		all_paths.insert(path.clone(), title.clone());
 
-		pent.entries.insert(
-			NavEntry::sort_entry(title, &previous, &pent.entries),
-			(title.clone(), path.clone(), previous),
-		);
+		pent.entries.push((title.clone(), path.clone(), previous));
 	}
 
+	// Sort entries
+	fn sort_entries(nav: &mut NavEntry) {
+		nav.entries
+			.sort_unstable_by(|l, r| NavEntry::sort_entry(l, r));
+
+		for (_, child) in &mut nav.children {
+			sort_entries(child);
+		}
+	}
+	sort_entries(&mut nav);
+
 	Ok(nav)
 }
 
 #[cfg(test)]
 mod tests {
+	use rand::rngs::OsRng;
+	use rand::RngCore;
+
 	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())),
+		let mut entries: Vec<(String, String, Option<String>)> = vec![
+			("Index".into(), "".into(), None),
+			("AB".into(), "".into(), Some("Index".into())),
+			("Getting Started".into(), "".into(), Some("Index".into())),
+			("Sections".into(), "".into(), Some("Getting Started".into())),
+			("Style".into(), "".into(), Some("Getting Started".into())),
 		];
+		let mut shuffled = entries.clone();
+		for _ in 0..10 {
+			for i in 0..5 {
+				let pos = OsRng.next_u64() % entries.len() as u64;
+				shuffled.swap(i, pos as usize);
+			}
 
-		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);
+			shuffled.sort_by(|l, r| NavEntry::sort_entry(l, r));
 
-		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
-		);
+			assert_eq!(shuffled, entries);
+		}
 	}
 }
diff --git a/src/elements/section.rs b/src/elements/section.rs
index 1116282..d7f9eab 100644
--- a/src/elements/section.rs
+++ b/src/elements/section.rs
@@ -407,3 +407,40 @@ mod section_style {
 
 	impl_elementstyle!(SectionStyle, STYLE_KEY);
 }
+
+#[cfg(test)]
+mod tests
+{
+	use crate::{parser::{langparser::LangParser, source::SourceFile}, validate_document};
+
+use super::*;
+
+	#[test]
+	fn parser()
+	{
+		let source = Rc::new(SourceFile::with_content(
+				"".to_string(),
+				r#"
+# 1
+##+ 2
+###* 3
+####+* 4
+#####*+ 5
+######{refname} 6
+		"#
+		.to_string(),
+		None,
+		));
+		let parser = LangParser::default();
+		let doc = parser.parse(source, None);
+
+		validate_document!(doc.content().borrow(), 0,
+			Section { depth == 1, title == "1" };
+			Section { depth == 2, title == "2", kind == section_kind::NO_TOC };
+			Section { depth == 3, title == "3", kind == section_kind::NO_NUMBER };
+			Section { depth == 4, title == "4", kind == section_kind::NO_NUMBER | section_kind::NO_TOC };
+			Section { depth == 5, title == "5", kind == section_kind::NO_NUMBER | section_kind::NO_TOC };
+			Section { depth == 6, title == "6", reference == Some("refname".to_string()) };
+		);
+	}
+}
diff --git a/src/main.rs b/src/main.rs
index d820a0d..be36b5c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,3 @@
-#![feature(char_indices_offset)]
 mod cache;
 mod compiler;
 mod document;