WIP: project the evaluated value; yank copies it

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-22 21:05:25 -07:00
parent 772c695d3e
commit 99797918bc
3 changed files with 16 additions and 9 deletions
@@ -282,6 +282,7 @@ fn cell_display(
numeric: matches!(value, Some(CellValue::Number(_))), numeric: matches!(value, Some(CellValue::Number(_))),
error: matches!(value, Some(CellValue::Error(_))), error: matches!(value, Some(CellValue::Error(_))),
id: cell_id(app, layout, row, col), id: cell_id(app, layout, row, col),
value: value.clone(),
}; };
} }
let key = layout.cell_key(row, col); let key = layout.cell_key(row, col);
@@ -293,6 +294,7 @@ fn cell_display(
// The *unformatted* value the TUI seeds its edit buffer from. Projecting it // The *unformatted* value the TUI seeds its edit buffer from. Projecting it
// beats a client un-formatting `text` (gotcha 47). // beats a client un-formatting `text` (gotcha 47).
edit_text: key.map(|key| app.display_value_in(layout, &key)), edit_text: key.map(|key| app.display_value_in(layout, &key)),
value: value.clone(),
} }
} }
+10 -4
View File
@@ -4,6 +4,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use improvise_core::model::cell::CellValue;
use improvise_core::model::record::{AggregateHash, RecordHash}; use improvise_core::model::record::{AggregateHash, RecordHash};
use improvise_core::view::Axis; use improvise_core::view::Axis;
use improvise_core::view::layout::PageAxis; use improvise_core::view::layout::PageAxis;
@@ -64,9 +65,9 @@ pub enum CreateKind {
Category, Category,
} }
/// One cell as the client draws it. Deliberately not a `CellValue` — the /// One cell as the client draws it. Drawn from `text`, never from `value` — the
/// formatting has already happened, and re-deriving it is how ends drift. /// formatting has already happened, and re-deriving it is how ends drift.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct CellDisplay { pub struct CellDisplay {
pub text: String, pub text: String,
/// Right-align numerics, left-align text — decided by the server, which /// Right-align numerics, left-align text — decided by the server, which
@@ -80,6 +81,9 @@ pub struct CellDisplay {
/// What an editor should start with — the value as the user last /// What an editor should start with — the value as the user last
/// **typed** it, not as it is drawn. The difference corrupts data (§3). /// **typed** it, not as it is drawn. The difference corrupts data (§3).
pub edit_text: Option<String>, pub edit_text: Option<String>,
/// The value the host evaluated here, unrounded and untyped-by-text: what
/// `yank` copies. Neither `text` nor `edit_text` can be re-parsed into it (§3).
pub value: Option<CellValue>,
} }
impl CellDisplay { impl CellDisplay {
@@ -98,6 +102,7 @@ impl CellDisplay {
numeric: true, numeric: true,
error: false, error: false,
id: None, id: None,
value: None,
} }
} }
@@ -110,6 +115,7 @@ impl CellDisplay {
error: true, error: true,
id: None, id: None,
edit_text: None, edit_text: None,
value: None,
} }
} }
@@ -416,7 +422,7 @@ fn shows(items: &[&Vec<String>], idx: usize, first_rendered: usize, level: usize
/// The grid widget's copy of the visible cells (improvise-n10), indexed by /// The grid widget's copy of the visible cells (improvise-n10), indexed by
/// position within the **viewport** and never by `CellKey`. /// position within the **viewport** and never by `CellKey`.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct GridCache { pub struct GridCache {
/// The row axis's frame: levels, label tuples and groups. **Not** a list /// The row axis's frame: levels, label tuples and groups. **Not** a list
/// of strings — see [`AxisFrame`]. /// of strings — see [`AxisFrame`].
@@ -563,7 +569,7 @@ enum LabelAxis {
/// What can change about the grid: cell values, the frames that name them, /// What can change about the grid: cell values, the frames that name them,
/// the widths, the totals, the page bar and the extent. /// the widths, the totals, the page bar and the extent.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GridUpdate { pub enum GridUpdate {
/// Display values for cells, by viewport position. /// Display values for cells, by viewport position.
Cells(Vec<((usize, usize), CellDisplay)>), Cells(Vec<((usize, usize), CellDisplay)>),
+4 -5
View File
@@ -530,12 +530,10 @@ fn composed(
state, state,
ModelCommand::ClearCell(record_cell_at_cursor(state)?), ModelCommand::ClearCell(record_cell_at_cursor(state)?),
)?]), )?]),
// `YankCell::execute`: the value, then the status. `edit_text` is // `YankCell::execute`: the value, then the status. The projected
// what the host evaluated, which is what the TUI yanks. // value, never text re-parsed: improvise-dsxq.
"yank" => Some(vec![ "yank" => Some(vec![
Command::SetYanked(typed( Command::SetYanked(cell_at_cursor(state)?.value.clone()),
cell_at_cursor(state)?.edit_text.as_deref().unwrap_or(""),
)),
Command::SetStatus("Yanked".to_string()), Command::SetStatus("Yanked".to_string()),
]), ]),
"paste" => Some(match state.view.yanked.clone() { "paste" => Some(match state.view.yanked.clone() {
@@ -3139,6 +3137,7 @@ mod tests {
#[test] #[test]
fn yy_yanks_the_cell_under_the_cursor_and_p_pastes_what_it_yanked() { fn yy_yanks_the_cell_under_the_cursor_and_p_pastes_what_it_yanked() {
let mut state = client_on(record_cell()); let mut state = client_on(record_cell());
state.grid.cells[0][0].value = Some(CellValue::Number(1200.0));
arm(&mut state, ev(KeyCode::Char('y'))); arm(&mut state, ev(KeyCode::Char('y')));
let KeyOutcome::Local(yank) = resolve(&state, ev(KeyCode::Char('y'))) else { let KeyOutcome::Local(yank) = resolve(&state, ev(KeyCode::Char('y'))) else {
panic!("`yy` is bound to yank"); panic!("`yy` is bound to yank");