feat(editor): translate column names to cell references on formula paste #308

Open
Yury wants to merge 6 commits from process-formula into version7-13
Owner

Summary

Pasting a formula that references column names by name (e.g. =A_COL * B_COL) into a grid cell now automatically translates those names into that row's actual cell references (=B4 * C4) before the value is written, so Handsontable's formulas plugin can evaluate it — covering both a normal grid-level paste and pasting directly into an already-open cell editor.

Live formula support has also been generalized: rather than being restricted to admin-designated HARDFORMULA/SOFTFORMULA columns, any character-typed column can now hold a live formula. Formulas are enabled for every table (not just ones with a formula rule), and a =-led value is only ever auto-escaped into inert text when it comes from the backend — a value the user types or pastes always evaluates as a real formula. A new "Apply as formula" context-menu action lets a user explicitly promote a backend-escaped cell to a live formula when they want to.

Intent

Column names aren't valid formula references — HyperFormula only understands cell references like B4, the same way Excel does. Without translation, a user pasting a formula written in terms of column names (the natural way to think about it, and the same convention already used for admin-defined HARDFORMULA/SOFTFORMULA rule values) would just get a #NAME? error or inert literal text. This closes that gap for the most common real path — pasting — without expanding into typed edits, where a user can just type the real reference directly.

That work surfaced a second problem: once formulas are enabled for a table at all, HyperFormula treats every =-led cell as a live formula, in any column — including backend data that merely happens to start with =. The original fix scoped live formulas to HARDFORMULA/SOFTFORMULA columns only, escaping everything else, but that also blocked a user from ever typing or pasting a real, working formula into any other column. The model here instead scopes the risk to what it actually is — backend-sourced text that looks like a formula — and leaves user input alone, since a user typing = is deliberately writing a formula. "Apply as formula" covers the case where a user wants to promote backend-seeded text after the fact.

