Compare commits
1 Commits
hackaton
...
feature/ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd12881972 |
@@ -69,21 +69,26 @@ impl Model {
|
|||||||
target_row: i32,
|
target_row: i32,
|
||||||
target_column: i32,
|
target_column: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let source_cell = self
|
if let Some(source_cell) = self
|
||||||
.workbook
|
.workbook
|
||||||
.worksheet(sheet)?
|
.worksheet(sheet)?
|
||||||
.cell(source_row, source_column)
|
.cell(source_row, source_column)
|
||||||
.ok_or("Expected Cell to exist")?;
|
{
|
||||||
let style = source_cell.get_style();
|
let style = source_cell.get_style();
|
||||||
// FIXME: we need some user_input getter instead of get_text
|
// FIXME: we need some user_input getter instead of get_text
|
||||||
let formula_or_value = self
|
let formula_or_value = self
|
||||||
.get_cell_formula(sheet, source_row, source_column)?
|
.get_cell_formula(sheet, source_row, source_column)?
|
||||||
.unwrap_or_else(|| source_cell.get_text(&self.workbook.shared_strings, &self.language));
|
.unwrap_or_else(|| {
|
||||||
self.set_user_input(sheet, target_row, target_column, formula_or_value);
|
source_cell.get_text(&self.workbook.shared_strings, &self.language)
|
||||||
self.workbook
|
});
|
||||||
.worksheet_mut(sheet)?
|
self.set_user_input(sheet, target_row, target_column, formula_or_value);
|
||||||
.set_cell_style(target_row, target_column, style);
|
self.workbook
|
||||||
self.cell_clear_all(sheet, source_row, source_column)?;
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +111,7 @@ impl Model {
|
|||||||
return Err("Cannot add a negative number of cells :)".to_string());
|
return Err("Cannot add a negative number of cells :)".to_string());
|
||||||
}
|
}
|
||||||
// check if it is possible:
|
// 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;
|
let last_column = dimensions.max_column + column_count;
|
||||||
if last_column > LAST_COLUMN {
|
if last_column > LAST_COLUMN {
|
||||||
return Err(
|
return Err(
|
||||||
@@ -263,7 +268,7 @@ impl Model {
|
|||||||
return Err("Cannot add a negative number of cells :)".to_string());
|
return Err("Cannot add a negative number of cells :)".to_string());
|
||||||
}
|
}
|
||||||
// Check if it is possible:
|
// 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;
|
let last_row = dimensions.max_row + row_count;
|
||||||
if last_row > LAST_ROW {
|
if last_row > LAST_ROW {
|
||||||
return Err(
|
return Err(
|
||||||
@@ -367,13 +372,162 @@ impl Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.workbook.worksheets[sheet as usize].rows = new_rows;
|
self.workbook.worksheets[sheet as usize].rows = new_rows;
|
||||||
self.displace_cells(
|
self.displace_cells(&DisplaceData::Row {
|
||||||
&(DisplaceData::Row {
|
sheet,
|
||||||
sheet,
|
row,
|
||||||
row,
|
delta: -row_count,
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::expressions::token;
|
use crate::expressions::token;
|
||||||
@@ -9,8 +11,11 @@ use super::{Lexer, LexerMode};
|
|||||||
/// A MarkedToken is a token together with its position on a formula
|
/// A MarkedToken is a token together with its position on a formula
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct MarkedToken {
|
pub struct MarkedToken {
|
||||||
|
/// Token type (see [token::TokenType])
|
||||||
pub token: token::TokenType,
|
pub token: token::TokenType,
|
||||||
|
/// Position of the start of the token (in bytes)
|
||||||
pub start: i32,
|
pub start: i32,
|
||||||
|
/// Position of the end of the token (in bytes)
|
||||||
pub end: i32,
|
pub end: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +24,7 @@ pub struct MarkedToken {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use ironcalc_base::expressions::{
|
/// use ironcalc_base::expressions::{
|
||||||
/// lexer::util::{get_tokens, MarkedToken},
|
/// lexer::marked_token::{get_tokens, MarkedToken},
|
||||||
/// token::{OpSum, TokenType},
|
/// token::{OpSum, TokenType},
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
//! A tokenizer for spreadsheet formulas.
|
//! A tokenizer for spreadsheet formulas.
|
||||||
//!
|
//!
|
||||||
//! This is meant to feed a formula parser.
|
//! This is meant to feed a formula parser.
|
||||||
@@ -7,8 +9,10 @@
|
|||||||
//! It supports two working modes:
|
//! It supports two working modes:
|
||||||
//!
|
//!
|
||||||
//! 1. A1 or display mode
|
//! 1. A1 or display mode
|
||||||
|
//!
|
||||||
//! This is for user formulas. References are like `D4`, `D$4` or `F5:T10`
|
//! This is for user formulas. References are like `D4`, `D$4` or `F5:T10`
|
||||||
//! 2. R1C1, internal or runtime mode
|
//! 2. R1C1, internal or runtime mode
|
||||||
|
//!
|
||||||
//! A reference like R1C1 refers to $A$1 and R3C4 to $D$4
|
//! 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
|
//! R[2]C[5] refers to a cell two rows below and five columns to the right
|
||||||
//! It uses the 'en' locale and language.
|
//! It uses the 'en' locale and language.
|
||||||
@@ -55,7 +59,8 @@ use super::token::{Error, TokenType};
|
|||||||
use super::types::*;
|
use super::types::*;
|
||||||
use super::utils;
|
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)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
@@ -63,17 +68,28 @@ mod test;
|
|||||||
mod ranges;
|
mod ranges;
|
||||||
mod structured_references;
|
mod structured_references;
|
||||||
|
|
||||||
|
/// This is the TokenType we return if we cannot recognize a token
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct LexerError {
|
pub struct LexerError {
|
||||||
|
/// Position of the beginning of the token in the byte string.
|
||||||
pub position: usize,
|
pub position: usize,
|
||||||
|
/// Message describing what we think the error is.
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) type Result<T> = std::result::Result<T, LexerError>;
|
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)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub enum LexerMode {
|
pub enum LexerMode {
|
||||||
|
/// Cell references are written `=S34`. This is the display mode
|
||||||
A1,
|
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,
|
R1C1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
mod test_common;
|
mod test_common;
|
||||||
mod test_language;
|
mod test_language;
|
||||||
mod test_locale;
|
mod test_locale;
|
||||||
|
mod test_marked_token;
|
||||||
mod test_ranges;
|
mod test_ranges;
|
||||||
mod test_tables;
|
mod test_tables;
|
||||||
mod test_util;
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::expressions::{
|
use crate::expressions::{
|
||||||
lexer::util::get_tokens,
|
lexer::marked_token::{get_tokens, MarkedToken},
|
||||||
token::{OpCompare, OpSum, TokenType},
|
token::{OpCompare, OpSum, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,6 +22,29 @@ fn test_get_tokens() {
|
|||||||
assert_eq!(l.end, 10);
|
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]
|
#[test]
|
||||||
fn test_simple_tokens() {
|
fn test_simple_tokens() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// public modules
|
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
/*!
|
//! # GRAMMAR
|
||||||
# GRAMAR
|
//!
|
||||||
|
//! <pre class="rust">
|
||||||
<pre class="rust">
|
//! opComp => '=' | '<' | '>' | '<=' } '>=' | '<>'
|
||||||
opComp => '=' | '<' | '>' | '<=' } '>=' | '<>'
|
//! opFactor => '*' | '/'
|
||||||
opFactor => '*' | '/'
|
//! unaryOp => '-' | '+'
|
||||||
unaryOp => '-' | '+'
|
//!
|
||||||
|
//! expr => concat (opComp concat)*
|
||||||
expr => concat (opComp concat)*
|
//! concat => term ('&' term)*
|
||||||
concat => term ('&' term)*
|
//! term => factor (opFactor factor)*
|
||||||
term => factor (opFactor factor)*
|
//! factor => prod (opProd prod)*
|
||||||
factor => prod (opProd prod)*
|
//! prod => power ('^' power)*
|
||||||
prod => power ('^' power)*
|
//! power => (unaryOp)* range '%'*
|
||||||
power => (unaryOp)* range '%'*
|
//! range => primary (':' primary)?
|
||||||
range => primary (':' primary)?
|
//! primary => '(' expr ')'
|
||||||
primary => '(' expr ')'
|
//! => number
|
||||||
=> number
|
//! => function '(' f_args ')'
|
||||||
=> function '(' f_args ')'
|
//! => name
|
||||||
=> name
|
//! => string
|
||||||
=> string
|
//! => '{' a_args '}'
|
||||||
=> '{' a_args '}'
|
//! => bool
|
||||||
=> bool
|
//! => bool()
|
||||||
=> bool()
|
//! => error
|
||||||
=> error
|
//!
|
||||||
|
//! f_args => e (',' e)*
|
||||||
f_args => e (',' e)*
|
//! </pre>
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -44,21 +42,15 @@ use super::utils::number_to_column;
|
|||||||
|
|
||||||
use token::OpCompare;
|
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 stringify;
|
||||||
pub mod walk;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod 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> {
|
pub(crate) fn parse_range(formula: &str) -> Result<(i32, i32, i32, i32), String> {
|
||||||
let mut lexer = lexer::Lexer::new(
|
let mut lexer = lexer::Lexer::new(
|
||||||
formula,
|
formula,
|
||||||
|
|||||||
@@ -1,43 +1,75 @@
|
|||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use super::{super::utils::quote_name, Node, Reference};
|
use super::{super::utils::quote_name, Node, Reference};
|
||||||
use crate::constants::{LAST_COLUMN, LAST_ROW};
|
use crate::constants::{LAST_COLUMN, LAST_ROW};
|
||||||
use crate::expressions::token::OpUnary;
|
use crate::expressions::token::OpUnary;
|
||||||
use crate::{expressions::types::CellReferenceRC, number_format::to_excel_precision_str};
|
use crate::{expressions::types::CellReferenceRC, number_format::to_excel_precision_str};
|
||||||
|
|
||||||
|
/// Displaced data
|
||||||
pub enum DisplaceData {
|
pub enum DisplaceData {
|
||||||
|
/// Displaces columns (inserting or deleting columns)
|
||||||
Column {
|
Column {
|
||||||
|
/// Sheet in which the displace data applies
|
||||||
sheet: u32,
|
sheet: u32,
|
||||||
|
/// Column from which the data is displaced
|
||||||
column: i32,
|
column: i32,
|
||||||
|
/// Number of columns displaced (might be negative, e.g. when deleting columns)
|
||||||
delta: i32,
|
delta: i32,
|
||||||
},
|
},
|
||||||
|
/// Displaces rows (Inserting or deleting rows)
|
||||||
Row {
|
Row {
|
||||||
|
/// Sheet in which the displace data applies
|
||||||
sheet: u32,
|
sheet: u32,
|
||||||
|
/// Row from which the data is displaced
|
||||||
row: i32,
|
row: i32,
|
||||||
|
/// Number of rows displaced (might be negative, e.g. when deleting rows)
|
||||||
delta: i32,
|
delta: i32,
|
||||||
},
|
},
|
||||||
CellHorizontal {
|
/// Displaces cells horizontally
|
||||||
|
ShiftCellsRight {
|
||||||
|
/// Sheet in which the displace data applies
|
||||||
sheet: u32,
|
sheet: u32,
|
||||||
|
/// Row of the to left corner
|
||||||
row: i32,
|
row: i32,
|
||||||
|
/// Column of the top left corner
|
||||||
column: i32,
|
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,
|
sheet: u32,
|
||||||
|
/// Row of the to left corner
|
||||||
row: i32,
|
row: i32,
|
||||||
|
/// Column of the top left corner
|
||||||
column: i32,
|
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 {
|
ColumnMove {
|
||||||
|
/// Sheet in which the displace data applies
|
||||||
sheet: u32,
|
sheet: u32,
|
||||||
|
/// Column that is moved
|
||||||
column: i32,
|
column: i32,
|
||||||
|
/// The position of the new column is column + delta (might be negative)
|
||||||
delta: i32,
|
delta: i32,
|
||||||
},
|
},
|
||||||
|
/// Doesn't do any cell displacement
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stringifies the AST formula in its internal R1C1 format
|
||||||
pub fn to_rc_format(node: &Node) -> String {
|
pub fn to_rc_format(node: &Node) -> String {
|
||||||
stringify(node, None, &DisplaceData::None, false)
|
stringify(node, None, &DisplaceData::None, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stringifies the formula applying the _displace_data_.
|
||||||
pub fn to_string_displaced(
|
pub fn to_string_displaced(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
context: &CellReferenceRC,
|
context: &CellReferenceRC,
|
||||||
@@ -46,10 +78,12 @@ pub fn to_string_displaced(
|
|||||||
stringify(node, Some(context), displace_data, false)
|
stringify(node, Some(context), displace_data, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stringifies a formula from the AST
|
||||||
pub fn to_string(node: &Node, context: &CellReferenceRC) -> String {
|
pub fn to_string(node: &Node, context: &CellReferenceRC) -> String {
|
||||||
stringify(node, Some(context), &DisplaceData::None, false)
|
stringify(node, Some(context), &DisplaceData::None, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stringifies the formula for Excel compatibility
|
||||||
pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String {
|
pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String {
|
||||||
stringify(node, Some(context), &DisplaceData::None, true)
|
stringify(node, Some(context), &DisplaceData::None, true)
|
||||||
}
|
}
|
||||||
@@ -116,41 +150,49 @@ pub(crate) fn stringify_reference(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DisplaceData::CellHorizontal {
|
DisplaceData::ShiftCellsRight {
|
||||||
sheet,
|
sheet,
|
||||||
row: displace_row,
|
row: displace_row,
|
||||||
column: displace_column,
|
column: displace_column,
|
||||||
delta,
|
column_delta,
|
||||||
|
row_delta,
|
||||||
} => {
|
} => {
|
||||||
if sheet_index == *sheet && displace_row == &row {
|
if sheet_index == *sheet
|
||||||
if *delta < 0 {
|
&& displace_row >= &row
|
||||||
|
&& *displace_row < row + *row_delta
|
||||||
|
{
|
||||||
|
if *column_delta < 0 {
|
||||||
if &column >= displace_column {
|
if &column >= displace_column {
|
||||||
if column < displace_column - *delta {
|
if column < displace_column - *column_delta {
|
||||||
return "#REF!".to_string();
|
return "#REF!".to_string();
|
||||||
}
|
}
|
||||||
column += *delta;
|
column += *column_delta;
|
||||||
}
|
}
|
||||||
} else if &column >= displace_column {
|
} else if &column >= displace_column {
|
||||||
column += *delta;
|
column += *column_delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DisplaceData::CellVertical {
|
DisplaceData::ShiftCellsDown {
|
||||||
sheet,
|
sheet,
|
||||||
row: displace_row,
|
row: displace_row,
|
||||||
column: displace_column,
|
column: displace_column,
|
||||||
delta,
|
row_delta,
|
||||||
|
column_delta,
|
||||||
} => {
|
} => {
|
||||||
if sheet_index == *sheet && displace_column == &column {
|
if sheet_index == *sheet
|
||||||
if *delta < 0 {
|
&& displace_column >= &column
|
||||||
|
&& *displace_column < column + *column_delta
|
||||||
|
{
|
||||||
|
if *row_delta < 0 {
|
||||||
if &row >= displace_row {
|
if &row >= displace_row {
|
||||||
if row < displace_row - *delta {
|
if row < displace_row - *row_delta {
|
||||||
return "#REF!".to_string();
|
return "#REF!".to_string();
|
||||||
}
|
}
|
||||||
row += *delta;
|
row += *row_delta;
|
||||||
}
|
}
|
||||||
} else if &row >= displace_row {
|
} 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 std::collections::HashMap;
|
||||||
|
|
||||||
use crate::expressions::lexer::LexerMode;
|
use crate::expressions::lexer::LexerMode;
|
||||||
use crate::expressions::parser::stringify::DisplaceData;
|
use crate::expressions::parser::stringify::{to_string_displaced, DisplaceData};
|
||||||
|
use crate::expressions::parser::{
|
||||||
use super::super::types::CellReferenceRC;
|
stringify::{to_rc_format, to_string},
|
||||||
use super::Parser;
|
Node, Parser,
|
||||||
use super::{
|
|
||||||
super::parser::{
|
|
||||||
stringify::{to_rc_format, to_string},
|
|
||||||
Node,
|
|
||||||
},
|
|
||||||
stringify::to_string_displaced,
|
|
||||||
};
|
};
|
||||||
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
struct Formula<'a> {
|
struct Formula<'a> {
|
||||||
initial: &'a str,
|
initial: &'a str,
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::expressions::parser::move_formula::{move_formula, MoveContext};
|
use crate::expressions::parser::move_formula::{move_formula, MoveContext};
|
||||||
use crate::expressions::types::Area;
|
use crate::expressions::parser::Parser;
|
||||||
|
use crate::expressions::types::{Area, CellReferenceRC};
|
||||||
use super::super::types::CellReferenceRC;
|
|
||||||
use super::Parser;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_move_formula() {
|
fn test_move_formula() {
|
||||||
@@ -2,9 +2,9 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::expressions::lexer::LexerMode;
|
use crate::expressions::lexer::LexerMode;
|
||||||
|
|
||||||
use super::super::parser::stringify::{to_rc_format, to_string};
|
use crate::expressions::parser::stringify::{to_rc_format, to_string};
|
||||||
use super::super::types::CellReferenceRC;
|
use crate::expressions::parser::Parser;
|
||||||
use super::Parser;
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
struct Formula<'a> {
|
struct Formula<'a> {
|
||||||
formula_a1: &'a str,
|
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::expressions::utils::{number_to_column, parse_reference_a1};
|
||||||
use crate::types::{Table, TableColumn, TableStyleInfo};
|
use crate::types::{Table, TableColumn, TableStyleInfo};
|
||||||
|
|
||||||
use super::super::types::CellReferenceRC;
|
use crate::expressions::parser::Parser;
|
||||||
use super::Parser;
|
use crate::expressions::types::CellReferenceRC;
|
||||||
|
|
||||||
fn create_test_table(
|
fn create_test_table(
|
||||||
table_name: &str,
|
table_name: &str,
|
||||||
@@ -3,7 +3,7 @@ use std::fmt;
|
|||||||
use crate::{
|
use crate::{
|
||||||
calc_result::CalcResult,
|
calc_result::CalcResult,
|
||||||
expressions::{
|
expressions::{
|
||||||
lexer::util::get_tokens,
|
lexer::marked_token::get_tokens,
|
||||||
parser::Node,
|
parser::Node,
|
||||||
token::{Error, OpSum, TokenType},
|
token::{Error, OpSum, TokenType},
|
||||||
types::CellReferenceIndex,
|
types::CellReferenceIndex,
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -229,7 +229,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
for row in row1..row2 + 1 {
|
for row in row1..row2 + 1 {
|
||||||
@@ -284,7 +284,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -292,7 +292,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
for row in row1..row2 + 1 {
|
for row in row1..row2 + 1 {
|
||||||
@@ -360,7 +360,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -368,7 +368,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
for row in row1..row2 + 1 {
|
for row in row1..row2 + 1 {
|
||||||
@@ -866,7 +866,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -874,7 +874,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
for row in row1..row2 + 1 {
|
for row in row1..row2 + 1 {
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -140,7 +140,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
for row in row1..row2 + 1 {
|
for row in row1..row2 + 1 {
|
||||||
@@ -199,7 +199,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -207,7 +207,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
for row in row1..row2 + 1 {
|
for row in row1..row2 + 1 {
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(first_range.left.sheet)
|
.worksheet(first_range.left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension();
|
.get_dimension();
|
||||||
let max_row = dimension.max_row;
|
let max_row = dimension.max_row;
|
||||||
let max_column = dimension.max_column;
|
let max_column = dimension.max_column;
|
||||||
|
|
||||||
@@ -530,7 +530,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(sum_range.left.sheet)
|
.worksheet(sum_range.left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if left_column == 1 && right_column == LAST_COLUMN {
|
if left_column == 1 && right_column == LAST_COLUMN {
|
||||||
@@ -538,7 +538,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(sum_range.left.sheet)
|
.worksheet(sum_range.left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -892,7 +892,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -900,7 +900,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
for row in row1..row2 + 1 {
|
for row in row1..row2 + 1 {
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_row;
|
.max_row;
|
||||||
}
|
}
|
||||||
if column1 == 1 && column2 == LAST_COLUMN {
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
@@ -263,7 +263,7 @@ impl Model {
|
|||||||
.workbook
|
.workbook
|
||||||
.worksheet(left.sheet)
|
.worksheet(left.sheet)
|
||||||
.expect("Sheet expected during evaluation.")
|
.expect("Sheet expected during evaluation.")
|
||||||
.dimension()
|
.get_dimension()
|
||||||
.max_column;
|
.max_column;
|
||||||
}
|
}
|
||||||
let left = CellReferenceIndex {
|
let left = CellReferenceIndex {
|
||||||
|
|||||||
@@ -1788,7 +1788,7 @@ impl Model {
|
|||||||
/// Returns markup representation of the given `sheet`.
|
/// Returns markup representation of the given `sheet`.
|
||||||
pub fn get_sheet_markup(&self, sheet: u32) -> Result<String, String> {
|
pub fn get_sheet_markup(&self, sheet: u32) -> Result<String, String> {
|
||||||
let worksheet = self.workbook.worksheet(sheet)?;
|
let worksheet = self.workbook.worksheet(sheet)?;
|
||||||
let dimension = worksheet.dimension();
|
let dimension = worksheet.get_dimension();
|
||||||
|
|
||||||
let mut rows = Vec::new();
|
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_actions;
|
||||||
mod test_binary_search;
|
mod test_binary_search;
|
||||||
mod test_cell;
|
mod test_cell;
|
||||||
@@ -8,50 +10,30 @@ mod test_criteria;
|
|||||||
mod test_currency;
|
mod test_currency;
|
||||||
mod test_date_and_time;
|
mod test_date_and_time;
|
||||||
mod test_error_propagation;
|
mod test_error_propagation;
|
||||||
mod test_fn_average;
|
mod test_escape_quotes;
|
||||||
mod test_fn_averageifs;
|
mod test_extend;
|
||||||
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_forward_references;
|
mod test_forward_references;
|
||||||
|
mod test_frozen_rows_and_columns;
|
||||||
mod test_frozen_rows_columns;
|
mod test_frozen_rows_columns;
|
||||||
mod test_general;
|
mod test_general;
|
||||||
|
mod test_get_cell_content;
|
||||||
mod test_math;
|
mod test_math;
|
||||||
mod test_metadata;
|
mod test_metadata;
|
||||||
mod test_model_cell_clear_all;
|
mod test_model_cell_clear_all;
|
||||||
mod test_model_is_empty_cell;
|
mod test_model_is_empty_cell;
|
||||||
mod test_move_formula;
|
mod test_move_formula;
|
||||||
|
mod test_number_format;
|
||||||
|
mod test_percentage;
|
||||||
mod test_quote_prefix;
|
mod test_quote_prefix;
|
||||||
mod test_set_user_input;
|
mod test_set_user_input;
|
||||||
mod test_sheet_markup;
|
mod test_sheet_markup;
|
||||||
mod test_sheets;
|
mod test_sheets;
|
||||||
|
mod test_shift_cells;
|
||||||
mod test_styles;
|
mod test_styles;
|
||||||
|
mod test_today;
|
||||||
mod test_trigonometric;
|
mod test_trigonometric;
|
||||||
|
mod test_types;
|
||||||
mod test_workbook;
|
mod test_workbook;
|
||||||
mod test_worksheet;
|
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;
|
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() {
|
fn test_worksheet_dimension_empty_sheet() {
|
||||||
let model = new_empty_model();
|
let model = new_empty_model();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 1,
|
min_row: 1,
|
||||||
min_column: 1,
|
min_column: 1,
|
||||||
@@ -25,7 +25,7 @@ fn test_worksheet_dimension_single_cell() {
|
|||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("W11", "1");
|
model._set("W11", "1");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 11,
|
min_row: 11,
|
||||||
min_column: 23,
|
min_column: 23,
|
||||||
@@ -41,7 +41,7 @@ fn test_worksheet_dimension_single_cell_set_empty() {
|
|||||||
model._set("W11", "1");
|
model._set("W11", "1");
|
||||||
model.cell_clear_contents(0, 11, 23).unwrap();
|
model.cell_clear_contents(0, 11, 23).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 11,
|
min_row: 11,
|
||||||
min_column: 23,
|
min_column: 23,
|
||||||
@@ -57,7 +57,7 @@ fn test_worksheet_dimension_single_cell_deleted() {
|
|||||||
model._set("W11", "1");
|
model._set("W11", "1");
|
||||||
model.cell_clear_all(0, 11, 23).unwrap();
|
model.cell_clear_all(0, 11, 23).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 1,
|
min_row: 1,
|
||||||
min_column: 1,
|
min_column: 1,
|
||||||
@@ -77,7 +77,7 @@ fn test_worksheet_dimension_multiple_cells() {
|
|||||||
model._set("B19", "1");
|
model._set("B19", "1");
|
||||||
model.cell_clear_all(0, 11, 23).unwrap();
|
model.cell_clear_all(0, 11, 23).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 11,
|
min_row: 11,
|
||||||
min_column: 2,
|
min_column: 2,
|
||||||
@@ -91,7 +91,7 @@ fn test_worksheet_dimension_multiple_cells() {
|
|||||||
fn test_worksheet_dimension_progressive() {
|
fn test_worksheet_dimension_progressive() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 1,
|
min_row: 1,
|
||||||
min_column: 1,
|
min_column: 1,
|
||||||
@@ -102,7 +102,7 @@ fn test_worksheet_dimension_progressive() {
|
|||||||
|
|
||||||
model.set_user_input(0, 30, 50, "Hello World".to_string());
|
model.set_user_input(0, 30, 50, "Hello World".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 30,
|
min_row: 30,
|
||||||
min_column: 50,
|
min_column: 50,
|
||||||
@@ -113,7 +113,7 @@ fn test_worksheet_dimension_progressive() {
|
|||||||
|
|
||||||
model.set_user_input(0, 10, 15, "Hello World".to_string());
|
model.set_user_input(0, 10, 15, "Hello World".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 10,
|
min_row: 10,
|
||||||
min_column: 15,
|
min_column: 15,
|
||||||
@@ -124,7 +124,7 @@ fn test_worksheet_dimension_progressive() {
|
|||||||
|
|
||||||
model.set_user_input(0, 5, 25, "Hello World".to_string());
|
model.set_user_input(0, 5, 25, "Hello World".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 5,
|
min_row: 5,
|
||||||
min_column: 15,
|
min_column: 15,
|
||||||
@@ -135,7 +135,7 @@ fn test_worksheet_dimension_progressive() {
|
|||||||
|
|
||||||
model.set_user_input(0, 10, 250, "Hello World".to_string());
|
model.set_user_input(0, 10, 250, "Hello World".to_string());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().get_dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
min_row: 5,
|
min_row: 5,
|
||||||
min_column: 15,
|
min_column: 15,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ mod test_evaluation;
|
|||||||
mod test_general;
|
mod test_general;
|
||||||
mod test_rename_sheet;
|
mod test_rename_sheet;
|
||||||
mod test_row_column;
|
mod test_row_column;
|
||||||
|
mod test_shift_cells;
|
||||||
mod test_styles;
|
mod test_styles;
|
||||||
mod test_to_from_bytes;
|
mod test_to_from_bytes;
|
||||||
mod test_undo_redo;
|
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,
|
column: i32,
|
||||||
old_data: Box<ColumnData>,
|
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 {
|
SetFrozenRowsCount {
|
||||||
sheet: u32,
|
sheet: u32,
|
||||||
new_value: i32,
|
new_value: i32,
|
||||||
@@ -713,6 +743,123 @@ impl UserModel {
|
|||||||
self.model.delete_columns(sheet, column, 1)
|
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
|
/// Sets the width of a column
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
@@ -1098,6 +1245,94 @@ impl UserModel {
|
|||||||
} => {
|
} => {
|
||||||
self.model.set_sheet_color(*index, old_value)?;
|
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 {
|
if needs_evaluation {
|
||||||
@@ -1218,6 +1453,72 @@ impl UserModel {
|
|||||||
} => {
|
} => {
|
||||||
self.model.set_sheet_color(*index, new_value)?;
|
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.
|
/// 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.
|
// FIXME: It's probably better to just track the size as operations happen.
|
||||||
if self.sheet_data.is_empty() {
|
if self.sheet_data.is_empty() {
|
||||||
return WorksheetDimension {
|
return WorksheetDimension {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use wasm_bindgen::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use ironcalc_base::{
|
use ironcalc_base::{
|
||||||
expressions::{lexer::util::get_tokens as tokenizer, types::Area},
|
expressions::{lexer::marked_token::get_tokens as tokenizer, types::Area},
|
||||||
types::CellType,
|
types::CellType,
|
||||||
UserModel as BaseModel,
|
UserModel as BaseModel,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
//! Tests an Excel xlsx file.
|
//! Converts an xlsx file into the binary IronCalc format
|
||||||
//! 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.
|
|
||||||
//!
|
//!
|
||||||
//! Usage: test file.xlsx
|
//! Usage: xlsx_2_icalc file.xlsx
|
||||||
|
|
||||||
use std::path;
|
use std::path;
|
||||||
|
|
||||||
@@ -15,7 +11,6 @@ fn main() {
|
|||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
panic!("Usage: {} <file.xlsx>", args[0]);
|
panic!("Usage: {} <file.xlsx>", args[0]);
|
||||||
}
|
}
|
||||||
// first test the file
|
|
||||||
let file_name = &args[1];
|
let file_name = &args[1];
|
||||||
|
|
||||||
let file_path = path::Path::new(file_name);
|
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
|
.workbook
|
||||||
.worksheet(sheet_index as u32)
|
.worksheet(sheet_index as u32)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.dimension();
|
.get_dimension();
|
||||||
let column_min_str = number_to_column(dimension.min_column).unwrap();
|
let column_min_str = number_to_column(dimension.min_column).unwrap();
|
||||||
let column_max_str = number_to_column(dimension.max_column).unwrap();
|
let column_max_str = number_to_column(dimension.max_column).unwrap();
|
||||||
let min_row = dimension.min_row;
|
let min_row = dimension.min_row;
|
||||||
|
|||||||
Reference in New Issue
Block a user