1use std::collections::HashSet;
4use std::sync::Arc;
5
6use parking_lot::Mutex;
7
8use frontend::commands::{block_commands, frame_commands, table_cell_commands, table_commands};
9use frontend::common::types::EntityId;
10
11use crate::FrameFormat;
12use crate::convert::to_usize;
13use crate::flow::{CellSnapshot, FlowElement, FlowElementSnapshot, FrameSnapshot, TableSnapshot};
14use crate::inner::TextDocumentInner;
15use crate::text_block::TextBlock;
16use crate::text_table::TextTable;
17
18#[derive(Clone)]
22pub struct TextFrame {
23 pub(crate) doc: Arc<Mutex<TextDocumentInner>>,
24 pub(crate) frame_id: usize,
25}
26
27impl TextFrame {
28 pub fn id(&self) -> usize {
30 self.frame_id
31 }
32
33 pub fn format(&self) -> FrameFormat {
35 let inner = self.doc.lock();
36 let frame_dto = frame_commands::get_frame(&inner.ctx, &(self.frame_id as EntityId))
37 .ok()
38 .flatten();
39 match frame_dto {
40 Some(f) => frame_dto_to_format(&f),
41 None => FrameFormat::default(),
42 }
43 }
44
45 pub fn flow(&self) -> Vec<FlowElement> {
48 let inner = self.doc.lock();
49 build_flow_elements(&inner, &self.doc, self.frame_id as EntityId)
50 }
51
52 pub fn snapshot(&self) -> FrameSnapshot {
56 let inner = self.doc.lock();
57 let format = frame_commands::get_frame(&inner.ctx, &(self.frame_id as EntityId))
58 .ok()
59 .flatten()
60 .map(|f| frame_dto_to_format(&f))
61 .unwrap_or_default();
62 let elements = build_flow_snapshot(&inner, self.frame_id as EntityId, inner.highlight_kind);
63 FrameSnapshot {
64 frame_id: self.frame_id,
65 format,
66 elements,
67 }
68 }
69}
70
71pub(crate) fn build_flow_elements(
80 inner: &TextDocumentInner,
81 doc_arc: &Arc<Mutex<TextDocumentInner>>,
82 frame_id: EntityId,
83) -> Vec<FlowElement> {
84 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
85 .ok()
86 .flatten()
87 {
88 Some(f) => f,
89 None => return Vec::new(),
90 };
91
92 if !frame_dto.child_order.is_empty() {
93 flow_from_child_order(inner, doc_arc, &frame_dto.child_order)
94 } else {
95 flow_fallback(inner, doc_arc, &frame_dto)
96 }
97}
98
99fn flow_from_child_order(
101 inner: &TextDocumentInner,
102 doc_arc: &Arc<Mutex<TextDocumentInner>>,
103 child_order: &[i64],
104) -> Vec<FlowElement> {
105 let mut elements = Vec::with_capacity(child_order.len());
106
107 for &entry in child_order {
108 if entry > 0 {
109 elements.push(FlowElement::Block(TextBlock {
111 doc: Arc::clone(doc_arc),
112 block_id: entry as usize,
113 }));
114 } else if entry < 0 {
115 let sub_frame_id = (-entry) as EntityId;
117 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
118 .ok()
119 .flatten()
120 {
121 if let Some(table_id) = sub_frame.table {
122 elements.push(FlowElement::Table(TextTable {
124 doc: Arc::clone(doc_arc),
125 table_id: table_id as usize,
126 }));
127 } else {
128 elements.push(FlowElement::Frame(TextFrame {
130 doc: Arc::clone(doc_arc),
131 frame_id: sub_frame_id as usize,
132 }));
133 }
134 }
135 }
136 }
138
139 elements
140}
141
142fn flow_fallback(
144 inner: &TextDocumentInner,
145 doc_arc: &Arc<Mutex<TextDocumentInner>>,
146 frame_dto: &frontend::frame::dtos::FrameDto,
147) -> Vec<FlowElement> {
148 let cell_frame_ids = build_cell_frame_ids(inner);
150
151 let block_ids = &frame_dto.blocks;
153 let mut block_dtos: Vec<_> = block_ids
154 .iter()
155 .filter_map(|&id| {
156 block_commands::get_block(&inner.ctx, &{ id })
157 .ok()
158 .flatten()
159 })
160 .collect();
161 let store = inner.ctx.db_context.get_store();
162 crate::inner::refresh_block_positions(&mut block_dtos, store);
163 block_dtos.sort_by_key(|b| b.document_position);
164
165 let mut elements: Vec<FlowElement> = block_dtos
166 .iter()
167 .map(|b| {
168 FlowElement::Block(TextBlock {
169 doc: Arc::clone(doc_arc),
170 block_id: b.id as usize,
171 })
172 })
173 .collect();
174
175 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
180 for f in &all_frames {
181 if f.id == frame_dto.id {
182 continue; }
184 if cell_frame_ids.contains(&(f.id as EntityId)) {
185 continue; }
187 if f.parent_frame == Some(frame_dto.id) {
189 if let Some(table_id) = f.table {
190 elements.push(FlowElement::Table(TextTable {
191 doc: Arc::clone(doc_arc),
192 table_id: table_id as usize,
193 }));
194 } else {
195 elements.push(FlowElement::Frame(TextFrame {
196 doc: Arc::clone(doc_arc),
197 frame_id: f.id as usize,
198 }));
199 }
200 }
201 }
202
203 elements
204}
205
206fn build_cell_frame_ids(inner: &TextDocumentInner) -> HashSet<EntityId> {
208 let mut ids = HashSet::new();
209 let all_cells = table_cell_commands::get_all_table_cell(&inner.ctx).unwrap_or_default();
210 for cell in &all_cells {
211 if let Some(frame_id) = cell.cell_frame {
212 ids.insert(frame_id);
213 }
214 }
215 ids
216}
217
218pub(crate) fn build_flow_snapshot(
228 inner: &TextDocumentInner,
229 frame_id: EntityId,
230 effective_kind: crate::highlight::HighlighterKind,
231) -> Vec<FlowElementSnapshot> {
232 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
233 .ok()
234 .flatten()
235 {
236 Some(f) => f,
237 None => return Vec::new(),
238 };
239
240 if !frame_dto.child_order.is_empty() {
241 let (elements, _) =
242 snapshot_from_child_order(inner, &frame_dto.child_order, 0, frame_id, effective_kind);
243 elements
244 } else {
245 snapshot_fallback(inner, &frame_dto, effective_kind)
246 }
247}
248
249fn snapshot_from_child_order(
255 inner: &TextDocumentInner,
256 child_order: &[i64],
257 start_pos: usize,
258 parent_frame_id: EntityId,
259 effective_kind: crate::highlight::HighlighterKind,
260) -> (Vec<FlowElementSnapshot>, usize) {
261 let mut elements = Vec::with_capacity(child_order.len());
262 let mut running_pos = start_pos;
263
264 for &entry in child_order {
265 if entry > 0 {
266 let block_id = entry as u64;
267 if let Some(snap) = crate::text_block::build_block_snapshot_with_position_and_parent(
268 inner,
269 block_id,
270 Some(running_pos),
271 Some(parent_frame_id),
272 effective_kind,
273 ) {
274 running_pos += snap.length + 1; elements.push(FlowElementSnapshot::Block(snap));
276 }
277 } else if entry < 0 {
278 let sub_frame_id = (-entry) as EntityId;
279 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
280 .ok()
281 .flatten()
282 {
283 if let Some(table_id) = sub_frame.table {
284 if let Some((snap, new_pos)) = build_table_snapshot_with_positions(
285 inner,
286 table_id,
287 running_pos,
288 effective_kind,
289 ) {
290 running_pos = new_pos;
291 elements.push(FlowElementSnapshot::Table(snap));
292 }
293 } else {
294 let (nested, new_pos) = snapshot_from_child_order(
295 inner,
296 &sub_frame.child_order,
297 running_pos,
298 sub_frame_id,
299 effective_kind,
300 );
301 running_pos = new_pos;
302 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
303 frame_id: sub_frame_id as usize,
304 format: frame_dto_to_format(&sub_frame),
305 elements: nested,
306 }));
307 }
308 }
309 }
310 }
311
312 (elements, running_pos)
313}
314
315fn snapshot_fallback(
316 inner: &TextDocumentInner,
317 frame_dto: &frontend::frame::dtos::FrameDto,
318 effective_kind: crate::highlight::HighlighterKind,
319) -> Vec<FlowElementSnapshot> {
320 let cell_frame_ids = build_cell_frame_ids(inner);
321
322 let block_ids = &frame_dto.blocks;
323 let mut block_dtos: Vec<_> = block_ids
324 .iter()
325 .filter_map(|&id| {
326 block_commands::get_block(&inner.ctx, &{ id })
327 .ok()
328 .flatten()
329 })
330 .collect();
331 let store = inner.ctx.db_context.get_store();
332 crate::inner::refresh_block_positions(&mut block_dtos, store);
333 block_dtos.sort_by_key(|b| b.document_position);
334
335 let mut elements: Vec<FlowElementSnapshot> = block_dtos
336 .iter()
337 .filter_map(|b| crate::text_block::build_block_snapshot(inner, b.id, effective_kind))
338 .map(FlowElementSnapshot::Block)
339 .collect();
340
341 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
342 for f in &all_frames {
343 if f.id == frame_dto.id {
344 continue;
345 }
346 if cell_frame_ids.contains(&(f.id as EntityId)) {
347 continue;
348 }
349 if f.parent_frame == Some(frame_dto.id) {
350 if let Some(table_id) = f.table {
351 if let Some(snap) = build_table_snapshot(inner, table_id, effective_kind) {
352 elements.push(FlowElementSnapshot::Table(snap));
353 }
354 } else {
355 let nested = build_flow_snapshot(inner, f.id as EntityId, effective_kind);
356 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
357 frame_id: f.id as usize,
358 format: frame_dto_to_format(f),
359 elements: nested,
360 }));
361 }
362 }
363 }
364
365 elements
366}
367
368pub(crate) fn build_table_snapshot(
370 inner: &TextDocumentInner,
371 table_id: u64,
372 effective_kind: crate::highlight::HighlighterKind,
373) -> Option<TableSnapshot> {
374 let table_dto = table_commands::get_table(&inner.ctx, &table_id)
375 .ok()
376 .flatten()?;
377
378 let mut cells = Vec::new();
379 for &cell_id in &table_dto.cells {
380 if let Some(cell_dto) = table_cell_commands::get_table_cell(&inner.ctx, &{ cell_id })
381 .ok()
382 .flatten()
383 {
384 let blocks = if let Some(cell_frame_id) = cell_dto.cell_frame {
385 crate::text_block::build_blocks_snapshot_for_frame(
386 inner,
387 cell_frame_id,
388 effective_kind,
389 )
390 } else {
391 Vec::new()
392 };
393 cells.push(CellSnapshot {
394 row: to_usize(cell_dto.row),
395 column: to_usize(cell_dto.column),
396 row_span: to_usize(cell_dto.row_span),
397 column_span: to_usize(cell_dto.column_span),
398 format: cell_dto_to_format(&cell_dto),
399 blocks,
400 });
401 }
402 }
403
404 Some(TableSnapshot {
405 table_id: table_id as usize,
406 rows: to_usize(table_dto.rows),
407 columns: to_usize(table_dto.columns),
408 column_widths: table_dto.column_widths.iter().map(|&v| v as i32).collect(),
409 format: table_dto_to_format(&table_dto),
410 cells,
411 })
412}
413
414fn build_table_snapshot_with_positions(
421 inner: &TextDocumentInner,
422 table_id: u64,
423 start_pos: usize,
424 effective_kind: crate::highlight::HighlighterKind,
425) -> Option<(TableSnapshot, usize)> {
426 let table_dto = table_commands::get_table(&inner.ctx, &table_id)
427 .ok()
428 .flatten()?;
429
430 let mut cell_dtos: Vec<_> = table_dto
432 .cells
433 .iter()
434 .filter_map(|&cell_id| {
435 table_cell_commands::get_table_cell(&inner.ctx, &{ cell_id })
436 .ok()
437 .flatten()
438 })
439 .collect();
440 cell_dtos.sort_by(|a, b| a.row.cmp(&b.row).then(a.column.cmp(&b.column)));
441
442 let mut running_pos = start_pos;
443 let mut cells = Vec::with_capacity(cell_dtos.len());
444 for cell_dto in &cell_dtos {
445 let blocks = if let Some(cell_frame_id) = cell_dto.cell_frame {
446 let (snaps, new_pos) =
447 crate::text_block::build_blocks_snapshot_for_frame_with_positions(
448 inner,
449 cell_frame_id,
450 running_pos,
451 effective_kind,
452 );
453 running_pos = new_pos;
454 snaps
455 } else {
456 Vec::new()
457 };
458 cells.push(CellSnapshot {
459 row: to_usize(cell_dto.row),
460 column: to_usize(cell_dto.column),
461 row_span: to_usize(cell_dto.row_span),
462 column_span: to_usize(cell_dto.column_span),
463 format: cell_dto_to_format(cell_dto),
464 blocks,
465 });
466 }
467
468 Some((
469 TableSnapshot {
470 table_id: table_id as usize,
471 rows: to_usize(table_dto.rows),
472 columns: to_usize(table_dto.columns),
473 column_widths: table_dto.column_widths.iter().map(|&v| v as i32).collect(),
474 format: table_dto_to_format(&table_dto),
475 cells,
476 },
477 running_pos,
478 ))
479}
480
481pub(crate) fn frame_dto_to_format(f: &frontend::frame::dtos::FrameDto) -> FrameFormat {
486 FrameFormat {
487 height: f.fmt_height.map(|v| v as i32),
488 width: f.fmt_width.map(|v| v as i32),
489 top_margin: f.fmt_top_margin.map(|v| v as i32),
490 bottom_margin: f.fmt_bottom_margin.map(|v| v as i32),
491 left_margin: f.fmt_left_margin.map(|v| v as i32),
492 right_margin: f.fmt_right_margin.map(|v| v as i32),
493 padding: f.fmt_padding.map(|v| v as i32),
494 border: f.fmt_border.map(|v| v as i32),
495 position: f.fmt_position.clone(),
496 is_blockquote: f.fmt_is_blockquote,
497 }
498}
499
500pub(crate) fn table_dto_to_format(t: &frontend::table::dtos::TableDto) -> crate::flow::TableFormat {
501 crate::flow::TableFormat {
502 border: t.fmt_border.map(|v| v as i32),
503 cell_spacing: t.fmt_cell_spacing.map(|v| v as i32),
504 cell_padding: t.fmt_cell_padding.map(|v| v as i32),
505 width: t.fmt_width.map(|v| v as i32),
506 alignment: t.fmt_alignment.clone(),
507 }
508}
509
510pub(crate) fn cell_dto_to_format(
511 c: &frontend::table_cell::dtos::TableCellDto,
512) -> crate::flow::CellFormat {
513 use frontend::common::entities::CellVerticalAlignment as BackendCVA;
514 crate::flow::CellFormat {
515 padding: c.fmt_padding.map(|v| v as i32),
516 border: c.fmt_border.map(|v| v as i32),
517 vertical_alignment: c.fmt_vertical_alignment.as_ref().map(|v| match v {
518 BackendCVA::Top => crate::flow::CellVerticalAlignment::Top,
519 BackendCVA::Middle => crate::flow::CellVerticalAlignment::Middle,
520 BackendCVA::Bottom => crate::flow::CellVerticalAlignment::Bottom,
521 }),
522 background_color: c.fmt_background_color.clone(),
523 }
524}