Changes

  • substituteColumnReferences.ts (new): pure function translating
    column names in a formula-like string to row-relative cell references.
    Deliberately does not delegate to the existing parseFormulaRule (used
    for admin-defined rule values, which also substitute
    DC.USER_NAME/DC.ORIG_VALUE/DC.ROW_STATUS) — this is scoped to
    column names only, since those DC.* variables have no clear meaning for
    a value a user is pasting fresh. Reuses parseFormulaRule's
    escapeRegExpMetacharacters/substituteBoundedToken helpers (now
    exported) rather than sharing its full quoted-span-walking loop, to
    avoid risking a subtle behavior change in that already-shipped,
    reviewed function.
  • isCharacterColumn.ts (new): identifies formula-eligible columns by
    the backend's own DDTYPE === 'C' classification, replacing the earlier
    HARDFORMULA/SOFTFORMULA-only allow-list.
  • escapeCharacterColumnValue.ts (new): escapes a =-led value to
    inert text; used only for backend-sourced data, never for user input.
  • autoEscapedCellTracker.ts (new): tracks which cells the app itself
    auto-escaped, in a PK-keyed map kept separate from the row objects (so
    the marker can never leak into the submit payload) and independent of
    physical row index (so it survives insert/delete/sort). Submission and
    "Apply as formula" only ever touch cells this map confirms the app
    marked — a genuinely-literal '=... value from the backend, or one the
    user typed themselves, is never touched.
  • unescapeFormula.ts (new): strips the app's own escape marker back
    off before submission.
  • editor.component.ts:
    • hotTable.formulas is now enabled unconditionally, for every table.
    • beforePaste hook and the open-cell-editor paste listener (see
      below) now translate column names for any character column, not just
      HARDFORMULA/SOFTFORMULA ones.
    • New paste listener on the grid's root element covers pasting
      directly into an open cell editor (double-click, then paste) — a
      separate Handsontable code path that beforePaste never sees, since
      it's a native browser paste into the editor's own textarea rather
      than a grid-level paste the CopyPaste plugin intercepts. Manually
      dispatches a synthetic input event afterward, mirroring what a real
      (non-prevented) paste would have triggered, so the editor's own
      internal value-tracking picks up the change.
    • Initial load escapes backend =-led values in character columns
      (skipping HARDFORMULA/SOFTFORMULA base columns, which legitimately
      hold a live formula string) and records which cells were escaped.
    • beforeChange no longer escapes anything — it only clears a cell's
      auto-escape marker if the user edits it, since the marker no longer
      describes what's there.
    • Submission strips the auto-escape marker only from cells the app
      itself marked, regardless of whether the table has any formula rules.
    • New "Apply as formula" context-menu item, shown only when the
      selection contains an auto-escaped cell; strips just that marker,
      leaving any genuinely-literal cell in the same selection untouched.
    • Fixed a real regression hit while enabling formulas everywhere:
      Handsontable's own maxRows grid setting (a licensing cap on how many
      rows can be added) silently overrides HyperFormula's own sheet-size
      limit, breaking any table whose existing row count exceeds that cap.
      maxRows now never goes below the actual loaded row count.
  • parseFormulaRule.ts: escapeRegExpMetacharacters and
    substituteBoundedToken exported (no logic change) for reuse above.
  • Mock data (getdata.js): seeded MPE_X_FORMULA_TEST's
    PLAIN_TEXT_COL with a backend =-led value and a genuinely-literal
    '=... value, to exercise the escape/round-trip behavior.
  • Tests:
    • substituteColumnReferences.spec.ts, isCharacterColumn.spec.ts,
      escapeCharacterColumnValue.spec.ts, autoEscapedCellTracker.spec.ts,
      unescapeFormula.spec.ts (all new, TDD).
    • characterColumnFormula.integration.spec.ts (new): real
      Handsontable + HyperFormula instance tests covering escape-on-load,
      user input never being escaped, the submit round-trip, "Apply as
      formula", and a regression guard for maxRows.
    • Full existing parseFormulaRule.spec.ts suite re-verified unchanged
      as a regression check after exporting the two shared helpers.
    • editor.cy.ts: end-to-end coverage for grid-level paste and
      open-cell-editor paste (including on a table with zero formula
      rules), quoted-text protection, pasting a formula-looking value into
      a character column (now evaluates live), the backend-seeded escape
      and submit round-trip, and the "Apply as formula" menu item.
