56 lines
1.7 KiB
Rust
56 lines
1.7 KiB
Rust
use crate::model::{BlockType, DocumentBlock, DocumentModel};
|
|||
|
|
|
||
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
|
|
pub struct OutlineEntry {
|
||
|
|
pub block_id: String,
|
||
|
|
pub level: u8,
|
||
|
|
pub title: String,
|
||
|
|
pub depth: usize,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
|
|
pub struct VisibilitySnapshot {
|
||
|
|
pub visible_block_ids: Vec<String>,
|
||
|
|
pub outline: Vec<OutlineEntry>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl VisibilitySnapshot {
|
||
|
|
pub fn derive(document: &DocumentModel) -> Self {
|
||
|
|
let mut hidden_ancestor_ids: Vec<String> = Vec::new();
|
||
|
|
let mut visible_block_ids = Vec::new();
|
||
|
|
let mut outline = Vec::new();
|
||
|
|
|
||
|
|
for block in document.blocks() {
|
||
|
|
hidden_ancestor_ids.retain(|ancestor_id| is_ancestor_of(document, ancestor_id, block));
|
||
|
|
let visible = hidden_ancestor_ids.is_empty();
|
||
|
|
if visible {
|
||
|
|
visible_block_ids.push(block.id.clone());
|
||
|
|
}
|
||
|
|
|
||
|
|
if matches!(block.block_type, BlockType::Heading) {
|
||
|
|
let level = block.heading_level.unwrap_or(1);
|
||
|
|
if visible {
|
||
|
|
outline.push(OutlineEntry {
|
||
|
|
block_id: block.id.clone(),
|
||
|
|
level,
|
||
|
|
title: block.content.text.clone(),
|
||
|
|
depth: block.indent as usize,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if block.collapsed {
|
||
|
|
hidden_ancestor_ids.push(block.id.clone());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Self {
|
||
|
|
visible_block_ids,
|
||
|
|
outline,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn is_ancestor_of(document: &DocumentModel, ancestor_id: &str, block: &DocumentBlock) -> bool {
|
||
|
|
document.is_descendant_of(&block.id, ancestor_id)
|
||
|
|
}
|