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),
121 _ => None,
123 }
124 }
125 _ => None,
126 }
127 }
128
129 pub fn get_display_string(&self, cell: &CellRef) -> String {
138 let value = self.get_result_data(cell);
139 let Some(code) = self
140 .get_cell_style(cell.row, cell.col)
141 .and_then(|s| s.num_format.as_deref())
142 else {
143 return value.to_string();
144 };
145 if !crate::core::date::is_date_code(code) {
146 return value.to_string();
147 }
148 let serial = match value {
150 ResultData::Float(f) => f,
151 ResultData::Integer(i) => i as f64,
152 _ => return value.to_string(),
153 };
154 if serial < 0.0 {
155 return value.to_string();
156 }
157 crate::core::date::render_date_code(
158 crate::core::date::excel_serial_to_date(serial),
159 code,
160 crate::core::date::StringCase::Title,
161 )
162 }
163
164 pub fn get_cell_type(&self, cell: &CellRef) -> CellType {
166 let col = self.columns.get(cell.col);
167 if let Some(col) = col {
168 col.cell_types
169 .get(cell.row)
170 .copied()
171 .unwrap_or(CellType::Empty)
172 } else {
173 CellType::Empty
174 }
175 }
176
177 pub fn set_cell_type(&mut self, row: usize, col: usize, cell_type: CellType) {
179 if let Some(column) = self.columns.get_mut(col)
180 && row < column.cell_types.len()
181 {
182 column.cell_types[row] = cell_type;
183 column.mark_dirty(row);
184 }
185 }
186
187 pub fn set_cell_with_type(&mut self, row: usize, col: usize, src: String, cell_type: CellType) {
189 let table_clone = self.clone();
190 if let Some(column) = self.columns.get_mut(col)
191 && row < column.src.len()
192 {
193 column.src[row] = src.clone();
194 column.cell_types[row] = cell_type;
195 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
196 column.compiled_src[row] = compiled;
197 column.mark_dirty(row);
198
199 self.uncommitted_actions
200 .push(crate::core::SheetAction::SetCellSrc {
201 sheet_name: self.name.clone(),
202 col,
203 row,
204 src,
205 });
206 }
207 }
208
209 pub fn set_cell_src(&mut self, row: usize, col: usize, src: String) {
214 let table_clone = self.clone();
215 if let Some(column) = self.columns.get_mut(col)
216 && row < column.src.len()
217 {
218 column.src[row] = src.clone();
219 column.cell_types[row] = CellType::Auto;
220 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
221 column.compiled_src[row] = compiled;
222 column.mark_dirty(row);
223
224 self.uncommitted_actions
225 .push(crate::core::SheetAction::SetCellSrc {
226 sheet_name: self.name.clone(),
227 col,
228 row,
229 src,
230 });
231 }
232 }
233
234 pub fn insert(&mut self, pos: TextCellRef, input: &str) {
241 let TextCellRef {
242 row,
243 col,
244 char_offset,
245 } = pos;
246 let table_clone = self.clone();
247 let existing_col = self.columns.get_mut(col);
248 match existing_col {
249 Some(existing_column) => {
250 existing_column.insert(ColumnPosition { row, char_offset }, input);
251 let src = existing_column.src[row].clone();
252 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
253 existing_column.compiled_src[row] = compiled;
254 existing_column.mark_dirty(row);
255 self.uncommitted_actions
256 .push(crate::core::SheetAction::SetCellSrc {
257 sheet_name: self.name.clone(),
258 col,
259 row,
260 src,
261 });
262 }
263 None => {
264 println!("Warning: column {} does not exist", col)
265 }
266 }
267 }
268
269 pub fn delete_one_before(&mut self, pos: TextCellRef) {
271 let char_offset = pos.char_offset;
272 let start = if char_offset > 0 {
273 TextCellRef {
274 row: pos.row,
275 col: pos.col,
276 char_offset: char_offset - 1,
277 }
278 } else {
279 pos.clone()
280 };
281 let end = pos;
282 self.delete(start, end);
283 }
284
285 pub fn delete(&mut self, start: TextCellRef, end: TextCellRef) {
292 if start.col > end.col || (start.col == end.col && start.row > end.row) {
294 return;
295 }
296 let table_clone = self.clone();
297 if start.col == end.col {
298 let start_index = start.row;
299 let end_index = end.row;
300
301 if let Some(column) = self.columns.get_mut(start.col) {
302 if start.row == end.row && start_index < column.src.len() {
303 let src = &mut column.src[start_index];
304 let end_offset = std::cmp::min(end.char_offset, src.len());
305 if start.char_offset < end_offset {
306 src.replace_range(start.char_offset..end_offset, "");
307 column.dirty_indices.push(start_index);
308 let updated_src = src.clone();
309 let compiled =
310 crate::core::parser::compile_formula(&updated_src, &[table_clone]);
311 column.compiled_src[start_index] = compiled;
312 }
313 } else if start_index < column.len() && end_index >= start_index {
314 column.drain_rows(start_index..=end_index);
315 }
316 }
317 } else {
318 for col in start.col..=end.col {
319 if let Some(column) = self.columns.get_mut(col) {
320 let start_index = if col == start.col { col } else { 0 };
321
322 let end_index = if col == end.col {
323 col
324 } else {
325 column.src.len() - 1
326 };
327
328 if start_index < column.len() && end_index >= start_index {
329 column.drain_rows(start_index..=end_index);
330 }
331 }
332 }
333 }
334 }
335
336 pub fn extend(&mut self, direction: Direction) {
341 if self.columns.is_empty() {
342 return;
343 }
344 let row_count = self.columns[0].src.len();
345 const MAX_COLS: usize = 26;
346 match direction {
347 Direction::Up => {
348 for column in &mut self.columns {
349 column.insert_row(0);
350 }
351 self.uncommitted_actions
352 .push(crate::core::SheetAction::InsertRow {
353 sheet_name: self.name.clone(),
354 index: 0,
355 });
356 }
357 Direction::Down => {
358 for column in &mut self.columns {
359 column.push_row();
360 }
361 self.uncommitted_actions
362 .push(crate::core::SheetAction::InsertRow {
363 sheet_name: self.name.clone(),
364 index: row_count,
365 });
366 }
367 Direction::Left => {
368 if self.columns.len() < MAX_COLS {
369 self.columns.insert(0, DataColumn::new(row_count));
370 self.uncommitted_actions
371 .push(crate::core::SheetAction::InsertCol {
372 sheet_name: self.name.clone(),
373 index: 0,
374 });
375 }
376 }
377 Direction::Right => {
378 if self.columns.len() < MAX_COLS {
379 self.columns.push(DataColumn::new(row_count));
380 self.uncommitted_actions
381 .push(crate::core::SheetAction::InsertCol {
382 sheet_name: self.name.clone(),
383 index: self.columns.len() - 1,
384 });
385 }
386 }
387 Direction::None => {}
388 }
389 }
390
391 pub fn ensure_capacity(&mut self, target_row: usize, target_col: usize) {
393 let current_rows = self.row_count();
394 let needed_rows = target_row + 1;
395 let final_rows = current_rows.max(needed_rows);
396
397 while self.columns.len() <= target_col {
398 let col_idx = self.columns.len();
399 let mut col = DataColumn::new(final_rows);
400 col.name = crate::core::parser::col_idx_to_letters(col_idx);
401 self.columns.push(col);
402 }
403
404 if final_rows > current_rows {
405 for col in &mut self.columns {
406 col.resize_rows(final_rows);
407 }
408 }
409 }
410
411 pub fn get_cell_style(&self, row: usize, col: usize) -> Option<&crate::core::CellStyle> {
416 self.columns
417 .get(col)
418 .and_then(|column| column.styles.get(row))
419 .and_then(|opt| opt.as_ref())
420 }
421
422 pub fn set_cell_style(&mut self, row: usize, col: usize, style: crate::core::CellStyle) {
425 self.ensure_capacity(row, col);
426 if let Some(column) = self.columns.get_mut(col)
427 && row < column.styles.len()
428 {
429 if style.is_empty() {
430 column.styles[row] = None;
431 } else {
432 column.styles[row] = Some(style);
433 }
434 }
435 }
436
437 pub fn update_cell_style<F>(&mut self, row: usize, col: usize, f: F)
442 where
443 F: FnOnce(&mut crate::core::CellStyle),
444 {
445 self.ensure_capacity(row, col);
446 if let Some(column) = self.columns.get_mut(col)
447 && row < column.styles.len()
448 {
449 let mut current = column.styles[row].clone().unwrap_or_default();
450 f(&mut current);
451 if current.is_empty() {
452 column.styles[row] = None;
453 } else {
454 column.styles[row] = Some(current);
455 }
456 }
457 }
458
459 pub fn clear_cell_style(&mut self, row: usize, col: usize) {
461 if let Some(column) = self.columns.get_mut(col)
462 && row < column.styles.len()
463 {
464 column.styles[row] = None;
465 }
466 }
467
468 pub fn insert_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.push_row();
475 }
476 self.uncommitted_actions
477 .push(crate::core::SheetAction::InsertRow {
478 sheet_name: self.name.clone(),
479 index: row_count,
480 });
481 } else {
482 for column in &mut self.columns {
483 column.insert_row(index);
484 }
485 self.uncommitted_actions
486 .push(crate::core::SheetAction::InsertRow {
487 sheet_name: self.name.clone(),
488 index,
489 });
490 }
491 }
492
493 pub fn delete_row(&mut self, index: usize) {
500 let row_count = self.row_count();
501 if index < row_count {
502 for column in &mut self.columns {
503 column.remove_row(index);
504 }
505 self.uncommitted_actions
506 .push(crate::core::SheetAction::DeleteRow {
507 sheet_name: self.name.clone(),
508 index,
509 });
510 self.mark_all_dirty();
511 }
512 }
513
514 pub fn insert_cells_shift_down(
532 &mut self,
533 row: usize,
534 first_col: usize,
535 last_col: usize,
536 count: usize,
537 ) {
538 let last_col = last_col.min(self.columns.len().saturating_sub(1));
539 if count == 0 || self.columns.is_empty() || first_col > last_col {
540 return;
541 }
542 for column in &mut self.columns {
545 for _ in 0..count {
546 column.push_row();
547 }
548 }
549 for column in &mut self.columns[first_col..=last_col] {
550 for _ in 0..count {
551 column.insert_row(row);
552 column.remove_row(column.len() - 1);
555 }
556 }
557 self.uncommitted_actions
558 .push(crate::core::SheetAction::InsertRow {
559 sheet_name: self.name.clone(),
560 index: row,
561 });
562 self.mark_all_dirty();
563 }
564
565 pub fn delete_cells_shift_up(
571 &mut self,
572 row: usize,
573 first_col: usize,
574 last_col: usize,
575 count: usize,
576 ) {
577 let last_col = last_col.min(self.columns.len().saturating_sub(1));
578 if count == 0 || self.columns.is_empty() || first_col > last_col || row >= self.row_count()
579 {
580 return;
581 }
582 for column in &mut self.columns[first_col..=last_col] {
583 for _ in 0..count {
584 if row < column.len() {
585 column.remove_row(row);
586 column.push_row();
589 }
590 }
591 }
592 self.uncommitted_actions
593 .push(crate::core::SheetAction::DeleteRow {
594 sheet_name: self.name.clone(),
595 index: row,
596 });
597 self.mark_all_dirty();
598 }
599
600 pub fn delete_col(&mut self, index: usize) {
604 if index < self.columns.len() {
605 self.columns.remove(index);
606 self.uncommitted_actions
607 .push(crate::core::SheetAction::DeleteCol {
608 sheet_name: self.name.clone(),
609 index,
610 });
611 self.mark_all_dirty();
612 }
613 }
614
615 pub fn insert_col(&mut self, index: usize) {
618 let row_count = self.row_count();
619 let new_col = DataColumn::new(row_count);
620 let col_count = self.columns.len();
621 if index >= col_count {
622 self.columns.push(new_col);
623 self.uncommitted_actions
624 .push(crate::core::SheetAction::InsertCol {
625 sheet_name: self.name.clone(),
626 index: col_count,
627 });
628 } else {
629 self.columns.insert(index, new_col);
630 self.uncommitted_actions
631 .push(crate::core::SheetAction::InsertCol {
632 sheet_name: self.name.clone(),
633 index,
634 });
635 }
636 self.mark_all_dirty();
637 }
638
639 pub fn columns(&self) -> &[DataColumn] {
646 &self.columns
647 }
648
649 pub fn row_count(&self) -> usize {
652 self.columns.first().map(|c| c.src.len()).unwrap_or(0)
653 }
654
655 pub fn col_count(&self) -> usize {
657 self.columns.len()
658 }
659}