Skip to main content

text_document/
text_table.rs

1//! Read-only table and table cell handles.
2
3use std::sync::Arc;
4
5use parking_lot::Mutex;
6
7use frontend::commands::{block_commands, frame_commands, table_cell_commands, table_commands};
8use frontend::common::types::EntityId;
9
10use crate::convert::to_usize;
11use crate::flow::{BlockSnapshot, CellFormat, TableFormat, TableSnapshot};
12use crate::inner::TextDocumentInner;
13use crate::text_block::TextBlock;
14use crate::text_frame::{cell_dto_to_format, table_dto_to_format};
15
16/// A read-only handle to a table in the document.
17///
18/// Obtained from [`FlowElement::Table`](crate::FlowElement::Table) during flow traversal.
19#[derive(Clone)]
20pub struct TextTable {
21    pub(crate) doc: Arc<Mutex<TextDocumentInner>>,
22    pub(crate) table_id: usize,
23}
24
25impl TextTable {
26    /// Stable entity ID. O(1).
27    pub fn id(&self) -> usize {
28        self.table_id
29    }
30
31    /// Number of rows. O(1).
32    pub fn rows(&self) -> usize {
33        let inner = self.doc.lock();
34        table_commands::get_table(&inner.ctx, &(self.table_id as u64))
35            .ok()
36            .flatten()
37            .map(|t| to_usize(t.rows))
38            .unwrap_or(0)
39    }
40
41    /// Number of columns. O(1).
42    pub fn columns(&self) -> usize {
43        let inner = self.doc.lock();
44        table_commands::get_table(&inner.ctx, &(self.table_id as u64))
45            .ok()
46            .flatten()
47            .map(|t| to_usize(t.columns))
48            .unwrap_or(0)
49    }
50
51    /// Cell at grid position. O(c) where c = total cells.
52    pub fn cell(&self, row: usize, col: usize) -> Option<TextTableCell> {
53        let inner = self.doc.lock();
54        let table_dto = table_commands::get_table(&inner.ctx, &(self.table_id as u64))
55            .ok()
56            .flatten()?;
57
58        for &cell_id in &table_dto.cells {
59            if let Some(cell_dto) = table_cell_commands::get_table_cell(&inner.ctx, &{ cell_id })
60                .ok()
61                .flatten()
62                && cell_dto.row as usize == row
63                && cell_dto.column as usize == col
64            {
65                return Some(TextTableCell {
66                    doc: Arc::clone(&self.doc),
67                    cell_id: cell_dto.id as usize,
68                });
69            }
70        }
71        None
72    }
73
74    /// Column widths. O(1).
75    pub fn column_widths(&self) -> Vec<i32> {
76        let inner = self.doc.lock();
77        table_commands::get_table(&inner.ctx, &(self.table_id as u64))
78            .ok()
79            .flatten()
80            .map(|t| t.column_widths.iter().map(|&v| v as i32).collect())
81            .unwrap_or_default()
82    }
83
84    /// Table-level formatting. O(1).
85    pub fn format(&self) -> TableFormat {
86        let inner = self.doc.lock();
87        table_commands::get_table(&inner.ctx, &(self.table_id as u64))
88            .ok()
89            .flatten()
90            .map(|t| table_dto_to_format(&t))
91            .unwrap_or_default()
92    }
93
94    /// All cells with block snapshots. O(c·k).
95    pub fn snapshot(&self) -> TableSnapshot {
96        let inner = self.doc.lock();
97        crate::text_frame::build_table_snapshot(
98            &inner,
99            self.table_id as u64,
100            crate::highlight::SnapshotHighlights {
101                kind: inner.highlight_kind,
102                mask: &crate::highlight::HighlightMask::ALL,
103                suppress_paint: false,
104            },
105        )
106        .unwrap_or_else(|| TableSnapshot {
107            table_id: self.table_id,
108            rows: 0,
109            columns: 0,
110            column_widths: Vec::new(),
111            format: TableFormat::default(),
112            cells: Vec::new(),
113        })
114    }
115}
116
117/// A read-only handle to a single cell within a table.
118#[derive(Clone)]
119pub struct TextTableCell {
120    pub(crate) doc: Arc<Mutex<TextDocumentInner>>,
121    pub(crate) cell_id: usize,
122}
123
124impl TextTableCell {
125    /// Stable entity ID. O(1).
126    pub fn id(&self) -> usize {
127        self.cell_id
128    }
129
130    /// Cell row index. O(1).
131    pub fn row(&self) -> usize {
132        let inner = self.doc.lock();
133        table_cell_commands::get_table_cell(&inner.ctx, &(self.cell_id as u64))
134            .ok()
135            .flatten()
136            .map(|c| to_usize(c.row))
137            .unwrap_or(0)
138    }
139
140    /// Cell column index. O(1).
141    pub fn column(&self) -> usize {
142        let inner = self.doc.lock();
143        table_cell_commands::get_table_cell(&inner.ctx, &(self.cell_id as u64))
144            .ok()
145            .flatten()
146            .map(|c| to_usize(c.column))
147            .unwrap_or(0)
148    }
149
150    /// Row span. O(1).
151    pub fn row_span(&self) -> usize {
152        let inner = self.doc.lock();
153        table_cell_commands::get_table_cell(&inner.ctx, &(self.cell_id as u64))
154            .ok()
155            .flatten()
156            .map(|c| to_usize(c.row_span))
157            .unwrap_or(1)
158    }
159
160    /// Column span. O(1).
161    pub fn column_span(&self) -> usize {
162        let inner = self.doc.lock();
163        table_cell_commands::get_table_cell(&inner.ctx, &(self.cell_id as u64))
164            .ok()
165            .flatten()
166            .map(|c| to_usize(c.column_span))
167            .unwrap_or(1)
168    }
169
170    /// Cell-level formatting. O(1).
171    pub fn format(&self) -> CellFormat {
172        let inner = self.doc.lock();
173        table_cell_commands::get_table_cell(&inner.ctx, &(self.cell_id as u64))
174            .ok()
175            .flatten()
176            .map(|c| cell_dto_to_format(&c))
177            .unwrap_or_default()
178    }
179
180    /// Blocks within this cell's frame. Returns empty `Vec` if `cell_frame` is `None`.
181    pub fn blocks(&self) -> Vec<TextBlock> {
182        let inner = self.doc.lock();
183        let cell_dto = match table_cell_commands::get_table_cell(&inner.ctx, &(self.cell_id as u64))
184            .ok()
185            .flatten()
186        {
187            Some(c) => c,
188            None => return Vec::new(),
189        };
190
191        let cell_frame_id = match cell_dto.cell_frame {
192            Some(id) => id,
193            None => return Vec::new(),
194        };
195
196        let frame_dto = match frame_commands::get_frame(&inner.ctx, &(cell_frame_id as EntityId))
197            .ok()
198            .flatten()
199        {
200            Some(f) => f,
201            None => return Vec::new(),
202        };
203
204        // Get blocks sorted by document_position
205        let mut block_dtos: Vec<_> = frame_dto
206            .blocks
207            .iter()
208            .filter_map(|&id| {
209                block_commands::get_block(&inner.ctx, &{ id })
210                    .ok()
211                    .flatten()
212            })
213            .collect();
214        let store = inner.ctx.db_context.get_store();
215        crate::inner::refresh_block_positions(&mut block_dtos, store);
216        block_dtos.sort_by_key(|b| b.document_position);
217
218        block_dtos
219            .iter()
220            .map(|b| TextBlock {
221                doc: Arc::clone(&self.doc),
222                block_id: b.id as usize,
223            })
224            .collect()
225    }
226
227    /// Snapshot all cell blocks in one lock. Returns empty `Vec` if `cell_frame` is `None`.
228    pub fn snapshot_blocks(&self) -> Vec<BlockSnapshot> {
229        let inner = self.doc.lock();
230        let cell_dto = match table_cell_commands::get_table_cell(&inner.ctx, &(self.cell_id as u64))
231            .ok()
232            .flatten()
233        {
234            Some(c) => c,
235            None => return Vec::new(),
236        };
237
238        match cell_dto.cell_frame {
239            Some(frame_id) => crate::text_block::build_blocks_snapshot_for_frame(
240                &inner,
241                frame_id,
242                crate::highlight::SnapshotHighlights {
243                    kind: inner.highlight_kind,
244                    mask: &crate::highlight::HighlightMask::ALL,
245                    suppress_paint: false,
246                },
247            ),
248            None => Vec::new(),
249        }
250    }
251}