diff --git a/crates/controller/src/api/worksheet.rs b/crates/controller/src/api/worksheet.rs index 74b1ab53..bd0c4c6c 100644 --- a/crates/controller/src/api/worksheet.rs +++ b/crates/controller/src/api/worksheet.rs @@ -981,7 +981,7 @@ impl<'a> Worksheet<'a> { let (keys, fields, random_entries) = match s { Schema::RowSchema(schema) => { let keys = schema - .get_all_key_cell_ids(id) + .get_all_key_cell_ids(id, &block_place) .into_iter() .map(|cell_id| { let idx = block_place @@ -1015,7 +1015,7 @@ impl<'a> Worksheet<'a> { } Schema::ColSchema(schema) => { let keys = schema - .get_all_key_cell_ids(id) + .get_all_key_cell_ids(id, &block_place) .into_iter() .map(|cell_id| { let idx = block_place @@ -1416,6 +1416,7 @@ impl<'a> Worksheet<'a> { let block_place = self.get_block_place(block_id)?; let (row_cnt, col_cnt) = block_place.get_block_size(); let (row_start, col_start) = self.get_block_master_cell(block_id)?; + let bp = self.controller.status.navigator.get_block_place(&self.sheet_id, &block_id)?; let schema = self .controller .status @@ -1443,7 +1444,7 @@ impl<'a> Worksheet<'a> { let (keys, fields, random_entries) = match s { Schema::RowSchema(schema) => { let keys = schema - .get_all_key_cell_ids(block_id) + .get_all_key_cell_ids(block_id, bp) .into_iter() .map(|cell_id| { let idx = block_place @@ -1474,7 +1475,7 @@ impl<'a> Worksheet<'a> { } Schema::ColSchema(schema) => { let keys = schema - .get_all_key_cell_ids(block_id) + .get_all_key_cell_ids(block_id, bp) .into_iter() .map(|cell_id| { let idx = block_place diff --git a/crates/controller/src/block_manager/schema_manager/manager.rs b/crates/controller/src/block_manager/schema_manager/manager.rs index b7b12f8e..9eeff03e 100644 --- a/crates/controller/src/block_manager/schema_manager/manager.rs +++ b/crates/controller/src/block_manager/schema_manager/manager.rs @@ -1,6 +1,8 @@ use imbl::HashMap; use logisheets_base::{BlockCellId, BlockId, SheetId}; +use crate::navigator::BlockPlace; + use super::schema::{Field, RenderId, Schema, SchemaTrait}; #[derive(Debug, Clone, Default)] @@ -30,10 +32,18 @@ impl SchemaManager { schema.partially_resolve(key, field) } - pub fn get_all_key_cell_ids(&self, ref_name: &str) -> Option<(SheetId, Vec)> { + pub fn get_all_key_cell_ids<'a, F>( + &'a self, + ref_name: &str, + f: &'a F, + ) -> Option<(SheetId, Vec)> + where + F: Fn(&'a SheetId, &'a BlockId) -> Option<&'a BlockPlace>, + { let (sheet_id, block_id) = self.refs.get(ref_name)?; let schema = self.schemas.get(&(*sheet_id, *block_id))?; - Some((*sheet_id, schema.get_all_key_cell_ids(*block_id))) + let bp = f(sheet_id, block_id)?; + Some((*sheet_id, schema.get_all_key_cell_ids(*block_id, bp))) } #[inline] diff --git a/crates/controller/src/block_manager/schema_manager/schema.rs b/crates/controller/src/block_manager/schema_manager/schema.rs index b265b248..211d9a86 100644 --- a/crates/controller/src/block_manager/schema_manager/schema.rs +++ b/crates/controller/src/block_manager/schema_manager/schema.rs @@ -1,5 +1,7 @@ use logisheets_base::{BlockCellId, BlockId, ColId, RowId}; +use crate::navigator::BlockPlace; + pub type RowSchema = FormSchema; pub type ColSchema = FormSchema; pub type RenderId = String; @@ -52,11 +54,11 @@ impl SchemaTrait for Schema { } } - fn get_all_key_cell_ids(&self, block_id: BlockId) -> Vec { + fn get_all_key_cell_ids(&self, block_id: BlockId, bp: &BlockPlace) -> Vec { match self { - Schema::RowSchema(form_schema) => form_schema.get_all_key_cell_ids(block_id), - Schema::ColSchema(form_schema) => form_schema.get_all_key_cell_ids(block_id), - Schema::RandomSchema(random_schema) => random_schema.get_all_key_cell_ids(block_id), + Schema::RowSchema(form_schema) => form_schema.get_all_key_cell_ids(block_id, bp), + Schema::ColSchema(form_schema) => form_schema.get_all_key_cell_ids(block_id, bp), + Schema::RandomSchema(random_schema) => random_schema.get_all_key_cell_ids(block_id, bp), } } @@ -73,7 +75,7 @@ pub trait SchemaTrait { fn get_render_id(&self, row: RowId, col: ColId) -> Option; fn get_ref_name(&self) -> String; fn get_all_fields(&self) -> Vec; - fn get_all_key_cell_ids(&self, block_id: BlockId) -> Vec; + fn get_all_key_cell_ids(&self, block_id: BlockId, bp: &BlockPlace) -> Vec; fn partially_resolve(&self, key: BlockCellId, field: &String) -> Option; } @@ -93,14 +95,14 @@ impl SchemaTrait for RowSchema { self.fields.iter().map(|(f, _)| f.clone()).collect() } - fn get_all_key_cell_ids(&self, block_id: BlockId) -> Vec { + fn get_all_key_cell_ids(&self, block_id: BlockId, bp: &BlockPlace) -> Vec { let key = self.key; - self.fields + bp.rows .iter() - .map(|(_, (f, _))| BlockCellId { + .map(|r| BlockCellId { block_id, - row: key, - col: *f, + row: *r, + col: key, }) .collect() } @@ -136,14 +138,14 @@ impl SchemaTrait for ColSchema { self.fields.iter().map(|(f, _)| f.clone()).collect() } - fn get_all_key_cell_ids(&self, block_id: BlockId) -> Vec { + fn get_all_key_cell_ids(&self, block_id: BlockId, bp: &BlockPlace) -> Vec { let key = self.key; - self.fields + bp.cols .iter() - .map(|(_, (f, _))| BlockCellId { + .map(|c| BlockCellId { block_id, - row: *f, - col: key, + row: key, + col: *c, }) .collect() } @@ -179,7 +181,7 @@ impl SchemaTrait for RandomSchema { .collect() } - fn get_all_key_cell_ids(&self, block_id: BlockId) -> Vec { + fn get_all_key_cell_ids(&self, block_id: BlockId, _bp: &BlockPlace) -> Vec { self.key_field .iter() .map(|(_, r, c, _)| BlockCellId { diff --git a/crates/controller/src/connectors/calc_connector.rs b/crates/controller/src/connectors/calc_connector.rs index 5b48c0ff..10982460 100644 --- a/crates/controller/src/connectors/calc_connector.rs +++ b/crates/controller/src/connectors/calc_connector.rs @@ -9,12 +9,13 @@ use logisheets_base::{ matrix_value::{cross_product_usize, MatrixValue}, Addr, CellId, CellValue, Error, FuncId, NameId, SheetId, TextId, }; -use logisheets_base::{BlockCellId, BlockRange, CubeCross, NormalRange, Range}; +use logisheets_base::{BlockCellId, BlockId, BlockRange, CubeCross, NormalRange, Range}; use logisheets_parser::ast; use crate::block_manager::schema_manager::SchemaManager; use crate::cube_manager::CubeManager; use crate::id_manager::SheetIdManager; +use crate::navigator::BlockPlace; use crate::range_manager::RangeManager; use crate::{ async_func_manager::AsyncFuncManager, @@ -549,9 +550,12 @@ where impl<'a> BlockRefTrait for CalcConnector<'a> { fn get_all_keys(&self, ref_name: &str) -> Vec<(String, SheetId, BlockCellId)> { + let f = |sheet_id: &SheetId, block_id: &BlockId| -> Option<&BlockPlace> { + self.navigator.get_block_place(sheet_id, block_id).ok() + }; let (sheet_id, block_ids) = self .block_schema_manager - .get_all_key_cell_ids(ref_name) + .get_all_key_cell_ids(ref_name, &f) .unwrap_or_default(); let text_fetcher = |id: TextId| self.text_id_manager.get_string(&id).unwrap().clone(); block_ids diff --git a/crates/controller/src/connectors/range_connector.rs b/crates/controller/src/connectors/range_connector.rs index d0dddbde..d2dace81 100644 --- a/crates/controller/src/connectors/range_connector.rs +++ b/crates/controller/src/connectors/range_connector.rs @@ -9,7 +9,6 @@ use logisheets_base::{ use crate::{ ext_book_manager::ExtBooksManager, - formula_manager::FormulaManager, id_manager::{FuncIdManager, NameIdManager, SheetIdManager, TextIdManager}, navigator::Navigator, range_manager::ctx::RangeExecCtx, @@ -24,7 +23,6 @@ pub struct RangeConnector<'a> { pub external_links_manager: &'a mut ExtBooksManager, pub navigator: &'a Navigator, pub sheet_pos_manager: &'a SheetInfoManager, - pub formula_manager: &'a FormulaManager, } impl<'a> IdFetcherTrait for RangeConnector<'a> { diff --git a/crates/controller/src/controller/executor.rs b/crates/controller/src/controller/executor.rs index 9f8d443a..34adec60 100644 --- a/crates/controller/src/controller/executor.rs +++ b/crates/controller/src/controller/executor.rs @@ -298,7 +298,6 @@ impl<'a> Executor<'a> { external_links_manager: &mut self.status.external_links_manager, navigator: &self.status.navigator, sheet_pos_manager: &self.status.sheet_info_manager, - formula_manager: &self.status.formula_manager, }; let executor = RangeExecutor::new(self.status.range_manager.clone()); executor.execute(&ctx, payload) diff --git a/crates/controller/src/edit_action/mod.rs b/crates/controller/src/edit_action/mod.rs index 46d5afa7..e670bba1 100644 --- a/crates/controller/src/edit_action/mod.rs +++ b/crates/controller/src/edit_action/mod.rs @@ -458,6 +458,12 @@ pub struct BindFormSchema { pub row: bool, } +impl From for EditPayload { + fn from(value: BindFormSchema) -> Self { + EditPayload::BindFormSchema(value) + } +} + #[derive(Debug, Clone, TS)] #[ts(file_name = "bind_random_schema.ts", builder, rename_all = "camelCase")] pub struct BindRandomSchema { @@ -467,6 +473,12 @@ pub struct BindRandomSchema { pub units: Vec, } +impl From for EditPayload { + fn from(value: BindRandomSchema) -> Self { + EditPayload::BindRandomSchema(value) + } +} + #[derive(Debug, Clone, TS)] #[ts(file_name = "random_schema_unit.ts", builder, rename_all = "camelCase")] pub struct RandomSchemaUnit { @@ -989,6 +1001,8 @@ impl Payload for LineFormatBrush {} impl Payload for EphemeralCellInput {} impl Payload for ConvertBlock {} impl Payload for UpsertFieldRenderInfo {} +impl Payload for BindFormSchema{} +impl Payload for BindRandomSchema{} #[cfg(test)] mod tests { diff --git a/crates/wasms/server/src/controller.rs b/crates/wasms/server/src/controller.rs index 0e12e4e7..5934b7ce 100644 --- a/crates/wasms/server/src/controller.rs +++ b/crates/wasms/server/src/controller.rs @@ -1,7 +1,7 @@ use logisheets_rs::{ lex_and_fmt, lex_success, AppData, AsyncCalcResult, AsyncErr, AsyncFuncResult, BasicError, - BlockId, ColId, EditAction, EditPayload, Error, PayloadsAction, RowId, RowInfo, SaveFileResult, - SheetCellId, SheetId, Workbook, + BlockId, ColId, EditAction, Error, PayloadsAction, RowId, RowInfo, SaveFileResult, SheetCellId, + SheetId, Workbook, }; use singlyton::{Singleton, SingletonUninit}; use wasm_bindgen::prelude::*; diff --git a/resources/funcs/blockref.json b/resources/funcs/blockref.json new file mode 100644 index 00000000..7c96e15d --- /dev/null +++ b/resources/funcs/blockref.json @@ -0,0 +1,10 @@ +{ + "name": "BLOCKREF", + "description": "functions.blockref.description", + "argCount": {"eq": 3}, + "args": [ + {"argName": "ref"}, + {"argName": "key"}, + {"argName": "field"} + ] +} diff --git a/resources/funcs/blockrefs.json b/resources/funcs/blockrefs.json new file mode 100644 index 00000000..edb09193 --- /dev/null +++ b/resources/funcs/blockrefs.json @@ -0,0 +1,10 @@ +{ + "name": "BLOCKREFS", + "description": "functions.blockrefs.description", + "argCount": {"eq": 3}, + "args": [ + {"argName": "ref"}, + {"argName": "keyFilter"}, + {"argName": "fieldFilter"} + ] +} diff --git a/resources/locale/en.json b/resources/locale/en.json index 5b76ad00..7efd3a2d 100644 --- a/resources/locale/en.json +++ b/resources/locale/en.json @@ -168,6 +168,12 @@ "bitrshift": { "description": "Shifts bits of a number to the right by a specified number of positions." }, + "blockref": { + "description": "Fetches a block cell value through its key and field" + }, + "blockrefs": { + "description": "Fetches block cell values whose key and field matches conditions" + }, "rand": { "description": "Returns an evenly distributed random real number greater than or equal to 0 and less than 1." }, diff --git a/tests/funcs/block_ref_data.script b/tests/funcs/block_ref_data.script new file mode 100644 index 00000000..d6154bd0 --- /dev/null +++ b/tests/funcs/block_ref_data.script @@ -0,0 +1,20 @@ +BLOCKCREATE 1 A1:C4 + +# A B(field1) C(field2) +# 1 key1 4 7 +# 2 key2 5 8 +# 3 key3 6 9 +# 4 kye4 10 11 + +INPUT A1 key1 +INPUT A2 key2 +INPUT A3 key3 +INPUT A4 kye4 +INPUT B1 4 +INPUT B2 5 +INPUT B3 6 +INPUT B4 10 +INPUT C1 7 +INPUT C2 8 +INPUT C3 9 +INPUT C4 11 \ No newline at end of file diff --git a/tests/test.rs b/tests/test.rs index d9ebc787..76ed0572 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -25,9 +25,12 @@ mod common; #[cfg(test)] mod funcs { + use glob::glob; + use logisheets::EditAction; - use crate::test_script; + use crate::{load_script, test_script}; + use logisheets_controller::edit_action::{BindFormSchema, CellInput, PayloadsAction}; #[test] fn test_funcs() { @@ -38,6 +41,46 @@ mod funcs { test_script(path) }); } + + #[test] + fn test_block_ref() { + let mut wb = load_script("tests/funcs/block_ref_data.script"); + wb.handle_action(EditAction::Payloads( + PayloadsAction::new() + .add_payload(BindFormSchema { + ref_name: "test_ref".to_string(), + sheet_idx: 0, + block_id: 1, // check it in the script + field_from: 1, + key_idx: 0, + fields: vec![String::from("field1"), String::from("field2")], + render_ids: vec![String::from("render1"), String::from("render2")], + row: true, + }) + .add_payload(CellInput { + sheet_idx: 0, + row: 10, + col: 10, + content: String::from(r#"=BLOCKREF("test_ref", "key2", "field2")"#), + }) + .add_payload(CellInput { + sheet_idx: 0, + row: 11, + col: 11, + content: String::from(r#"=SUM(BLOCKREFS("test_ref", "key*", "field2"))"#), + }), + )); + let v = wb.get_sheet_by_idx(0).unwrap().get_value(10, 10).unwrap(); + match v { + logisheets::Value::Number(v) => assert_eq!(v, 8.0), + _ => panic!("wrong result in blockref"), + } + let v = wb.get_sheet_by_idx(0).unwrap().get_value(11, 11).unwrap(); + match v { + logisheets::Value::Number(v) => assert_eq!(v, 24.0), + _ => panic!("wrong result in blockrefs"), + } + } } #[cfg(test)] mod shift;