Add the data model and the document file format
The model holds the rules: a document is a tree under one root, with loose nodes beside it and cross-links between any two nodes. Every change runs as a transaction that rolls back on error and returns an undo step (the old and new values of whatever it touched), so undo and redo need no code of their own per command. Text from outside (titles, tags, names) is checked once, when it becomes a typed value. Folding (Open, Auto, Closed, plus focus) is one function that every view will share. storage::document_file reads and writes `.cum` files in RON, with multi-line bodies kept readable, `\n` line endings on every OS, and errors that name the node they're about. The tests cover the tree rules, 500 random edits undone and redone step by step, folding, and the file format (the example from DESIGN.md is written back character for character). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
0e03c396bf
commit
69aa7eee58
17 files changed
+2832
No files matched your search
@@ -0,0 +1,85 @@
|
||||
//! The data: documents, nodes, links and how they look. No UI and no files
|
||||
//! here, just the rules.
|
||||
//!
|
||||
//! - A [`Document`] owns a tree of [`Node`]s under one root, loose nodes
|
||||
//! beside it, and cross-[`Link`]s, and keeps its rules (one root, no
|
||||
//! cycles, links between nodes that exist).
|
||||
//! - Every change goes through [`Document::edit`], which returns a
|
||||
//! [`Change`] that [`History`] can undo and redo.
|
||||
//! - Text from outside ([`Title`], [`Tag`]…) is checked once, when it's
|
||||
//! turned into these types.
|
||||
//! - [`Visible`] works out which nodes folding leaves on show.
|
||||
|
||||
mod document;
|
||||
mod edit;
|
||||
mod fold;
|
||||
mod geometry;
|
||||
mod history;
|
||||
mod ids;
|
||||
mod link;
|
||||
mod look;
|
||||
mod node;
|
||||
mod portal;
|
||||
mod text;
|
||||
|
||||
pub use document::{Document, NodeRecord, Place, StructureError};
|
||||
pub use edit::{Change, Edit, EditError, Spot};
|
||||
pub use fold::Visible;
|
||||
pub use geometry::{CanvasOffset, CanvasPos};
|
||||
pub use history::{History, StateId};
|
||||
pub use ids::{DocUid, NodeId};
|
||||
pub use link::{Link, LinkKey};
|
||||
pub use look::{Arrows, Border, LineColor, LineLook, NodeLook, Stroke};
|
||||
pub use node::{Content, Fold, Idea, Node};
|
||||
pub use portal::{Backlink, EmptyPath, LinkPath, Portal};
|
||||
pub use text::{Body, ColorName, Label, StyleName, Tag, Tags, TextError, Title};
|
||||
|
||||
/// Small documents and helpers shared by the tests.
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test_support {
|
||||
use super::*;
|
||||
|
||||
pub fn title(text: &str) -> Title {
|
||||
Title::parse(text).expect("a valid title")
|
||||
}
|
||||
|
||||
pub fn idea(text: &str) -> Node {
|
||||
Node::idea(title(text), CanvasPos::default())
|
||||
}
|
||||
|
||||
/// The ids in [`garden`].
|
||||
pub struct Garden {
|
||||
pub root: NodeId,
|
||||
pub soil: NodeId,
|
||||
pub beds: NodeId,
|
||||
pub cedar: NodeId,
|
||||
pub watering: NodeId,
|
||||
pub compost: NodeId,
|
||||
}
|
||||
|
||||
/// The document from the design mockup:
|
||||
///
|
||||
/// ```text
|
||||
/// Garden plans ─┬─ Soil
|
||||
/// ├─ Raised beds ── Cedar
|
||||
/// └─ Watering
|
||||
/// ask about compost (loose)
|
||||
/// ```
|
||||
pub fn garden() -> (Document, Garden) {
|
||||
let mut doc = Document::with_uid(title("Garden plans"), DocUid::parse("7f3c9a1e52b04d18").unwrap());
|
||||
let root = doc.root();
|
||||
let at = |text: &str, x: i32, y: i32| Node::idea(title(text), CanvasPos::new(x, y));
|
||||
let (ids, _) = doc
|
||||
.edit(|e| {
|
||||
e.update(root, |node| node.pos = CanvasPos::new(0, 4))?;
|
||||
let soil = e.add(at("Soil", 22, 0), Spot::LastChildOf(root))?;
|
||||
let beds = e.add(at("Raised beds", 22, 4), Spot::LastChildOf(root))?;
|
||||
let cedar = e.add(at("Cedar", 40, 4), Spot::LastChildOf(beds))?;
|
||||
let watering = e.add(at("Watering", 22, 8), Spot::LastChildOf(root))?;
|
||||
let compost = e.add(at("ask about compost", 0, 14), Spot::Loose)?;
|
||||
Ok(Garden { root, soil, beds, cedar, watering, compost })
|
||||
})
|
||||
.expect("the garden builds");
|
||||
(doc, ids)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user