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) {
13 if text.is_empty() {
14 return (0, 0);
15 }
16
17 let chars: Vec<char> = text.chars().collect();
18 let len = chars.len();
19 let offset = char_offset.min(len);
20
21 let is_word_char = |c: char| c.is_alphanumeric() || c == '_';
22
23 let (on_idx, on_c) = if offset < len {
24 if chars[offset].is_whitespace() && offset > 0 && is_word_char(chars[offset - 1]) {
25 (offset - 1, chars[offset - 1])
26 } else {
27 (offset, chars[offset])
28 }
29 } else if offset > 0 {
30 (offset - 1, chars[offset - 1])
31 } else {
32 return (0, 0);
33 };
34
35 if on_c.is_whitespace() {
36 let mut start = on_idx;
37 while start > 0 && chars[start - 1].is_whitespace() {
38 start -= 1;
39 }
40 let mut end = on_idx + 1;
41 while end < len && chars[end].is_whitespace() {
42 end += 1;
43 }
44 return (start, end);
45 }
46
47 if is_word_char(on_c) {
48 let mut start = on_idx;
49 while start > 0 && is_word_char(chars[start - 1]) {
50 start -= 1;
51 }
52 let mut end = on_idx + 1;
53 while end < len && is_word_char(chars[end]) {
54 end += 1;
55 }
56 return (start, end);
57 }
58
59 let mut start = on_idx;
60 while start > 0 && !is_word_char(chars[start - 1]) && !chars[start - 1].is_whitespace() {
61 start -= 1;
62 }
63 let mut end = on_idx + 1;
64 while end < len && !is_word_char(chars[end]) && !chars[end].is_whitespace() {
65 end += 1;
66 }
67 (start, end)
68}
69impl Sheet {
70 pub fn get_result_data(&self, cell: &CellRef) -> ResultData {
80 let col = self.columns.get(cell.col);
81 if let Some(col) = col {
82 col.data.get(cell.row).unwrap_or(ResultData::None)
83 } else {
84 ResultData::None
85 }
86 }
87
88 pub(super) fn inherited_date_format(&self, ast: &crate::core::parser::Expr) -> Option<String> {
102 use crate::core::parser::{Expr, Op};
103 match ast {
104 Expr::CellRef {
105 sheet, row, col, ..
106 } if sheet.is_none() => self
107 .get_cell_style(*row, *col)
108 .and_then(|s| s.num_format.clone())
109 .filter(|code| crate::core::date::is_date_code(code)),
110 Expr::BinaryOp {
111 op: Op::Add | Op::Sub,
112 left,
113 right,
114 } => {
115 let left_fmt = self.inherited_date_format(left);
116 let right_fmt = self.inherited_date_format(right);
117 match (left_fmt, right_fmt) {
118 (Some(fmt), None) | (None, Some(fmt)) => Some(fmt),
119 _ => None,
120 }
121 }
122 _ => None,
123 }
124 }
125
126 pub fn get_display_string(&self, cell: &CellRef) -> String {
135 let value = self.get_result_data(cell);
136 let Some(code) = self
137 .get_cell_style(cell.row, cell.col)
138 .and_then(|s| s.num_format.as_deref())
139 else {
140 return value.to_string();
141 };
142 if !crate::core::date::is_date_code(code) {
143 return value.to_string();
144 }
145 let serial = match value {
146 ResultData::Float(f) => f,
147 ResultData::Integer(i) => i as f64,
148 _ => return value.to_string(),
149 };
150 if serial < 0.0 {
151 return value.to_string();
152 }
153 crate::core::date::render_date_code(
154 crate::core::date::excel_serial_to_date(serial),
155 code,
156 crate::core::date::StringCase::Title,
157 )
158 }
159
160 pub fn get_cell_type(&self, cell: &CellRef) -> CellType {
162 let col = self.columns.get(cell.col);
163 if let Some(col) = col {
164 col.cell_types
165 .get(cell.row)
166 .copied()
167 .unwrap_or(CellType::Empty)
168 } else {
169 CellType::Empty
170 }
171 }
172
173 pub fn set_cell_type(&mut self, row: usize, col: usize, cell_type: CellType) {
175 if let Some(column) = self.columns.get_mut(col)
176 && row < column.cell_types.len()
177 {
178 column.cell_types[row] = cell_type;
179 column.mark_dirty(row);
180 }
181 }
182
183 pub fn set_cell_with_type(&mut self, row: usize, col: usize, src: String, cell_type: CellType) {
185 let table_clone = self.clone();
186 if let Some(column) = self.columns.get_mut(col)
187 && row < column.src.len()
188 {
189 column.src[row] = src.clone();
190 column.cell_types[row] = cell_type;
191 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
192 column.compiled_src[row] = compiled;
193 column.mark_dirty(row);
194
195 self.uncommitted_actions
196 .push(crate::core::SheetAction::SetCellSrc {
197 sheet_name: self.name.clone(),
198 col,
199 row,
200 src,
201 });
202 }
203 }
204
205 pub fn set_cell_src(&mut self, row: usize, col: usize, src: String) {
210 let table_clone = self.clone();
211 if let Some(column) = self.columns.get_mut(col)
212 && row < column.src.len()
213 {
214 column.src[row] = src.clone();
215 column.cell_types[row] = CellType::Auto;
216 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
217 column.compiled_src[row] = compiled;
218 column.mark_dirty(row);
219
220 self.uncommitted_actions
221 .push(crate::core::SheetAction::SetCellSrc {
222 sheet_name: self.name.clone(),
223 col,
224 row,
225 src,
226 });
227 }
228 }
229
230 pub fn insert(&mut self, pos: TextCellRef, input: &str) {
237 let TextCellRef {
238 row,
239 col,
240 char_offset,
241 } = pos;
242 let table_clone = self.clone();
243 let existing_col = self.columns.get_mut(col);
244 match existing_col {
245 Some(existing_column) => {
246 existing_column.insert(ColumnPosition { row, char_offset }, input);
247 let src = existing_column.src[row].clone();
248 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
249 existing_column.compiled_src[row] = compiled;
250 existing_column.mark_dirty(row);
251 self.uncommitted_actions
252 .push(crate::core::SheetAction::SetCellSrc {
253 sheet_name: self.name.clone(),
254 col,
255 row,
256 src,
257 });
258 }
259 None => {
260 println!("Warning: column {} does not exist", col)
261 }
262 }
263 }
264
265 pub fn delete_one_before(&mut self, pos: TextCellRef) {
267 let char_offset = pos.char_offset;
268 let start = if char_offset > 0 {
269 TextCellRef {
270 row: pos.row,
271 col: pos.col,
272 char_offset: char_offset - 1,
273 }
274 } else {
275 pos.clone()
276 };
277 let end = pos;
278 self.delete(start, end);
279 }
280
281 pub fn delete(&mut self, start: TextCellRef, end: TextCellRef) {
288 if start.col > end.col || (start.col == end.col && start.row > end.row) {
289 return;
290 }
291 let table_clone = self.clone();
292 if start.col == end.col {
293 let start_index = start.row;
294 let end_index = end.row;
295
296 if let Some(column) = self.columns.get_mut(start.col) {
297 if start.row == end.row && start_index < column.src.len() {
298 let src = &mut column.src[start_index];
299 let end_offset = std::cmp::min(end.char_offset, src.len());
300 if start.char_offset < end_offset {
301 src.replace_range(start.char_offset..end_offset, "");
302 column.dirty_indices.push(start_index);
303 let updated_src = src.clone();
304 let compiled =
305 crate::core::parser::compile_formula(&updated_src, &[table_clone]);
306 column.compiled_src[start_index] = compiled;
307 }
308 } else if start_index < column.len() && end_index >= start_index {
309 column.drain_rows(start_index..=end_index);
310 }
311 }
312 } else {
313 for col in start.col..=end.col {
314 if let Some(column) = self.columns.get_mut(col) {
315 let start_index = if col == start.col { col } else { 0 };
316
317 let end_index = if col == end.col {
318 col
319 } else {
320 column.src.len() - 1
321 };
322
323 if start_index < column.len() && end_index >= start_index {
324 column.drain_rows(start_index..=end_index);
325 }
326 }
327 }
328 }
329 }
330
331 pub fn extend(&mut self, direction: Direction) {
336 if self.columns.is_empty() {
337 return;
338 }
339 let row_count = self.columns[0].src.len();
340 const MAX_COLS: usize = 26;
341 match direction {
342 Direction::Up => {
343 for column in &mut self.columns {
344 column.insert_row(0);
345 }
346 self.row_heights.insert(0, None);
347 self.uncommitted_actions
348 .push(crate::core::SheetAction::InsertRow {
349 sheet_name: self.name.clone(),
350 index: 0,
351 });
352 }
353 Direction::Down => {
354 for column in &mut self.columns {
355 column.push_row();
356 }
357 self.row_heights.push(None);
358 self.uncommitted_actions
359 .push(crate::core::SheetAction::InsertRow {
360 sheet_name: self.name.clone(),
361 index: row_count,
362 });
363 }
364 Direction::Left => {
365 if self.columns.len() < MAX_COLS {
366 self.columns.insert(0, DataColumn::new(row_count));
367 self.uncommitted_actions
368 .push(crate::core::SheetAction::InsertCol {
369 sheet_name: self.name.clone(),
370 index: 0,
371 });
372 }
373 }
374 Direction::Right => {
375 if self.columns.len() < MAX_COLS {
376 self.columns.push(DataColumn::new(row_count));
377 self.uncommitted_actions
378 .push(crate::core::SheetAction::InsertCol {
379 sheet_name: self.name.clone(),
380 index: self.columns.len() - 1,
381 });
382 }
383 }
384 Direction::None => {}
385 }
386 }
387
388 pub fn ensure_capacity(&mut self, target_row: usize, target_col: usize) {
390 let current_rows = self.row_count();
391 let needed_rows = target_row + 1;
392 let final_rows = current_rows.max(needed_rows);
393
394 while self.columns.len() <= target_col {
395 let col_idx = self.columns.len();
396 let mut col = DataColumn::new(final_rows);
397 col.name = crate::core::parser::col_idx_to_letters(col_idx);
398 self.columns.push(col);
399 }
400
401 if final_rows > current_rows {
402 for col in &mut self.columns {
403 col.resize_rows(final_rows);
404 }
405 self.row_heights.resize(final_rows, None);
406 }
407 }
408
409 pub fn get_cell_style(&self, row: usize, col: usize) -> Option<&crate::core::CellStyle> {
414 self.columns
415 .get(col)
416 .and_then(|column| column.styles.get(row))
417 .and_then(|opt| opt.as_ref())
418 }
419
420 pub fn set_cell_style(&mut self, row: usize, col: usize, style: crate::core::CellStyle) {
423 self.ensure_capacity(row, col);
424 if let Some(column) = self.columns.get_mut(col)
425 && row < column.styles.len()
426 {
427 if style.is_empty() {
428 column.styles[row] = None;
429 } else {
430 column.styles[row] = Some(style);
431 }
432 }
433 }
434
435 pub fn update_cell_style<F>(&mut self, row: usize, col: usize, f: F)
440 where
441 F: FnOnce(&mut crate::core::CellStyle),
442 {
443 self.ensure_capacity(row, col);
444 if let Some(column) = self.columns.get_mut(col)
445 && row < column.styles.len()
446 {
447 let mut current = column.styles[row].clone().unwrap_or_default();
448 f(&mut current);
449 if current.is_empty() {
450 column.styles[row] = None;
451 } else {
452 column.styles[row] = Some(current);
453 }
454 }
455 }
456
457 pub fn clear_cell_style(&mut self, row: usize, col: usize) {
459 if let Some(column) = self.columns.get_mut(col)
460 && row < column.styles.len()
461 {
462 column.styles[row] = None;
463 }
464 }
465
466 pub fn get_column_width(&self, col: usize) -> Option<f64> {
468 self.columns.get(col).and_then(|column| column.width)
469 }
470
471 pub fn set_column_width(&mut self, col: usize, width: Option<f64>) {
473 if let Some(column) = self.columns.get_mut(col) {
474 column.width = width.filter(|value| value.is_finite() && *value >= 0.0);
475 }
476 }
477
478 pub fn get_row_height(&self, row: usize) -> Option<f64> {
480 self.row_heights.get(row).and_then(|height| *height)
481 }
482
483 pub fn set_row_height(&mut self, row: usize, height: Option<f64>) {
485 if row >= self.row_count() {
486 return;
487 }
488 let row_count = self.row_count();
489 self.row_heights.resize(row_count, None);
490 self.row_heights[row] = height.filter(|value| value.is_finite() && *value >= 0.0);
491 }
492
493 pub fn insert_row(&mut self, index: usize) {
496 let row_count = self.row_count();
497 self.row_heights.resize(row_count, None);
498 if index >= row_count {
499 for column in &mut self.columns {
500 column.push_row();
501 }
502 self.row_heights.push(None);
503 self.uncommitted_actions
504 .push(crate::core::SheetAction::InsertRow {
505 sheet_name: self.name.clone(),
506 index: row_count,
507 });
508 } else {
509 for column in &mut self.columns {
510 column.insert_row(index);
511 }
512 self.row_heights.insert(index, None);
513 self.uncommitted_actions
514 .push(crate::core::SheetAction::InsertRow {
515 sheet_name: self.name.clone(),
516 index,
517 });
518 }
519 }
520
521 pub fn delete_row(&mut self, index: usize) {
528 let row_count = self.row_count();
529 if index < row_count {
530 for column in &mut self.columns {
531 column.remove_row(index);
532 }
533 if index < self.row_heights.len() {
534 self.row_heights.remove(index);
535 }
536 self.uncommitted_actions
537 .push(crate::core::SheetAction::DeleteRow {
538 sheet_name: self.name.clone(),
539 index,
540 });
541 self.mark_all_dirty();
542 }
543 }
544
545 pub fn insert_cells_shift_down(
563 &mut self,
564 row: usize,
565 first_col: usize,
566 last_col: usize,
567 count: usize,
568 ) {
569 let last_col = last_col.min(self.columns.len().saturating_sub(1));
570 if count == 0 || self.columns.is_empty() || first_col > last_col {
571 return;
572 }
573 for column in &mut self.columns {
574 for _ in 0..count {
575 column.push_row();
576 }
577 }
578 let row_count = self.row_count();
579 self.row_heights.resize(row_count, None);
580 for column in &mut self.columns[first_col..=last_col] {
581 for _ in 0..count {
582 column.insert_row(row);
583 column.remove_row(column.len() - 1);
584 }
585 }
586 self.uncommitted_actions
587 .push(crate::core::SheetAction::InsertRow {
588 sheet_name: self.name.clone(),
589 index: row,
590 });
591 self.mark_all_dirty();
592 }
593
594 pub fn delete_cells_shift_up(
600 &mut self,
601 row: usize,
602 first_col: usize,
603 last_col: usize,
604 count: usize,
605 ) {
606 let last_col = last_col.min(self.columns.len().saturating_sub(1));
607 if count == 0 || self.columns.is_empty() || first_col > last_col || row >= self.row_count()
608 {
609 return;
610 }
611 for column in &mut self.columns[first_col..=last_col] {
612 for _ in 0..count {
613 if row < column.len() {
614 column.remove_row(row);
615 column.push_row();
616 }
617 }
618 }
619 self.uncommitted_actions
620 .push(crate::core::SheetAction::DeleteRow {
621 sheet_name: self.name.clone(),
622 index: row,
623 });
624 self.mark_all_dirty();
625 }
626
627 pub fn delete_col(&mut self, index: usize) {
631 if index < self.columns.len() {
632 self.columns.remove(index);
633 self.uncommitted_actions
634 .push(crate::core::SheetAction::DeleteCol {
635 sheet_name: self.name.clone(),
636 index,
637 });
638 self.mark_all_dirty();
639 }
640 }
641
642 pub fn insert_col(&mut self, index: usize) {
645 let row_count = self.row_count();
646 let new_col = DataColumn::new(row_count);
647 let col_count = self.columns.len();
648 if index >= col_count {
649 self.columns.push(new_col);
650 self.uncommitted_actions
651 .push(crate::core::SheetAction::InsertCol {
652 sheet_name: self.name.clone(),
653 index: col_count,
654 });
655 } else {
656 self.columns.insert(index, new_col);
657 self.uncommitted_actions
658 .push(crate::core::SheetAction::InsertCol {
659 sheet_name: self.name.clone(),
660 index,
661 });
662 }
663 self.mark_all_dirty();
664 }
665
666 pub fn columns(&self) -> &[DataColumn] {
673 &self.columns
674 }
675
676 pub fn row_count(&self) -> usize {
679 self.columns.first().map(|c| c.src.len()).unwrap_or(0)
680 }
681
682 pub fn col_count(&self) -> usize {
684 self.columns.len()
685 }
686}