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(
63 &inner,
64 self.frame_id as EntityId,
65 crate::highlight::SnapshotHighlights {
66 kind: inner.highlight_kind,
67 mask: &crate::highlight::HighlightMask::ALL,
68 suppress_paint: false,
69 },
70 );
71 FrameSnapshot {
72 frame_id: self.frame_id,
73 format,
74 elements,
75 }
76 }
77}
78
79pub(crate) fn build_flow_elements(
88 inner: &TextDocumentInner,
89 doc_arc: &Arc<Mutex<TextDocumentInner>>,
90 frame_id: EntityId,
91) -> Vec<FlowElement> {
92 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
93 .ok()
94 .flatten()
95 {
96 Some(f) => f,
97 None => return Vec::new(),
98 };
99
100 if !frame_dto.child_order.is_empty() {
101 flow_from_child_order(inner, doc_arc, &frame_dto.child_order)
102 } else {
103 flow_fallback(inner, doc_arc, &frame_dto)
104 }
105}
106
107fn flow_from_child_order(
109 inner: &TextDocumentInner,
110 doc_arc: &Arc<Mutex<TextDocumentInner>>,
111 child_order: &[i64],
112) -> Vec<FlowElement> {
113 let mut elements = Vec::with_capacity(child_order.len());
114
115 for &entry in child_order {
116 if entry > 0 {
117 elements.push(FlowElement::Block(TextBlock {
119 doc: Arc::clone(doc_arc),
120 block_id: entry as usize,
121 }));
122 } else if entry < 0 {
123 let sub_frame_id = (-entry) as EntityId;
125 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
126 .ok()
127 .flatten()
128 {
129 if let Some(table_id) = sub_frame.table {
130 elements.push(FlowElement::Table(TextTable {
132 doc: Arc::clone(doc_arc),
133 table_id: table_id as usize,
134 }));
135 } else {
136 elements.push(FlowElement::Frame(TextFrame {
138 doc: Arc::clone(doc_arc),
139 frame_id: sub_frame_id as usize,
140 }));
141 }
142 }
143 }
144 }
146
147 elements
148}
149
150fn flow_fallback(
152 inner: &TextDocumentInner,
153 doc_arc: &Arc<Mutex<TextDocumentInner>>,
154 frame_dto: &frontend::frame::dtos::FrameDto,
155) -> Vec<FlowElement> {
156 let cell_frame_ids = build_cell_frame_ids(inner);
158
159 let block_ids = &frame_dto.blocks;
161 let mut block_dtos: Vec<_> = block_ids
162 .iter()
163 .filter_map(|&id| {
164 block_commands::get_block(&inner.ctx, &{ id })
165 .ok()
166 .flatten()
167 })
168 .collect();
169 let store = inner.ctx.db_context.get_store();
170 crate::inner::refresh_block_positions(&mut block_dtos, store);
171 block_dtos.sort_by_key(|b| b.document_position);
172
173 let mut elements: Vec<FlowElement> = block_dtos
174 .iter()
175 .map(|b| {
176 FlowElement::Block(TextBlock {
177 doc: Arc::clone(doc_arc),
178 block_id: b.id as usize,
179 })
180 })
181 .collect();
182
183 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
188 for f in &all_frames {
189 if f.id == frame_dto.id {
190 continue; }
192 if cell_frame_ids.contains(&(f.id as EntityId)) {
193 continue; }
195 if f.parent_frame == Some(frame_dto.id) {
197 if let Some(table_id) = f.table {
198 elements.push(FlowElement::Table(TextTable {
199 doc: Arc::clone(doc_arc),
200 table_id: table_id as usize,
201 }));
202 } else {
203 elements.push(FlowElement::Frame(TextFrame {
204 doc: Arc::clone(doc_arc),
205 frame_id: f.id as usize,
206 }));
207 }
208 }
209 }
210
211 elements
212}
213
214fn build_cell_frame_ids(inner: &TextDocumentInner) -> HashSet<EntityId> {
216 let mut ids = HashSet::new();
217 let all_cells = table_cell_commands::get_all_table_cell(&inner.ctx).unwrap_or_default();
218 for cell in &all_cells {
219 if let Some(frame_id) = cell.cell_frame {
220 ids.insert(frame_id);
221 }
222 }
223 ids
224}
225
226pub(crate) fn build_flow_snapshot(
236 inner: &TextDocumentInner,
237 frame_id: EntityId,
238 hl: crate::highlight::SnapshotHighlights,
239) -> Vec<FlowElementSnapshot> {
240 let frame_dto = match frame_commands::get_frame(&inner.ctx, &frame_id)
241 .ok()
242 .flatten()
243 {
244 Some(f) => f,
245 None => return Vec::new(),
246 };
247
248 if !frame_dto.child_order.is_empty() {
249 let (elements, _) =
250 snapshot_from_child_order(inner, &frame_dto.child_order, 0, frame_id, hl);
251 elements
252 } else {
253 snapshot_fallback(inner, &frame_dto, hl)
254 }
255}
256
257fn snapshot_from_child_order(
263 inner: &TextDocumentInner,
264 child_order: &[i64],
265 start_pos: usize,
266 parent_frame_id: EntityId,
267 hl: crate::highlight::SnapshotHighlights,
268) -> (Vec<FlowElementSnapshot>, usize) {
269 let mut elements = Vec::with_capacity(child_order.len());
270 let mut running_pos = start_pos;
271
272 for &entry in child_order {
273 if entry > 0 {
274 let block_id = entry as u64;
275 if let Some(snap) = crate::text_block::build_block_snapshot_with_position_and_parent(
276 inner,
277 block_id,
278 Some(running_pos),
279 Some(parent_frame_id),
280 hl,
281 ) {
282 running_pos += snap.length + 1; elements.push(FlowElementSnapshot::Block(snap));
284 }
285 } else if entry < 0 {
286 let sub_frame_id = (-entry) as EntityId;
287 if let Some(sub_frame) = frame_commands::get_frame(&inner.ctx, &sub_frame_id)
288 .ok()
289 .flatten()
290 {
291 if let Some(table_id) = sub_frame.table {
292 if let Some((snap, new_pos)) =
293 build_table_snapshot_with_positions(inner, table_id, running_pos, hl)
294 {
295 running_pos = new_pos;
296 elements.push(FlowElementSnapshot::Table(snap));
297 }
298 } else {
299 let (nested, new_pos) = snapshot_from_child_order(
300 inner,
301 &sub_frame.child_order,
302 running_pos,
303 sub_frame_id,
304 hl,
305 );
306 running_pos = new_pos;
307 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
308 frame_id: sub_frame_id as usize,
309 format: frame_dto_to_format(&sub_frame),
310 elements: nested,
311 }));
312 }
313 }
314 }
315 }
316
317 (elements, running_pos)
318}
319
320fn snapshot_fallback(
321 inner: &TextDocumentInner,
322 frame_dto: &frontend::frame::dtos::FrameDto,
323 hl: crate::highlight::SnapshotHighlights,
324) -> Vec<FlowElementSnapshot> {
325 let cell_frame_ids = build_cell_frame_ids(inner);
326
327 let block_ids = &frame_dto.blocks;
328 let mut block_dtos: Vec<_> = block_ids
329 .iter()
330 .filter_map(|&id| {
331 block_commands::get_block(&inner.ctx, &{ id })
332 .ok()
333 .flatten()
334 })
335 .collect();
336 let store = inner.ctx.db_context.get_store();
337 crate::inner::refresh_block_positions(&mut block_dtos, store);
338 block_dtos.sort_by_key(|b| b.document_position);
339
340 let mut elements: Vec<FlowElementSnapshot> = block_dtos
341 .iter()
342 .filter_map(|b| crate::text_block::build_block_snapshot(inner, b.id, hl))
343 .map(FlowElementSnapshot::Block)
344 .collect();
345
346 let all_frames = frame_commands::get_all_frame(&inner.ctx).unwrap_or_default();
347 for f in &all_frames {
348 if f.id == frame_dto.id {
349 continue;
350 }
351 if cell_frame_ids.contains(&(f.id as EntityId)) {
352 continue;
353 }
354 if f.parent_frame == Some(frame_dto.id) {
355 if let Some(table_id) = f.table {
356 if let Some(snap) = build_table_snapshot(inner, table_id, hl) {
357 elements.push(FlowElementSnapshot::Table(snap));
358 }
359 } else {
360 let nested = build_flow_snapshot(inner, f.id as EntityId, hl);
361 elements.push(FlowElementSnapshot::Frame(FrameSnapshot {
362 frame_id: f.id as usize,
363 format: frame_dto_to_format(f),
364 elements: nested,
365 }));
366 }
367 }
368 }
369
370 elements
371}
372
373pub(crate) fn build_table_snapshot(
375 inner: &TextDocumentInner,
376 table_id: u64,
377 hl: crate::highlight::SnapshotHighlights,
378) -> Option<TableSnapshot> {
379 let table_dto = table_commands::get_table(&inner.ctx, &table_id)
380 .ok()
381 .flatten()?;
382
383 let mut cells = Vec::new();
384 for &cell_id in &table_dto.cells {
385 if let Some(cell_dto) = table_cell_commands::get_table_cell(&inner.ctx, &{ cell_id })
386 .ok()
387 .flatten()
388 {
389 let blocks = if let Some(cell_frame_id) = cell_dto.cell_frame {
390 crate::text_block::build_blocks_snapshot_for_frame(inner, cell_frame_id, hl)
391 } else {
392 Vec::new()
393 };
394 cells.push(CellSnapshot {
395 row: to_usize(cell_dto.row),
396 column: to_usize(cell_dto.column),
397 row_span: to_usize(cell_dto.row_span),
398 column_span: to_usize(cell_dto.column_span),
399 format: cell_dto_to_format(&cell_dto),
400 blocks,
401 });
402 }
403 }
404
405 Some(TableSnapshot {
406 table_id: table_id as usize,
407 rows: to_usize(table_dto.rows),
408 columns: to_usize(table_dto.columns),
409 column_widths: table_dto.column_widths.iter().map(|&v| v as i32).collect(),
410 format: table_dto_to_format(&table_dto),
411 cells,
412 })
413}
414
415fn build_table_snapshot_with_positions(
422 inner: &TextDocumentInner,
423 table_id: u64,
424 start_pos: usize,
425 hl: crate::highlight::SnapshotHighlights,
426) -> Option<(TableSnapshot, usize)> {
427 let table_dto = table_commands::get_table(&inner.ctx, &table_id)
428 .ok()
429 .flatten()?;
430
431 let mut cell_dtos: Vec<_> = table_dto
433 .cells
434 .iter()
435 .filter_map(|&cell_id| {
436 table_cell_commands::get_table_cell(&inner.ctx, &{ cell_id })
437 .ok()
438 .flatten()
439 })
440 .collect();
441 cell_dtos.sort_by(|a, b| a.row.cmp(&b.row).then(a.column.cmp(&b.column)));
442
443 let mut running_pos = start_pos;
444 let mut cells = Vec::with_capacity(cell_dtos.len());
445 for cell_dto in &cell_dtos {
446 let blocks = if let Some(cell_frame_id) = cell_dto.cell_frame {
447 let (snaps, new_pos) =
448 crate::text_block::build_blocks_snapshot_for_frame_with_positions(
449 inner,
450 cell_frame_id,
451 running_pos,
452 hl,
453 );
454 running_pos = new_pos;
455 snaps
456 } else {
457 Vec::new()
458 };
459 cells.push(CellSnapshot {
460 row: to_usize(cell_dto.row),
461 column: to_usize(cell_dto.column),
462 row_span: to_usize(cell_dto.row_span),
463 column_span: to_usize(cell_dto.column_span),
464 format: cell_dto_to_format(cell_dto),
465 blocks,
466 });
467 }
468
469 Some((
470 TableSnapshot {
471 table_id: table_id as usize,
472 rows: to_usize(table_dto.rows),
473 columns: to_usize(table_dto.columns),
474 column_widths: table_dto.column_widths.iter().map(|&v| v as i32).collect(),
475 format: table_dto_to_format(&table_dto),
476 cells,
477 },
478 running_pos,
479 ))
480}
481
482pub(crate) fn frame_dto_to_format(f: &frontend::frame::dtos::FrameDto) -> FrameFormat {
487 FrameFormat {
488 height: f.fmt_height.map(|v| v as i32),
489 width: f.fmt_width.map(|v| v as i32),
490 top_margin: f.fmt_top_margin.map(|v| v as i32),
491 bottom_margin: f.fmt_bottom_margin.map(|v| v as i32),
492 left_margin: f.fmt_left_margin.map(|v| v as i32),
493 right_margin: f.fmt_right_margin.map(|v| v as i32),
494 padding: f.fmt_padding.map(|v| v as i32),
495 border: f.fmt_border.map(|v| v as i32),
496 position: f.fmt_position.clone(),
497 is_blockquote: f.fmt_is_blockquote,
498 }
499}
500
501pub(crate) fn table_dto_to_format(t: &frontend::table::dtos::TableDto) -> crate::flow::TableFormat {
502 crate::flow::TableFormat {
503 border: t.fmt_border.map(|v| v as i32),
504 cell_spacing: t.fmt_cell_spacing.map(|v| v as i32),
505 cell_padding: t.fmt_cell_padding.map(|v| v as i32),
506 width: t.fmt_width.map(|v| v as i32),
507 alignment: t.fmt_alignment.clone(),
508 }
509}
510
511pub(crate) fn cell_dto_to_format(
512 c: &frontend::table_cell::dtos::TableCellDto,
513) -> crate::flow::CellFormat {
514 use frontend::common::entities::CellVerticalAlignment as BackendCVA;
515 crate::flow::CellFormat {
516 padding: c.fmt_padding.map(|v| v as i32),
517 border: c.fmt_border.map(|v| v as i32),
518 vertical_alignment: c.fmt_vertical_alignment.as_ref().map(|v| match v {
519 BackendCVA::Top => crate::flow::CellVerticalAlignment::Top,
520 BackendCVA::Middle => crate::flow::CellVerticalAlignment::Middle,
521 BackendCVA::Bottom => crate::flow::CellVerticalAlignment::Bottom,
522 }),
523 background_color: c.fmt_background_color.clone(),
524 }
525}