Skip to content

A FunctionElement rename carries into the interface dictionary's catalog, in both editors - #67

Merged
ww-mw merged 3 commits into
mainfrom
function-rename-carries-into-catalog
Oct 16, 2026
Merged

ww-mw merged 3 commits into
mainfrom
function-rename-carries-into-catalog

Conversation

@ww-mw

@ww-mw ww-mw commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

The maintainer's ruling A5: "a FunctionElement rename also renames the System Composer interface dictionary's copy (p_Name / p_FunctionPrototype), through the existing catalog-rename code, in both formats."

What was wrong

Renaming a Simulink.FunctionElement (a function of a service interface) changed the data part only. Core's own save carries that rename into the catalog (mathworks/data-explorer-core#109), but neither editor saves through core: each one carries renames into the catalog itself, and catalogRenameOf only lets through a rename of a top-level entry. So a function rename left the catalog's p_Name and p_FunctionPrototype at the old name, and when the file was reopened MATLAB found the interface dictionary and the data part disagreeing about the function's name.

The fix

src/host/scRename.ts adds a second gate, catalogFunctionRenameOf. It matches a Name edit of a Simulink.FunctionElement directly under the top-level entry, and it is read before the mutation, so node.name is still the name the file has. It never fires together with catalogRenameOf, because that one needs the row to be the entry and this one needs a function directly under it. Two format helpers then run core's own function edits (scFunctionRenameEdits over scanScJsonFunctions / scanScXmlFunctions), the same ones core's save applies. Each provider gains one branch in its existing scEdits / scPart expression.

  • Text editor (SlddTextEditorProvider): the catalog ranges go into the same WorkspaceEdit as the entry-span edit, so the rename is one undo step.
  • Binary editor (BinarySlddEditorProvider): the catalog is a zip member the editor only passes through, so the carry is a member swap. scXmlFunctionRenamePatch has the same shape as scXmlRenamePatch and goes through pushEdit, whose undo restores the member's original bytes exactly.

If the catalog does not list the function, there are no extra ranges and no patch.

Refused when the Prototype cannot follow

