diff --git a/Cargo.lock b/Cargo.lock
index f7d3e8f..5783a54 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -28,15 +28,6 @@ dependencies = [
"cpufeatures",
]
-[[package]]
-name = "aho-corasick"
-version = "0.6.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -182,12 +173,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
[[package]]
name = "bitflags"
version = "2.6.0"
@@ -293,9 +278,9 @@ dependencies = [
[[package]]
name = "chrono-tz"
-version = "0.9.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb"
+checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f"
dependencies = [
"chrono",
"chrono-tz-build",
@@ -304,12 +289,11 @@ dependencies = [
[[package]]
name = "chrono-tz-build"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1"
+checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7"
dependencies = [
"parse-zoneinfo",
- "phf",
"phf_codegen",
]
@@ -431,7 +415,7 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags",
"crossterm_winapi",
"futures-core",
"mio",
@@ -483,19 +467,6 @@ dependencies = [
"memchr",
]
-[[package]]
-name = "csv-sniffer"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b8e952164bb270a505d6cb6136624174c34cfb9abd16e0011f5e53058317f39"
-dependencies = [
- "bitflags 1.3.2",
- "csv",
- "csv-core",
- "memchr",
- "regex 0.2.11",
-]
-
[[package]]
name = "csvx"
version = "0.1.17"
@@ -869,8 +840,8 @@ dependencies = [
[[package]]
name = "ironcalc"
-version = "0.2.0"
-source = "git+https://github.com/ironcalc/IronCalc#98dc557a017b2ad640fb46eece17afda14177e59"
+version = "0.3.0"
+source = "git+https://github.com/ironcalc/IronCalc#b2c5027f56a16a0c606b01a071b816b941972aef"
dependencies = [
"bitcode",
"chrono",
@@ -885,18 +856,17 @@ dependencies = [
[[package]]
name = "ironcalc_base"
-version = "0.2.0"
-source = "git+https://github.com/ironcalc/IronCalc#98dc557a017b2ad640fb46eece17afda14177e59"
+version = "0.3.0"
+source = "git+https://github.com/ironcalc/IronCalc#b2c5027f56a16a0c606b01a071b816b941972aef"
dependencies = [
"bitcode",
"chrono",
"chrono-tz",
"csv",
- "csv-sniffer",
"js-sys",
"once_cell",
"rand",
- "regex 1.11.1",
+ "regex",
"ryu",
"serde",
]
@@ -949,12 +919,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-
[[package]]
name = "libc"
version = "0.2.161"
@@ -1085,7 +1049,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
dependencies = [
- "regex 1.11.1",
+ "regex",
]
[[package]]
@@ -1251,7 +1215,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags",
"cassowary",
"compact_str",
"crossterm",
@@ -1281,20 +1245,7 @@ version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
dependencies = [
- "bitflags 2.6.0",
-]
-
-[[package]]
-name = "regex"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
-dependencies = [
- "aho-corasick 0.6.10",
- "memchr",
- "regex-syntax 0.5.6",
- "thread_local",
- "utf8-ranges",
+ "bitflags",
]
[[package]]
@@ -1303,10 +1254,10 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
- "aho-corasick 1.1.3",
+ "aho-corasick",
"memchr",
"regex-automata",
- "regex-syntax 0.8.5",
+ "regex-syntax",
]
[[package]]
@@ -1315,18 +1266,9 @@ version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
- "aho-corasick 1.1.3",
+ "aho-corasick",
"memchr",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
-dependencies = [
- "ucd-util",
+ "regex-syntax",
]
[[package]]
@@ -1370,7 +1312,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "regex 1.11.1",
+ "regex",
"relative-path",
"rustc_version",
"syn",
@@ -1398,7 +1340,7 @@ version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags",
"errno",
"libc",
"linux-raw-sys",
@@ -1636,15 +1578,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "thread_local"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
-dependencies = [
- "lazy_static",
-]
-
[[package]]
name = "time"
version = "0.3.36"
@@ -1722,12 +1655,6 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
-[[package]]
-name = "ucd-util"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abd2fc5d32b590614af8b0a20d837f32eca055edd0bbead59a9cfe80858be003"
-
[[package]]
name = "unicode-ident"
version = "1.0.13"
@@ -1763,12 +1690,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
-[[package]]
-name = "utf8-ranges"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba"
-
[[package]]
name = "utf8parse"
version = "0.2.2"
diff --git a/Cargo.toml b/Cargo.toml
index 5da5cb0..32ceef4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,13 +10,14 @@ anyhow = { version = "1.0.91", features = ["backtrace"] }
clap = { version = "4.5.20", features = ["derive"] }
crossterm = { version = "0.28.1", features = ["event-stream", "serde"] }
csvx = "0.1.17"
+# this revision introduces a way to get the Model back out of the UserModel
ironcalc = { git = "https://github.com/ironcalc/IronCalc" }
futures = "0.3.31"
ratatui = "0.29.0"
thiserror = "1.0.65"
tui-textarea = "0.7.0"
tui-prompts = "0.5.0"
-slice-utils = { git = "https://dev.zaphar.net/zaphar/slice-cursor-rs.git", ref = "main" }
+slice-utils = { git = "https://dev.zaphar.net/zaphar/slice-cursor-rs.git" }
tui-popup = "0.6.0"
serde_json = "1.0.133"
colorsys = "0.6.7"
diff --git a/result b/result
new file mode 120000
index 0000000..eacebb1
--- /dev/null
+++ b/result
@@ -0,0 +1 @@
+/nix/store/k826wsv9zc73jamdff1yl1rky2bw9lc6-sheetui-0.1.0
\ No newline at end of file
diff --git a/src/book/mod.rs b/src/book/mod.rs
index e454cd4..6651a82 100644
--- a/src/book/mod.rs
+++ b/src/book/mod.rs
@@ -3,9 +3,10 @@ use std::cmp::max;
use anyhow::{anyhow, Result};
use ironcalc::{
base::{
- types::{Border, Col, Fill, Font, Row, SheetData, Style, Worksheet},
+ expressions::types::Area,
+ types::{SheetData, Style, Worksheet},
worksheet::WorksheetDimension,
- Model,
+ Model, UserModel,
},
export::save_xlsx_to_writer,
import::load_from_xlsx,
@@ -16,7 +17,12 @@ use crate::ui::Address;
#[cfg(test)]
mod test;
-const COL_PIXELS: f64 = 5.0;
+pub(crate) const COL_PIXELS: f64 = 5.0;
+// NOTE(zaphar): This is stolen from ironcalc but ironcalc doesn't expose it
+// publically.
+pub(crate) const LAST_COLUMN: i32 = 16_384;
+pub(crate) const LAST_ROW: i32 = 1_048_576;
+
#[derive(Debug, Clone)]
pub struct AddressRange<'book> {
@@ -37,7 +43,7 @@ impl<'book> AddressRange<'book> {
}
rows
}
-
+
pub fn as_series(&self) -> Vec
{
let (row_range, col_range) = self.get_ranges();
let mut rows = Vec::with_capacity(row_range.len() * col_range.len());
@@ -78,14 +84,14 @@ impl<'book> AddressRange<'book> {
/// A spreadsheet book with some internal state tracking.
pub struct Book {
- pub(crate) model: Model,
+ pub(crate) model: UserModel,
pub current_sheet: u32,
pub location: crate::ui::Address,
}
impl Book {
/// Construct a new book from a Model
- pub fn new(model: Model) -> Self {
+ pub fn new(model: UserModel) -> Self {
Self {
model,
current_sheet: 0,
@@ -93,9 +99,17 @@ impl Book {
}
}
+ pub fn from_model(model: Model) -> Self {
+ Self::new(UserModel::from_model(model))
+ }
+
/// Construct a new book from an xlsx file.
pub fn new_from_xlsx(path: &str) -> Result {
- Ok(Self::new(load_from_xlsx(path, "en", "America/New_York")?))
+ Ok(Self::from_model(load_from_xlsx(
+ path,
+ "en",
+ "America/New_York",
+ )?))
}
/// Evaluate the spreadsheet calculating formulas and style changes.
@@ -104,10 +118,9 @@ impl Book {
self.model.evaluate();
}
- // TODO(zaphar): Should I support ICalc?
/// Construct a new book from a path.
pub fn new_from_xlsx_with_locale(path: &str, locale: &str, tz: &str) -> Result {
- Ok(Self::new(load_from_xlsx(path, locale, tz)?))
+ Ok(Self::from_model(load_from_xlsx(path, locale, tz)?))
}
/// Save book to an xlsx file.
@@ -116,7 +129,7 @@ impl Book {
let file_path = std::path::Path::new(path);
let file = std::fs::File::create(file_path)?;
let writer = std::io::BufWriter::new(file);
- save_xlsx_to_writer(&self.model, writer)?;
+ save_xlsx_to_writer(self.model.get_model(), writer)?;
Ok(())
}
@@ -124,10 +137,9 @@ impl Book {
/// is the sheet name and the u32 is the sheet index.
pub fn get_all_sheets_identifiers(&self) -> Vec<(String, u32)> {
self.model
- .workbook
- .worksheets
+ .get_worksheets_properties()
.iter()
- .map(|sheet| (sheet.get_name(), sheet.get_sheet_id()))
+ .map(|sheet| (sheet.name.to_owned(), sheet.sheet_id))
.collect()
}
@@ -136,16 +148,22 @@ impl Book {
Ok(&self.get_sheet()?.name)
}
- pub fn set_sheet_name(&mut self, idx: usize, sheet_name: &str) -> Result<()> {
- self.get_sheet_by_idx_mut(idx)?.set_name(sheet_name);
+ pub fn set_sheet_name(&mut self, idx: u32, sheet_name: &str) -> Result<()> {
+ self.model
+ .rename_sheet(idx, sheet_name)
+ .map_err(|e| anyhow!(e))?;
Ok(())
}
pub fn new_sheet(&mut self, sheet_name: Option<&str>) -> Result<()> {
- let (_, idx) = self.model.new_sheet();
+ self.model.new_sheet().map_err(|e| anyhow!(e))?;
+ let idx = self.model.get_selected_sheet();
if let Some(name) = sheet_name {
- self.set_sheet_name(idx as usize, name)?;
+ self.set_sheet_name(idx, name)?;
}
+ self.model
+ .set_selected_sheet(self.current_sheet)
+ .map_err(|e| anyhow!(e))?;
Ok(())
}
@@ -174,6 +192,7 @@ impl Book {
{
let contents = self
.model
+ .get_model()
.extend_to(
self.current_sheet,
from.row as i32,
@@ -187,7 +206,7 @@ impl Book {
self.current_sheet,
cell.row as i32,
cell.col as i32,
- contents,
+ &contents,
)
.map_err(|e| anyhow!(e))?;
}
@@ -206,32 +225,42 @@ impl Book {
pub fn clear_cell_contents(&mut self, sheet: u32, Address { row, col }: Address) -> Result<()> {
Ok(self
.model
- .cell_clear_contents(sheet, row as i32, col as i32)
+ .range_clear_contents(&Area {
+ sheet,
+ row: row as i32,
+ column: col as i32,
+ width: 1,
+ height: 1,
+ })
.map_err(|s| anyhow!("Unable to clear cell contents {}", s))?)
}
pub fn clear_cell_range(&mut self, sheet: u32, start: Address, end: Address) -> Result<()> {
- for row in start.row..=end.row {
- for col in start.col..=end.col {
- self.clear_cell_contents(sheet, Address { row, col })?;
- }
- }
+ let area = calculate_area(sheet, &start, &end);
+ self.model
+ .range_clear_contents(&area)
+ .map_err(|s| anyhow!("Unable to clear cell contents {}", s))?;
Ok(())
}
pub fn clear_cell_all(&mut self, sheet: u32, Address { row, col }: Address) -> Result<()> {
Ok(self
.model
- .cell_clear_all(sheet, row as i32, col as i32)
+ .range_clear_all(&Area {
+ sheet,
+ row: row as i32,
+ column: col as i32,
+ width: 1,
+ height: 1,
+ })
.map_err(|s| anyhow!("Unable to clear cell contents {}", s))?)
}
pub fn clear_cell_range_all(&mut self, sheet: u32, start: Address, end: Address) -> Result<()> {
- for row in start.row..=end.row {
- for col in start.col..=end.col {
- self.clear_cell_all(sheet, Address { row, col })?;
- }
- }
+ let area = calculate_area(sheet, &start, &end);
+ self.model
+ .range_clear_all(&area)
+ .map_err(|s| anyhow!("Unable to clear cell contents {}", s))?;
Ok(())
}
@@ -244,114 +273,115 @@ impl Book {
// TODO(jwall): This is modeled a little weird. We should probably record
// the error *somewhere* but for the user there is nothing to be done except
// not use a style.
- match self.model.get_style_for_cell(sheet, cell.row as i32, cell.col as i32)
+ match self
+ .model
+ .get_cell_style(sheet, cell.row as i32, cell.col as i32)
{
Err(_) => None,
Ok(s) => Some(s),
}
}
- fn get_column(&self, sheet: u32, col: usize) -> Result