visi_core/core/engine/sheet/
edit.rs1use super::super::column::{ColumnPosition, DataColumn};
8use super::{CellRef, CellType, Direction, ResultData, Sheet, TextCellRef};
9
10pub fn get_word_boundaries_from_str(text: &str, char_offset: usize) -> (usize, usize) {
19 if text.is_empty() {
20 return (0, 0);
21 }
22
23 let chars: Vec<char> = text.chars().collect();
24 let len = chars.len();
25 let offset = char_offset.min(len);
26
27 let is_word_char = |c: char| c.is_alphanumeric() || c == '_';
28
29 let (on_idx, on_c) = if offset < len {
30 if chars[offset].is_whitespace() && offset > 0 && is_word_char(chars[offset - 1]) {
31 (offset - 1, chars[offset - 1])
32 } else {
33 (offset, chars[offset])
34 }
35 } else if offset > 0 {
36 (offset - 1, chars[offset - 1])
37 } else {
38 return (0, 0);
39 };
40
41 if on_c.is_whitespace() {
42 let mut start = on_idx;
43 while start > 0 && chars[start - 1].is_whitespace() {
44 start -= 1;
45 }
46 let mut end = on_idx + 1;
47 while end < len && chars[end].is_whitespace() {
48 end += 1;
49 }
50 return (start, end);
51 }
52
53 if is_word_char(on_c) {
54 let mut start = on_idx;
55 while start > 0 && is_word_char(chars[start - 1]) {
56 start -= 1;
57 }
58 let mut end = on_idx + 1;
59 while end < len && is_word_char(chars[end]) {
60 end += 1;
61 }
62 return (start, end);
63 }
64
65 let mut start = on_idx;
66 while start > 0 && !is_word_char(chars[start - 1]) && !chars[start - 1].is_whitespace() {
67 start -= 1;
68 }
69 let mut end = on_idx + 1;
70 while end < len && !is_word_char(chars[end]) && !chars[end].is_whitespace() {
71 end += 1;
72 }
73 (start, end)
74}
75impl Sheet {
76 pub fn get_result_data(&self, cell: &CellRef) -> ResultData {
86 let col = self.columns.get(cell.col);
87 if let Some(col) = col {
88 col.data.get(cell.row).unwrap_or(ResultData::None)
89 } else {
90 ResultData::None
91 }
92 }
93
94 pub(super) fn inherited_date_format(&self, ast: &crate::core::parser::Expr) -> Option<String> {
108 use crate::core::parser::{Expr, Op};
109 match ast {
110 Expr::CellRef {
111 sheet, row, col, ..
112 } if sheet.is_none() => self
113 .get_cell_style(*row, *col)
114 .and_then(|s| s.num_format.clone())
115 .filter(|code| crate::core::date::is_date_code(code)),
116 Expr::BinaryOp {
117 op: Op::Add | Op::Sub,
118 left,
119 right,
120 } => {
121 let left_fmt = self.inherited_date_format(left);
122 let right_fmt = self.inherited_date_format(right);
123 match (left_fmt, right_fmt) {
124 (Some(fmt), None) | (None, Some(fmt)) => Some(fmt),
127 _ => None,
129 }
130 }
131 _ => None,
132 }
133 }
134
135 pub fn get_display_string(&self, cell: &CellRef) -> String {
144 let value = self.get_result_data(cell);
145 let Some(code) = self
146 .get_cell_style(cell.row, cell.col)
147 .and_then(|s| s.num_format.as_deref())
148 else {
149 return value.to_string();
150 };
151 if !crate::core::date::is_date_code(code) {
152 return value.to_string();
153 }
154 let serial = match value {
156 ResultData::Float(f) => f,
157 ResultData::Integer(i) => i as f64,
158 _ => return value.to_string(),
159 };
160 if serial < 0.0 {
161 return value.to_string();
162 }
163 crate::core::date::render_date_code(
164 crate::core::date::excel_serial_to_date(serial),
165 code,
166 crate::core::date::StringCase::Title,
167 )
168 }
169
170 pub fn get_cell_type(&self, cell: &CellRef) -> CellType {
172 let col = self.columns.get(cell.col);
173 if let Some(col) = col {
174 col.cell_types
175 .get(cell.row)
176 .copied()
177 .unwrap_or(CellType::Empty)
178 } else {
179 CellType::Empty
180 }
181 }
182
183 pub fn set_cell_type(&mut self, row: usize, col: usize, cell_type: CellType) {
185 if let Some(column) = self.columns.get_mut(col)
186 && row < column.cell_types.len()
187 {
188 column.cell_types[row] = cell_type;
189 column.mark_dirty(row);
190 }
191 }
192
193 pub fn set_cell_with_type(&mut self, row: usize, col: usize, src: String, cell_type: CellType) {
195 let table_clone = self.clone();
196 if let Some(column) = self.columns.get_mut(col)
197 && row < column.src.len()
198 {
199 column.src[row] = src.clone();
200 column.cell_types[row] = cell_type;
201 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
202 column.compiled_src[row] = compiled;
203 column.mark_dirty(row);
204
205 self.uncommitted_actions
206 .push(crate::core::SheetAction::SetCellSrc {
207 sheet_name: self.name.clone(),
208 col,
209 row,
210 src,
211 });
212 }
213 }
214
215 pub fn set_cell_src(&mut self, row: usize, col: usize, src: String) {
220 let table_clone = self.clone();
221 if let Some(column) = self.columns.get_mut(col)
222 && row < column.src.len()
223 {
224 column.src[row] = src.clone();
225 column.cell_types[row] = CellType::Auto;
226 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
227 column.compiled_src[row] = compiled;
228 column.mark_dirty(row);
229
230 self.uncommitted_actions
231 .push(crate::core::SheetAction::SetCellSrc {
232 sheet_name: self.name.clone(),
233 col,
234 row,
235 src,
236 });
237 }
238 }
239
240 pub fn insert(&mut self, pos: TextCellRef, input: &str) {
247 let TextCellRef {
248 row,
249 col,
250 char_offset,
251 } = pos;
252 let table_clone = self.clone();
253 let existing_col = self.columns.get_mut(col);
254 match existing_col {
255 Some(existing_column) => {
256 existing_column.insert(ColumnPosition { row, char_offset }, input);
257 let src = existing_column.src[row].clone();
258 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
259 existing_column.compiled_src[row] = compiled;
260 existing_column.mark_dirty(row);
261 self.uncommitted_actions
262 .push(crate::core::SheetAction::SetCellSrc {
263 sheet_name: self.name.clone(),
264 col,
265 row,
266 src,
267 });
268 }
269 None => {
270 println!("Warning: column {} does not exist", col)
271 }
272 }
273 }
274
275 pub fn delete_one_before(&mut self, pos: TextCellRef) {
277 let char_offset = pos.char_offset;
278 let start = if char_offset > 0 {
279 TextCellRef {
280 row: pos.row,
281 col: pos.col,
282 char_offset: char_offset - 1,
283 }
284 } else {
285 pos.clone()
286 };
287 let end = pos;
288 self.delete(start, end);
289 }
290
291 pub fn delete(&mut self, start: TextCellRef, end: TextCellRef) {
298 if start.col > end.col || (start.col == end.col && start.row > end.row) {
300 return;
301 }
302 let table_clone = self.clone();
303 if start.col == end.col {
305 let start_index = start.row;
306 let end_index = end.row;
307
308 if let Some(column) = self.columns.get_mut(start.col) {
309 if start.row == end.row && start_index < column.src.len() {
311 let src = &mut column.src[start_index];
312 let end_offset = std::cmp::min(end.char_offset, src.len());
313 if start.char_offset < end_offset {
314 src.replace_range(start.char_offset..end_offset, "");
315 column.dirty_indices.push(start_index);
316 let updated_src = src.clone();
317 let compiled =
318 crate::core::parser::compile_formula(&updated_src, &[table_clone]);
319 column.compiled_src[start_index] = compiled;
320 }
321 }
322 else if start_index < column.len() {
324 if end_index >= start_index {
326 column.drain_rows(start_index..=end_index);
327 }
328 }
329 }
330 } else {
331 for col in start.col..=end.col {
333 if let Some(column) = self.columns.get_mut(col) {
334 let start_index = if col == start.col { col } else { 0 };
335
336 let end_index = if col == end.col {
337 col
338 } else {
339 column.src.len() - 1
340 };
341
342 if start_index < column.len() {
343 if end_index >= start_index {
345 column.drain_rows(start_index..=end_index);
346 }
347 }
348 }
349 }
350 }
351 }
352
353 pub fn extend(&mut self, direction: Direction) {
358 if self.columns.is_empty() {
359 return;
360 }
361 let row_count = self.columns[0].src.len();
362 const MAX_COLS: usize = 26;
363 match direction {
364 Direction::Up => {
365 for column in &mut self.columns {
366 column.insert_row(0);
367 }
368 self.uncommitted_actions
369 .push(crate::core::SheetAction::InsertRow {
370 sheet_name: self.name.clone(),
371 index: 0,
372 });
373 }
374 Direction::Down => {
375 for column in &mut self.columns {
376 column.push_row();
377 }
378 self.uncommitted_actions
379 .push(crate::core::SheetAction::InsertRow {
380 sheet_name: self.name.clone(),
381 index: row_count,
382 });
383 }
384 Direction::Left => {
385 if self.columns.len() < MAX_COLS {
386 self.columns.insert(0, DataColumn::new(row_count));
387 self.uncommitted_actions
388 .push(crate::core::SheetAction::InsertCol {
389 sheet_name: self.name.clone(),
390 index: 0,
391 });
392 }
393 }
394 Direction::Right => {
395 if self.columns.len() < MAX_COLS {
396 self.columns.push(DataColumn::new(row_count));
397 self.uncommitted_actions
398 .push(crate::core::SheetAction::InsertCol {
399 sheet_name: self.name.clone(),
400 index: self.columns.len() - 1,
401 });
402 }
403 }
404 Direction::None => {}
405 }
406 }
407
408 pub fn ensure_capacity(&mut self, target_row: usize, target_col: usize) {
410 let current_rows = self.row_count();
411 let needed_rows = target_row + 1;
412 let final_rows = current_rows.max(needed_rows);
413
414 while self.columns.len() <= target_col {
415 let col_idx = self.columns.len();
416 let mut col = DataColumn::new(final_rows);
417 col.name = crate::core::parser::col_idx_to_letters(col_idx);
418 self.columns.push(col);
419 }
420
421 if final_rows > current_rows {
422 for col in &mut self.columns {
423 col.resize_rows(final_rows);
424 }
425 }
426 }
427
428 pub fn get_cell_style(&self, row: usize, col: usize) -> Option<&crate::core::CellStyle> {
433 self.columns
434 .get(col)
435 .and_then(|column| column.styles.get(row))
436 .and_then(|opt| opt.as_ref())
437 }
438
439 pub fn set_cell_style(&mut self, row: usize, col: usize, style: crate::core::CellStyle) {
442 self.ensure_capacity(row, col);
443 if let Some(column) = self.columns.get_mut(col)
444 && row < column.styles.len()
445 {
446 if style.is_empty() {
447 column.styles[row] = None;
448 } else {
449 column.styles[row] = Some(style);
450 }
451 }
452 }
453
454 pub fn update_cell_style<F>(&mut self, row: usize, col: usize, f: F)
459 where
460 F: FnOnce(&mut crate::core::CellStyle),
461 {
462 self.ensure_capacity(row, col);
463 if let Some(column) = self.columns.get_mut(col)
464 && row < column.styles.len()
465 {
466 let mut current = column.styles[row].clone().unwrap_or_default();
467 f(&mut current);
468 if current.is_empty() {
469 column.styles[row] = None;
470 } else {
471 column.styles[row] = Some(current);
472 }
473 }
474 }
475
476 pub fn clear_cell_style(&mut self, row: usize, col: usize) {
478 if let Some(column) = self.columns.get_mut(col)
479 && row < column.styles.len()
480 {
481 column.styles[row] = None;
482 }
483 }
484
485 pub fn insert_row(&mut self, index: usize) {
488 let row_count = self.row_count();
489 if index >= row_count {
490 for column in &mut self.columns {
492 column.push_row();
493 }
494 self.uncommitted_actions
495 .push(crate::core::SheetAction::InsertRow {
496 sheet_name: self.name.clone(),
497 index: row_count,
498 });
499 } else {
500 for column in &mut self.columns {
502 column.insert_row(index);
503 }
504 self.uncommitted_actions
505 .push(crate::core::SheetAction::InsertRow {
506 sheet_name: self.name.clone(),
507 index,
508 });
509 }
510 }
511
512 pub fn delete_row(&mut self, index: usize) {
519 let row_count = self.row_count();
520 if index < row_count {
521 for column in &mut self.columns {
522 column.remove_row(index);
523 }
524 self.uncommitted_actions
525 .push(crate::core::SheetAction::DeleteRow {
526 sheet_name: self.name.clone(),
527 index,
528 });
529 self.mark_all_dirty();
530 }
531 }
532
533 pub fn insert_cells_shift_down(
551 &mut self,
552 row: usize,
553 first_col: usize,
554 last_col: usize,
555 count: usize,
556 ) {
557 let last_col = last_col.min(self.columns.len().saturating_sub(1));
558 if count == 0 || self.columns.is_empty() || first_col > last_col {
559 return;
560 }
561 for column in &mut self.columns {
564 for _ in 0..count {
565 column.push_row();
566 }
567 }
568 for column in &mut self.columns[first_col..=last_col] {
569 for _ in 0..count {
570 column.insert_row(row);
571 column.remove_row(column.len() - 1);
574 }
575 }
576 self.uncommitted_actions
577 .push(crate::core::SheetAction::InsertRow {
578 sheet_name: self.name.clone(),
579 index: row,
580 });
581 self.mark_all_dirty();
582 }
583
584 pub fn delete_cells_shift_up(
590 &mut self,
591 row: usize,
592 first_col: usize,
593 last_col: usize,
594 count: usize,
595 ) {
596 let last_col = last_col.min(self.columns.len().saturating_sub(1));
597 if count == 0 || self.columns.is_empty() || first_col > last_col || row >= self.row_count()
598 {
599 return;
600 }
601 for column in &mut self.columns[first_col..=last_col] {
602 for _ in 0..count {
603 if row < column.len() {
604 column.remove_row(row);
605 column.push_row();
608 }
609 }
610 }
611 self.uncommitted_actions
612 .push(crate::core::SheetAction::DeleteRow {
613 sheet_name: self.name.clone(),
614 index: row,
615 });
616 self.mark_all_dirty();
617 }
618
619 pub fn delete_col(&mut self, index: usize) {
623 if index < self.columns.len() {
624 self.columns.remove(index);
625 self.uncommitted_actions
626 .push(crate::core::SheetAction::DeleteCol {
627 sheet_name: self.name.clone(),
628 index,
629 });
630 self.mark_all_dirty();
631 }
632 }
633
634 pub fn insert_col(&mut self, index: usize) {
637 let row_count = self.row_count();
638 let new_col = DataColumn::new(row_count);
639 let col_count = self.columns.len();
640 if index >= col_count {
641 self.columns.push(new_col);
642 self.uncommitted_actions
643 .push(crate::core::SheetAction::InsertCol {
644 sheet_name: self.name.clone(),
645 index: col_count,
646 });
647 } else {
648 self.columns.insert(index, new_col);
649 self.uncommitted_actions
650 .push(crate::core::SheetAction::InsertCol {
651 sheet_name: self.name.clone(),
652 index,
653 });
654 }
655 self.mark_all_dirty();
656 }
657
658 pub fn columns(&self) -> &[DataColumn] {
665 &self.columns
666 }
667
668 pub fn row_count(&self) -> usize {
671 self.columns.first().map(|c| c.src.len()).unwrap_or(0)
672 }
673
674 pub fn col_count(&self) -> usize {
676 self.columns.len()
677 }
678}