## Summary Pasting a formula that references column names by name (e.g. `=A_COL * B_COL`) into a grid cell now automatically translates those names into that row's actual cell references (`=B4 * C4`) before the value is written, so Handsontable's formulas plugin can evaluate it — covering both a normal grid-level paste and pasting directly into an already-open cell editor. Live formula support has also been generalized: rather than being restricted to admin-designated HARDFORMULA/SOFTFORMULA columns, any character-typed column can now hold a live formula. Formulas are enabled for every table (not just ones with a formula rule), and a `=`-led value is only ever auto-escaped into inert text when it comes from the backend — a value the user types or pastes always evaluates as a real formula. A new "Apply as formula" context-menu action lets a user explicitly promote a backend-escaped cell to a live formula when they want to. ## Intent Column names aren't valid formula references — HyperFormula only understands cell references like `B4`, the same way Excel does. Without translation, a user pasting a formula written in terms of column names (the natural way to think about it, and the same convention already used for admin-defined HARDFORMULA/SOFTFORMULA rule values) would just get a `#NAME?` error or inert literal text. This closes that gap for the most common real path — pasting — without expanding into typed edits, where a user can just type the real reference directly. That work surfaced a second problem: once formulas are enabled for a table at all, HyperFormula treats *every* `=`-led cell as a live formula, in any column — including backend data that merely happens to start with `=`. The original fix scoped live formulas to HARDFORMULA/SOFTFORMULA columns only, escaping everything else, but that also blocked a user from ever typing or pasting a real, working formula into any other column. The model here instead scopes the *risk* to what it actually is — backend-sourced text that looks like a formula — and leaves user input alone, since a user typing `=` is deliberately writing a formula. "Apply as formula" covers the case where a user wants to promote backend-seeded text after the fact. ## Changes - **`substituteColumnReferences.ts`** (new): pure function translating column names in a formula-like string to row-relative cell references. Deliberately does not delegate to the existing `parseFormulaRule` (used for admin-defined rule values, which also substitute `DC.USER_NAME`/`DC.ORIG_VALUE`/`DC.ROW_STATUS`) — this is scoped to column names only, since those DC.\* variables have no clear meaning for a value a user is pasting fresh. Reuses `parseFormulaRule`'s `escapeRegExpMetacharacters`/`substituteBoundedToken` helpers (now exported) rather than sharing its full quoted-span-walking loop, to avoid risking a subtle behavior change in that already-shipped, reviewed function. - **`isCharacterColumn.ts`** (new): identifies formula-eligible columns by the backend's own `DDTYPE === 'C'` classification, replacing the earlier HARDFORMULA/SOFTFORMULA-only allow-list. - **`escapeCharacterColumnValue.ts`** (new): escapes a `=`-led value to inert text; used only for backend-sourced data, never for user input. - **`autoEscapedCellTracker.ts`** (new): tracks which cells the app itself auto-escaped, in a PK-keyed map kept separate from the row objects (so the marker can never leak into the submit payload) and independent of physical row index (so it survives insert/delete/sort). Submission and "Apply as formula" only ever touch cells this map confirms the app marked — a genuinely-literal `'=...` value from the backend, or one the user typed themselves, is never touched. - **`unescapeFormula.ts`** (new): strips the app's own escape marker back off before submission. - **`editor.component.ts`**: - `hotTable.formulas` is now enabled unconditionally, for every table. - `beforePaste` hook and the open-cell-editor `paste` listener (see below) now translate column names for any character column, not just HARDFORMULA/SOFTFORMULA ones. - New `paste` listener on the grid's root element covers pasting directly into an open cell editor (double-click, then paste) — a separate Handsontable code path that `beforePaste` never sees, since it's a native browser paste into the editor's own textarea rather than a grid-level paste the CopyPaste plugin intercepts. Manually dispatches a synthetic `input` event afterward, mirroring what a real (non-prevented) paste would have triggered, so the editor's own internal value-tracking picks up the change. - Initial load escapes backend `=`-led values in character columns (skipping HARDFORMULA/SOFTFORMULA base columns, which legitimately hold a live formula string) and records which cells were escaped. - `beforeChange` no longer escapes anything — it only clears a cell's auto-escape marker if the user edits it, since the marker no longer describes what's there. - Submission strips the auto-escape marker only from cells the app itself marked, regardless of whether the table has any formula rules. - New "Apply as formula" context-menu item, shown only when the selection contains an auto-escaped cell; strips just that marker, leaving any genuinely-literal cell in the same selection untouched. - Fixed a real regression hit while enabling formulas everywhere: Handsontable's own `maxRows` grid setting (a licensing cap on how many rows can be *added*) silently overrides HyperFormula's own sheet-size limit, breaking any table whose *existing* row count exceeds that cap. `maxRows` now never goes below the actual loaded row count. - **`parseFormulaRule.ts`**: `escapeRegExpMetacharacters` and `substituteBoundedToken` exported (no logic change) for reuse above. - **Mock data** (`getdata.js`): seeded `MPE_X_FORMULA_TEST`'s `PLAIN_TEXT_COL` with a backend `=`-led value and a genuinely-literal `'=...` value, to exercise the escape/round-trip behavior. - **Tests**: - `substituteColumnReferences.spec.ts`, `isCharacterColumn.spec.ts`, `escapeCharacterColumnValue.spec.ts`, `autoEscapedCellTracker.spec.ts`, `unescapeFormula.spec.ts` (all new, TDD). - `characterColumnFormula.integration.spec.ts` (new): real Handsontable + HyperFormula instance tests covering escape-on-load, user input never being escaped, the submit round-trip, "Apply as formula", and a regression guard for `maxRows`. - Full existing `parseFormulaRule.spec.ts` suite re-verified unchanged as a regression check after exporting the two shared helpers. - `editor.cy.ts`: end-to-end coverage for grid-level paste and open-cell-editor paste (including on a table with zero formula rules), quoted-text protection, pasting a formula-looking value into a character column (now evaluates live), the backend-seeded escape and submit round-trip, and the "Apply as formula" menu item.
Yury added 1 commit 2026-08-19 09:53:54 +00:00
feat(editor): translate column names to cell references on formula paste
Build / Build-and-ng-test (pull_request) Successful in 5m48s
Lighthouse Checks / lighthouse (pull_request) Successful in 22m28s
Build / Build-and-test-development (pull_request) Successful in 26m35s
1031ea7ed7
- Paste a formula using column names (e.g. "=A_COL * B_COL") into any
  cell and have it translated to that row's cell references
  ("=B4 * C4") so HyperFormula can evaluate it - covers both a
  grid-level paste and pasting directly into an open cell editor
