Skip to content

Conversation

@chhoumann
Copy link
Owner

@chhoumann chhoumann commented Dec 21, 2025

Summary

  • Preserve script-provided VALUE variables through formatting and preflight resolution.
  • Make VALUE lookup tolerant of label-scoped keys and case-only mismatches (unique only).
  • Stop quickAddApi.format() from wiping preexisting variables by restoring snapshots instead of clearing.

Problem

Scripts can set QuickAdd.variables/params.variables correctly, but subsequent Template steps still prompt for {{VALUE:...}} placeholders. Two failure modes were observed:

  1. Key mismatches introduced by label-scoped VALUE tokens (and occasional case drift).
  2. Helper formatting calls (quickAddApi.format) clearing the shared variables map.

Fix

  • VALUE lookup now resolves an existing variable key by trying:
    • exact key
    • base key for label-scoped VALUE lists
    • case-insensitive unique match
  • VALUE "concreteness" treats null as intentional; only undefined is considered missing.
  • One-page preflight uses the same resolver so it won’t re-ask already-provided values.
  • quickAddApi.format() snapshots variables and restores them when shouldClearVariables is true (preserves pre-existing script values while still cleaning up format-local additions).

Behavior changes / Notes

  • null is now treated as an explicit value (no prompt). To force prompting, set the variable to undefined or delete it.
  • Case-insensitive fallback only applies when there is a single unique match; ambiguous matches still prompt.

Test Plan

  • bun run test

Issue

Fixes #1059

Summary by CodeRabbit

  • Bug Fixes

    • Variables with null values no longer trigger input prompts; they are now treated as valid concrete values.
    • Improved variable key resolution with case-insensitive matching and base key fallback support.
    • Enhanced variable state preservation during quick add formatting operations.
  • Tests

    • Updated test expectations for null value handling in formatter.
    • Added comprehensive test coverage for variable key resolution scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
quickadd Ready Ready Preview Dec 21, 2025 0:46am

@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

Walkthrough

This PR fixes a bug where variables set in user scripts were not being passed to the template phase. It introduces key resolution logic to handle variable lookups, treats null values as concrete (non-prompting) variables, adds snapshot/restore functionality for variable state, and integrates these changes across the formatter, preflight, and API layers.

Changes

Cohort / File(s) Summary
Variable Key Resolution
src/utils/valueSyntax.ts, src/utils/valueSyntax.test.ts
Introduces resolveExistingVariableKey() function to resolve variable keys by checking exact key match, base key derivation, and case-insensitive matching. Includes internal helper findCaseInsensitiveMatch(). Adds comprehensive test coverage for exact match, labeled token fallback, case-insensitive resolution, ambiguity handling, and undefined vs. null distinction.
Formatter Integration
src/formatters/formatter.ts, src/formatters/formatter.test.ts, src/formatters/formatter-issue929-integration.test.ts
Integrates resolveExistingVariableKey into variable lookup flow; uses resolved key for all subsequent retrievals. Changes hasConcreteVariable logic to treat only undefined as non-concrete, making null values concrete. Modifies value handling to convert null to empty string when pre-set. Updates test expectations for null value behavior and script-provided null title handling.
Preflight Input Detection
src/preflight/runOnePagePreflight.ts
Updates unresolved input detection to use resolveExistingVariableKey() instead of direct undefined/null checks, aligning criteria with new key-resolution semantics.
Variable Snapshot/Restore
src/quickAddApi.ts
Adds snapshotVariables() and restoreVariables() helpers to capture and restore Map state. Modifies QuickAddApi.format() to snapshot variables when shouldClearVariables is true and restore from snapshot post-formatting instead of always clearing.

Sequence Diagram

sequenceDiagram
    participant Script as User Script
    participant QA as QuickAddApi
    participant Fmt as Formatter
    participant VS as Value Syntax
    participant Template as Templater

    Script->>QA: Set variables (e.g., title, backdrop)
    QA->>QA: Snapshot variables (if shouldClearVariables)
    QA->>Fmt: format(template, variables)
    Fmt->>Fmt: Process {{VALUE:*}} tokens
    Fmt->>VS: resolveExistingVariableKey(variables, key)
    VS->>VS: Check exact match
    alt No exact match
        VS->>VS: Derive base key, try case-insensitive
    end
    VS-->>Fmt: Return resolved key (or null)
    alt Resolved key found
        Fmt->>Fmt: Use resolved key for lookup
        Fmt->>Fmt: hasConcreteVariable (null = concrete)
        Fmt-->>QA: Substituted value (no prompt)
    else No resolved key
        Fmt-->>QA: Prompt user
    end
    alt shouldClearVariables was true
        QA->>QA: Restore from snapshot
    end
    QA-->>Template: Fully formatted template
    Template->>Template: Render note
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • New key resolution algorithm: resolveExistingVariableKey() introduces multiple matching strategies (exact, base-key, case-insensitive); verify logic handles edge cases correctly and null/undefined distinction throughout.
  • Null semantics change: Reversal of null-as-concrete treatment affects multiple codepaths; confirm all hasConcreteVariable usages and prompt-triggering logic are correctly updated.
  • Variable snapshot/restore flow: Ensure snapshot is taken at the right point and restore correctly restores state without side effects.
  • Integration consistency: Verify resolveExistingVariableKey is used uniformly across formatter.ts, preflight, and other lookups; check for any missed callsites still using raw keys.

