585f25bf78
improvise-ni6k. Twelve effects called `app.reset_cursor()`; that table is
the shape gotcha 24 names as the disease, and it had a hole the day it
was written. The observation replaces it: `view_frames` fingerprints
every view's drawn frame, `apply_effects` diffs it per model effect, and
the cursor resets when the view it is in reframes. No effect declares
anything, and `adopt_cursor_epoch` reads the same set.
FOUR THINGS THE TICKET DID NOT PREDICT, each one a fixture or a design
question rather than a line:
1. The frame fingerprint and `layout_version` answer DIFFERENT questions,
so they are two observations and not one. `AddCategory` of an empty
category moves no row or column but does change the coordinates a
client's CellKey hangs off — layout_version must bump, the cursor need
not. `view_shape`/`last_reshaped_views` (config) is untouched and still
drives `layout_version`; `view_frame`/`last_reframed_views` (drawn) is
new and drives the cursor. Unifying them broke two tests in both
directions, which is how the distinction was found.
2. The observation must run INSIDE the effect loop, not after it. At
end-of-batch it clobbered deliberate cursor placement in the same batch
— `[AddItem(measure), <place cursor>]` sent the cursor home, failing
three records-mode tests. In-loop, a later effect wins, which is the
same ordering property gotcha 45 gives `document_config`.
3. `GridLayout::new` does NOT apply `prune_empty`; `with_frozen_records`
does. A frame hash built on `new` cannot see a pruned row, which is
exactly the class improvise-7kvw is in.
4. Two per-effect no-op guards were carrying the whole weight and are now
orphaned: `effective_items` and `item_is_drawn` are deleted, along with
six inline `moved`/`drawn`/`real`/`existed` predicates.
COST, measured as the ticket required, `benches/reshape_observation.rs`
(new), one SetCell batch, same fixtures as `grid_projection`:
typical 400 cells 172µs -> 206µs (+20%)
large 9.6k cells 3.70ms -> 4.17ms (+13%)
18k cells 7.09ms -> 8.41ms (+19%)
pathological 90k 35.2ms -> 39.8ms (+13%)
The batch is dominated by `rebuild_layout`/`recompute_formulas`, not by
the fingerprint.
WHAT CLOSES AS A CONSEQUENCE: improvise-7kvw, asserted by
`paging_across_a_prune_that_drops_a_row_invalidates_the_cursor`.
AddFormula/RemoveFormula join the shared `reshaping_commands` table (a
formula target is a `_Measure` item, so it is a column — gotcha 7), each
proved reshaping by the table's own `assert_ne!` on the grid shape.
WHAT DOES NOT CLOSE, and this is the honest part: `SortData`. A pure
record reorder changes no label and no count, and records-mode `row_items`
are positional, so a row_items fingerprint cannot see it. I wrote the
mechanism (hashing `layout.records`' coords) and then REMOVED it, because
I could not build a fixture where `SortData` actually reorders anything —
records came back in key order whatever order the cells went in. Shipping
a confident comment over a mechanism I could not demonstrate is this
repo's named defect, so it is reported instead. See the hand-back.
Negative arms unchanged and still passing, including the refused-command
arm and `SetNumberFormat`; disabling the observation fails the whole
reshaping table, which is the proof it is what does the work.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
82 lines
2.5 KiB
TOML
82 lines
2.5 KiB
TOML
[workspace]
|
|
members = [".", "crates/improvise-formula", "crates/improvise-core", "crates/improvise-protocol", "crates/improvise-io", "crates/improvise-command", "crates/improvise-wasm-client", "crates/improvise-ws-server", "crates/improvise-worker-server", "crates/gp-tree", "crates/improvise-gp"]
|
|
|
|
[package]
|
|
name = "improvise"
|
|
version = "0.1.0-rc2"
|
|
edition = "2024"
|
|
description = "Terminal pivot-table modeling in the spirit of Lotus Improv"
|
|
license = "Apache-2.0"
|
|
repository = "https://github.com/fiddlerwoaroof/improvise"
|
|
homepage = "https://github.com/fiddlerwoaroof/improvise"
|
|
readme = "README.md"
|
|
keywords = ["tui", "pivot", "spreadsheet", "data", "improv"]
|
|
categories = ["command-line-utilities", "visualization"]
|
|
|
|
[[bin]]
|
|
name = "improvise"
|
|
path = "src/main.rs"
|
|
|
|
# improvise-i5tk's acceptance criterion: whether the TUI's whole-grid GridCache
|
|
# is worth narrowing to a viewport is a measurement, not an argument.
|
|
# `test = false`: it takes seconds by design, and the suite's rule is that no
|
|
# single test binary exceeds two of them.
|
|
[[bench]]
|
|
name = "grid_projection"
|
|
harness = false
|
|
test = false
|
|
|
|
[[bench]]
|
|
name = "reshape_observation"
|
|
harness = false
|
|
test = false
|
|
|
|
[dependencies]
|
|
gp-tree = { path = "crates/gp-tree" }
|
|
improvise-core = { path = "crates/improvise-core" }
|
|
improvise-formula = { path = "crates/improvise-formula" }
|
|
improvise-io = { path = "crates/improvise-io" }
|
|
improvise-command = { path = "crates/improvise-command" }
|
|
ratatui = "0.30"
|
|
crossterm = "0.29"
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
anyhow = "1"
|
|
thiserror = "2"
|
|
indexmap = { version = "2", features = ["serde"] }
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
flate2 = "1"
|
|
unicode-width = "^0.2"
|
|
csv = "1"
|
|
clap = { version = "4.6.7", features = ["derive"] }
|
|
enum_dispatch = "0.3.13"
|
|
|
|
[dev-dependencies]
|
|
# Only `tests/grid_frame_agreement.rs` needs it: that test drives the TUI
|
|
# widget and the browser client over ONE `GridCache` and asserts they draw
|
|
# the same grid frame (improvise-anr9). A dev-dependency, so the terminal
|
|
# binary does not grow a wasm client; `improvise-wasm-client` builds
|
|
# natively, which is how its own tests already run.
|
|
improvise-wasm-client = { path = "crates/improvise-wasm-client" }
|
|
improvise-protocol = { path = "crates/improvise-protocol" }
|
|
pest = "2.9.1"
|
|
pest_derive = "2.9.1"
|
|
pest_meta = "2.9.1"
|
|
proptest = "1"
|
|
tempfile = "3"
|
|
|
|
[profile.release]
|
|
opt-level = 3
|
|
lto = true
|
|
codegen-units = 1
|
|
strip = true
|
|
|
|
[profile.profiling]
|
|
inherits = "release"
|
|
strip = false
|
|
debug = 2
|
|
|
|
# The profile that 'dist' will build with
|
|
[profile.dist]
|
|
inherits = "release"
|