- Only applies on tables that already have formulas enabled (an
  existing HARDFORMULA/SOFTFORMULA column); otherwise the pasted text
  is left untouched rather than becoming inert translated text
- New substituteColumnReferences() reuses parseFormulaRule's
  boundary-matching helpers but deliberately skips DC.* variable
  substitution, which only makes sense for admin-defined rule values
Yury added 1 commit 2026-08-19 09:54:09 +00:00
Merge branch 'version7-13' into process-formula
Build / Build-and-ng-test (pull_request) Successful in 5m40s
Lighthouse Checks / lighthouse (pull_request) Successful in 22m30s
Build / Build-and-test-development (pull_request) Successful in 26m26s
772a359da7
Collaborator

Test Coverage

Services: 22/60 (37%) | Macros: 12/42 (29%) | Overall: 34/102 (33%) — run 2026-08-19T10:00Z on 772a359

PR diff


Generated by Hermes Agent

## Test Coverage **Services: 22/60 (37%) | Macros: 12/42 (29%) | Overall: 34/102 (33%)** — run `2026-08-19T10:00Z` on `772a359` [PR diff](https://git.datacontroller.io/dc/dc/pulls/308/files) --- *Generated by Hermes Agent*
hermes reviewed 2026-08-19 10:02:03 +00:00
hermes left a comment
Collaborator

Hermes Agent Code Review

Verdict: Approve (with minor suggestions) — clean, well-documented, well-tested change that correctly mirrors the already-shipped parseFormulaRule column→cell-ref mapping.

Critical

None.

Warnings

None blocking. A couple of shared limitations inherited from parseFormulaRule (same regex/guard approach) — noted for awareness, not as regressions:

  • The quoted-span regex "[^"]*"|'[^']*' in substituteColumnReferences.ts does not handle doubled-quote escaping ("" inside a quoted string, the SAS/Excel convention). A formula like ="say ""PRICE"" " would mis-segment the quoted span and could expose PRICE to substitution. This is identical to parseFormulaRule's pattern, so it's an accepted pre-existing limitation rather than a new bug — worth a follow-up issue if formulas with embedded escaped quotes are expected in pasted values.
  • If a real column is literally named like a produced cell reference (e.g. a column named B1), a prior substitution's output could be re-matched by a later column's pass. Extreme edge case; same behavior as parseFormulaRule.

Suggestions

  1. paste listener cleanup (editor.component.ts:4244): the root-element paste listener is attached once in initSetup (good — not per edit session, despite the comment mentioning edit sessions) but has no corresponding removeEventListener in ngOnDestroy. The existing mousedown listener at line 3269 follows the same no-cleanup pattern, and the rootElement is part of Angular's DOM that gets torn down with the component (so the listener is GC'd with the node), so this is consistent rather than a leak. If you ever address the mousedown one, address this in the same pass.

  2. Guard ordering in the paste listener (editor.component.ts:4248): editor.row === null is the weaker of the two guards — getActiveEditor() can return a finished-state editor whose row may not be null. The primary, reliable guard is event.target !== editor.TEXTAREA (the editor's TEXTAREA only matches the paste target while editing). Consider reordering so the event.target !== editor.TEXTAREA check reads first for clarity, though the current order is functionally safe because the target check is ANDed in the same expression.

Looks Good

  • substituteColumnReferences is a pure function with 8 focused unit cases covering the core example, row-relative refs, boundary rules (no surrounding blanks → no match), quoted-string protection, and explicit no-DC.* substitution. TDD coverage is solid.
  • The if (this.hotTable.formulas) gate correctly restricts translation to tables where HyperFormula is enabled — on formula-disabled tables the pasted text is left as an inert literal, verified by e2e test 41.
  • Column→cell-ref mapping uses this.headerColumns (which includes the appended EDIT_STATUS_COLUMN_NAME), keeping index alignment consistent with both the grid and parseFormulaRule — no off-by-one.
  • No double-translation: the open-editor paste path and the grid-level beforePaste path are mutually exclusive (native textarea paste vs. CopyPaste-plugin-intercepted paste), and the event.target !== editor.TEXTAREA guard prevents the root listener from re-processing grid-level pastes.
  • parseFormulaRule.ts change is purely additive (two export keywords) — zero logic change, and the existing parseFormulaRule.spec.ts regression-checks it.
  • coerceNumericRow runs before substitution but safely skips formula strings (isNaN("=...") is true), so no coercion corruption of pasted formulas.
  • e2e tests 40–43 cover grid-level paste, the formula-disabled no-op, quoted-text protection, and open-editor paste — the first tests in this suite to simulate a clipboard paste into Handsontable.

Reviewed by Hermes Agent

## Hermes Agent Code Review **Verdict: Approve (with minor suggestions)** — clean, well-documented, well-tested change that correctly mirrors the already-shipped `parseFormulaRule` column→cell-ref mapping. ### Critical None. ### Warnings None blocking. A couple of shared limitations inherited from `parseFormulaRule` (same regex/guard approach) — noted for awareness, not as regressions: - The quoted-span regex `"[^"]*"|'[^']*'` in `substituteColumnReferences.ts` does not handle doubled-quote escaping (`""` inside a quoted string, the SAS/Excel convention). A formula like `="say ""PRICE"" "` would mis-segment the quoted span and could expose `PRICE` to substitution. This is identical to `parseFormulaRule`'s pattern, so it's an accepted pre-existing limitation rather than a new bug — worth a follow-up issue if formulas with embedded escaped quotes are expected in pasted values. - If a real column is literally named like a produced cell reference (e.g. a column named `B1`), a prior substitution's output could be re-matched by a later column's pass. Extreme edge case; same behavior as `parseFormulaRule`. ### Suggestions 1. **`paste` listener cleanup** (`editor.component.ts:4244`): the root-element `paste` listener is attached once in `initSetup` (good — not per edit session, despite the comment mentioning edit sessions) but has no corresponding `removeEventListener` in `ngOnDestroy`. The existing `mousedown` listener at line 3269 follows the same no-cleanup pattern, and the rootElement is part of Angular's DOM that gets torn down with the component (so the listener is GC'd with the node), so this is consistent rather than a leak. If you ever address the `mousedown` one, address this in the same pass. 2. **Guard ordering in the `paste` listener** (`editor.component.ts:4248`): `editor.row === null` is the weaker of the two guards — `getActiveEditor()` can return a finished-state editor whose `row` may not be `null`. The primary, reliable guard is `event.target !== editor.TEXTAREA` (the editor's TEXTAREA only matches the paste target while editing). Consider reordering so the `event.target !== editor.TEXTAREA` check reads first for clarity, though the current order is functionally safe because the target check is ANDed in the same expression. ### Looks Good - `substituteColumnReferences` is a pure function with 8 focused unit cases covering the core example, row-relative refs, boundary rules (no surrounding blanks → no match), quoted-string protection, and explicit no-`DC.*` substitution. TDD coverage is solid. - The `if (this.hotTable.formulas)` gate correctly restricts translation to tables where HyperFormula is enabled — on formula-disabled tables the pasted text is left as an inert literal, verified by e2e test 41. - Column→cell-ref mapping uses `this.headerColumns` (which includes the appended `EDIT_STATUS_COLUMN_NAME`), keeping index alignment consistent with both the grid and `parseFormulaRule` — no off-by-one. - No double-translation: the open-editor `paste` path and the grid-level `beforePaste` path are mutually exclusive (native textarea paste vs. CopyPaste-plugin-intercepted paste), and the `event.target !== editor.TEXTAREA` guard prevents the root listener from re-processing grid-level pastes. - `parseFormulaRule.ts` change is purely additive (two `export` keywords) — zero logic change, and the existing `parseFormulaRule.spec.ts` regression-checks it. - `coerceNumericRow` runs before substitution but safely skips formula strings (`isNaN("=...")` is true), so no coercion corruption of pasted formulas. - e2e tests 40–43 cover grid-level paste, the formula-disabled no-op, quoted-text protection, and open-editor paste — the first tests in this suite to simulate a clipboard paste into Handsontable. Reviewed by Hermes Agent
@@ -4215,0 +4241,4 @@
// element (paste events bubble) rather than attaching a new listener
// per edit session, to avoid accumulating listeners across repeated
// edits.
hot.rootElement.addEventListener('paste', (event: ClipboardEvent) => {
Collaborator

This paste listener is attached once in initSetup (which runs once per table load, not per edit session — so the comment's intent is correct), but there's no matching removeEventListener in ngOnDestroy. This matches the existing mousedown listener at line 3269 (same no-cleanup pattern), and since hot.rootElement is part of the component DOM that Angular tears down, the listener is GC'd with the node — so it's consistent rather than a leak. Worth addressing together with the mousedown listener if you ever do a cleanup pass.

Nit on the comment: "rather than attaching a new listener per edit session" — editTable() (the per-edit-session entry point) is a different method; this listener lives in initSetup, so the phrasing slightly mislocates the alternative it's guarding against. The real risk it avoids is re-attachment on repeated initSetup retries (line 3510), which don't reach here once hotInstance is ready.

This `paste` listener is attached once in `initSetup` (which runs once per table load, not per edit session — so the comment's intent is correct), but there's no matching `removeEventListener` in `ngOnDestroy`. This matches the existing `mousedown` listener at line 3269 (same no-cleanup pattern), and since `hot.rootElement` is part of the component DOM that Angular tears down, the listener is GC'd with the node — so it's consistent rather than a leak. Worth addressing together with the `mousedown` listener if you ever do a cleanup pass. Nit on the comment: "rather than attaching a new listener per edit session" — `editTable()` (the per-edit-session entry point) is a different method; this listener lives in `initSetup`, so the phrasing slightly mislocates the alternative it's guarding against. The real risk it avoids is re-attachment on repeated `initSetup` retries (line 3510), which don't reach here once `hotInstance` is ready.
@@ -4215,0 +4245,4 @@
if (!this.hotTable.formulas) return
const editor: any = hot.getActiveEditor()
if (!editor || editor.row === null || event.target !== editor.TEXTAREA)
Collaborator

editor.row === null is the weaker guard here — getActiveEditor() can return a finished-state editor whose row isn't reliably null across Handsontable versions. The load-bearing check is event.target !== editor.TEXTAREA (the editor's TEXTAREA only matches the paste target while a cell is actively being edited). Functionally safe as written (both are ANDed), but consider leading with the event.target !== editor.TEXTAREA check for clarity, since that's the one that actually distinguishes grid-level paste from in-editor paste.

`editor.row === null` is the weaker guard here — `getActiveEditor()` can return a finished-state editor whose `row` isn't reliably `null` across Handsontable versions. The load-bearing check is `event.target !== editor.TEXTAREA` (the editor's TEXTAREA only matches the paste target while a cell is actively being edited). Functionally safe as written (both are ANDed), but consider leading with the `event.target !== editor.TEXTAREA` check for clarity, since that's the one that actually distinguishes grid-level paste from in-editor paste.
@@ -0,0 +17,4 @@
const hasLeadingEquals = formulaText.startsWith('=')
const formulaBody = hasLeadingEquals ? formulaText.slice(1) : formulaText
const quotedSpanPattern = /"[^"]*"|'[^']*'/g
Collaborator

Shared limitation (identical to parseFormulaRule.ts:53): this quoted-span regex doesn't handle doubled-quote escaping ("" inside a quoted string). A pasted value like ="say ""PRICE"" " would mis-segment and could expose PRICE to substitution. Not a regression since parseFormulaRule has the same pattern, but worth a shared follow-up if pasted formulas may contain escaped embedded quotes.

Shared limitation (identical to `parseFormulaRule.ts:53`): this quoted-span regex doesn't handle doubled-quote escaping (`""` inside a quoted string). A pasted value like `="say ""PRICE"" "` would mis-segment and could expose `PRICE` to substitution. Not a regression since `parseFormulaRule` has the same pattern, but worth a shared follow-up if pasted formulas may contain escaped embedded quotes.
@@ -0,0 +24,4 @@
const substituteUnquotedSpan = (span: string): string => {
let substituted = span
columnNames.forEach((columnName, columnIndex) => {
Collaborator

Subtle: substitutions run sequentially over columnNames, and substituteBoundedToken's output is fed back into the next column's pass. If a real column were named like a produced cell ref (e.g. a column literally named B1), an earlier substitution's output could be re-matched and re-substituted by this later pass. substituteBoundedToken's surrounding-blank requirement makes this unlikely in practice, and parseFormulaRule has the same ordering — flagging only as an accepted shared edge case.

Subtle: substitutions run sequentially over `columnNames`, and `substituteBoundedToken`'s output is fed back into the next column's pass. If a real column were named like a produced cell ref (e.g. a column literally named `B1`), an earlier substitution's output could be re-matched and re-substituted by this later pass. `substituteBoundedToken`'s surrounding-blank requirement makes this unlikely in practice, and `parseFormulaRule` has the same ordering — flagging only as an accepted shared edge case.
hermes added 1 commit 2026-08-19 16:17:35 +00:00
fix: agent skills and nextviya deploys
Build / Build-and-ng-test (pull_request) Successful in 5m8s
Lighthouse Checks / lighthouse (pull_request) Successful in 21m24s
Build / Build-and-test-development (pull_request) Successful in 25m20s
f7db8719f5
hermes added 1 commit 2026-08-20 11:08:02 +00:00
chore: sample record in mpe_x_test
Build / Build-and-ng-test (pull_request) Successful in 5m29s
Lighthouse Checks / lighthouse (pull_request) Successful in 21m51s
Build / Build-and-test-development (pull_request) Successful in 25m32s
9dab3d50ef
Yury added 2 commits 2026-08-20 15:50:18 +00:00
Only backend-sourced `=`-led values are auto-escaped now; anything the
user types or pastes into a character column evaluates as a real
formula, and "Apply as formula" lets a user promote an auto-escaped
cell explicitly. Formulas are enabled on every table instead of only
ones with formula rules.
chore: merge branch 'process-formula' of https://git.datacontroller.io/dc/dc into process-formula
Build / Build-and-ng-test (pull_request) Successful in 5m18s
Lighthouse Checks / lighthouse (pull_request) Successful in 21m47s
Build / Build-and-test-development (pull_request) Failing after 26m20s
d0d17995d3
Some required checks failed
Build / Build-and-ng-test (pull_request) Successful in 5m18s
Lighthouse Checks / lighthouse (pull_request) Successful in 21m47s
Build / Build-and-test-development (pull_request) Failing after 26m20s
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin process-formula:process-formula
git checkout process-formula
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dc/dc#308