visi_core/core/engine/sheet/
edit.rs1use super::super::column::{ColumnPosition, DataColumn};
2use super::{CellRef, CellType, Direction, ResultData, Sheet, TextCellRef};
3
4pub fn get_word_boundaries_from_str(text: &str, char_offset: usize) -> (usize, usize) {
7 if text.is_empty() {
8 return (0, 0);
9 }
10
11 let chars: Vec<char> = text.chars().collect();
12 let len = chars.len();
13 let offset = char_offset.min(len);
14
15 let is_word_char = |c: char| c.is_alphanumeric() || c == '_';
16
17 let (on_idx, on_c) = if offset < len {
18 if chars[offset].is_whitespace() && offset > 0 && is_word_char(chars[offset - 1]) {
19 (offset - 1, chars[offset - 1])
20 } else {
21 (offset, chars[offset])
22 }
23 } else if offset > 0 {
24 (offset - 1, chars[offset - 1])
25 } else {
26 return (0, 0);
27 };
28
29 if on_c.is_whitespace() {
30 let mut start = on_idx;
31 while start > 0 && chars[start - 1].is_whitespace() {
32 start -= 1;
33 }
34 let mut end = on_idx + 1;
35 while end < len && chars[end].is_whitespace() {
36 end += 1;
37 }
38 return (start, end);
39 }
40
41 if is_word_char(on_c) {
42 let mut start = on_idx;
43 while start > 0 && is_word_char(chars[start - 1]) {
44 start -= 1;
45 }
46 let mut end = on_idx + 1;
47 while end < len && is_word_char(chars[end]) {
48 end += 1;
49 }
50 return (start, end);
51 }
52
53 let mut start = on_idx;
54 while start > 0 && !is_word_char(chars[start - 1]) && !chars[start - 1].is_whitespace() {
55 start -= 1;
56 }
57 let mut end = on_idx + 1;
58 while end < len && !is_word_char(chars[end]) && !chars[end].is_whitespace() {
59 end += 1;
60 }
61 (start, end)
62}
63impl Sheet {
64 pub fn get_result_data(&self, cell: &CellRef) -> ResultData {
67 let col = self.columns.get(cell.col);
68 if let Some(col) = col {
69 col.data.get(cell.row).unwrap_or(ResultData::None)
70 } else {
71 ResultData::None
72 }
73 }
74
75 pub(super) fn inherited_date_format(&self, ast: &crate::core::parser::Expr) -> Option<String> {
78 use crate::core::parser::{Expr, Op};
79 match ast {
80 Expr::CellRef {
81 sheet, row, col, ..
82 } if sheet.is_none() => self
83 .get_cell_style(*row, *col)
84 .and_then(|s| s.num_format.clone())
85 .filter(|code| crate::core::date::is_date_code(code)),
86 Expr::BinaryOp {
87 op: Op::Add | Op::Sub,
88 left,
89 right,
90 } => {
91 let left_fmt = self.inherited_date_format(left);
92 let right_fmt = self.inherited_date_format(right);
93 match (left_fmt, right_fmt) {
94 (Some(fmt), None) | (None, Some(fmt)) => Some(fmt),
95 _ => None,
96 }
97 }
98 _ => None,
99 }
100 }
101
102 pub fn get_display_string(&self, cell: &CellRef) -> String {
105 let value = self.get_result_data(cell);
106 let Some(code) = self
107 .get_cell_style(cell.row, cell.col)
108 .and_then(|s| s.num_format.as_deref())
109 else {
110 return value.to_string();
111 };
112 match value {
113 ResultData::Float(f) => crate::core::text::format_number_format(f, code)
114 .unwrap_or_else(|_| ResultData::Float(f).to_string()),
115 ResultData::Integer(i) => crate::core::text::format_number_format(i as f64, code)
116 .unwrap_or_else(|_| ResultData::Integer(i).to_string()),
117 ResultData::String(s) => crate::core::text::format_text_format(&s, code)
118 .unwrap_or(ResultData::String(s).to_string()),
119 other => other.to_string(),
120 }
121 }
122
123 pub fn get_cell_type(&self, cell: &CellRef) -> CellType {
125 let col = self.columns.get(cell.col);
126 if let Some(col) = col {
127 col.cell_types
128 .get(cell.row)
129 .copied()
130 .unwrap_or(CellType::Empty)
131 } else {
132 CellType::Empty
133 }
134 }
135
136 pub fn set_cell_type(&mut self, row: usize, col: usize, cell_type: CellType) {
138 if let Some(column) = self.columns.get_mut(col)
139 && row < column.cell_types.len()
140 {
141 column.cell_types[row] = cell_type;
142 column.mark_dirty(row);
143 }
144 }
145
146 pub fn set_cell_with_type(&mut self, row: usize, col: usize, src: String, cell_type: CellType) {
148 let table_clone = self.clone();
149 if let Some(column) = self.columns.get_mut(col)
150 && row < column.src.len()
151 {
152 column.src[row] = src.clone();
153 column.cell_types[row] = cell_type;
154 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
155 column.compiled_src[row] = compiled;
156 column.mark_dirty(row);
157
158 self.uncommitted_actions
159 .push(crate::core::SheetAction::SetCellSrc {
160 sheet_name: self.name.clone(),
161 col,
162 row,
163 src,
164 });
165 }
166 }
167
168 pub fn set_cell_src(&mut self, row: usize, col: usize, src: String) {
173 let table_clone = self.clone();
174 if let Some(column) = self.columns.get_mut(col)
175 && row < column.src.len()
176 {
177 column.src[row] = src.clone();
178 column.cell_types[row] = CellType::Empty;
179 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
180 column.compiled_src[row] = compiled;
181 column.mark_dirty(row);
182
183 self.uncommitted_actions
184 .push(crate::core::SheetAction::SetCellSrc {
185 sheet_name: self.name.clone(),
186 col,
187 row,
188 src,
189 });
190 }
191 }
192
193 pub fn insert(&mut self, pos: TextCellRef, input: &str) {
196 let TextCellRef {
197 row,
198 col,
199 char_offset,
200 } = pos;
201 let table_clone = self.clone();
202 let existing_col = self.columns.get_mut(col);
203 match existing_col {
204 Some(existing_column) => {
205 existing_column.insert(ColumnPosition { row, char_offset }, input);
206 let src = existing_column.src[row].clone();
207 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
208 existing_column.compiled_src[row] = compiled;
209 existing_column.mark_dirty(row);
210 self.uncommitted_actions
211 .push(crate::core::SheetAction::SetCellSrc {
212 sheet_name: self.name.clone(),
213 col,
214 row,
215 src,
216 });
217 }
218 None => {
219 println!("Warning: column {} does not exist", col)
220 }
221 }
222 }
223
224 pub fn delete_one_before(&mut self, pos: TextCellRef) {
226 let char_offset = pos.char_offset;
227 let start = if char_offset > 0 {
228 TextCellRef {
229 row: pos.row,
230 col: pos.col,
231 char_offset: char_offset - 1,
232 }
233 } else {
234 pos.clone()
235 };
236 let end = pos;
237 self.delete(start, end);
238 }
239
240 pub fn delete(&mut self, start: TextCellRef, end: TextCellRef) {
243 if start.col > end.col || (start.col == end.col && start.row > end.row) {
244 return;
245 }
246 let table_clone = self.clone();
247 if start.col == end.col {
248 let start_index = start.row;
249 let end_index = end.row;
250
251 if let Some(column) = self.columns.get_mut(start.col) {
252 if start.row == end.row && start_index < column.src.len() {
253 let src = &mut column.src[start_index];
254 let end_offset = std::cmp::min(end.char_offset, src.len());
255 if start.char_offset < end_offset {
256 src.replace_range(start.char_offset..end_offset, "");
257 column.dirty_indices.push(start_index);
258 let updated_src = src.clone();
259 let compiled =
260 crate::core::parser::compile_formula(&updated_src, &[table_clone]);
261 column.compiled_src[start_index] = compiled;
262 }
263 } else if start_index < column.len() && end_index >= start_index {
264 column.drain_rows(start_index..=end_index);
265 }
266 }
267 } else {
268 for col in start.col..=end.col {
269 if let Some(column) = self.columns.get_mut(col) {
270 let start_index = if col == start.col { col } else { 0 };
271
272 let end_index = if col == end.col {
273 col
274 } else {
275 column.src.len() - 1
276 };
277
278 if start_index < column.len() && end_index >= start_index {
279 column.drain_rows(start_index..=end_index);
280 }
281 }
282 }
283 }
284 }
285
286 pub fn extend(&mut self, direction: Direction) {
288 if self.columns.is_empty() {
289 return;
290 }
291 let row_count = self.columns[0].src.len();
292 const MAX_COLS: usize = 26;
293 match direction {
294 Direction::Up => {
295 for column in &mut self.columns {
296 column.insert_row(0);
297 }
298 self.row_heights.insert(0, None);
299 self.uncommitted_actions
300 .push(crate::core::SheetAction::InsertRow {
301 sheet_name: self.name.clone(),
302 index: 0,
303 });
304 }
305 Direction::Down => {
306 for column in &mut self.columns {
307 column.push_row();
308 }
309 self.row_heights.push(None);
310 self.uncommitted_actions
311 .push(crate::core::SheetAction::InsertRow {
312 sheet_name: self.name.clone(),
313 index: row_count,
314 });
315 }
316 Direction::Left => {
317 if self.columns.len() < MAX_COLS {
318 self.columns.insert(0, DataColumn::new(row_count));
319 self.uncommitted_actions
320 .push(crate::core::SheetAction::InsertCol {
321 sheet_name: self.name.clone(),
322 index: 0,
323 });
324 }
325 }
326 Direction::Right => {
327 if self.columns.len() < MAX_COLS {
328 self.columns.push(DataColumn::new(row_count));
329 self.uncommitted_actions
330 .push(crate::core::SheetAction::InsertCol {
331 sheet_name: self.name.clone(),
332 index: self.columns.len() - 1,
333 });
334 }
335 }
336 Direction::None => {}
337 }
338 }
339
340 pub fn ensure_capacity(&mut self, target_row: usize, target_col: usize) {
342 let current_rows = self.row_count();
343 let needed_rows = target_row + 1;
344 let final_rows = current_rows.max(needed_rows);
345
346 while self.columns.len() <= target_col {
347 let col_idx = self.columns.len();
348 let mut col = DataColumn::new(final_rows);
349 col.name = crate::core::parser::col_idx_to_letters(col_idx);
350 self.columns.push(col);
351 }
352
353 if final_rows > current_rows {
354 for col in &mut self.columns {
355 col.resize_rows(final_rows);
356 }
357 self.row_heights.resize(final_rows, None);
358 }
359 }
360
361 pub fn get_cell_style(&self, row: usize, col: usize) -> Option<&crate::core::CellStyle> {
363 self.columns
364 .get(col)
365 .and_then(|column| column.styles.get(row))
366 .and_then(|opt| opt.as_ref())
367 }
368
369 pub fn set_cell_style(&mut self, row: usize, col: usize, style: crate::core::CellStyle) {
372 self.ensure_capacity(row, col);
373 if let Some(column) = self.columns.get_mut(col)
374 && row < column.styles.len()
375 {
376 if style.is_empty() {
377 column.styles[row] = None;
378 } else {
379 column.styles[row] = Some(style);
380 }
381 }
382 }
383
384 pub fn update_cell_style<F>(&mut self, row: usize, col: usize, f: F)
388 where
389 F: FnOnce(&mut crate::core::CellStyle),
390 {
391 self.ensure_capacity(row, col);
392 if let Some(column) = self.columns.get_mut(col)
393 && row < column.styles.len()
394 {
395 let mut current = column.styles[row].clone().unwrap_or_default();
396 f(&mut current);
397 if current.is_empty() {
398 column.styles[row] = None;
399 } else {
400 column.styles[row] = Some(current);
401 }
402 }
403 }
404
405 pub fn clear_cell_style(&mut self, row: usize, col: usize) {
407 if let Some(column) = self.columns.get_mut(col)
408 && row < column.styles.len()
409 {
410 column.styles[row] = None;
411 }
412 }
413
414 pub fn get_column_width(&self, col: usize) -> Option<f64> {
416 self.columns.get(col).and_then(|column| column.width)
417 }
418
419 pub fn set_column_width(&mut self, col: usize, width: Option<f64>) {
421 if let Some(column) = self.columns.get_mut(col) {
422 column.width = width.filter(|value| value.is_finite() && *value >= 0.0);
423 }
424 }
425
426 pub fn get_row_height(&self, row: usize) -> Option<f64> {
428 self.row_heights.get(row).and_then(|height| *height)
429 }
430
431 pub fn set_row_height(&mut self, row: usize, height: Option<f64>) {
433 if row >= self.row_count() {
434 return;
435 }
436 let row_count = self.row_count();
437 self.row_heights.resize(row_count, None);
438 self.row_heights[row] = height.filter(|value| value.is_finite() && *value >= 0.0);
439 }
440
441 pub fn insert_row(&mut self, index: usize) {
444 let row_count = self.row_count();
445 self.row_heights.resize(row_count, None);
446 if index >= row_count {
447 for column in &mut self.columns {
448 column.push_row();
449 }
450 self.row_heights.push(None);
451 self.uncommitted_actions
452 .push(crate::core::SheetAction::InsertRow {
453 sheet_name: self.name.clone(),
454 index: row_count,
455 });
456 } else {
457 for column in &mut self.columns {
458 column.insert_row(index);
459 }
460 self.row_heights.insert(index, None);
461 self.uncommitted_actions
462 .push(crate::core::SheetAction::InsertRow {
463 sheet_name: self.name.clone(),
464 index,
465 });
466 }
467 }
468
469 pub fn delete_row(&mut self, index: usize) {
471 let row_count = self.row_count();
472 if index < row_count {
473 for column in &mut self.columns {
474 column.remove_row(index);
475 }
476 if index < self.row_heights.len() {
477 self.row_heights.remove(index);
478 }
479 self.uncommitted_actions
480 .push(crate::core::SheetAction::DeleteRow {
481 sheet_name: self.name.clone(),
482 index,
483 });
484 self.mark_all_dirty();
485 }
486 }
487
488 pub fn insert_cells_shift_down(
490 &mut self,
491 row: usize,
492 first_col: usize,
493 last_col: usize,
494 count: usize,
495 ) {
496 let last_col = last_col.min(self.columns.len().saturating_sub(1));
497 if count == 0 || self.columns.is_empty() || first_col > last_col {
498 return;
499 }
500 for column in &mut self.columns {
501 for _ in 0..count {
502 column.push_row();
503 }
504 }
505 let row_count = self.row_count();
506 self.row_heights.resize(row_count, None);
507 for column in &mut self.columns[first_col..=last_col] {
508 for _ in 0..count {
509 column.insert_row(row);
510 column.remove_row(column.len() - 1);
511 }
512 }
513 self.uncommitted_actions
514 .push(crate::core::SheetAction::InsertRow {
515 sheet_name: self.name.clone(),
516 index: row,
517 });
518 self.mark_all_dirty();
519 }
520
521 pub fn delete_cells_shift_up(
524 &mut self,
525 row: usize,
526 first_col: usize,
527 last_col: usize,
528 count: usize,
529 ) {
530 let last_col = last_col.min(self.columns.len().saturating_sub(1));
531 if count == 0 || self.columns.is_empty() || first_col > last_col || row >= self.row_count()
532 {
533 return;
534 }
535 for column in &mut self.columns[first_col..=last_col] {
536 for _ in 0..count {
537 if row < column.len() {
538 column.remove_row(row);
539 column.push_row();
540 }
541 }
542 }
543 self.uncommitted_actions
544 .push(crate::core::SheetAction::DeleteRow {
545 sheet_name: self.name.clone(),
546 index: row,
547 });
548 self.mark_all_dirty();
549 }
550
551 pub fn delete_col(&mut self, index: usize) {
553 if index < self.columns.len() {
554 self.columns.remove(index);
555 self.uncommitted_actions
556 .push(crate::core::SheetAction::DeleteCol {
557 sheet_name: self.name.clone(),
558 index,
559 });
560 self.mark_all_dirty();
561 }
562 }
563
564 pub fn insert_col(&mut self, index: usize) {
567 let row_count = self.row_count();
568 let new_col = DataColumn::new(row_count);
569 let col_count = self.columns.len();
570 if index >= col_count {
571 self.columns.push(new_col);
572 self.uncommitted_actions
573 .push(crate::core::SheetAction::InsertCol {
574 sheet_name: self.name.clone(),
575 index: col_count,
576 });
577 } else {
578 self.columns.insert(index, new_col);
579 self.uncommitted_actions
580 .push(crate::core::SheetAction::InsertCol {
581 sheet_name: self.name.clone(),
582 index,
583 });
584 }
585 self.mark_all_dirty();
586 }
587
588 pub fn columns(&self) -> &[DataColumn] {
590 &self.columns
591 }
592
593 pub fn row_count(&self) -> usize {
595 self.columns.first().map(|c| c.src.len()).unwrap_or(0)
596 }
597
598 pub fn col_count(&self) -> usize {
600 self.columns.len()
601 }
602}