This extension adds TiddlyWiki5 syntax-highlighting and snippets to Visual Studio Code. TiddlyWiki5 is a rich, interactive tool for manipulating complex data, with structures that do not easily fit into conventional tools like spreadsheets or wordprocessors. It is a javascript environment that lives in the Browser and Node.js (and a few other implementations) which builds a Wiki/Content Management System. The design recommends building with "the smallest semantically meaningful chunks" – named tiddlers. A tiddler could be a system javascript module that is loaded on wiki boot, or a few paragraphs of user notes on a topic, or a wikitext function that build a Table of Contents out of a set of related Tags.
When run in the browser, each tiddler is a javascript object in memory (and is also represented as a json in custom <script class="tiddlywiki-tiddler-store" type="application/json"></script> element when the wiki is saved as a "single file" *.html). When run under Node.js, TiddlyWiki saves each wikitext tiddler to disk as a separate text file with a *.tid extension. Other "MIME Types" (images, json text, etc) are saved by the server along with a *.meta file that describes all other tiddler-fields (the file-content becomes the "text" field when loaded into the wiki).
This extension is intended for editing separate tiddler files on disk with Visual Studio Code.
This extension provides five languages, each with its own grammar and scope.
| language | files | scope | covers |
|---|---|---|---|
tiddlywiki5 |
.tw, .tw5, .tiddlywiki5 |
text.html.tiddlywiki5 |
TiddlyWiki wikitext, following the rules in core/modules/parsers/wikiparser/rules |
tid |
.tid, .meta |
source.tiddlywiki5.tid-file |
the tiddler file format: a field block, a blank line, then a body parsed as wikitext |
multids |
.multids |
source.tiddlywiki5.multids-file |
the multiple-tiddler file format |
memetic-wikitext |
.mem |
text.html.tiddlywiki5.memetic-wikitext |
Memetic-Wikitext, a superset of TiddlyWiki wikitext |
tiddlywiki5.test |
.tw5.test |
text.html.tiddlywiki5.test |
the syntax-test files that check the grammars |
Each language applies only to the file extensions listed, so adding one changes nothing about how the others behave.
*.mem files open as Memetic-Wikitext (text/memetic-wikitext+tiddlywiki), a small extension of
TiddlyWiki wikitext used by the Lares agent tooling. Its scope,
text.html.tiddlywiki5.memetic-wikitext, includes the wikitext grammar, so ordinary wikitext highlights
inside a *.mem file exactly as it does in a *.tid file. On top of that it highlights:
<<~ name …>>sigils and their closing form<<~/name>>- the carrier frame's
<<^ code="&#x…;" …>>markers: themarker seats beside theopener and wraps all the content through itsclose, thetoml metafence included lar:URIs, read as addresses with their structure — the three-term root, the path, each?key=valuein the query, and the fragmentni:digests, the block check a carrier seals its body with- named parameters written
key=value, which TiddlyWiki 5.4 accepts alongsidekey:value
Every addition uses syntax TiddlyWiki already parses — each of the first two reads as a macro call, and a
lar: URI reads as plain text. A *.mem file therefore loads and renders in an unmodified TiddlyWiki
without errors, and this extension simply shows more of its structure while editing.
If you do not use *.mem files, nothing here affects you: the language applies to that extension alone.
The colours answer to TiddlyWiki's own parser. Every reading asks what TiddlyWiki builds from the text, and paints that — so a mistake stays where you made it (an unclosed tag, call or inline run stops at the blank line that ends its block, as TiddlyWiki stops it), error colours appear only where TiddlyWiki refuses something, and a pragma, a dash run, a code span or a list item reads the way your wiki will render it.
A tiddler reads in the language its type names. A *.tid or *.meta file parses its field
block, then hands the body to the grammar its type: names: wikitext by default; JSON, CSS,
JavaScript, HTML, Markdown and plain text as themselves; and image/svg+xml, text/xml and
application/xml to the XML grammar. A *.multids file reads the same way from the one type:
its header carries, since TiddlyWiki applies that type to every tiddler in the file.
Fenced code reads as real code. A fenced block labelled with a language hands that language
to the editor as well as the colourer — 24 of them — so comment toggling, brackets and word
selection inside the block follow it. A toml fence reads full TOML 1.1 through a grammar this
extension carries privately, since VS Code ships none (ThirdPartyNotices.txt credits it).
Editing meets you halfway. [[ and << close themselves as you type. A double-click takes a
TiddlyWiki word, so a system title like $:/core/ui/EditTemplate selects whole. Folding leaves
the blank line after a block where it stands.
Your theme reads the structure. Bold and italic carry their font style, not only a colour. A filter reads as its parts — prefix, brackets, operator and operand. A URL's scheme reads as one unit. Struck text dims like an aside in every bundled theme and strikes through wherever the theme supports it. A widget attribute names itself in the scope inspector.
What a line-at-a-time grammar cannot reach, it names. Italic or code carried across a line
break and an HTML tag carried across a blank line keep a line or blank-line bound on purpose, so
one stray mark cannot repaint every paragraph after it. The ceiling record
(editions/tw5-syntax/tiddlers/TextMateCeiling.tid) names each such limit and the kind of reader
that could close it.
The grammar grew from these, heavily reworked since:
- https://github.com/shikijs/textmate-grammars-themes/blob/main/packages/tm-grammars/grammars/html.json
- https://github.com/PaulPorfiroff/atom-language-tiddlywiki5
- https://github.com/roma0104/sublime-tid
contributing.md carries the detail and SCOPE-NAMES.md carries the rule this repository names its
scopes by — what a name promises, the three precedents that settled it, and the three parts of it a gate
checks. Every gate answers under two TiddlyWiki readers: the development fork for local work and the
pinned 5.4.1 release for CI. LEDGER-3.0.0.md holds the record of every construct, ruling and
measurement behind this release. In short, the grammars answer to more than their own tests:
npm test— assertion files stating what each construct should scopenpm run test-tools— the tools themselves, which the gates read throughnpm run snap— every sample's whole tokenization, pinned beside itnpm run canary— an ordinary sentence appended to every sample, which must stay ordinarynpm run corpus— broad ground, gated on the corpus reaching every declared scope and on nothing bleedingnpm run test-toolsalso pins WHICH TiddlyWiki answers: a checkout beside this repository outranks the pinnedtiddlywikidevDependency, andTW5_PATHoutranks both. Parser work happens in a checkout; the package exists so a contributor holding only this repository runs every gate rather than skipping themnpm run upstream-coverage -- <path-to-TiddlyWiki5>— TiddlyWiki's own rule regexes, taken to its own tiddlersnpm run overreach -- --corpus— every scope the grammar paints, handed back to TiddlyWiki's parser: a claim over text it refuses, and a verdict over a construct it buildsnpm run overreach-host— the same question over TiddlyWiki's own tiddlers, against the written rulingsnpm run overreach-cut— the same tiddlers cut short at a seeded offset, so the ground leaves well-formed input behindnpm run tests-known-gaps— the specimens stating what the grammar does not yet do, which fails when one of them starts passingnpm run colour-witness— what a reader sees rather than what a scope name says: an opener and its closer read alike in every bundled theme, and a declared distinction reaches enough of them to shownpm run backtrack-witness— what a half-typed construct costs the tokenizer, over every pattern in every grammarnpm run attribute-guard— what an attribute-list guard would cost, taken to TiddlyWiki's own tags: how many it would refuse that the parser builds.-- --cutcuts each tag short first, matching the input such a guard would actually meetnpm run tw5-oracle -- '<wikitext>'— the tree TiddlyWiki builds, and-- --rulesthe rules it standsnpm run rule-inventory— every parser rule, the config tiddlers it answers to, and what TiddlyWiki ships for eachnpm run theme-paint -- <scope>— how many bundled themes paint a scope, and-- --familiesthe whole grammar rankednpm run gruvbox-report— the sixjdinhlife.gruvboxthemes, read beside the bundled 65 — reported, never held to a floor. Reaches the installed extension the waygrammars.shreaches VS Code's own bundled grammars (GRUVBOX_THEMES_DIRorVSCODE_EXTENSIONS_DIRname it outright); a machine carrying neither extension nor override printsgruvbox-report SKIP — …and exits 0, which every standalone CI checkout meets honestlynpm run overreach-corpus-files/overreach-corpus-memetic— the corpus, in both dialects, against the parser and the written rulingsnpm run compose-memes— composition over whatever memetic writing stands beside this checkout; setMEMESto one or more directoriesnpm run legibility— whether a reader can tell one construct from another: every pair over every bundled theme, each carrying its own floornpm run ceiling— what a TextMate grammar CANNOT reach about TiddlyWiki, measured against the host and naming the kind of reader that closes each one. A ceiling somebody closes RETIRES, and the gate fails until it goes. The bare name READS, so the gate never writes;npm run ceiling:writemeasures the same and refreshes the harvestedTextMateCeilingtiddler with itnpm run delimiters— what a delimiter inherits from the content it bounds: everycontentNamein the tree, keyed by shape, each ruled incorpus/delimiter-ledger.txtas a parting that serves a reader or costs onenpm run package-contents— every path the manifest names, checked inside the built packagenpm run bench— a disposable editor in a container, so you can look at the grammar with your own eyesnpm run uri-span-measure— a scout instrument: the resolved style of each named span of a samplelar:/ni:/https:line, across every bundled theme, dumped as JSON (> /tmp/uri-measure-<label>.json);npm run uri-span-compare -- before.json after.jsondiffs two such dumps span by span. Both back the URI-palette rulings cited inLEDGER-3.0.0.mdand thememetic-wikitext.jsoncomments; neither is part of the shipped gate set
A TextMate grammar decides how loudly a construct reads and never which parser rules a wiki
stands — nothing in the VS Code API hands a grammar to an extension at runtime. That claim
answers to Microsoft's tree; its consequence answers here, and
tools/invariants/ships-no-runtime.test.js holds it: no entry point, no activation events, only
declarative contributions, no runtime dependency, and nothing executable in the package. A theme
paints a scope when one of its own rules names that scope or a dotted prefix of it, which makes
the scope name the default. npm run theme-paint -- <scope> measures any scope against the
bundled theme set, and -- --families ranks every family this grammar emits.
Two groups repay turning, and both turn from editor.tokenColorCustomizations in your
own settings. Nothing else needs a switch: the grammar's structural families already read
quiet in every bundled theme.
TiddlyWiki has shipped CamelCase linking disabled since 5.3.0
($:/config/WikiParserRules/Inline/wikilink), so HelloWorld builds no link in a new wiki
and the grammar reads it quiet — meta.link.wikilink.tiddlywiki5, which few themes paint
where a markup.underline.link. scope inherits nearly every theme's link colour. The
construct still carries its own scope, so a wiki that enables CamelCase in
Control Panel → Settings colours it back with one rule:
"editor.tokenColorCustomizations": {
"textMateRules": [
{ "scope": "meta.link.wikilink.tiddlywiki5", "settings": { "fontStyle": "underline" } }
]
}A rule carrying only fontStyle leaves the colour to your theme. A foreground holds one
literal colour across every theme you switch to, so give each theme its own block when you
want one:
"editor.tokenColorCustomizations": {
"[Default Dark+]": { "textMateRules": [
{ "scope": "meta.link.wikilink.tiddlywiki5", "settings": { "foreground": "#4fc1ff" } } ] },
"[Default Light+]": { "textMateRules": [
{ "scope": "meta.link.wikilink.tiddlywiki5", "settings": { "foreground": "#0451a5" } } ] }
}The suppressing ~ keeps its punctuation colour either way: wikilinkprefix carries its own
rule, TiddlyWiki ships it enabled, and it consumes the ~ whether or not CamelCase stands.
No construct reads as body text in more than a quarter of the 65 bundled themes, so this grammar asks a reader for no fontStyle rule of their own. A construct that starts reading quiet joins a table here the day it does.
Generated by npm run reading-recipe from corpus/prose-reading-ledger.txt.
A verdict answers to what TiddlyWiki refuses, and never to what another language retired.
TiddlyWiki parses any tag name, any attribute name, a lone angle bracket and a stray & into
the page, so none of them draws one. A single verdict stands: invalid.illegal.field.name, on a
field name a *.tid or *.meta header cannot hold. Most themes paint it as an error — the two
VS Code ships among them — and MIGRATION.md records the names that went.
Name the family, never a member. A theme selector reaches a scope by dot-bounded prefix,
so one rule on invalid.illegal reaches that verdict and keeps reaching it when a name gains a
segment. To read it quietly:
"editor.tokenColorCustomizations": {
"textMateRules": [
{ "scope": "invalid.illegal", "settings": { "foreground": "#808080" } }
]
}That selector also reaches an embedded block's own verdicts — a CSS or JavaScript region
inside a tiddler — which is usually what a reader quieting errors wants. npm run rule-inventory prints the exact names, so you never need this page to list them.
Both blocks work per workspace folder in .vscode/settings.json, so one wiki can answer
differently from another.
An extension may contribute editor.tokenColorCustomizations through
contributes.configurationDefaults. VS Code registers it, warns nothing, and it takes
effect; some 450 published manifests do exactly that. This one declines, for three reasons
that hold whatever the colour.
No hardcoded colour survives an arbitrary theme. A foreground that reads well on
Default Dark+ reads as noise on a light, high-contrast or pastel theme, and the extension
never learns which one you run. Theming belongs to the theme and to you.
A contributed default cannot be scoped to a language. Only settings marked
language-overridable reach the per-language schema, and editor.tokenColorCustomizations
carries no such mark — VS Code's theme maintainers have closed that request directly. So a
colour contributed here would apply in every file you open, not only in wikitext. The one
containment mechanism left is the language suffix every scope name carries: each grammar closes
its names on its own — .tiddlywiki5, .memetic-wikitext, .tid-file, .multids-file,
.fields — and tools/invariants/scope-suffix.test.js holds every one to it.
A contributed default merges INTO your settings rather than sitting under them. VS Code
deep-merges object defaults, so writing your own editor.tokenColorCustomizations would
not clear the extension's rules — removing them would take an explicit empty
"textMateRules": [], which would destroy your own alongside. An opinion you cannot turn
off without losing your own work is not a default.
So the two blocks above stay a snippet you paste, theme-scoped, under your hand. What this
extension does contribute by default is behavioural and carries no colour:
files.associations, mapping each extension to its language. VS Code's own 24 built-in
extensions that use configurationDefaults set no colour either.
Per-rule switching — one toggle for each of TiddlyWiki's parser rules, resolved down a bag
stack — waits on the tree-sitter and language-server work, where a live parser can answer
for the file in front of it. npm run rule-inventory reports that surface today.
Please report issues or offer Pull Requests at the GitHub Repository:
CHANGELOG.md— what each release grants youLEDGER-3.0.0.md— every construct, ruling and measurement behind 3.0.0MIGRATION.md— every scope name 3.0.0 retired, with what it stands as now
Thank You for trying TiddlyWiki5 Syntax Highlighting for Visual Studio Code
Enjoy!