diff --git a/base/src/actions.rs b/base/src/actions.rs index bd79520..9aea331 100644 --- a/base/src/actions.rs +++ b/base/src/actions.rs @@ -69,21 +69,26 @@ impl Model { target_row: i32, target_column: i32, ) -> Result<(), String> { - let source_cell = self + if let Some(source_cell) = self .workbook .worksheet(sheet)? .cell(source_row, source_column) - .ok_or("Expected Cell to exist")?; - let style = source_cell.get_style(); - // FIXME: we need some user_input getter instead of get_text - let formula_or_value = self - .get_cell_formula(sheet, source_row, source_column)? - .unwrap_or_else(|| source_cell.get_text(&self.workbook.shared_strings, &self.language)); - self.set_user_input(sheet, target_row, target_column, formula_or_value); - self.workbook - .worksheet_mut(sheet)? - .set_cell_style(target_row, target_column, style); - self.cell_clear_all(sheet, source_row, source_column)?; + { + let style = source_cell.get_style(); + // FIXME: we need some user_input getter instead of get_text + let formula_or_value = self + .get_cell_formula(sheet, source_row, source_column)? + .unwrap_or_else(|| { + source_cell.get_text(&self.workbook.shared_strings, &self.language) + }); + self.set_user_input(sheet, target_row, target_column, formula_or_value); + self.workbook + .worksheet_mut(sheet)? + .set_cell_style(target_row, target_column, style); + self.cell_clear_all(sheet, source_row, source_column)?; + } else { + self.cell_clear_all(sheet, target_row, target_column)?; + } Ok(()) } @@ -106,7 +111,7 @@ impl Model { return Err("Cannot add a negative number of cells :)".to_string()); } // check if it is possible: - let dimensions = self.workbook.worksheet(sheet)?.dimension(); + let dimensions = self.workbook.worksheet(sheet)?.get_dimension(); let last_column = dimensions.max_column + column_count; if last_column > LAST_COLUMN { return Err( @@ -263,7 +268,7 @@ impl Model { return Err("Cannot add a negative number of cells :)".to_string()); } // Check if it is possible: - let dimensions = self.workbook.worksheet(sheet)?.dimension(); + let dimensions = self.workbook.worksheet(sheet)?.get_dimension(); let last_row = dimensions.max_row + row_count; if last_row > LAST_ROW { return Err( @@ -367,13 +372,162 @@ impl Model { } } self.workbook.worksheets[sheet as usize].rows = new_rows; - self.displace_cells( - &(DisplaceData::Row { - sheet, - row, - delta: -row_count, - }), - ); + self.displace_cells(&DisplaceData::Row { + sheet, + row, + delta: -row_count, + }); + Ok(()) + } + + pub fn delete_cells_and_shift_left( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let worksheet = &mut self.workbook.worksheet_mut(sheet)?; + let max_column = worksheet.get_dimension().max_column; + + // Delete all cells in the range + for r in row..row + row_delta { + for c in column..column + column_delta { + self.cell_clear_all(sheet, r, c)?; + } + } + + // Move all cells in the range + for r in row..row + row_delta { + for c in column + 1..max_column + 1 { + println!("{r}-{c}"); + self.move_cell(sheet, r, c, r, c - column_delta)?; + } + } + + // Update all formulas in the workbook + self.displace_cells(&DisplaceData::ShiftCellsRight { + sheet, + row, + column, + column_delta: -column_delta, + row_delta, + }); + + Ok(()) + } + + /// Insert cells and shift right + pub fn insert_cells_and_shift_right( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let worksheet = &mut self.workbook.worksheet_mut(sheet)?; + let max_column = worksheet.get_dimension().max_column; + + // Move all cells in the range + for r in row..row + row_delta { + for c in (column..max_column + 1).rev() { + self.move_cell(sheet, r, c, r, c + column_delta)?; + } + } + + // Delete all cells in the range + for r in row..row + row_delta { + for c in column..column + column_delta { + self.cell_clear_all(sheet, r, c)?; + } + } + + // Update all formulas in the workbook + self.displace_cells(&DisplaceData::ShiftCellsRight { + sheet, + row, + column, + column_delta, + row_delta, + }); + + Ok(()) + } + + /// Insert cells and shift down + pub fn insert_cells_and_shift_down( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let worksheet = &mut self.workbook.worksheet_mut(sheet)?; + let max_row = worksheet.get_dimension().max_row; + + // Move all cells in the range + for r in (row..row + max_row + 1).rev() { + for c in column..column + column_delta { + self.move_cell(sheet, r, c, r, c + column_delta)?; + } + } + + // Delete all cells in the range + for r in row..row + row_delta { + for c in column..column + column_delta { + self.cell_clear_all(sheet, r, c)?; + } + } + + // Update all formulas in the workbook + self.displace_cells(&DisplaceData::ShiftCellsDown { + sheet, + row, + column, + column_delta, + row_delta, + }); + + Ok(()) + } + + pub fn delete_cells_and_shift_up( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let worksheet = &mut self.workbook.worksheet_mut(sheet)?; + let max_row = worksheet.get_dimension().max_row; + + // Delete all cells in the range + for r in row..row + row_delta { + for c in column..column + column_delta { + self.cell_clear_all(sheet, r, c)?; + } + } + + // Move all cells in the range + for r in row..max_row + 1 { + for c in column + 1..column + column_delta { + self.move_cell(sheet, r, c, r - row_delta, c)?; + } + } + + // Update all formulas in the workbook + self.displace_cells(&DisplaceData::ShiftCellsDown { + sheet, + row, + column, + column_delta, + row_delta: -row_delta, + }); + Ok(()) } diff --git a/base/src/expressions/lexer/util.rs b/base/src/expressions/lexer/marked_token.rs similarity index 89% rename from base/src/expressions/lexer/util.rs rename to base/src/expressions/lexer/marked_token.rs index d1f4de6..08bc482 100644 --- a/base/src/expressions/lexer/util.rs +++ b/base/src/expressions/lexer/marked_token.rs @@ -1,3 +1,5 @@ +#![deny(missing_docs)] + use serde::{Deserialize, Serialize}; use crate::expressions::token; @@ -9,8 +11,11 @@ use super::{Lexer, LexerMode}; /// A MarkedToken is a token together with its position on a formula #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct MarkedToken { + /// Token type (see [token::TokenType]) pub token: token::TokenType, + /// Position of the start of the token (in bytes) pub start: i32, + /// Position of the end of the token (in bytes) pub end: i32, } @@ -19,7 +24,7 @@ pub struct MarkedToken { /// # Examples /// ``` /// use ironcalc_base::expressions::{ -/// lexer::util::{get_tokens, MarkedToken}, +/// lexer::marked_token::{get_tokens, MarkedToken}, /// token::{OpSum, TokenType}, /// }; /// diff --git a/base/src/expressions/lexer/mod.rs b/base/src/expressions/lexer/mod.rs index 0e4dded..49bb9b8 100644 --- a/base/src/expressions/lexer/mod.rs +++ b/base/src/expressions/lexer/mod.rs @@ -1,3 +1,5 @@ +#![deny(missing_docs)] + //! A tokenizer for spreadsheet formulas. //! //! This is meant to feed a formula parser. @@ -7,8 +9,10 @@ //! It supports two working modes: //! //! 1. A1 or display mode +//! //! This is for user formulas. References are like `D4`, `D$4` or `F5:T10` //! 2. R1C1, internal or runtime mode +//! //! A reference like R1C1 refers to $A$1 and R3C4 to $D$4 //! R[2]C[5] refers to a cell two rows below and five columns to the right //! It uses the 'en' locale and language. @@ -55,7 +59,8 @@ use super::token::{Error, TokenType}; use super::types::*; use super::utils; -pub mod util; +/// Returns an iterator over tokens together with their position in the byte string. +pub mod marked_token; #[cfg(test)] mod test; @@ -63,17 +68,28 @@ mod test; mod ranges; mod structured_references; +/// This is the TokenType we return if we cannot recognize a token #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct LexerError { + /// Position of the beginning of the token in the byte string. pub position: usize, + /// Message describing what we think the error is. pub message: String, } pub(super) type Result = std::result::Result; +/// Whether we try to parse formulas in A1 mode or in the internal R1C1 mode #[derive(Clone, PartialEq, Eq)] pub enum LexerMode { + /// Cell references are written `=S34`. This is the display mode A1, + /// R1C1, internal or runtime mode + /// + /// A reference like R1C1 refers to $A$1 and R3C4 to $D$4 + /// R[2]C[5] refers to a cell two rows below and five columns to the right + /// It uses the 'en' locale and language. + /// This is used internally at runtime. R1C1, } diff --git a/base/src/expressions/lexer/test/mod.rs b/base/src/expressions/lexer/test/mod.rs index f795ba5..8e73849 100644 --- a/base/src/expressions/lexer/test/mod.rs +++ b/base/src/expressions/lexer/test/mod.rs @@ -1,6 +1,6 @@ mod test_common; mod test_language; mod test_locale; +mod test_marked_token; mod test_ranges; mod test_tables; -mod test_util; diff --git a/base/src/expressions/lexer/test/test_util.rs b/base/src/expressions/lexer/test/test_marked_token.rs similarity index 85% rename from base/src/expressions/lexer/test/test_util.rs rename to base/src/expressions/lexer/test/test_marked_token.rs index fff5985..8cc1245 100644 --- a/base/src/expressions/lexer/test/test_util.rs +++ b/base/src/expressions/lexer/test/test_marked_token.rs @@ -1,5 +1,5 @@ use crate::expressions::{ - lexer::util::get_tokens, + lexer::marked_token::{get_tokens, MarkedToken}, token::{OpCompare, OpSum, TokenType}, }; @@ -22,6 +22,29 @@ fn test_get_tokens() { assert_eq!(l.end, 10); } +#[test] +fn chinese_characters() { + let formula = "\"你好\" & \"世界!\""; + let marked_tokens = get_tokens(formula); + assert_eq!(marked_tokens.len(), 3); + let first_t = MarkedToken { + token: TokenType::String("你好".to_string()), + start: 0, + end: 4, + }; + let second_t = MarkedToken { + token: TokenType::And, + start: 4, + end: 6, + }; + let third_t = MarkedToken { + token: TokenType::String("世界!".to_string()), + start: 6, + end: 12, + }; + assert_eq!(marked_tokens, vec![first_t, second_t, third_t]); +} + #[test] fn test_simple_tokens() { assert_eq!( diff --git a/base/src/expressions/mod.rs b/base/src/expressions/mod.rs index 35f9756..da845b1 100644 --- a/base/src/expressions/mod.rs +++ b/base/src/expressions/mod.rs @@ -1,4 +1,3 @@ -// public modules pub mod lexer; pub mod parser; pub mod token; diff --git a/base/src/expressions/parser/mod.rs b/base/src/expressions/parser/mod.rs index afeec68..b2537ee 100644 --- a/base/src/expressions/parser/mod.rs +++ b/base/src/expressions/parser/mod.rs @@ -1,31 +1,29 @@ -/*! -# GRAMAR - -
-opComp   => '=' | '<' | '>' | '<=' } '>=' | '<>'
-opFactor => '*' | '/'
-unaryOp  => '-' | '+'
-
-expr    => concat (opComp concat)*
-concat  => term ('&' term)*
-term    => factor (opFactor factor)*
-factor  => prod (opProd prod)*
-prod    => power ('^' power)*
-power   => (unaryOp)* range '%'*
-range   => primary (':' primary)?
-primary => '(' expr ')'
-        => number
-        => function '(' f_args ')'
-        => name
-        => string
-        => '{' a_args '}'
-        => bool
-        => bool()
-        => error
-
-f_args  => e (',' e)*
-
-*/ +//! # GRAMMAR +//! +//!
+//! opComp   => '=' | '<' | '>' | '<=' } '>=' | '<>'
+//! opFactor => '*' | '/'
+//! unaryOp  => '-' | '+'
+//!
+//! expr    => concat (opComp concat)*
+//! concat  => term ('&' term)*
+//! term    => factor (opFactor factor)*
+//! factor  => prod (opProd prod)*
+//! prod    => power ('^' power)*
+//! power   => (unaryOp)* range '%'*
+//! range   => primary (':' primary)?
+//! primary => '(' expr ')'
+//!         => number
+//!         => function '(' f_args ')'
+//!         => name
+//!         => string
+//!         => '{' a_args '}'
+//!         => bool
+//!         => bool()
+//!         => error
+//!
+//! f_args  => e (',' e)*
+//! 
use std::collections::HashMap; @@ -44,21 +42,15 @@ use super::utils::number_to_column; use token::OpCompare; -pub mod move_formula; +pub(crate) mod move_formula; +pub(crate) mod walk; + +/// Produces a string representation of a formula from the AST. pub mod stringify; -pub mod walk; #[cfg(test)] mod test; -#[cfg(test)] -mod test_ranges; - -#[cfg(test)] -mod test_move_formula; -#[cfg(test)] -mod test_tables; - pub(crate) fn parse_range(formula: &str) -> Result<(i32, i32, i32, i32), String> { let mut lexer = lexer::Lexer::new( formula, diff --git a/base/src/expressions/parser/stringify.rs b/base/src/expressions/parser/stringify.rs index 352b285..e8772de 100644 --- a/base/src/expressions/parser/stringify.rs +++ b/base/src/expressions/parser/stringify.rs @@ -1,43 +1,75 @@ +#![deny(missing_docs)] + use super::{super::utils::quote_name, Node, Reference}; use crate::constants::{LAST_COLUMN, LAST_ROW}; use crate::expressions::token::OpUnary; use crate::{expressions::types::CellReferenceRC, number_format::to_excel_precision_str}; +/// Displaced data pub enum DisplaceData { + /// Displaces columns (inserting or deleting columns) Column { + /// Sheet in which the displace data applies sheet: u32, + /// Column from which the data is displaced column: i32, + /// Number of columns displaced (might be negative, e.g. when deleting columns) delta: i32, }, + /// Displaces rows (Inserting or deleting rows) Row { + /// Sheet in which the displace data applies sheet: u32, + /// Row from which the data is displaced row: i32, + /// Number of rows displaced (might be negative, e.g. when deleting rows) delta: i32, }, - CellHorizontal { + /// Displaces cells horizontally + ShiftCellsRight { + /// Sheet in which the displace data applies sheet: u32, + /// Row of the to left corner row: i32, + /// Column of the top left corner column: i32, - delta: i32, + /// Number of rows to be displaced + row_delta: i32, + /// Number of columns to be displaced (might be negative) + column_delta: i32, }, - CellVertical { + /// Displaces cells vertically + ShiftCellsDown { + /// Sheet in which the displace data applies sheet: u32, + /// Row of the to left corner row: i32, + /// Column of the top left corner column: i32, - delta: i32, + /// Number of rows displaced (might be negative) + row_delta: i32, + /// Number of columns to be displaced + column_delta: i32, }, + /// Displaces data due to a column move from column to column + delta ColumnMove { + /// Sheet in which the displace data applies sheet: u32, + /// Column that is moved column: i32, + /// The position of the new column is column + delta (might be negative) delta: i32, }, + /// Doesn't do any cell displacement None, } +/// Stringifies the AST formula in its internal R1C1 format pub fn to_rc_format(node: &Node) -> String { stringify(node, None, &DisplaceData::None, false) } +/// Stringifies the formula applying the _displace_data_. pub fn to_string_displaced( node: &Node, context: &CellReferenceRC, @@ -46,10 +78,12 @@ pub fn to_string_displaced( stringify(node, Some(context), displace_data, false) } +/// Stringifies a formula from the AST pub fn to_string(node: &Node, context: &CellReferenceRC) -> String { stringify(node, Some(context), &DisplaceData::None, false) } +/// Stringifies the formula for Excel compatibility pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String { stringify(node, Some(context), &DisplaceData::None, true) } @@ -116,41 +150,49 @@ pub(crate) fn stringify_reference( } } } - DisplaceData::CellHorizontal { + DisplaceData::ShiftCellsRight { sheet, row: displace_row, column: displace_column, - delta, + column_delta, + row_delta, } => { - if sheet_index == *sheet && displace_row == &row { - if *delta < 0 { + if sheet_index == *sheet + && displace_row >= &row + && *displace_row < row + *row_delta + { + if *column_delta < 0 { if &column >= displace_column { - if column < displace_column - *delta { + if column < displace_column - *column_delta { return "#REF!".to_string(); } - column += *delta; + column += *column_delta; } } else if &column >= displace_column { - column += *delta; + column += *column_delta; } } } - DisplaceData::CellVertical { + DisplaceData::ShiftCellsDown { sheet, row: displace_row, column: displace_column, - delta, + row_delta, + column_delta, } => { - if sheet_index == *sheet && displace_column == &column { - if *delta < 0 { + if sheet_index == *sheet + && displace_column >= &column + && *displace_column < column + *column_delta + { + if *row_delta < 0 { if &row >= displace_row { - if row < displace_row - *delta { + if row < displace_row - *row_delta { return "#REF!".to_string(); } - row += *delta; + row += *row_delta; } } else if &row >= displace_row { - row += *delta; + row += *row_delta; } } } diff --git a/base/src/expressions/parser/test/mod.rs b/base/src/expressions/parser/test/mod.rs new file mode 100644 index 0000000..1e08dec --- /dev/null +++ b/base/src/expressions/parser/test/mod.rs @@ -0,0 +1,4 @@ +mod test_genertal; +mod test_move_formula; +mod test_ranges; +mod test_tables; diff --git a/base/src/expressions/parser/test.rs b/base/src/expressions/parser/test/test_genertal.rs similarity index 98% rename from base/src/expressions/parser/test.rs rename to base/src/expressions/parser/test/test_genertal.rs index 3d8369a..e1dab21 100644 --- a/base/src/expressions/parser/test.rs +++ b/base/src/expressions/parser/test/test_genertal.rs @@ -1,17 +1,12 @@ use std::collections::HashMap; use crate::expressions::lexer::LexerMode; -use crate::expressions::parser::stringify::DisplaceData; - -use super::super::types::CellReferenceRC; -use super::Parser; -use super::{ - super::parser::{ - stringify::{to_rc_format, to_string}, - Node, - }, - stringify::to_string_displaced, +use crate::expressions::parser::stringify::{to_string_displaced, DisplaceData}; +use crate::expressions::parser::{ + stringify::{to_rc_format, to_string}, + Node, Parser, }; +use crate::expressions::types::CellReferenceRC; struct Formula<'a> { initial: &'a str, diff --git a/base/src/expressions/parser/test_move_formula.rs b/base/src/expressions/parser/test/test_move_formula.rs similarity index 99% rename from base/src/expressions/parser/test_move_formula.rs rename to base/src/expressions/parser/test/test_move_formula.rs index 0b84811..63196f0 100644 --- a/base/src/expressions/parser/test_move_formula.rs +++ b/base/src/expressions/parser/test/test_move_formula.rs @@ -1,10 +1,8 @@ use std::collections::HashMap; use crate::expressions::parser::move_formula::{move_formula, MoveContext}; -use crate::expressions::types::Area; - -use super::super::types::CellReferenceRC; -use super::Parser; +use crate::expressions::parser::Parser; +use crate::expressions::types::{Area, CellReferenceRC}; #[test] fn test_move_formula() { diff --git a/base/src/expressions/parser/test_ranges.rs b/base/src/expressions/parser/test/test_ranges.rs similarity index 94% rename from base/src/expressions/parser/test_ranges.rs rename to base/src/expressions/parser/test/test_ranges.rs index 2e876dc..adc0f5e 100644 --- a/base/src/expressions/parser/test_ranges.rs +++ b/base/src/expressions/parser/test/test_ranges.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use crate::expressions::lexer::LexerMode; -use super::super::parser::stringify::{to_rc_format, to_string}; -use super::super::types::CellReferenceRC; -use super::Parser; +use crate::expressions::parser::stringify::{to_rc_format, to_string}; +use crate::expressions::parser::Parser; +use crate::expressions::types::CellReferenceRC; struct Formula<'a> { formula_a1: &'a str, diff --git a/base/src/expressions/parser/test_tables.rs b/base/src/expressions/parser/test/test_tables.rs similarity index 97% rename from base/src/expressions/parser/test_tables.rs rename to base/src/expressions/parser/test/test_tables.rs index 89fd880..ebb177a 100644 --- a/base/src/expressions/parser/test_tables.rs +++ b/base/src/expressions/parser/test/test_tables.rs @@ -6,8 +6,8 @@ use crate::expressions::parser::stringify::to_string; use crate::expressions::utils::{number_to_column, parse_reference_a1}; use crate::types::{Table, TableColumn, TableStyleInfo}; -use super::super::types::CellReferenceRC; -use super::Parser; +use crate::expressions::parser::Parser; +use crate::expressions::types::CellReferenceRC; fn create_test_table( table_name: &str, diff --git a/base/src/functions/engineering/complex.rs b/base/src/functions/engineering/complex.rs index a291294..45c4e8d 100644 --- a/base/src/functions/engineering/complex.rs +++ b/base/src/functions/engineering/complex.rs @@ -3,7 +3,7 @@ use std::fmt; use crate::{ calc_result::CalcResult, expressions::{ - lexer::util::get_tokens, + lexer::marked_token::get_tokens, parser::Node, token::{Error, OpSum, TokenType}, types::CellReferenceIndex, diff --git a/base/src/functions/financial.rs b/base/src/functions/financial.rs index 59fe2a8..19e8af7 100644 --- a/base/src/functions/financial.rs +++ b/base/src/functions/financial.rs @@ -221,7 +221,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -229,7 +229,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } for row in row1..row2 + 1 { @@ -284,7 +284,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -292,7 +292,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } for row in row1..row2 + 1 { @@ -360,7 +360,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -368,7 +368,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } for row in row1..row2 + 1 { @@ -866,7 +866,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -874,7 +874,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } for row in row1..row2 + 1 { diff --git a/base/src/functions/mathematical.rs b/base/src/functions/mathematical.rs index 936fd94..372b2d2 100644 --- a/base/src/functions/mathematical.rs +++ b/base/src/functions/mathematical.rs @@ -132,7 +132,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -140,7 +140,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } for row in row1..row2 + 1 { @@ -199,7 +199,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -207,7 +207,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } for row in row1..row2 + 1 { diff --git a/base/src/functions/statistical.rs b/base/src/functions/statistical.rs index 637b9bd..7679a92 100644 --- a/base/src/functions/statistical.rs +++ b/base/src/functions/statistical.rs @@ -385,7 +385,7 @@ impl Model { .workbook .worksheet(first_range.left.sheet) .expect("Sheet expected during evaluation.") - .dimension(); + .get_dimension(); let max_row = dimension.max_row; let max_column = dimension.max_column; @@ -530,7 +530,7 @@ impl Model { .workbook .worksheet(sum_range.left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if left_column == 1 && right_column == LAST_COLUMN { @@ -538,7 +538,7 @@ impl Model { .workbook .worksheet(sum_range.left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } diff --git a/base/src/functions/text.rs b/base/src/functions/text.rs index bcb3d35..191d646 100644 --- a/base/src/functions/text.rs +++ b/base/src/functions/text.rs @@ -892,7 +892,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -900,7 +900,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } for row in row1..row2 + 1 { diff --git a/base/src/functions/xlookup.rs b/base/src/functions/xlookup.rs index 3a915b0..0b7cf0d 100644 --- a/base/src/functions/xlookup.rs +++ b/base/src/functions/xlookup.rs @@ -255,7 +255,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_row; } if column1 == 1 && column2 == LAST_COLUMN { @@ -263,7 +263,7 @@ impl Model { .workbook .worksheet(left.sheet) .expect("Sheet expected during evaluation.") - .dimension() + .get_dimension() .max_column; } let left = CellReferenceIndex { diff --git a/base/src/model.rs b/base/src/model.rs index a50a1b0..3f8c3ee 100644 --- a/base/src/model.rs +++ b/base/src/model.rs @@ -1788,7 +1788,7 @@ impl Model { /// Returns markup representation of the given `sheet`. pub fn get_sheet_markup(&self, sheet: u32) -> Result { let worksheet = self.workbook.worksheet(sheet)?; - let dimension = worksheet.dimension(); + let dimension = worksheet.get_dimension(); let mut rows = Vec::new(); diff --git a/base/src/test/functions/mod.rs b/base/src/test/functions/mod.rs new file mode 100644 index 0000000..237d077 --- /dev/null +++ b/base/src/test/functions/mod.rs @@ -0,0 +1,18 @@ +mod test_fn_average; +mod test_fn_averageifs; +mod test_fn_choose; +mod test_fn_concatenate; +mod test_fn_count; +mod test_fn_exact; +mod test_fn_financial; +mod test_fn_if; +mod test_fn_maxifs; +mod test_fn_minifs; +mod test_fn_offset; +mod test_fn_product; +mod test_fn_rept; +mod test_fn_sum; +mod test_fn_sumifs; +mod test_fn_textbefore; +mod test_fn_textjoin; +mod test_fn_type; diff --git a/base/src/test/test_fn_average.rs b/base/src/test/functions/test_fn_average.rs similarity index 100% rename from base/src/test/test_fn_average.rs rename to base/src/test/functions/test_fn_average.rs diff --git a/base/src/test/test_fn_averageifs.rs b/base/src/test/functions/test_fn_averageifs.rs similarity index 100% rename from base/src/test/test_fn_averageifs.rs rename to base/src/test/functions/test_fn_averageifs.rs diff --git a/base/src/test/test_fn_choose.rs b/base/src/test/functions/test_fn_choose.rs similarity index 100% rename from base/src/test/test_fn_choose.rs rename to base/src/test/functions/test_fn_choose.rs diff --git a/base/src/test/test_fn_concatenate.rs b/base/src/test/functions/test_fn_concatenate.rs similarity index 100% rename from base/src/test/test_fn_concatenate.rs rename to base/src/test/functions/test_fn_concatenate.rs diff --git a/base/src/test/test_fn_count.rs b/base/src/test/functions/test_fn_count.rs similarity index 100% rename from base/src/test/test_fn_count.rs rename to base/src/test/functions/test_fn_count.rs diff --git a/base/src/test/test_fn_exact.rs b/base/src/test/functions/test_fn_exact.rs similarity index 100% rename from base/src/test/test_fn_exact.rs rename to base/src/test/functions/test_fn_exact.rs diff --git a/base/src/test/test_fn_financial.rs b/base/src/test/functions/test_fn_financial.rs similarity index 100% rename from base/src/test/test_fn_financial.rs rename to base/src/test/functions/test_fn_financial.rs diff --git a/base/src/test/test_fn_if.rs b/base/src/test/functions/test_fn_if.rs similarity index 100% rename from base/src/test/test_fn_if.rs rename to base/src/test/functions/test_fn_if.rs diff --git a/base/src/test/test_fn_maxifs.rs b/base/src/test/functions/test_fn_maxifs.rs similarity index 100% rename from base/src/test/test_fn_maxifs.rs rename to base/src/test/functions/test_fn_maxifs.rs diff --git a/base/src/test/test_fn_minifs.rs b/base/src/test/functions/test_fn_minifs.rs similarity index 100% rename from base/src/test/test_fn_minifs.rs rename to base/src/test/functions/test_fn_minifs.rs diff --git a/base/src/test/test_fn_offset.rs b/base/src/test/functions/test_fn_offset.rs similarity index 100% rename from base/src/test/test_fn_offset.rs rename to base/src/test/functions/test_fn_offset.rs diff --git a/base/src/test/test_fn_product.rs b/base/src/test/functions/test_fn_product.rs similarity index 100% rename from base/src/test/test_fn_product.rs rename to base/src/test/functions/test_fn_product.rs diff --git a/base/src/test/test_fn_rept.rs b/base/src/test/functions/test_fn_rept.rs similarity index 100% rename from base/src/test/test_fn_rept.rs rename to base/src/test/functions/test_fn_rept.rs diff --git a/base/src/test/test_fn_sum.rs b/base/src/test/functions/test_fn_sum.rs similarity index 100% rename from base/src/test/test_fn_sum.rs rename to base/src/test/functions/test_fn_sum.rs diff --git a/base/src/test/test_fn_sumifs.rs b/base/src/test/functions/test_fn_sumifs.rs similarity index 100% rename from base/src/test/test_fn_sumifs.rs rename to base/src/test/functions/test_fn_sumifs.rs diff --git a/base/src/test/test_fn_textbefore.rs b/base/src/test/functions/test_fn_textbefore.rs similarity index 100% rename from base/src/test/test_fn_textbefore.rs rename to base/src/test/functions/test_fn_textbefore.rs diff --git a/base/src/test/test_fn_textjoin.rs b/base/src/test/functions/test_fn_textjoin.rs similarity index 100% rename from base/src/test/test_fn_textjoin.rs rename to base/src/test/functions/test_fn_textjoin.rs diff --git a/base/src/test/test_fn_type.rs b/base/src/test/functions/test_fn_type.rs similarity index 100% rename from base/src/test/test_fn_type.rs rename to base/src/test/functions/test_fn_type.rs diff --git a/base/src/test/mod.rs b/base/src/test/mod.rs index 8f3a666..6f432d3 100644 --- a/base/src/test/mod.rs +++ b/base/src/test/mod.rs @@ -1,3 +1,5 @@ +mod engineering; +mod functions; mod test_actions; mod test_binary_search; mod test_cell; @@ -8,50 +10,30 @@ mod test_criteria; mod test_currency; mod test_date_and_time; mod test_error_propagation; -mod test_fn_average; -mod test_fn_averageifs; -mod test_fn_choose; -mod test_fn_concatenate; -mod test_fn_count; -mod test_fn_exact; -mod test_fn_financial; -mod test_fn_if; -mod test_fn_maxifs; -mod test_fn_minifs; -mod test_fn_product; -mod test_fn_rept; -mod test_fn_sum; -mod test_fn_sumifs; -mod test_fn_textbefore; -mod test_fn_textjoin; +mod test_escape_quotes; +mod test_extend; mod test_forward_references; +mod test_frozen_rows_and_columns; mod test_frozen_rows_columns; mod test_general; +mod test_get_cell_content; mod test_math; mod test_metadata; mod test_model_cell_clear_all; mod test_model_is_empty_cell; mod test_move_formula; +mod test_number_format; +mod test_percentage; mod test_quote_prefix; mod test_set_user_input; mod test_sheet_markup; mod test_sheets; +mod test_shift_cells; mod test_styles; +mod test_today; mod test_trigonometric; +mod test_types; mod test_workbook; mod test_worksheet; -pub(crate) mod util; - -mod engineering; -mod test_fn_offset; -mod test_number_format; - -mod test_escape_quotes; -mod test_extend; -mod test_fn_type; -mod test_frozen_rows_and_columns; -mod test_get_cell_content; -mod test_percentage; -mod test_today; -mod test_types; mod user_model; +pub(crate) mod util; diff --git a/base/src/test/test_shift_cells.rs b/base/src/test/test_shift_cells.rs new file mode 100644 index 0000000..1f859e9 --- /dev/null +++ b/base/src/test/test_shift_cells.rs @@ -0,0 +1,110 @@ +#![allow(clippy::unwrap_used)] + +use crate::test::util::new_empty_model; + +#[test] +fn shift_cells_right() { + let mut model = new_empty_model(); + let (sheet, row, column) = (0, 5, 3); // C5 + model.set_user_input(sheet, row, column, "Hi".to_string()); + model.set_user_input(sheet, row, column + 1, "world".to_string()); + model.set_user_input(sheet, row, column + 2, "!".to_string()); + + model + .insert_cells_and_shift_right(sheet, row, column, 1, 1) + .unwrap(); + model.evaluate(); + + assert_eq!(model.get_cell_content(0, 5, 3), Ok("".to_string())); + assert_eq!(model.get_cell_content(0, 5, 4), Ok("Hi".to_string())); +} + +#[test] +fn shift_cells_right_with_formulas() { + let mut model = new_empty_model(); + let (sheet, row, column) = (0, 5, 3); // C5 + model.set_user_input(sheet, row, column - 1, "23".to_string()); + model.set_user_input(sheet, row, column, "42".to_string()); + model.set_user_input(sheet, row, column + 1, "=C5*2".to_string()); + model.set_user_input(sheet, row, column + 2, "=A5+2".to_string()); + model.set_user_input(sheet, 20, 3, "=C5*A2".to_string()); + model.evaluate(); + + model + .insert_cells_and_shift_right(sheet, row, column, 1, 1) + .unwrap(); + + model.evaluate(); + assert_eq!( + model.get_cell_content(0, row, column - 1), + Ok("23".to_string()) + ); + assert_eq!(model.get_cell_content(0, row, column), Ok("".to_string())); + assert_eq!( + model.get_cell_content(0, row, column + 1), + Ok("42".to_string()) + ); + assert_eq!( + model.get_cell_content(0, row, column + 2), + Ok("=D5*2".to_string()) + ); + assert_eq!( + model.get_cell_content(0, row, column + 3), + Ok("=A5+2".to_string()) + ); + assert_eq!(model.get_cell_content(0, 20, 3), Ok("=D5*A2".to_string())); +} + +#[test] +fn shift_cells_left() { + let mut model = new_empty_model(); + let (sheet, row, column) = (0, 5, 10); // J5 + model.set_user_input(sheet, row, column - 1, "23".to_string()); + model.set_user_input(sheet, row, column, "42".to_string()); + model.set_user_input(sheet, row, column + 1, "Hi".to_string()); + model.set_user_input(sheet, row, column + 2, "honey!".to_string()); + model.evaluate(); + + model + .delete_cells_and_shift_left(sheet, row, column, 1, 1) + .unwrap(); + + model.evaluate(); + assert_eq!( + model.get_cell_content(0, row, column - 1), + Ok("23".to_string()) + ); + + assert_eq!(model.get_cell_content(0, row, column), Ok("Hi".to_string())); + assert_eq!( + model.get_cell_content(0, row, column + 1), + Ok("honey!".to_string()) + ); +} + +#[test] +fn shift_cells_left_with_formulas() { + let mut model = new_empty_model(); + let (sheet, row, column) = (0, 5, 10); // J5 + model.set_user_input(sheet, row, column - 1, "23".to_string()); + model.set_user_input(sheet, row, column, "42".to_string()); + model.set_user_input(sheet, row, column + 1, "33".to_string()); + model.set_user_input(sheet, row, column + 2, "=K5*A5".to_string()); + + model.set_user_input(sheet, row, column + 20, "=K5*A5".to_string()); + model.evaluate(); + + model + .delete_cells_and_shift_left(sheet, row, column, 1, 1) + .unwrap(); + + model.evaluate(); + assert_eq!( + model.get_cell_content(0, row, column + 1), + Ok("=J5*A5".to_string()) + ); + assert_eq!( + model.get_cell_content(0, row, column + 19), + Ok("=J5*A5".to_string()) + ); +} diff --git a/base/src/test/test_worksheet.rs b/base/src/test/test_worksheet.rs index da0a8ec..b3fdf9c 100644 --- a/base/src/test/test_worksheet.rs +++ b/base/src/test/test_worksheet.rs @@ -10,7 +10,7 @@ use crate::{ fn test_worksheet_dimension_empty_sheet() { let model = new_empty_model(); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 1, min_column: 1, @@ -25,7 +25,7 @@ fn test_worksheet_dimension_single_cell() { let mut model = new_empty_model(); model._set("W11", "1"); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 11, min_column: 23, @@ -41,7 +41,7 @@ fn test_worksheet_dimension_single_cell_set_empty() { model._set("W11", "1"); model.cell_clear_contents(0, 11, 23).unwrap(); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 11, min_column: 23, @@ -57,7 +57,7 @@ fn test_worksheet_dimension_single_cell_deleted() { model._set("W11", "1"); model.cell_clear_all(0, 11, 23).unwrap(); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 1, min_column: 1, @@ -77,7 +77,7 @@ fn test_worksheet_dimension_multiple_cells() { model._set("B19", "1"); model.cell_clear_all(0, 11, 23).unwrap(); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 11, min_column: 2, @@ -91,7 +91,7 @@ fn test_worksheet_dimension_multiple_cells() { fn test_worksheet_dimension_progressive() { let mut model = new_empty_model(); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 1, min_column: 1, @@ -102,7 +102,7 @@ fn test_worksheet_dimension_progressive() { model.set_user_input(0, 30, 50, "Hello World".to_string()); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 30, min_column: 50, @@ -113,7 +113,7 @@ fn test_worksheet_dimension_progressive() { model.set_user_input(0, 10, 15, "Hello World".to_string()); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 10, min_column: 15, @@ -124,7 +124,7 @@ fn test_worksheet_dimension_progressive() { model.set_user_input(0, 5, 25, "Hello World".to_string()); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 5, min_column: 15, @@ -135,7 +135,7 @@ fn test_worksheet_dimension_progressive() { model.set_user_input(0, 10, 250, "Hello World".to_string()); assert_eq!( - model.workbook.worksheet(0).unwrap().dimension(), + model.workbook.worksheet(0).unwrap().get_dimension(), WorksheetDimension { min_row: 5, min_column: 15, diff --git a/base/src/test/user_model/mod.rs b/base/src/test/user_model/mod.rs index ef0cddb..0f00482 100644 --- a/base/src/test/user_model/mod.rs +++ b/base/src/test/user_model/mod.rs @@ -5,6 +5,7 @@ mod test_evaluation; mod test_general; mod test_rename_sheet; mod test_row_column; +mod test_shift_cells; mod test_styles; mod test_to_from_bytes; mod test_undo_redo; diff --git a/base/src/test/user_model/test_shift_cells.rs b/base/src/test/user_model/test_shift_cells.rs new file mode 100644 index 0000000..5121ce7 --- /dev/null +++ b/base/src/test/user_model/test_shift_cells.rs @@ -0,0 +1,23 @@ +#![allow(clippy::unwrap_used)] + +use crate::UserModel; + +#[test] +fn shift_cells_general() { + let mut model = UserModel::new_empty("model", "en", "UTC").unwrap(); + // some reference value in A1 + model.set_user_input(0, 1, 1, "42").unwrap(); + + // We put some values in row 5 + model.set_user_input(0, 5, 3, "=1 + 1").unwrap(); // C5 + model.set_user_input(0, 5, 7, "=C5*A1").unwrap(); + + // Insert one cell in C5 and push right + model.insert_cells_and_shift_right(0, 5, 3, 1, 1).unwrap(); + + // C5 should now be empty + assert_eq!(model.get_cell_content(0, 5, 3), Ok("".to_string())); + + // D5 should have 2 + assert_eq!(model.get_cell_content(0, 5, 4), Ok("=1+1".to_string())); +} diff --git a/base/src/user_model.rs b/base/src/user_model.rs index f002b2a..4985bb8 100644 --- a/base/src/user_model.rs +++ b/base/src/user_model.rs @@ -91,6 +91,36 @@ enum Diff { column: i32, old_data: Box, }, + InsertCellsShiftRight { + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + }, + InsertCellsShiftDown { + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + }, + DeleteCellsShiftLeft { + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + old_data: Vec>>, + }, + DeleteCellsShiftUp { + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + old_data: Vec>>, + }, SetFrozenRowsCount { sheet: u32, new_value: i32, @@ -713,6 +743,123 @@ impl UserModel { self.model.delete_columns(sheet, column, 1) } + /// Insert cells in the area pushing the existing ones to the right + /// + /// See also: + /// * [Model::insert_cells_and_shift_right] + pub fn insert_cells_and_shift_right( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let diff_list = vec![Diff::InsertCellsShiftRight { + sheet, + row, + column, + row_delta, + column_delta, + }]; + self.push_diff_list(diff_list); + self.model + .insert_cells_and_shift_right(sheet, row, column, row_delta, column_delta)?; + self.model.evaluate(); + Ok(()) + } + + /// Insert cells in the area pushing the existing ones down + pub fn insert_cells_and_shift_down( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let diff_list = vec![Diff::InsertCellsShiftDown { + sheet, + row, + column, + row_delta, + column_delta, + }]; + self.push_diff_list(diff_list); + self.model + .insert_cells_and_shift_down(sheet, row, column, row_delta, column_delta)?; + self.model.evaluate(); + Ok(()) + } + + /// Delete cells in the specified area and then shift cells left to fill the gap. + pub fn delete_cells_and_shift_left( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let mut old_data = Vec::new(); + let worksheet = self.model.workbook.worksheet(sheet)?; + for r in row..row + row_delta { + let mut row_data = Vec::new(); + for c in column..column + column_delta { + let cell = worksheet.cell(r, c); + row_data.push(cell.cloned()); + } + old_data.push(row_data); + } + let diff_list = vec![Diff::DeleteCellsShiftLeft { + sheet, + row, + column, + row_delta, + column_delta, + old_data, + }]; + self.push_diff_list(diff_list); + self.model + .delete_cells_and_shift_left(sheet, row, column, row_delta, column_delta)?; + self.model.evaluate(); + Ok(()) + } + + /// Delete cells in the specified area and then shift cells upward to fill the gap. + pub fn delete_cells_and_shift_up( + &mut self, + sheet: u32, + row: i32, + column: i32, + row_delta: i32, + column_delta: i32, + ) -> Result<(), String> { + let mut old_data = Vec::new(); + let worksheet = self.model.workbook.worksheet(sheet)?; + for r in row..row + row_delta { + let mut row_data = Vec::new(); + for c in column..column + column_delta { + let cell = worksheet.cell(r, c); + row_data.push(cell.cloned()); + } + old_data.push(row_data); + } + let diff_list = vec![Diff::DeleteCellsShiftUp { + sheet, + row, + column, + row_delta, + column_delta, + old_data, + }]; + self.push_diff_list(diff_list); + self.model + .delete_cells_and_shift_up(sheet, row, column, row_delta, column_delta)?; + self.model.evaluate(); + Ok(()) + } + /// Sets the width of a column /// /// See also: @@ -1098,6 +1245,94 @@ impl UserModel { } => { self.model.set_sheet_color(*index, old_value)?; } + Diff::InsertCellsShiftRight { + sheet, + row, + column, + row_delta, + column_delta, + } => { + needs_evaluation = true; + self.model.delete_cells_and_shift_left( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + } + Diff::InsertCellsShiftDown { + sheet, + row, + column, + row_delta, + column_delta, + } => { + needs_evaluation = true; + self.model.delete_cells_and_shift_up( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + } + Diff::DeleteCellsShiftLeft { + sheet, + row, + column, + row_delta, + column_delta, + old_data, + } => { + needs_evaluation = true; + // Sets old data + let worksheet = self.model.workbook.worksheet_mut(*sheet)?; + for r in *row..*row + *row_delta { + for c in *column..*column + *column_delta { + if let Some(cell) = &old_data[r as usize][c as usize] { + worksheet.update_cell(r, c, cell.clone()); + } else { + worksheet.cell_clear_contents(r, c); + } + } + } + self.model.insert_cells_and_shift_right( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + } + Diff::DeleteCellsShiftUp { + sheet, + row, + column, + row_delta, + column_delta, + old_data, + } => { + needs_evaluation = true; + // Sets old data + let worksheet = self.model.workbook.worksheet_mut(*sheet)?; + for r in *row..*row + *row_delta { + for c in *column..*column + *column_delta { + if let Some(cell) = &old_data[r as usize][c as usize] { + worksheet.update_cell(r, c, cell.clone()); + } else { + worksheet.cell_clear_contents(r, c); + } + } + } + self.model.insert_cells_and_shift_down( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + } } } if needs_evaluation { @@ -1218,6 +1453,72 @@ impl UserModel { } => { self.model.set_sheet_color(*index, new_value)?; } + Diff::InsertCellsShiftRight { + sheet, + row, + column, + row_delta, + column_delta, + } => { + self.model.insert_cells_and_shift_right( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + needs_evaluation = true; + } + Diff::InsertCellsShiftDown { + sheet, + row, + column, + row_delta, + column_delta, + } => { + self.model.insert_cells_and_shift_down( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + needs_evaluation = true; + } + Diff::DeleteCellsShiftLeft { + sheet, + row, + column, + row_delta, + column_delta, + old_data: _, + } => { + self.model.delete_cells_and_shift_left( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + needs_evaluation = true; + } + Diff::DeleteCellsShiftUp { + sheet, + row, + column, + row_delta, + column_delta, + old_data: _, + } => { + self.model.delete_cells_and_shift_up( + *sheet, + *row, + *column, + *row_delta, + *column_delta, + )?; + needs_evaluation = true; + } } } diff --git a/base/src/worksheet.rs b/base/src/worksheet.rs index e26c17c..ae46b5a 100644 --- a/base/src/worksheet.rs +++ b/base/src/worksheet.rs @@ -394,7 +394,7 @@ impl Worksheet { } /// Calculates dimension of the sheet. This function isn't cheap to calculate. - pub fn dimension(&self) -> WorksheetDimension { + pub fn get_dimension(&self) -> WorksheetDimension { // FIXME: It's probably better to just track the size as operations happen. if self.sheet_data.is_empty() { return WorksheetDimension { diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index a09aaec..629c773 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -4,7 +4,7 @@ use wasm_bindgen::{ }; use ironcalc_base::{ - expressions::{lexer::util::get_tokens as tokenizer, types::Area}, + expressions::{lexer::marked_token::get_tokens as tokenizer, types::Area}, types::CellType, UserModel as BaseModel, }; diff --git a/xlsx/src/bin/xlsx_2_icalc.rs b/xlsx/src/bin/xlsx_2_icalc.rs index d760770..7a977c1 100644 --- a/xlsx/src/bin/xlsx_2_icalc.rs +++ b/xlsx/src/bin/xlsx_2_icalc.rs @@ -1,10 +1,6 @@ -//! Tests an Excel xlsx file. -//! Returns a list of differences in json format. -//! Saves an IronCalc version -//! This is primary for QA internal testing and will be superseded by an official -//! IronCalc CLI. +//! Converts an xlsx file into the binary IronCalc format //! -//! Usage: test file.xlsx +//! Usage: xlsx_2_icalc file.xlsx use std::path; @@ -15,7 +11,6 @@ fn main() { if args.len() != 2 { panic!("Usage: {} ", args[0]); } - // first test the file let file_name = &args[1]; let file_path = path::Path::new(file_name); diff --git a/xlsx/src/export/mod.rs b/xlsx/src/export/mod.rs index 4dad2a3..443879c 100644 --- a/xlsx/src/export/mod.rs +++ b/xlsx/src/export/mod.rs @@ -108,7 +108,7 @@ pub fn save_xlsx_to_writer(model: &Model, writer: W) -> Result< .workbook .worksheet(sheet_index as u32) .unwrap() - .dimension(); + .get_dimension(); let column_min_str = number_to_column(dimension.min_column).unwrap(); let column_max_str = number_to_column(dimension.max_column).unwrap(); let min_row = dimension.min_row;