Rendered from specs/F11-toml-support.md — edit it there, not here.
F11 — TOML File Support
Overview
Add TOML (.toml) as a first-class data format alongside JSON, JSONC, JSONL, and YAML. TOML files can be validated against a JSON Schema, have a schema inferred from their contents, and display a bound schema in the status bar.
Because VS Code has no built-in toml.schemas setting, and to avoid coupling to or conflicting with third-party TOML extensions (e.g. tamasfe.even-better-toml, tamasfe.taplo), schema binding for TOML uses Inline only: the "$schema" key is written directly into the TOML file. This approach is also recognised natively by every TOML tool that honours $schema, making it the most portable choice. Inline $schema binding (F10) is extended to cover TOML syntax.
Note: TOML is not a subset of JSON. It is its own format that maps cleanly to JSON-compatible value types (strings, numbers, booleans, arrays, tables/objects) but uses distinct syntax (key = value, [table] headers, [[array-of-tables]]) and adds native date/time types. JSON Schema validation operates on the parsed value tree, so date/time values are validated as strings.
User Stories
- As a developer using TOML for project configuration (e.g.
Cargo.toml,pyproject.toml, custom app config), I want to validate my TOML file against a JSON Schema so I catch structural errors early without leaving VS Code. - As a team member, I want to run "Generate Schema from This File" on a TOML data file and get a draft JSON Schema I can refine and share.
- As a TOML author, I want the status bar to show which schema is bound to my open
.tomlfile, and be able to bind one with the same Bind Schema… command I use for JSON and YAML files.
Functional Requirements
Language Registration
- F11-FR-01 The extension MUST add
onLanguage:tomlto its activation events inpackage.jsonso it activates when a TOML file is opened. - F11-FR-02
languages.tsMUST add aTOML_LANGS = ['toml']constant and include it inALL_LANGSso allisSupported()checks handle TOML without per-call changes. - F11-FR-03 A new exported function
isToml(languageId: string): booleanMUST be added tolanguages.ts. - F11-FR-04 A new exported function
parseToml(text: string): unknownMUST be added tolanguages.ts. It MUST use a pure-JS TOML parser from npm (see Non-Functional Requirements) and MUST throw on invalid TOML.
TOML Parser Dependency
- F11-FR-05 The project MUST add
smol-tomlas a production dependency. It is pure TypeScript (~15 KB), dual ESM/CJS (compatible with the VS Code extension host's CommonJS runtime), BSD-3-Clause-licensed (a permissive, MIT-compatible OSI licence), and actively maintained with no native bindings and no transitive dependencies.
Validation
- F11-FR-06
ValidationManagerMUST parse TOML documents usingparseTomlbefore passing the resulting value to Ajv, consistent with how YAML usesYAML.parseand JSONL usesparseJsonl. - F11-FR-07 The "Validation supports…" information message MUST be updated to include TOML.
- F11-FR-08 Validation errors MUST use
locateInDocumentwith the existinginstancePath-to-range logic. Because TOML key syntax differs from JSON, the locator will match bare key names (e.g.name) using the same regex heuristic already applied for JSON/YAML; exact column accuracy is not required for this first version.
Schema Inference
- F11-FR-09 The
jsonschema.inferSchemacommand MUST parse TOML files usingparseTomland pass the result togenson-js, producing a draft JSON Schema in a new untitled editor — identical behaviour to JSON and YAML inference. - F11-FR-10 TOML native date/time values (parsed as JavaScript
Dateobjects by the chosen library) MUST be converted to ISO-8601 strings before being passed togenson-js, so they are inferred as{ type: "string" }rather than causing inference errors.
Schema Binding
- F11-FR-11 The status bar item MUST be visible when a TOML file is the active editor (consistent with F04-FR-05). It reflects whatever
extractInlineSchemaUrlreturns for the file. - F11-FR-12 The Bind Schema… command MUST be available for TOML files. For TOML the scope picker MUST offer Inline (this file) only — no folder/workspace/user settings options — because VS Code has no built-in
toml.schemasmechanism and writing to third-party extension settings is out of scope. The picker MUST show an explanatory subtitle: "TOML binding is inline only — the $schema key is written into your file." - F11-FR-13 The extension MUST NOT write to
json.schemas,yaml.schemas, or any setting owned by a third-party TOML extension.
Inline $schema for TOML (extends F10)
- F11-FR-15
extractInlineSchemaUrlMUST be extended to detect TOML files and extract the"$schema"key value using a lightweight regex (/^"\$schema"\s*=\s*"([^"]+)"/m) rather than full TOML parsing, consistent with how YAML inline extraction uses a regex. The search MUST be restricted to the text before the first[table]or[[array-of-table]]header line — a"$schema"key appearing after such a header is a nested key (e.g.tool.foo."$schema"), not the document's own binding, and MUST NOT be mistaken for one. - F11-FR-16 The F10 inline binding write path MUST support TOML: inserting or replacing
"$schema" = "<ref>"as the first non-comment line of the file (searched under the same root-table restriction as F11-FR-15), using aWorkspaceEdit. The<ref>value MUST be escaped as a valid TOML basic string (backslash and"escaped) before being embedded — a raw Windows absolute path (e.g.C:\Users\...) contains backslashes that are otherwise interpreted as TOML escape sequences, an invalid one (e.g.\Unot followed by 8 hex digits) can make the whole file fail to parse. Extraction (F11-FR-15) MUST reverse the same escaping when reading the value back. - F11-FR-17 Inline removal for TOML MUST delete the
"$schema" = ...line entirely (TOML key–value pairs are self-contained lines; no trailing-comma repair is needed unlike JSON), restricted to the same root-table region as F11-FR-15.
Menus and Command Palette
- F11-FR-18 All editor/title and editor/context menu
whenclauses that currently listresourceLangId == json || ... || resourceLangId == ymlMUST be extended to includeresourceLangId == tomlwhere the command is relevant to data files (validate, infer, bind). - F11-FR-19 TOML files MUST NOT trigger the preview, visual-editor, or configure-preview commands, as those operate on JSON Schema files only.
Non-Functional Requirements
- F11-NFR-01
parseTomlMUST be synchronous and MUST NOT spawn a subprocess, consistent with Article III.1 — the TOML parser runs in the extension host on already-loaded document text. - F11-NFR-02 The chosen TOML library's bundle size MUST be evaluated; if it adds more than ~50 KB to the packaged
.vsix, the team should consider lazyrequire()at first use. - F11-NFR-03 All new code paths in
languages.tsMUST be covered by unit tests (≥ 80 % on all axes), consistent with Article V.
Out of Scope
- Rendering HTML documentation for TOML Schema files — TOML is a data format, not a schema format;
json-schema-for-humansoperates on JSON Schema files only. - Visual schema editing for TOML files.
- Supporting TOML Schema (an experimental schema format for TOML) — this spec covers JSON Schema validation of TOML data only.
- Live preview updates for TOML (live-update is schema-preview specific, F02).
- Full TOML-aware error location (exact column ranges for validation errors require a TOML AST; deferred to a follow-up spec).
Acceptance Criteria
- Opening
config.tomlactivates the extension and shows the status bar item. - Running Validate This File on a valid TOML file against a matching schema shows "✓ config.toml is valid".
- Running Validate This File on an invalid TOML file shows validation errors in the Problems panel.
- Running Generate Schema from This File on
config.tomlopens a new JSON editor with an inferred schema. - Running Bind Schema… on
config.tomlshows only the Inline scope option with the explanatory subtitle; choosing it inserts"$schema" = "./schemas/config.schema.json"as the first line. - After inline binding the status bar shows
$(file-symlink-file) Schema: config.schema.json. - Running Remove via Bind Schema… on the same file strips the
"$schema" = ...line and the file parses cleanly as TOML. - A TOML file containing TOML native dates validates without errors (dates treated as strings).
- The "Validate", "Generate Schema", and "Bind Schema…" commands do NOT appear for
.tomlfiles in the Preview / Edit Schema menu group.
Open Questions
All questions resolved — no open items.
Relation to Existing Specs
- Extends F03 (validation) — new
isTomlbranch in the parse step; no changes to Ajv compilation. - Extends F04 (binding) — status bar mechanic only; no new settings target. TOML schema is resolved exclusively via
extractInlineSchemaUrl. - Extends F06 (inference) — new
isTomlparse branch; samegenson-jscall. - Extends F10 (inline binding) — TOML write/remove path added; F10-FR-15 through F10-FR-17 are owned here to keep TOML concerns in one spec.
- S01 (security):
parseTomlprocesses workspace file content; no HTML involved, noeval, consistent with existing parsers. - S02 (workspace trust): TOML validation and file mutation (inline binding) MUST be blocked in untrusted workspaces, same as all other data-file operations.
- S03 (performance): TOML parsing is synchronous and in-process; no timeout needed. Binding writes remain async (VS Code config API).
- Requires constitution amendment to add
TOML parsing: smol-tomlto Article II's technology table.
Requirement traceability
Status of each requirement in specs/traceability.json. Test coverage is auto-discovered from [ID] tags in test titles and is not listed here.
| Requirement | Status | Implementation | Note |
|---|---|---|---|
F11-FR-01 | implemented | package.json | onLanguage:toml activation |
F11-FR-02 | implemented | src/languages.ts | TOML_LANGS constant included in ALL_LANGS |
F11-FR-03 | implemented | src/languages.ts | isToml(languageId) |
F11-FR-04 | implemented | src/languages.ts | parseToml via smol-toml; throws on invalid |
F11-FR-05 | implemented | package.json | smol-toml production dependency |
F11-FR-06 | implemented | src/ValidationManager.ts | ValidationManager parses TOML via parseToml before Ajv |
F11-FR-07 | implemented | src/ValidationManager.ts | supports message updated to include TOML |
F11-FR-08 | implemented | src/ValidationManager.ts | locateInDocument reused; exact column not required (first version) |
F11-FR-09 | implemented | src/extension.ts | inferSchema parses TOML via parseToml into genson-js |
F11-FR-10 | implemented | src/languages.ts | parseToml normalises TOML Date values to ISO-8601 strings |
F11-FR-11 | implemented | src/SchemaBindingManager.ts | status bar visible for TOML via isSupported; reflects inline $schema |
F11-FR-12 | implemented | src/SchemaBindingManager.ts | pickScope tomlInlineOnly: Inline-only picker with required subtitle |
F11-FR-13 | implemented | src/SchemaBindingManager.ts | TOML always resolves to INLINE_SCOPE; json/yaml.schemas never written |
F11-FR-15 | implemented | src/SchemaBindingManager.ts | extractInlineSchemaUrl TOML regex restricted to tomlRootTableText(); unescapes via unescapeTomlBasicString |
F11-FR-16 | implemented | src/SchemaBindingManager.ts | computeTomlSchemaUpsertEdit escapes the ref via escapeTomlBasicString and restricts existing-key detection to tomlRootTableText() |
F11-FR-17 | implemented | src/SchemaBindingManager.ts | computeTomlSchemaRemoveEdit restricts removal to tomlRootTableText() |
F11-FR-18 | implemented | package.json | data-file menus include resourceLangId == toml |
F11-FR-19 | implemented | package.json | preview/edit/configure require isJsonSchema, never target toml |
F11-NFR-01 | implemented | src/languages.ts | parseToml synchronous, no subprocess |
F11-NFR-02 | manual | package.json | smol-toml ~15KB, no transitive deps; static import acceptable |
F11-NFR-03 | implemented | src/languages.ts | new languages.ts paths unit-tested (languages.test.ts) |