Part of #201
Tracer-bullet slice. End-to-end loop on the simplest case: a non-LET formula with cell refs and ranges, including spill refs (A1#) distinct from their anchor (A1). Existing LETs are refused with a "coming in PR 2" message — removed in #PR2.
Scope
- Shared-infra change: add
bool IsSpilled to FormulaRef (equality + hash + DisplayAddress); CellRefExtractor.BuildFormulaRef populates it from the existing (?<spill>\#) regex group. Verify existing CellRefExtractorTests and GatherEngineTests still pass. If any /Gather test fails due to double-counting A1 vs A1#, fall back to a RefactorEngine-local key wrapper instead.
- Register
/Refactor slash command in LambdaPopup.xaml.cs. On an empty / literal active cell, popup closes silently (matches /Gather's pattern).
- New
RefactorEngine.Refactor(string formula, string activeSheet) walks via CellRefExtractor.Extract, dedupes by FormulaRef, assigns inputN auto-names, rewrites via CellRefExtractor.Rewrite, emits LET via FormulaFormatter.AppendLet.
- New
RefactorToLetWindow: original-formula header (read-only), inputs section with rename + Include checkbox + reorder (Alt+Up/Down + drag, reusing KeptRowReorderHandler), preview pane, Save/Cancel. No promotable section, no calc-binding section in this PR.
- New
Commands/RefactorCommand.cs — mirrors GatherCommand for slash-command wiring and active-cell write-back via Formula2.
- Existing LET refused via
RefactorDiagnostic of kind ExistingLet. Slash command shows the diagnostic via MessageBox.
- External refs handled in-line as inputs in this PR (same as
/Gather today). PR 3 moves them into the promotable section.
Files
New:
addin/lambda-boss/RefactorEngine.cs
addin/lambda-boss/RefactorTypes.cs
addin/lambda-boss/UI/RefactorToLetWindow.xaml
addin/lambda-boss/UI/RefactorToLetWindow.xaml.cs
addin/lambda-boss/Commands/RefactorCommand.cs
addin/lambda-boss.Tests/RefactorEngineTests.cs
addin/lambda-boss.Tests/CellRefExtractorSpillTests.cs
Modified:
addin/lambda-boss/GatherTypes.cs (add IsSpilled to FormulaRef)
addin/lambda-boss/CellRefExtractor.cs (populate IsSpilled)
addin/lambda-boss/UI/LambdaPopup.xaml.cs (register /Refactor)
Tests (RefactorEngineTests)
- single cell ref
- multiple refs deduped
- range
- spill-distinct (
A1 and A1# produce two bindings)
- sheet-qualified ref dedupe (
A1 and Sheet1!A1 collapse when active sheet is Sheet1)
- cross-sheet ref stays distinct
- rename through body
- round-trip via
LetParser.Parse + LetToLambdaBuilder.Build
Manual Excel test
Type =IF(A1<10, IF(A1>2, SUM(B1:B5), 0), SUM(B2:B6)) into a cell. Run /Refactor. Save. Confirm the resulting LET evaluates identically.
Reference
Plan: plans/0008-refactor-to-let.md § PR 1
Part of #201
Tracer-bullet slice. End-to-end loop on the simplest case: a non-LET formula with cell refs and ranges, including spill refs (
A1#) distinct from their anchor (A1). Existing LETs are refused with a "coming in PR 2" message — removed in #PR2.Scope
bool IsSpilledtoFormulaRef(equality + hash +DisplayAddress);CellRefExtractor.BuildFormulaRefpopulates it from the existing(?<spill>\#)regex group. Verify existingCellRefExtractorTestsandGatherEngineTestsstill pass. If any/Gathertest fails due to double-countingA1vsA1#, fall back to aRefactorEngine-local key wrapper instead./Refactorslash command inLambdaPopup.xaml.cs. On an empty / literal active cell, popup closes silently (matches/Gather's pattern).RefactorEngine.Refactor(string formula, string activeSheet)walks viaCellRefExtractor.Extract, dedupes byFormulaRef, assignsinputNauto-names, rewrites viaCellRefExtractor.Rewrite, emits LET viaFormulaFormatter.AppendLet.RefactorToLetWindow: original-formula header (read-only), inputs section with rename + Include checkbox + reorder (Alt+Up/Down + drag, reusingKeptRowReorderHandler), preview pane, Save/Cancel. No promotable section, no calc-binding section in this PR.Commands/RefactorCommand.cs— mirrorsGatherCommandfor slash-command wiring and active-cell write-back viaFormula2.RefactorDiagnosticof kindExistingLet. Slash command shows the diagnostic viaMessageBox./Gathertoday). PR 3 moves them into the promotable section.Files
New:
addin/lambda-boss/RefactorEngine.csaddin/lambda-boss/RefactorTypes.csaddin/lambda-boss/UI/RefactorToLetWindow.xamladdin/lambda-boss/UI/RefactorToLetWindow.xaml.csaddin/lambda-boss/Commands/RefactorCommand.csaddin/lambda-boss.Tests/RefactorEngineTests.csaddin/lambda-boss.Tests/CellRefExtractorSpillTests.csModified:
addin/lambda-boss/GatherTypes.cs(addIsSpilledtoFormulaRef)addin/lambda-boss/CellRefExtractor.cs(populateIsSpilled)addin/lambda-boss/UI/LambdaPopup.xaml.cs(register/Refactor)Tests (
RefactorEngineTests)A1andA1#produce two bindings)A1andSheet1!A1collapse when active sheet isSheet1)LetParser.Parse+LetToLambdaBuilder.BuildManual Excel test
Type
=IF(A1<10, IF(A1>2, SUM(B1:B5), 0), SUM(B2:B6))into a cell. Run/Refactor. Save. Confirm the resulting LET evaluates identically.Reference
Plan: plans/0008-refactor-to-let.md § PR 1