Possibly related PRs

  • PR #1048: Introduces value-token parsing and labeled placeholders (parseValueToken), which directly precedes the key-resolution logic added in this PR; changes to variable-key handling and hasConcreteVariable logic build on those foundations.

Poem

🐰 A rabbit hops through variable lands,
Where null and undefined hold many hands,
With snapshot spells and keys resolved with care,
Variables now flow—from script to template, fair! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix VALUE variables from scripts reaching templates' clearly describes the main change: ensuring script-provided VALUE variables are properly passed through to templates.
Linked Issues check ✅ Passed All code changes address the requirements from issue #1059: VALUE lookup resolution [valueSyntax.ts], null-as-explicit-value semantics [formatter.ts], preflight integration [runOnePagePreflight.ts], and variable preservation [quickAddApi.ts].
Out of Scope Changes check ✅ Passed All changes are directly related to fixing VALUE variable resolution: new resolveExistingVariableKey function, updated formatter logic for null handling, preflight integration, and variable snapshot/restore in quickAddApi.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 1059

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/utils/valueSyntax.ts (1)

50-70: Key resolution function correctly implements the described lookup order.

The implementation follows the PR's resolution strategy:

  1. Exact key match (with non-undefined value)
  2. Base key fallback for label-scoped tokens
  3. Case-insensitive unique match

The guard for empty variableKey at line 54 prevents unnecessary processing.

Minor: Consider caching the Map.get() result

Line 61 calls both vars.has(candidate) and vars.get(candidate), resulting in two lookups. You could slightly optimize by using only get() and checking against undefined:

 for (const candidate of candidates) {
-    if (vars.has(candidate) && vars.get(candidate) !== undefined) {
+    const value = vars.get(candidate);
+    if (value !== undefined) {
         return candidate;
     }

This is a minor optimization and the current code is perfectly correct.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6c261a and d44e7ea.

📒 Files selected for processing (7)
  • src/formatters/formatter-issue929-integration.test.ts (1 hunks)
  • src/formatters/formatter.test.ts (3 hunks)
  • src/formatters/formatter.ts (6 hunks)
  • src/preflight/runOnePagePreflight.ts (2 hunks)
  • src/quickAddApi.ts (3 hunks)
  • src/utils/valueSyntax.test.ts (2 hunks)
  • src/utils/valueSyntax.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,mts,mjs,js,json}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,mts,mjs,js,json}: Use tab indentation with width 2 in TypeScript and configuration files (enforced by Biome).
Follow an 80-character line guide (enforced by Biome).

Files:

  • src/quickAddApi.ts
  • src/utils/valueSyntax.test.ts
  • src/preflight/runOnePagePreflight.ts
  • src/formatters/formatter.ts
  • src/utils/valueSyntax.ts
  • src/formatters/formatter-issue929-integration.test.ts
  • src/formatters/formatter.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use camelCase for variables and functions in TypeScript.
Prefer type-only imports in TypeScript.

Files:

  • src/quickAddApi.ts
  • src/utils/valueSyntax.test.ts
  • src/preflight/runOnePagePreflight.ts
  • src/formatters/formatter.ts
  • src/utils/valueSyntax.ts
  • src/formatters/formatter-issue929-integration.test.ts
  • src/formatters/formatter.test.ts
**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for classes and Svelte components.

Files:

  • src/quickAddApi.ts
  • src/utils/valueSyntax.test.ts
  • src/preflight/runOnePagePreflight.ts
  • src/formatters/formatter.ts
  • src/utils/valueSyntax.ts
  • src/formatters/formatter-issue929-integration.test.ts
  • src/formatters/formatter.test.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Route logging through the logger utilities for consistent output.

Files:

  • src/quickAddApi.ts
  • src/utils/valueSyntax.test.ts
  • src/preflight/runOnePagePreflight.ts
  • src/formatters/formatter.ts
  • src/utils/valueSyntax.ts
  • src/formatters/formatter-issue929-integration.test.ts
  • src/formatters/formatter.test.ts
src/{engine,services,utils}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Structure production code so Obsidian dependencies are injected behind interfaces to enable unit testing without loading real Obsidian modules.

Files:

  • src/utils/valueSyntax.test.ts
  • src/utils/valueSyntax.ts
🧬 Code graph analysis (3)
src/utils/valueSyntax.test.ts (1)
src/utils/valueSyntax.ts (2)
  • resolveExistingVariableKey (50-70)
  • buildValueVariableKey (17-25)
src/preflight/runOnePagePreflight.ts (2)
src/preflight/RequirementCollector.ts (1)
  • FieldRequirement (18-35)
src/utils/valueSyntax.ts (1)
  • resolveExistingVariableKey (50-70)
