Skip to content

Refactor to LET — PR 3: promotable named ranges + external refs #204

@jimmytacks

Description

@jimmytacks

Part of #201

Adds the "Promote to input" section for named ranges and external refs. Defined names appear in the section with default-off Promote toggles. LAMBDA names are excluded (they're function calls, not inputs). External refs move from PR 1's auto-extracted behaviour to default-off promotable.

Scope

  • New IWorkbookContext interface: IReadOnlyDictionary<string, string> WorkbookNames mapping name → RefersTo. Lookup is OrdinalIgnoreCase. The live adapter (in RefactorCommand) populates this from workbook.Names AND the active sheet's worksheet.Names (unioned) once when the dialog opens.
  • LAMBDA-name detection: a name with RefersTo starting with =LAMBDA( (via LambdaSignatureParser.IsLambdaFormula) is excluded from the promotable list. The engine treats those identifiers as function calls and leaves them inline. Applies regardless of whether the formula text has a trailing ( after the identifier.
  • Identifier tokenizer: new helper that walks formula text (skipping strings, mirroring CellRefExtractor's SkipString rule) and yields (identifier, position) for each bare identifier that isn't a function call (not immediately followed by (). Lookbehind / trailing guards match CellRefExtractor's identifier-boundary rules to avoid splitting Help?Foo style names.
  • For each unique candidate identifier present in WorkbookNames and not a LAMBDA, emit a RefactorPromotableRow(kind: NamedRange, token: identifier, occurrences: N, promote: false).
  • External refs: change the engine so refs where FormulaRef.IsExternal == true emit as RefactorPromotableRow(kind: ExternalRef, token: original-text, occurrences: N, promote: false) instead of as inputs (PR 1 behaviour). Authors can still promote them.
  • Dialog gains a "Promote to input" section below the inputs (and above the calc-binding section). Each promotable row shows token text, occurrences count, and a Promote checkbox. Promoting moves the row into the inputs section with an auto-name and editable name field; un-promoting moves it back.
  • Rewrite step: when a named range is promoted, the rewrite pass adds the identifier to a separate rewrite map that swaps the bare name for the binding name. Fall back to a second-pass identifier rewriter rather than extending CellRefExtractor.Rewrite (which is tightly coupled to A1-shaped patterns).

Files

New:

  • Identifier tokenizer (either standalone class or internal helper in RefactorEngine.cs)

Modified:

  • addin/lambda-boss/RefactorEngine.cs
  • addin/lambda-boss/RefactorTypes.cs (add IWorkbookContext, RefactorPromotableRow)
  • addin/lambda-boss/Commands/RefactorCommand.cs (live IWorkbookContext adapter)
  • addin/lambda-boss/UI/RefactorToLetWindow.xaml / .xaml.cs (promotable section)
  • addin/lambda-boss.Tests/RefactorEngineTests.cs

Tests

  • workbook-scoped named range promoted (rewrites everywhere; binding RHS is the original identifier text)
  • worksheet-scoped named range promoted
  • LAMBDA name excluded from promotables (with and without trailing ()
  • named range left un-promoted (stays inline, no binding)
  • external ref defaults to promotable
  • external ref promoted produces a binding
  • identifier-boundary correctness (Help?Foo not split)

Manual Excel test

Define a workbook name Tax_Rate = 0.2. Type =A1 * Tax_Rate + Tax_Rate. Run /Refactor. Verify Tax_Rate appears in the promotable section with occurrences = 2. Promote it. Save. Confirm the LET evaluates.

Reference

Plan: plans/0008-refactor-to-let.md § PR 3

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions