diff --git a/quadratic-core/src/controller/operations/clipboard.rs b/quadratic-core/src/controller/operations/clipboard.rs index b377fd7e31..94a2560c22 100644 --- a/quadratic-core/src/controller/operations/clipboard.rs +++ b/quadratic-core/src/controller/operations/clipboard.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use anyhow::{Error, Result}; +use indexmap::IndexMap; use regex::Regex; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -15,6 +16,7 @@ use crate::grid::js_types::JsClipboard; use crate::grid::js_types::JsSnackbarSeverity; use crate::grid::sheet::borders::BordersUpdates; use crate::grid::sheet::validations::validation::Validation; +use crate::grid::DataTable; use crate::grid::{CodeCellLanguage, DataTableKind}; use crate::{a1::A1Selection, CellValue, Pos, Rect, SheetPos, SheetRect}; @@ -83,6 +85,9 @@ pub struct Clipboard { #[serde(skip_serializing_if = "Option::is_none", default)] pub validations: Option, + #[serde(with = "crate::util::indexmap_serde")] + pub data_tables: IndexMap, + pub operation: ClipboardOperation, } @@ -223,7 +228,6 @@ impl GridController { |(output_rect, intersection_rect, data_table)| { let contains_source_cell = intersection_rect.contains(output_rect.min); - // there is no pasting on top of code cell output if !data_table.readonly && !contains_source_cell { let adjusted_rect = Rect::from_numbers( @@ -330,16 +334,24 @@ impl GridController { y: clipboard.origin.y + *y as i64, }; - if let Some(data_table) = sheet.data_table(source_pos) { - if matches!(data_table.kind, DataTableKind::Import(_)) { - ops.push(Operation::SetDataTable { - sheet_pos, - data_table: Some(data_table.to_owned()), - index: 0, - }); - } + if let Some(data_table) = clipboard.data_tables.get(&source_pos) { + ops.push(Operation::SetDataTable { + sheet_pos, + data_table: Some(data_table.to_owned()), + index: 0, + }); } + // if let Some(data_table) = sheet.data_table(source_pos) { + // if matches!(data_table.kind, DataTableKind::Import(_)) { + // ops.push(Operation::SetDataTable { + // sheet_pos, + // data_table: Some(data_table.to_owned()), + // index: 0, + // }); + // } + // } + ops.push(Operation::ComputeCode { sheet_pos }); }); } @@ -447,7 +459,6 @@ impl GridController { special: PasteSpecial, ) -> Result> { let error = |e, msg| Error::msg(format!("Clipboard Paste {:?}: {:?}", msg, e)); - let insert_at = selection.cursor; // use regex to find data-quadratic @@ -521,7 +532,7 @@ mod test { use serial_test::{parallel, serial}; use super::{PasteSpecial, *}; - use crate::a1::A1Selection; + use crate::a1::{A1Context, A1Selection, CellRefRange, TableRef}; use crate::controller::active_transactions::transaction_name::TransactionName; use crate::controller::user_actions::import::tests::{simple_csv, simple_csv_at}; use crate::grid::js_types::JsClipboard; @@ -737,7 +748,7 @@ mod test { #[test] #[parallel] - fn paste_clipboard_with_data_table() { + fn copy_paste_clipboard_with_data_table() { let (mut gc, sheet_id, _, _) = simple_csv(); let paste = |gc: &mut GridController, x, y, html| { gc.paste_from_clipboard( @@ -749,20 +760,73 @@ mod test { ); }; + let table_ref = TableRef::new("simple.csv"); + let cell_ref_range = CellRefRange::Table { range: table_ref }; + let context = A1Context::test( + &[("Sheet1", sheet_id)], + &[( + "simple.csv", + &["city", "region", "country", "population"], + Rect::test_a1("A1:D11"), + )], + ); + let selection = A1Selection::from_range(cell_ref_range, sheet_id, &context); + let JsClipboard { html, .. } = gc .sheet(sheet_id) - .copy_to_clipboard( - &A1Selection::from_xy(0, 0, sheet_id), - ClipboardOperation::Copy, - ) + .copy_to_clipboard(&selection, ClipboardOperation::Copy) .unwrap(); let expected_row1 = vec!["city", "region", "country", "population"]; // paste side by side - paste(&mut gc, 4, 0, html.clone()); - print_table(&gc, sheet_id, Rect::from_numbers(0, 0, 8, 10)); - assert_cell_value_row(&gc, sheet_id, 4, 0, 0, expected_row1); + paste(&mut gc, 10, 1, html.clone()); + print_table(&gc, sheet_id, Rect::from_numbers(10, 1, 4, 11)); + assert_cell_value_row(&gc, sheet_id, 10, 13, 0, expected_row1); + } + + #[test] + #[parallel] + fn cut_paste_clipboard_with_data_table() { + let (mut gc, sheet_id, _, _) = simple_csv(); + let paste = |gc: &mut GridController, x, y, html| { + gc.paste_from_clipboard( + &A1Selection::from_xy(x, y, sheet_id), + None, + Some(html), + PasteSpecial::None, + None, + ); + }; + + let table_ref = TableRef::new("simple.csv"); + let cell_ref_range = CellRefRange::Table { range: table_ref }; + let context = A1Context::test( + &[("Sheet1", sheet_id)], + &[( + "simple.csv", + &["city", "region", "country", "population"], + Rect::test_a1("A1:BV11"), + )], + ); + let selection = A1Selection::from_range(cell_ref_range, sheet_id, &context); + + // let selection = A1Selection::test_a1("A1:B4"); + + let (ops, js_clipboard) = gc.cut_to_clipboard_operations(&selection).unwrap(); + gc.start_user_transaction(ops, None, TransactionName::CutClipboard); + + // let js_clipboard = gc + // .sheet(sheet_id) + // .copy_to_clipboard(&selection, ClipboardOperation::Copy) + // .unwrap(); + + let expected_row1 = vec!["city", "region", "country", "population"]; + + // paste side by side + paste(&mut gc, 10, 1, js_clipboard.html.clone()); + print_table(&gc, sheet_id, Rect::from_numbers(10, 1, 4, 11)); + assert_cell_value_row(&gc, sheet_id, 10, 13, 0, expected_row1); } #[test] diff --git a/quadratic-core/src/controller/user_actions/clipboard.rs b/quadratic-core/src/controller/user_actions/clipboard.rs index d7314f295a..b4ec3b0d1b 100644 --- a/quadratic-core/src/controller/user_actions/clipboard.rs +++ b/quadratic-core/src/controller/user_actions/clipboard.rs @@ -16,6 +16,7 @@ impl GridController { ) -> Result { let (ops, js_clipboard) = self.cut_to_clipboard_operations(selection)?; self.start_user_transaction(ops, cursor, TransactionName::CutClipboard); + Ok(js_clipboard) } diff --git a/quadratic-core/src/grid/sheet/clipboard.rs b/quadratic-core/src/grid/sheet/clipboard.rs index 849d9dd0c5..353a03e106 100644 --- a/quadratic-core/src/grid/sheet/clipboard.rs +++ b/quadratic-core/src/grid/sheet/clipboard.rs @@ -1,3 +1,5 @@ +use indexmap::IndexMap; + use crate::a1::A1Selection; use crate::cell_values::CellValues; use crate::color::Rgba; @@ -21,10 +23,10 @@ impl Sheet { let mut html_body = String::from(""); let mut cells = CellValues::default(); let mut values = CellValues::default(); + let mut data_tables = IndexMap::new(); let mut sheet_bounds: Option = None; let context = self.a1_context(); - // TODO(ddimaria): this doesn't work properly for TableRefs if let Some(bounds) = self.selection_bounds(selection, true) { clipboard_origin.x = bounds.min.x; clipboard_origin.y = bounds.min.y; @@ -249,6 +251,11 @@ impl Sheet { // add the CellValue to cells if the code is not included in the clipboard let include_in_cells = !bounds.contains(data_table_pos); + // if the source cell is included in the clipboard, add the data_table to the clipboard + if !include_in_cells { + data_tables.insert(data_table_pos, data_table.clone()); + } + // add the code_run output to clipboard.values for y in y_start..=y_end { for x in x_start..=x_end { @@ -274,9 +281,7 @@ impl Sheet { } let formats = self.formats.to_clipboard(self, selection); - let borders = self.borders.to_clipboard(self, selection); - let validations = self .validations .to_clipboard(selection, &clipboard_origin, &context); @@ -291,6 +296,7 @@ impl Sheet { origin: clipboard_origin, selection: selection.clone(), validations, + data_tables, operation: clipboard_operation, }; diff --git a/quadratic-core/src/test_util.rs b/quadratic-core/src/test_util.rs index 2e41116c06..0b4658c85d 100644 --- a/quadratic-core/src/test_util.rs +++ b/quadratic-core/src/test_util.rs @@ -122,7 +122,7 @@ pub fn assert_cell_value_row( if let Some(cell_value) = value.get(index) { assert_display_cell_value(grid_controller, sheet_id, x, y, cell_value); } else { - println!("No value at position ({},{})", index, y); + panic!("No value at position ({},{})", index, y); } } } diff --git a/quadratic-core/src/values/cellvalue.rs b/quadratic-core/src/values/cellvalue.rs index beea75d7a1..53b2a30ddb 100644 --- a/quadratic-core/src/values/cellvalue.rs +++ b/quadratic-core/src/values/cellvalue.rs @@ -909,7 +909,6 @@ impl CellValue { pub fn code_cell_value_mut(&mut self) -> Option<&mut CodeCellValue> { match self { CellValue::Code(code) => Some(code), - CellValue::Import(_) => None, _ => None, } } diff --git a/quadratic-files/src/state/stats.rs b/quadratic-files/src/state/stats.rs index 5433c6070d..f46c2a3f84 100644 --- a/quadratic-files/src/state/stats.rs +++ b/quadratic-files/src/state/stats.rs @@ -15,8 +15,8 @@ pub(crate) struct StatsResponse { pub(crate) last_processed_file_time: String, pub(crate) last_processed_file_elapsed: String, pub(crate) files_to_process_in_pubsub: u64, - pub(crate) last_processed_transaction_time: String, - pub(crate) last_processed_transaction_elapsed: String, + pub(crate) last_truncated_transaction_time: String, + pub(crate) last_truncated_transaction_elapsed: String, pub(crate) channels_to_truncate_in_pubsub: u64, } @@ -32,8 +32,8 @@ impl From<&Stats> for StatsResponse { last_processed_file_time: to_rfc3339(stats.last_processed_file_time), last_processed_file_elapsed: last_processed_file_elapsed, files_to_process_in_pubsub: stats.files_to_process_in_pubsub, - last_processed_transaction_time: to_rfc3339(stats.last_truncated_transaction_time), - last_processed_transaction_elapsed: last_processed_transaction_elapsed, + last_truncated_transaction_time: to_rfc3339(stats.last_truncated_transaction_time), + last_truncated_transaction_elapsed: last_processed_transaction_elapsed, channels_to_truncate_in_pubsub: stats.channels_to_truncate_in_pubsub, } }