src/formatters/formatter.ts (1)
src/utils/valueSyntax.ts (1)
  • resolveExistingVariableKey (50-70)
🔇 Additional comments (16)
src/formatters/formatter-issue929-integration.test.ts (1)

172-187: Test expectations correctly reflect the new null-handling semantics.

The updated test properly validates the behavioral change where:

  • null is treated as an explicit value → converts to empty string, no prompt
  • undefined triggers fallback behavior → prompts and uses file basename

This aligns with the PR objective distinguishing null (intentional) from undefined (missing).

src/preflight/runOnePagePreflight.ts (2)

28-28: LGTM!

Import added for the new centralized key resolution utility.


238-244: Unresolved input detection now uses centralized key resolution.

The updated filter correctly leverages resolveExistingVariableKey to determine which inputs are already satisfied. This ensures:

  • Exact key matches are recognized
  • Base keys for label-scoped tokens are checked
  • Case-insensitive unique matches are honored

The negation logic (!resolveExistingVariableKey(...)) properly identifies truly unresolved inputs that need prompting.

src/quickAddApi.ts (2)

34-48: Snapshot/restore helpers correctly preserve variable state.

The implementation properly:

  • Creates a shallow copy of Map entries for snapshot
  • Clears and repopulates the Map on restore, maintaining the original reference

This approach ensures format-local variables are cleaned up while preserving pre-existing script values.


230-248: Variable restoration logic correctly fixes the clearing issue.

The snapshot-restore pattern ensures that when shouldClearVariables is true:

  1. Pre-existing variables are captured before formatting
  2. Formatting proceeds normally (may add temporary variables)
  3. Original state is restored afterward

This preserves script-provided variables across formatting calls, fixing the core issue where quickAddApi.format() was clearing the shared variables map.

src/formatters/formatter.test.ts (2)

13-18: hasConcreteVariable logic correctly updated for new semantics.

The updated implementation properly distinguishes:

  • undefined → not concrete (triggers prompt)
  • null → concrete (intentional empty value, no prompt)

The comment accurately documents this behavior change.


174-179: Test correctly reflects null as a concrete value.

The test now validates that:

  • Setting a variable to null does not trigger a prompt
  • The result is an empty string (null coerced to "")

This is consistent with the PR's behavioral change where null is treated as an explicit value.

src/utils/valueSyntax.test.ts (1)

47-84: Comprehensive test coverage for resolveExistingVariableKey.

The test suite covers all key resolution scenarios:

  • Exact key matches
  • Base key fallback for label-scoped tokens
  • Case-insensitive unique matches
  • Ambiguous case-insensitive matches returning null
  • Distinction between undefined (missing) and null (present)

These tests validate the resolution order described in the PR: exact key → base key → case-insensitive unique match.

src/utils/valueSyntax.ts (1)

33-48: Case-insensitive matching helper is well-implemented.

The function correctly:

  • Performs lowercase comparison for case-insensitivity
  • Skips undefined values (treating them as missing)
  • Returns null for ambiguous matches (multiple keys differ only by case)
  • Returns the actual key (preserving original casing) when a unique match is found
src/formatters/formatter.ts (7)

29-32: LGTM! Import additions support key resolution logic.

The new imports are used correctly throughout the file to enable variable key resolution with label-scope and case-insensitive matching.


63-68: LGTM! Null is correctly treated as a concrete value.

The updated logic ensures that only undefined is considered missing, while null, empty strings, and other falsy values are treated as intentional. This aligns with the PR objective to honor explicit null values without prompting.


153-156: LGTM! Null-to-empty-string conversion is appropriate.

The special handling for the reserved value variable correctly treats null as an explicit (non-prompting) value while converting it to an empty string for template replacement. This ensures string operations work correctly.


299-327: LGTM! Conditional prompting prevents re-asking for existing variables.

The logic correctly skips prompting when a resolved key is found, which preserves script-provided variables. When prompting is necessary, setting the value under the original variableKey ensures future lookups work correctly.


329-332: LGTM! Effective key pattern ensures correct variable lookups.

The effectiveKey correctly prioritizes the resolved key (for case/label-scoped matches) while falling back to the original key when needed. This enables consistent variable access throughout the replacement logic.


345-346: LGTM! Replacement uses the effective key consistently.

The replacement value is correctly retrieved using effectiveKey, maintaining consistency with the rawValue lookup and ensuring script-provided variables are used regardless of key casing or label-scope differences.


293-296: LGTM! Variable resolution logic correctly handles key matching.

The resolution call enables finding variables set by scripts even when keys differ by label-scope or case. For each candidate key (exact first, then base key if labeled), the function tries an exact match with undefined check, then falls back to a case-insensitive match. This properly handles edge cases: ambiguous case-insensitive matches return null, undefined values are treated as missing, and null values are allowed as valid variables.

@chhoumann chhoumann merged commit 36d43ba into master Dec 21, 2025
4 checks passed
@chhoumann chhoumann deleted the 1059 branch December 21, 2025 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Variables properties appear to not be correctly passed to the templater phase

2 participants