Skip to content

feat!: one ErrorCode space every binding reports - #883

Merged
andiwand merged 1 commit into
mainfrom
feat/one-error-code-space
Sep 10, 2026
Merged

feat!: one ErrorCode space every binding reports#883
andiwand merged 1 commit into
mainfrom
feat/one-error-code-space

Conversation

@andiwand

@andiwand andiwand commented Sep 10, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Why

The same fifteen exception kinds were written out by hand in five places, and nothing could check that the five agreed:

Place Form
jni/src/odr_jni.cpp throw_java catch ladder → 15 Java class names
apple/src/ODRInternal.mm error_code() catch ladder → ODRError
apple/…/ODRError.h NS_ERROR_ENUM, values 1…15
wasm/src/odr_wasm.cpp catch ladder → 15 type strings
python/src/bind_core.cpp 15 register_exception calls

Separately, the codes the rendered page reports for a refused edit lived only in editing.js — 1 to 8 in a literal table, plus a code 9 that had no entry at all, only the comment "holds 9". The apps then mapped those numbers a third time, in Kotlin and Swift. docs/design/spreadsheet-editing.md called code 1 errorIllegalEditNewLine; the code called it newLine. The doc and the code had already parted.

What

odr::ErrorCode (src/odr/error_code.hpp) — one banded number space:

  • 1–15 keep the order ODRError already ships, so no value an installed iOS app reads moves.
  • 16–52 the rest of the exception types.
  • 1001–1009 what the page refuses. Nothing in C++ throws these.

Every exception states its code in its declaration by deriving from CodedException<ErrorCode::…>, so there is no per-type boilerplate. odr::error_code() recovers it from any std::exception, and all four catch ladders are gone:

binding now
odr_jni.cpp class named from error_code_name, base class as fallback; OdrException.getCode() carries the number
ODRInternal.mm a cast, held in place by 15 static_asserts
odr_wasm.cpp one catch; the envelope gains code beside type
bind_core.cpp odr.ErrorCode derived from the table
enumTables() ErrorCode derived alongside FileType

The page reads the numbers from C++. frontend.cpp::write_error_codes writes the editing band into every document view as odr.errorCodes, always inline so it also works when the config links the scripts as shipped resources. editing.js keeps the wording — it is for a console, and nothing here is localised — and holds no number. document.js's literal 9 became unnameableEdit, which now has a name and a message it never had.

test/browser/serve.py synthesises the same table from error_code.{hpp,cpp}, so the check pages are not a fourth copy.

Breaking

The page's refusal codes moved from 1…9 to 1001…1009 — readOnly is 1005, not 5. A host mapping them by number has to move with them. The reason string on the event is unchanged, so a host matching on that is unaffected.

Verification

  • odr_test: 1695 passed, 6 pre-existing skips. error_code_test.cpp pins both bands, checks every declared exception states a code, and that no code or name repeats.
  • Browser checks, headless: text 93/93, plaintext 45/45, sheet 105/105.
  • Public headers still compile under C++20.
  • -Wall -Wextra -Werror object builds of the jni and apple units, emcc -Werror -fsyntax-only on the wasm units, pybind syntax check on the python unit. That caught throw_new going dead, which the default local build does not.
  • Reference output regenerated: 691 files gain the emitted block and nothing else — 47 distinct added lines, 18 removed, no deletions, no untracked. Pins advanced to cd84aae / f3603a2.

`odr::ErrorCode` replaces five hand-maintained lists of the same fifteen
exception kinds — the ladders in `odr_jni.cpp`, `ODRInternal.mm`,
`odr_wasm.cpp` and `bind_core.cpp` — and takes in the codes the rendered page
raises for a refused edit, which lived only in `editing.js` and which no C++
path knew about.

Every exception states its code in its declaration through `CodedException`,
so `error_code()` recovers it from any `std::exception` and no binding needs a
ladder. The head keeps the order `ODRError` shipped, so no value an installed
iOS app reads moves; the refusals move onto the band from 1001.

`html/frontend.cpp::write_error_codes` writes that band into every document
view as `odr.errorCodes`, always inline, so the scripts hold the wording and
not the numbers. `test/browser/serve.py` builds the same table out of the
header rather than being a fourth copy of it.

BREAKING CHANGE: the codes the page reports through `odr.onError` and
`odr.onEditRefused` moved from 1 to 9 onto 1001 to 1009. `readOnly` is 1005,
not 5. The `reason` string on the event is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SV2gW7QGeAEyuZLbzATDGH
@andiwand
andiwand force-pushed the feat/one-error-code-space branch from 2fa28b4 to 3093aca Compare September 10, 2026 19:29
@andiwand
andiwand enabled auto-merge (squash) September 10, 2026 19:30
@andiwand
andiwand merged commit 5988a6f into main Sep 10, 2026
36 checks passed
@andiwand
andiwand deleted the feat/one-error-code-space branch September 10, 2026 19:45
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