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 {
128 let value = self.get_result_data(cell);
129 let Some(code) = self
130 .get_cell_style(cell.row, cell.col)
131 .and_then(|s| s.num_format.as_deref())
132 else {
133 return value.to_string();
134 };
135 match value {
136 ResultData::Float(f) => crate::core::text::format_number_format(f, code)
137 .unwrap_or_else(|_| ResultData::Float(f).to_string()),
138 ResultData::Integer(i) => crate::core::text::format_number_format(i as f64, code)
139 .unwrap_or_else(|_| ResultData::Integer(i).to_string()),
140 ResultData::String(s) => crate::core::text::format_text_format(&s, code)
141 .unwrap_or(ResultData::String(s).to_string()),
142 other => other.to_string(),
143 }
144 }
145
146 pub fn get_cell_type(&self, cell: &CellRef) -> CellType {
148 let col = self.columns.get(cell.col);
149 if let Some(col) = col {
150 col.cell_types
151 .get(cell.row)
152 .copied()
153 .unwrap_or(CellType::Empty)
154 } else {
155 CellType::Empty
156 }
157 }
158
159 pub fn set_cell_type(&mut self, row: usize, col: usize, cell_type: CellType) {
161 if let Some(column) = self.columns.get_mut(col)
162 && row < column.cell_types.len()
163 {
164 column.cell_types[row] = cell_type;
165 column.mark_dirty(row);
166 }
167 }
168
169 pub fn set_cell_with_type(&mut self, row: usize, col: usize, src: String, cell_type: CellType) {
171 let table_clone = self.clone();
172 if let Some(column) = self.columns.get_mut(col)
173 && row < column.src.len()
174 {
175 column.src[row] = src.clone();
176 column.cell_types[row] = cell_type;
177 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
178 column.compiled_src[row] = compiled;
179 column.mark_dirty(row);
180
181 self.uncommitted_actions
182 .push(crate::core::SheetAction::SetCellSrc {
183 sheet_name: self.name.clone(),
184 col,
185 row,
186 src,
187 });
188 }
189 }
190
191 pub fn set_cell_src(&mut self, row: usize, col: usize, src: String) {
196 let table_clone = self.clone();
197 if let Some(column) = self.columns.get_mut(col)
198 && row < column.src.len()
199 {
200 column.src[row] = src.clone();
201 column.cell_types[row] = CellType::Empty;
202 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
203 column.compiled_src[row] = compiled;
204 column.mark_dirty(row);
205
206 self.uncommitted_actions
207 .push(crate::core::SheetAction::SetCellSrc {
208 sheet_name: self.name.clone(),
209 col,
210 row,
211 src,
212 });
213 }
214 }
215
216 pub fn insert(&mut self, pos: TextCellRef, input: &str) {
223 let TextCellRef {
224 row,
225 col,
226 char_offset,
227 } = pos;
228 let table_clone = self.clone();
229 let existing_col = self.columns.get_mut(col);
230 match existing_col {
231 Some(existing_column) => {
232 existing_column.insert(ColumnPosition { row, char_offset }, input);
233 let src = existing_column.src[row].clone();
234 let compiled = crate::core::parser::compile_formula(&src, &[table_clone]);
235 existing_column.compiled_src[row] = compiled;
236 existing_column.mark_dirty(row);
237 self.uncommitted_actions
238 .push(crate::core::SheetAction::SetCellSrc {
239 sheet_name: self.name.clone(),
240 col,
241 row,
242 src,
243 });
244 }
245 None => {
246 println!("Warning: column {} does not exist", col)
247 }
248 }
249 }
250
251 pub fn delete_one_before(&mut self, pos: TextCellRef) {
253 let char_offset = pos.char_offset;
254 let start = if char_offset > 0 {
255 TextCellRef {
256 row: pos.row,
257 col: pos.col,
258 char_offset: char_offset - 1,
259 }
260 } else {
261 pos.clone()
262 };
263 let end = pos;
264 self.delete(start, end);
265 }
266
267 pub fn delete(&mut self, start: TextCellRef, end: TextCellRef) {
274 if start.col > end.col || (start.col == end.col && start.row > end.row) {
275 return;
276 }
277 let table_clone = self.clone();
278 if start.col == end.col {
279 let start_index = start.row;
280 let end_index = end.row;
281
282 if let Some(column) = self.columns.get_mut(start.col) {
283 if start.row == end.row && start_index < column.src.len() {
284 let src = &mut column.src[start_index];
285 let end_offset = std::cmp::min(end.char_offset, src.len());
286 if start.char_offset < end_offset {
287 src.replace_range(start.char_offset..end_offset, "");
288 column.dirty_indices.push(start_index);
289 let updated_src = src.clone();
290 let compiled =
291 crate::core::parser::compile_formula(&updated_src, &[table_clone]);
292 column.compiled_src[start_index] = compiled;
293 }
294 } else if start_index < column.len() && end_index >= start_index {
295 column.drain_rows(start_index..=end_index);
296 }
297 }
298 } else {
299 for col in start.col..=end.col {
300 if let Some(column) = self.columns.get_mut(col) {
301 let start_index = if col == start.col { col } else { 0 };
302
303 let end_index = if col == end.col {
304 col
305 } else {
306 column.src.len() - 1
307 };
308
309 if start_index < column.len() && end_index >= start_index {
310 column.drain_rows(start_index..=end_index);
311 }
312 }
313 }
314 }
315 }
316
317 pub fn extend(&mut self, direction: Direction) {
322 if self.columns.is_empty() {
323 return;
324 }
325 let row_count = self.columns[0].src.len();
326 const MAX_COLS: usize = 26;
327 match direction {
328 Direction::Up => {
329 for column in &mut self.columns {
330 column.insert_row(0);
331 }
332 self.row_heights.insert(0, None);
333 self.uncommitted_actions
334 .push(crate::core::SheetAction::InsertRow {
335 sheet_name: self.name.clone(),
336 index: 0,
337 });
338 }
339 Direction::Down => {
340 for column in &mut self.columns {
341 column.push_row();
342 }
343 self.row_heights.push(None);
344 self.uncommitted_actions
345 .push(crate::core::SheetAction::InsertRow {
346 sheet_name: self.name.clone(),
347 index: row_count,
348 });
349 }
350 Direction::Left => {
351 if self.columns.len() < MAX_COLS {
352 self.columns.insert(0, DataColumn::new(row_count));
353 self.uncommitted_actions
354 .push(crate::core::SheetAction::InsertCol {
355 sheet_name: self.name.clone(),
356 index: 0,
357 });
358 }
359 }
360 Direction::Right => {
361 if self.columns.len() < MAX_COLS {
362 self.columns.push(DataColumn::new(row_count));
363 self.uncommitted_actions
364 .push(crate::core::SheetAction::InsertCol {
365 sheet_name: self.name.clone(),
366 index: self.columns.len() - 1,
367 });
368 }
369 }
370 Direction::None => {}
371 }
372 }
373
374 pub fn ensure_capacity(&mut self, target_row: usize, target_col: usize) {
376 let current_rows = self.row_count();
377 let needed_rows = target_row + 1;
378 let final_rows = current_rows.max(needed_rows);
379
380 while self.columns.len() <= target_col {
381 let col_idx = self.columns.len();
382 let mut col = DataColumn::new(final_rows);
383 col.name = crate::core::parser::col_idx_to_letters(col_idx);
384 self.columns.push(col);
385 }
386
387 if final_rows > current_rows {
388 for col in &mut self.columns {
389 col.resize_rows(final_rows);
390 }
391 self.row_heights.resize(final_rows, None);
392 }
393 }
394
395 pub fn get_cell_style(&self, row: usize, col: usize) -> Option<&crate::core::CellStyle> {
400 self.columns
401 .get(col)
402 .and_then(|column| column.styles.get(row))
403 .and_then(|opt| opt.as_ref())
404 }
405
406 pub fn set_cell_style(&mut self, row: usize, col: usize, style: crate::core::CellStyle) {
409 self.ensure_capacity(row, col);
410 if let Some(column) = self.columns.get_mut(col)
411 && row < column.styles.len()
412 {
413 if style.is_empty() {
414 column.styles[row] = None;
415 } else {
416 column.styles[row] = Some(style);
417 }
418 }
419 }
420
421 pub fn update_cell_style<F>(&mut self, row: usize, col: usize, f: F)
426 where
427 F: FnOnce(&mut crate::core::CellStyle),
428 {
429 self.ensure_capacity(row, col);
430 if let Some(column) = self.columns.get_mut(col)
431 && row < column.styles.len()
432 {
433 let mut current = column.styles[row].clone().unwrap_or_default();
434 f(&mut current);
435 if current.is_empty() {
436 column.styles[row] = None;
437 } else {
438 column.styles[row] = Some(current);
439 }
440 }
441 }
442
443 pub fn clear_cell_style(&mut self, row: usize, col: usize) {
445 if let Some(column) = self.columns.get_mut(col)
446 && row < column.styles.len()
447 {
448 column.styles[row] = None;
449 }
450 }
451
452 pub fn get_column_width(&self, col: usize) -> Option<f64> {
454 self.columns.get(col).and_then(|column| column.width)
455 }
456
457 pub fn set_column_width(&mut self, col: usize, width: Option<f64>) {
459 if let Some(column) = self.columns.get_mut(col) {
460 column.width = width.filter(|value| value.is_finite() && *value >= 0.0);
461 }
462 }
463
464 pub fn get_row_height(&self, row: usize) -> Option<f64> {
466 self.row_heights.get(row).and_then(|height| *height)
467 }
468
469 pub fn set_row_height(&mut self, row: usize, height: Option<f64>) {
471 if row >= self.row_count() {
472 return;
473 }
474 let row_count = self.row_count();
475 self.row_heights.resize(row_count, None);
476 self.row_heights[row] = height.filter(|value| value.is_finite() && *value >= 0.0);
477 }
478
479 pub fn insert_row(&mut self, index: usize) {
482 let row_count = self.row_count();
483 self.row_heights.resize(row_count, None);
484 if index >= row_count {
485 for column in &mut self.columns {
486 column.push_row();
487 }
488 self.row_heights.push(None);
489 self.uncommitted_actions
490 .push(crate::core::SheetAction::InsertRow {
491 sheet_name: self.name.clone(),
492 index: row_count,
493 });
494 } else {
495 for column in &mut self.columns {
496 column.insert_row(index);
497 }
498 self.row_heights.insert(index, None);
499 self.uncommitted_actions
500 .push(crate::core::SheetAction::InsertRow {
501 sheet_name: self.name.clone(),
502 index,
503 });
504 }
505 }
506
507 pub fn delete_row(&mut self, index: usize) {
514 let row_count = self.row_count();
515 if index < row_count {
516 for column in &mut self.columns {
517 column.remove_row(index);
518 }
519 if index < self.row_heights.len() {
520 self.row_heights.remove(index);
521 }
522 self.uncommitted_actions
523 .push(crate::core::SheetAction::DeleteRow {
524 sheet_name: self.name.clone(),
525 index,
526 });
527 self.mark_all_dirty();
528 }
529 }
530
531 pub fn insert_cells_shift_down(
549 &mut self,
550 row: usize,
551 first_col: usize,
552 last_col: usize,
553 count: usize,
554 ) {
555 let last_col = last_col.min(self.columns.len().saturating_sub(1));
556 if count == 0 || self.columns.is_empty() || first_col > last_col {
557 return;
558 }
559 for column in &mut self.columns {
560 for _ in 0..count {
561 column.push_row();
562 }
563 }
564 let row_count = self.row_count();
565 self.row_heights.resize(row_count, None);
566 for column in &mut self.columns[first_col..=last_col] {
567 for _ in 0..count {
568 column.insert_row(row);
569 column.remove_row(column.len() - 1);
570 }
571 }
572 self.uncommitted_actions
573 .push(crate::core::SheetAction::InsertRow {
574 sheet_name: self.name.clone(),
575 index: row,
576 });
577 self.mark_all_dirty();
578 }
579
580 pub fn delete_cells_shift_up(
586 &mut self,
587 row: usize,
588 first_col: usize,
589 last_col: usize,
590 count: usize,
591 ) {
592 let last_col = last_col.min(self.columns.len().saturating_sub(1));
593 if count == 0 || self.columns.is_empty() || first_col > last_col || row >= self.row_count()
594 {
595 return;
596 }
597 for column in &mut self.columns[first_col..=last_col] {
598 for _ in 0..count {
599 if row < column.len() {
600 column.remove_row(row);
601 column.push_row();
602 }
603 }
604 }
605 self.uncommitted_actions
606 .push(crate::core::SheetAction::DeleteRow {
607 sheet_name: self.name.clone(),
608 index: row,
609 });
610 self.mark_all_dirty();
611 }
612
613 pub fn delete_col(&mut self, index: usize) {
617 if index < self.columns.len() {
618 self.columns.remove(index);
619 self.uncommitted_actions
620 .push(crate::core::SheetAction::DeleteCol {
621 sheet_name: self.name.clone(),
622 index,
623 });
624 self.mark_all_dirty();
625 }
626 }
627
628 pub fn insert_col(&mut self, index: usize) {
631 let row_count = self.row_count();
632 let new_col = DataColumn::new(row_count);
633 let col_count = self.columns.len();
634 if index >= col_count {
635 self.columns.push(new_col);
636 self.uncommitted_actions
637 .push(crate::core::SheetAction::InsertCol {
638 sheet_name: self.name.clone(),
639 index: col_count,
640 });
641 } else {
642 self.columns.insert(index, new_col);
643 self.uncommitted_actions
644 .push(crate::core::SheetAction::InsertCol {
645 sheet_name: self.name.clone(),
646 index,
647 });
648 }
649 self.mark_all_dirty();
650 }
651
652 pub fn columns(&self) -> &[DataColumn] {
659 &self.columns
660 }
661
662 pub fn row_count(&self) -> usize {
665 self.columns.first().map(|c| c.src.len()).unwrap_or(0)
666 }
667
668 pub fn col_count(&self) -> usize {
670 self.columns.len()
671 }
672}