Core refuses a function rename that its Prototype cannot follow (mathworks/data-explorer-core#104) with 'f' cannot be renamed: its Prototype '...' does not declare the function 'f' in the form [outputs =] f(inputs), so the Prototype cannot be renamed with it, and MATLAB requires the two to match. It also refuses a sibling's name. Both refusals happen in setProperty, before any catalog edit is computed, and appear as the same validationError as any other refused edit. Nothing is written: the text is unchanged, and in binary the member is the same object and the chunk is unchanged.

Tests

  • test/scRenameCarry.test.ts, 31 new tests:
    • Gate: the positive case, plus 5 cases that must not carry (another column, the same name, a data-bus element, a function's argument, the entry itself).
    • Exact sites, both formats: 3 ranges in text. In binary, the data interface f and g's argument g are left as read.
    • Same result as core's save: each scenario runs through the provider's composition and through a fresh core createSession/ingest/serializeSource, and the two catalogs must be identical. Scenarios: one rename, interface renamed before, interface renamed after, a chain, a rename back, and (text only) a swap through a temporary name.
    • Refusals, the no-catalog / unlisted-function cases, binary undo toBe(part.before), and the member surviving the save's re-zip.
  • test-integration/suite/archRenameCarry.test.ts, 2 new tests: a save writes the function renamed in the chunk and in the catalog, at exactly the two sites. Undo restores the member byte for byte, redo swaps it back, and a save after undo writes the original.
  • test-integration/suite/functionRenameThroughEditors.test.ts, 12 new tests, 6 per editor. These go through the real providers. Each one sends the edit message the table sends (columnId: 'Name', with the row id taken from the rows the provider painted) through a fake panel (tools/hostHarness.ts's fakePanel). It then checks what the provider wrote against core's own serializeSource of the same renames. The cases, in each editor:
    • one rename: the catalog moves at exactly p_Name and p_FunctionPrototype, and the saved catalog equals core's;
    • the interface renamed first, then its function;
    • a chain;
    • a function the catalog does not list: the catalog is untouched;
    • a Prototype that cannot follow: one validationError whose reason equals core's own, and nothing written (in text, the document is not dirty);
    • undo is one step. Text: the rename is one change event (the one-rename test asserts its 3 ranges), and its inverse, applied as one applyEdit, gives back the original bytes. Binary: one edit, whose undo() restores the chunk and the member read from the file, and a save then writes that member byte for byte.
  • New fixture test/fixtures/arch_service_binary.sldd, made by make-fixtures.mjs. arch_binary.sldd could not be reused: it has no service interface, and other suites count its entries. The new fixture holds ServiceInterface with f: y = f(u) and g: [a,b] = g(g), the shape of core's functionElementRename.test.ts (example/dd1.sldd). It has two traps: a data interface also named f, and g's first argument, named g. Running the generator rewrites the other zip fixtures' mtimes, so only the new file is committed.

Fail-before. I stubbed the three helpers to main's behaviour (the gate returns null, no edits, no patch) without committing the stub. Then:

  • 17 of the 31 new unit tests fail. 14 fail at an assertion: 5 at the gate's own return value, which the stub makes null, and 9 at the catalog, where core writes y = compute(u) / <p_Name>compute and the stub leaves y = f(u) / <p_Name>f. The other 3 fail with a TypeError on a null part rather than at a content assertion (Cannot read properties of null (reading 'after') twice, and 'before' once). These are the binary tests "leaves the data interface named f…", "survives the re-zip…" and "puts both surfaces back on undo…".
  • The 2 new archRenameCarry tests fail, and 8 of the 12 provider-level tests fail.

The other 14 new tests pass on main as well, which is right for guards: the 5 negative gate cases, rename back in both formats, the re-read check, the 4 refusals (core already refuses), and the 2 no-patch cases. The file's 13 existing tests stay green.

Fail with a provider reverted. The tests above pin the helpers, but they do not pin the branch each provider adds. scRenameCarry.test.ts and archRenameCarry.test.ts compose the edit from the helpers themselves, so with both providers put back to origin/main (and this PR's scRename.ts kept), all 44 tests in scRenameCarry and all 5 in archRenameCarry stay green. functionRenameThroughEditors.test.ts closes that gap. I put one provider back at a time with git show origin/main:<path> > <path> and restored it after, without committing the revert:

  • Only SlddTextEditorProvider.ts reverted: 4 of the 6 text cases fail (one rename, interface first, chain, undo). All 6 binary cases pass.
  • Only BinarySlddEditorProvider.ts reverted: the same 4 of the 6 binary cases fail. All 6 text cases pass.

The 2 cases that pass in each editor are the controls, the unlisted function and the refusal, because on main they write no catalog edit either.

Core pin

data-explorer-core moves from #v1.38.1 to #v1.39.0 (tag on 8e33408, mathworks/data-explorer-core#120). That release exports the carry from the barrel (mathworks/data-explorer-core#119): scFunctionRenameEdits, scanScJsonFunctions, scanScXmlFunctions, ScFunction, CatalogFunctionRenames. In the lockfile, only core's two entries change. There is no CHANGELOG change; that is added at release.

Gates (at ffe323b, core v1.39.0)

  • npm run verify: rc 0. vitest ran 169 files with 3,264 passed (3,233 on main, +31). check:leak OK, with all 646 lockfile resolutions public.
  • npm run test:integration: 191 passing, 1 pending (179 at cb72803, +12). The pending test is cutPasteUndo's same-document move, which skips itself on a host that does not apply undo (undoAvailable). It has nothing to do with this change and it runs on CI.
  • CI at ffe323b: all 4 checks pass. build-test's integration run has 192 passing, with cutPasteUndo's move running there, and it includes both new suites.

Finishes the maintainer's A5 ruling (core #109, #119).

ww-mw added 3 commits October 16, 2026 11:50
Renaming a function of a service interface now renames System Composer's
copy of it too (p_Name and p_FunctionPrototype in the interface
dictionary), in text and binary dictionaries, as one undo step (ruling
A5). Core's own save already does this (data-explorer-core #109), but
neither editor saves through core, and catalogRenameOf only passes a
rename of the entry itself, so the catalog kept the old name.

- scRename.ts: a second gate, catalogFunctionRenameOf (a Name edit of a
  Simulink.FunctionElement directly under the top-level entry, read
  before the mutation), and two format helpers over core's
  scanScJsonFunctions / scanScXmlFunctions and scFunctionRenameEdits.
- Each provider takes one more branch in its scEdits / scPart
  expression. The text editor puts the catalog ranges in the same
  WorkspaceEdit. The binary editor's member swap rides pushEdit, whose
  undo restores the member's original bytes.
- A rename the Prototype cannot follow is refused by core before any
  catalog edit is computed, and shows as a validationError like every
  other refused edit.

Tests: scRenameCarry.test.ts runs each scenario (one rename, interface
renamed before or after, chain, rename back, swap) through the
provider's composition and through core's own serializeSource, in both
formats, and requires the same catalog. archRenameCarry.test.ts adds a
save test and an undo test on a new fixture, arch_service_binary.sldd.

This needs the barrel exports on data-explorer-core's
export-function-rename-catalog-carry branch. The pin moves to the core
release that carries them in a separate change.
data-explorer-core v1.39.0 (mathworks/data-explorer-core#119, released
in #120 there) exports scFunctionRenameEdits, scanScJsonFunctions,
scanScXmlFunctions and the ScFunction and CatalogFunctionRenames types
from its barrel, which the previous commit calls to carry a
FunctionElement rename into the System Composer catalog. The lockfile
changes only core's entries: the root dependency spec and
node_modules/data-explorer-core, now 1.39.0 resolved at
8e334082682eae7901cc07016f4fb90b3cdc7c73.
scRenameCarry.test.ts and archRenameCarry.test.ts compose the edit from
the scRename helpers themselves, so neither reaches the providers' edit
handler: with SlddTextEditorProvider.ts and BinarySlddEditorProvider.ts
put back to main, all 44 unit and 5 integration tests there stay green.

This file sends the table's own `edit` message through each real
provider and a fake panel, and grades what the provider wrote against
core's serializeSource of the same renames: one rename (p_Name and
p_FunctionPrototype move, and nothing else), the interface renamed
first, a chain, a function the catalog does not list, a Prototype that
cannot follow (refused with core's own reason, nothing written), and
undo as one step (text: the one change event's inverse gives back the
original bytes; binary: the member read from the file, byte for byte).

With only SlddTextEditorProvider.ts reverted to main, 4 of the 6 text
cases fail and the 6 binary cases pass; with only
BinarySlddEditorProvider.ts reverted, 4 of the 6 binary cases fail and
the 6 text cases pass. The 2 that pass in each are the controls (the
unlisted function and the refusal), which write no catalog edit on main
either.
@ww-mw
ww-mw merged commit d0141e3 into main Oct 16, 2026
4 checks passed
@ww-mw
ww-mw deleted the function-rename-carries-into-catalog branch October 16, 2026 16:58
@ww-mw ww-mw mentioned this pull request Oct 16, 2026
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.

1 participant