Piece Tree
Purely functional (immutable) implementation of Piece Tree, inspired by fredbuf.
Usage
You can access full documentation at: https://docs.rs/piece-tree/0.1.1/piece_tree.
Basic editing
use PieceTree;
let mut tree = new;
// Insert text at a byte offset
tree.insert;
assert_eq!;
// Insert in the middle
tree.insert;
assert_eq!;
// Remove a range by byte offset and length
tree.remove;
assert_eq!;
Coordinate queries
All coordinate functions work in O(log n) time.
use PieceTree;
let mut tree = new;
tree.insert;
// Char index <-> byte offset
assert_eq!; // '🌐' starts at byte 6
assert_eq!; // 'w' after 🌐 is at byte 10
assert_eq!;
// Line number -> byte offset of line start (0-indexed)
assert_eq!;
assert_eq!;
assert_eq!;
// Byte offset -> (line, col) where col is a char count
assert_eq!;
assert_eq!; // 'w', line 1, col 1
assert_eq!; // 'П', line 2, col 0
Undo and redo
Each call to insert or remove automatically pushes an undo entry.
try_undo and try_redo return the cursor offset that was saved with the entry.
use PieceTree;
let mut tree = new;
tree.insert;
tree.insert;
assert_eq!;
tree.try_undo;
assert_eq!;
tree.try_redo;
assert_eq!;
Use begin_undo_group and end_undo_group to batch multiple mutations into a single undo step. For manual control, use the *_no_commit variants to make silent changes, followed by commit_head call to record a checkpoint.
Tree Snapshotting
fn main() {
use piece_tree::PieceTree;
let mut tree = PieceTree::new();
let mut cursor = 0;
// Basic typing and taking a snapshot
tree.insert(cursor, "Hello "); cursor += 6;
tree.insert(cursor, "World!"); cursor += 6;
let snap = tree.take_snapshot(cursor); // Saves "Hello World!" state
// Batch mutations into a single undo group
tree.begin_undo_group(cursor);
tree.insert_no_commit(cursor, " Everything"); cursor += 11;
tree.insert_no_commit(cursor, " is great!"); cursor += 10;
tree.end_undo_group();
assert_eq!(tree.to_string(), "Hello World! Everything is great!");
// Undo and Redo the transaction group
cursor = tree.try_undo(cursor).unwrap();
assert_eq!(tree.to_string(), "Hello World!");
cursor = tree.try_redo(cursor).unwrap();
assert_eq!(tree.to_string(), "Hello World! Everything is great!");
// Revert to snapshot (and show that the jump itself can be undone)
cursor = tree.snap_to(snap, cursor);
assert_eq!(tree.to_string(), "Hello World!");
cursor = tree.try_undo(cursor).unwrap();
assert_eq!(tree.to_string(), "Hello World! Everything is great!");
}
Vendoring
To keep piece-tree highly optimized and tailored for its specific use case, portions of the following third-party crates have been integrated directly into the source code:
cranelift-entity(Apache-2.0 with LLVM Exception).smallvec(MIT).bytecount(MIT).
This copy-pasted code remains under its original respective licenses. Full attribution notices are maintained at the top of the relevant source files, and the complete license texts can be found in THIRD-PARTY-LICENSES.md.
If you prefer to use the upstream, non-vendored versions of these crates via Cargo, you can enable the dont_vendor feature flag.