Skip to content

This closes #2308, add RecalcOptions to scope File.Recalc to a sheet or range#2313

Open
krystophny wants to merge 4 commits into
qax-os:masterfrom
sloppy-org:2303-recalc-scope
Open

This closes #2308, add RecalcOptions to scope File.Recalc to a sheet or range#2313
krystophny wants to merge 4 commits into
qax-os:masterfrom
sloppy-org:2303-recalc-scope

Conversation

@krystophny
Copy link
Copy Markdown
Contributor

@krystophny krystophny commented Apr 23, 2026

Description

Add a variadic RecalcOptions argument to (*File).Recalc so callers can narrow the sweep:

type RecalcOptions struct {
    Sheet string // limit recalc to a single worksheet
    Ref   string // limit recalc to an A1-style range (requires Sheet)
}

func (f *File) Recalc(opts ...RecalcOptions) error

An empty RecalcOptions (the zero value, also the default when no option is passed) preserves the whole-workbook behaviour introduced in the companion PR #2312. Setting Sheet narrows to one worksheet; setting Ref (which requires Sheet) narrows further to an A1 range. Invalid combinations produce a wrapped ErrParameterRequired or a clear diagnostic pointing at the offending field.

Related Issue

Closes #2308. Depends on #2311 (RecalcCell) and #2312 (whole-workbook Recalc). The branch is stacked on #2312 and will rebase cleanly on master once that PR merges.

Documentation

Reference-site entries for this PR ship in a companion docs PR: xuri/excelize-doc#31. That PR covers the English version; other language versions follow the project's usual rollout cadence.

Motivation and Context

Bulk whole-workbook recalc is overkill when a caller knows which part of the workbook changed. For large workbooks where a mutation touches one sheet, or a single row band, walking every formula on every sheet is wasted work. Scoping is additive to the API introduced by #2303 / #2312 and does not change the default behaviour.

How Has This Been Tested

$ go test -run 'TestRecalc' -timeout 60s -v . | tail -20
=== RUN   TestRecalcNonASCIISheetName
--- PASS: TestRecalcNonASCIISheetName (0.00s)
=== RUN   TestRecalcSheetScope
--- PASS: TestRecalcSheetScope (0.00s)
=== RUN   TestRecalcRefScope
--- PASS: TestRecalcRefScope (0.00s)
=== RUN   TestRecalcOptionRejects
--- PASS: TestRecalcOptionRejects (0.00s)
PASS
ok  	github.com/xuri/excelize/v2	0.016s

Full suite: go test -skip TestZip64 -timeout 10m . passes cleanly. gofmt -s -l . and go vet ./... are clean. ARM cross-builds (GOARM=5|6|7, arm64, android/arm64) all build.

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

CalcCellValue returns a formatted string but does not touch the
stored workbook. Every SetCell* writer strips the <f> element, so
callers that need to refresh the cached value of a formula cell
without losing the formula currently have no path. Readers that
honour <v> without recomputing (LibreOffice headless, Numbers,
etc.) therefore see stale numbers after any excelize round trip.

Add (*File).RecalcCell(sheet, cell string) error. It evaluates
the formula via the existing recursive calc engine, then writes
the typed result into <v>/<t> through a private
setCellCachedValue helper that leaves <f>, the shared-formula
master, ref span, and si index intact. A new sentinel
ErrCellNoFormula is returned when the target cell has no
formula. Dependency resolution reuses calcCellValue, so chained
formulas across cells converge in one pass; circular references
are bounded by the workbook's existing MaxCalcIterations option.

Signed-off-by: Christopher Albert <albert@tugraz.at>
Users who open a workbook, mutate inputs, and save it back have
had no way to refresh the cached values their downstream readers
depend on. LibreOffice headless, Numbers, and readers that trust
<v> without recomputing therefore see stale results after any
excelize round trip.

Build (*File).Recalc on top of the single-cell RecalcCell added
in the previous commit. It walks every formula cell in the
workbook and calls RecalcCell on each, preserving <f> and shared
formula grouping while refreshing <v>/<t> through the private
setCellCachedValue helper. Dependency resolution reuses the
existing recursive calc engine, so chained formulas across
sheets converge in a single pass; the calc cache is cleared
before and after the sweep to avoid cross-call contamination.

Failures never abort the pass. Each failing cell contributes an
fmt.Errorf("<sheet>!<cell>: %w", cause) entry to an
errors.Join(...) result; cells that did compute are still
persisted. Callers can descend with errors.Is / errors.As
through the Unwrap() []error provided by the joined error.

Signed-off-by: Christopher Albert <albert@tugraz.at>
Bulk workbook recalc is overkill when a caller knows which part
of the workbook changed. For large workbooks where a mutation
touches one sheet, or a single row band, walking every formula
on every sheet is expensive.

Add a variadic RecalcOptions{Sheet, Ref} argument to File.Recalc
so callers can narrow the sweep. An empty RecalcOptions (the
zero value, also the default when no option is passed) preserves
the existing whole-workbook behaviour. Setting Sheet limits the
sweep to one worksheet; setting Ref (which requires Sheet) limits
it further to an A1-style range. Invalid combinations produce a
wrapped ErrParameterRequired or a clear diagnostic pointing at
the offending field.

Signed-off-by: Christopher Albert <albert@tugraz.at>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RecalcOptions{Sheet, Ref} to scope File.Recalc

2 participants