Add type-based auto-alignment and formula format inference#27
Conversation
📝 WalkthroughWalkthroughAdds datetime parsing and format inference export, moves format-application logic into a shared function used by Sheet and Calculator, updates calculator to apply inferred formatting after synchronous formula evaluation, adjusts date parsing/formatting shape, and infers horizontal alignment in the grid renderer. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Verification: verify:selfResult: ✅ PASS in 119.9s
Verification: verify:integrationResult: ✅ PASS |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/sheet/src/model/calculator.ts`:
- Around line 67-69: The recalculation currently always overwrites explicit
number/currency formats by calling applyInferredFormat(cell.s, inferred); change
this so inference only seeds formatting when the cell has no explicit number
format: check the cell's style number-format fields (e.g., cell.s.nf and
cell.s.cu) before applying inference and skip calling applyInferredFormat (or
call it with a flag) if an explicit nf/cu exists; update the evaluate ->
inferInput -> applyInferredFormat flow (the evaluate, inferInput,
applyInferredFormat call sites and the cell.s object) to preserve any
user-provided nf/cu and only set inferred formatting when those fields are
empty/undefined.
In `@packages/sheet/src/model/input.ts`:
- Around line 159-166: The parseDatetime function currently only validates the
YYYY-MM-DD portion and lets invalid clock values like "99:99:99" through; update
parseDatetime to capture hours, minutes, and seconds (e.g., extend the regex to
/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/), parse Number(match[4..6])
as hour/minute/second, and explicitly reject values outside valid ranges (hour
0–23, minute 0–59, second 0–59) in addition to the existing toIsoDate(year,
month, day) check so only fully valid datetimes return the input string.
In `@packages/sheet/src/view/gridcanvas.ts`:
- Line 1241: Overflow detection uses style?.al || 'left' while
renderCellContent() uses defaultAlign(rawData, style?.nf), causing mismatched
alignment for implicit cases; update buildTextOverflowRenderData() call to use
the same computed align as renderCellContent by calling defaultAlign(rawData,
style?.nf) (or reuse the existing const align = style?.al ||
defaultAlign(rawData, style?.nf)) instead of relying on style?.al || 'left', and
pass that align into buildTextOverflowRenderData so overflow/reserved columns
match visual alignment.
- Around line 58-67: The defaultAlign function currently uses Number(rawData) to
decide right alignment, which wrongly treats leading-zero/ID-like strings (e.g.,
"00123") as numeric; change defaultAlign to reuse the same inference used by
inferInput (call or replicate inferInput(rawData)) and only right-align when the
inference indicates a numeric value (or nf === 'date'), otherwise keep left
alignment for inferred text/IDs so leading zeros are preserved; reference the
defaultAlign and inferInput symbols and ensure you import or call inferInput in
this function rather than using Number(rawData).
In `@packages/sheet/test/model/input.test.ts`:
- Around line 66-80: The new datetime parser test lacks invalid-time coverage:
add assertions to the test case in input.test.ts that verify inferInput rejects
datetime strings with invalid time components (e.g., '2025-01-01 99:99:99' and
other hour/minute/second out-of-range values) so that parseDatetime() behavior
is guarded; update the 'infers datetime (NOW() output format)' spec to include
expect(inferInput('2025-01-01 99:99:99').type).toBe('text') (and similar
assertions for seconds >59 and minutes >59) to ensure inferInput/parseDatetime
treat those as text.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d36f7bbc-10e5-4fde-b118-ba8b5849480f
📒 Files selected for processing (5)
packages/sheet/src/model/calculator.tspackages/sheet/src/model/input.tspackages/sheet/src/model/sheet.tspackages/sheet/src/view/gridcanvas.tspackages/sheet/test/model/input.test.ts
| const value = evaluate(cell.f!, grid); | ||
| const inferred = inferInput(value); | ||
| const style = applyInferredFormat(cell.s, inferred); |
There was a problem hiding this comment.
Don't clobber explicit cell formats during recalculation.
applyInferredFormat(cell.s, inferred) overwrites nf/cu every time the formula runs. That means a user-formatted formula cell can snap back to the inferred date/percent/currency format on each dependency change. This needs a guard so inference only seeds formatting when the cell does not already have an explicit number format.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/sheet/src/model/calculator.ts` around lines 67 - 69, The
recalculation currently always overwrites explicit number/currency formats by
calling applyInferredFormat(cell.s, inferred); change this so inference only
seeds formatting when the cell has no explicit number format: check the cell's
style number-format fields (e.g., cell.s.nf and cell.s.cu) before applying
inference and skip calling applyInferredFormat (or call it with a flag) if an
explicit nf/cu exists; update the evaluate -> inferInput -> applyInferredFormat
flow (the evaluate, inferInput, applyInferredFormat call sites and the cell.s
object) to preserve any user-provided nf/cu and only set inferred formatting
when those fields are empty/undefined.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/sheet/test/model/input.test.ts (1)
128-158: Consider adding test coverage for currency format.The test suite covers date, datetime, percent, and plain text scenarios, but does not test the currency branch of
applyInferredFormat(lines 326-336 in input.ts). Adding a test would ensure thenf: 'currency'andcuproperties are correctly set.Suggested test case
it('sets nf: currency and cu for currency input', () => { const result = applyInferredFormat(undefined, inferInput('$100')); expect(result).toEqual({ nf: 'currency', cu: 'USD' }); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/test/model/input.test.ts` around lines 128 - 158, Add a test covering the currency branch of applyInferredFormat: call applyInferredFormat(undefined, inferInput('$100')) and assert the returned format equals { nf: 'currency', cu: 'USD' } (use the same test file and describe block as other cases); reference the functions applyInferredFormat and inferInput and add this single it(...) case to ensure currency symbol parsing sets nf to 'currency' and cu to the expected currency code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/sheet/src/model/format.ts`:
- Around line 51-56: The datetime branch currently only checks year/month/day
against localDate and can accept invalid times; before returning { date:
localDate, hasTime: true } validate the parsed time components (e.g. hour,
minute, second — the variables used to build localDate) enforcing 0 <= hour <=
23, 0 <= minute <= 59, 0 <= second <= 59 (and millisecond if present), and if
any component is out of range do not treat it as a valid datetime (return null
or the function's failure value) instead of returning hasTime: true; update the
code around the localDate/year/month/day checks in this datetime parsing branch
(the block referencing localDate.getFullYear(), getMonth(), getDate() and
returning { date: localDate, hasTime: true }) to include these explicit bounds
checks.
- Around line 68-85: Add unit tests for safeFormatDate to cover the datetime
branch (when parseDateValue returns hasTime=true) in addition to the existing
date-only case: create tests that call safeFormatDate with valid datetime
strings (e.g., "2023-08-15T13:45:30" or equivalent input that parseDateValue
recognizes) and assert the returned string equals "YYYY-MM-DD HH:mm:ss"; include
edge cases like midnight ("00:00:00") and seconds rounding/zero-padding (e.g.,
"2023-01-05T00:00:05"), and also add tests for invalid datetime inputs that
should fall back to returning the original value; locate tests around the module
that imports safeFormatDate/parseDateValue (or in the same test file as other
format tests) and name them to indicate datetime-format coverage.
---
Nitpick comments:
In `@packages/sheet/test/model/input.test.ts`:
- Around line 128-158: Add a test covering the currency branch of
applyInferredFormat: call applyInferredFormat(undefined, inferInput('$100')) and
assert the returned format equals { nf: 'currency', cu: 'USD' } (use the same
test file and describe block as other cases); reference the functions
applyInferredFormat and inferInput and add this single it(...) case to ensure
currency symbol parsing sets nf to 'currency' and cu to the expected currency
code.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bc9584c9-4fe9-4f93-b6c6-984b770fd6d9
📒 Files selected for processing (5)
packages/sheet/src/model/calculator.tspackages/sheet/src/model/format.tspackages/sheet/src/model/input.tspackages/sheet/src/view/gridcanvas.tspackages/sheet/test/model/input.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/sheet/src/model/calculator.ts
- packages/sheet/src/view/gridcanvas.ts
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
The auto-alignment feature changes default cell rendering (e.g. numbers right-aligned), so harness screenshots need refreshing. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Summary
Formula results now inherit inferred format metadata (date, percent, currency) via inferInput + applyInferredFormat in the calculator. The rendering layer uses a new defaultAlign() function to implicitly align cells: numbers and dates right-align, booleans center-align, and text left-aligns — matching Excel/Google Sheets behavior.
Also extracts applyInferredFormat from Sheet into a standalone utility and adds datetime parsing for NOW() output format.
Why
Linked Issues
Fixes #
Verification
CI automatically posts a verification summary comment on this PR with
per-lane results for both
verify:selfandverify:integration.Skip reason (if applicable):
Risk Assessment
Notes for Reviewers
Summary by CodeRabbit
New Features
Tests