Compare commits
1 Commits
v0.5.1
...
feature/ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd12881972 |
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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},
|
||||
/// };
|
||||
///
|
||||
@@ -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<T> = std::result::Result<T, LexerError>;
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!(
|
||||
@@ -1,4 +1,3 @@
|
||||
// public modules
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod token;
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
/*!
|
||||
# GRAMAR
|
||||
|
||||
<pre class="rust">
|
||||
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)*
|
||||
</pre>
|
||||
*/
|
||||
//! # GRAMMAR
|
||||
//!
|
||||
//! <pre class="rust">
|
||||
//! 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)*
|
||||
//! </pre>
|
||||
|
||||
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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
base/src/expressions/parser/test/mod.rs
Normal file
4
base/src/expressions/parser/test/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
mod test_genertal;
|
||||
mod test_move_formula;
|
||||
mod test_ranges;
|
||||
mod test_tables;
|
||||
@@ -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,
|
||||
@@ -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() {
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1788,7 +1788,7 @@ impl Model {
|
||||
/// Returns markup representation of the given `sheet`.
|
||||
pub fn get_sheet_markup(&self, sheet: u32) -> Result<String, String> {
|
||||
let worksheet = self.workbook.worksheet(sheet)?;
|
||||
let dimension = worksheet.dimension();
|
||||
let dimension = worksheet.get_dimension();
|
||||
|
||||
let mut rows = Vec::new();
|
||||
|
||||
|
||||
18
base/src/test/functions/mod.rs
Normal file
18
base/src/test/functions/mod.rs
Normal file
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
110
base/src/test/test_shift_cells.rs
Normal file
110
base/src/test/test_shift_cells.rs
Normal file
@@ -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())
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
23
base/src/test/user_model/test_shift_cells.rs
Normal file
23
base/src/test/user_model/test_shift_cells.rs
Normal file
@@ -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()));
|
||||
}
|
||||
@@ -91,6 +91,36 @@ enum Diff {
|
||||
column: i32,
|
||||
old_data: Box<ColumnData>,
|
||||
},
|
||||
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<Vec<Option<Cell>>>,
|
||||
},
|
||||
DeleteCellsShiftUp {
|
||||
sheet: u32,
|
||||
row: i32,
|
||||
column: i32,
|
||||
row_delta: i32,
|
||||
column_delta: i32,
|
||||
old_data: Vec<Vec<Option<Cell>>>,
|
||||
},
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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: {} <file.xlsx>", args[0]);
|
||||
}
|
||||
// first test the file
|
||||
let file_name = &args[1];
|
||||
|
||||
let file_path = path::Path::new(file_name);
|
||||
|
||||
@@ -108,7 +108,7 @@ pub fn save_xlsx_to_writer<W: Write + Seek>(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;
|
||||
|
||||
Reference in New Issue
Block a user