todo.parse-tags
Read a tags box as typed, "#Home, work errands", into normalised tags: split on spaces and commas.
1.0.0 · published 2026-10-03 by charlie · Anterra
Pinned by 12 tests, run in TypeScript, Python and Rust.
What it does
Reads a tags box as a person types it into the tags a todo stores: `#Home, work errands` is `["home", "work", "errands"]`.
- **Separators:** every run of ASCII whitespace (space, tab, CR, LF, VT, FF) or commas ends one tag and starts the next. So `home office` is two tags; type `home-office` for one. Semicolons and other punctuation are not separators: they end up as hyphens inside the slug (`a;b` is `a-b`). - **Each piece** then goes through todo.normalise-tags: leading `#`s dropped, lower-case slug form (`Café` is `cafe`), pieces that come out empty (`#`, `日本`) dropped, and repeats removed, keeping the first position. - Only ASCII whitespace separates, so every language splits the same way (Python's `\s` and JavaScript's `\s` disagree on Unicode spaces).
For example
parseTags(home work)→ home, work words separated by a space are two tagsparseTags(#Home, #Work)→ home, work hashes and a comma with a space after itparseTags(home,work,, errands)→ home, work, errands runs of commas and spaces are one separator, and empty pieces vanish
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
export function parseTags(text: string): readonly string[]
| text | string | what was typed in the box, e.g. "#Home, work errands" |
| returns | string[] | e.g. ["home", "work", "errands"], normalised by todo.normalise-tags |
Your code names it in one line, in the file that uses it
import { parseTags } from "#fune/todo.parse-tags@^1";
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { normaliseTags } from "./todo_normalise_tags.ts"; ← from todo.normalise-tags ^1.0.0 · built alongside by fune
/**
* A tags box as typed: every run of ASCII whitespace or commas separates two
* tags, and each piece then goes through todo.normalise-tags (no #, slug
* form, empties and repeats dropped). A tag of two words is typed with a
* hyphen, "home-office", since a space starts the next tag.
*/
export function parseTags(text: string): readonly string[] {
return normaliseTags(text.split(/[ \t\n\r\v\f,]+/));
}Install
fune build
With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:
fune add todo.parse-tags
The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./todo.parse-tags-1.0.0-typescript.fune, or fetch it from a terminal with fune pull todo.parse-tags@1.0.0:typescript.
The whole function, every language, is one file too: todo.parse-tags-1.0.0.fune, 5,584 bytes, sha256 734fe72a197d9ff6cf0d95a156b13bbc0e71941a27aa25bb703f1bc63f4a768c. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
// fune: before todo.parse-tags
after — your function gets the result and the arguments, and returns the final result.
// fune: after todo.parse-tags
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
// fune: replace todo.normalise-tags in todo.parse-tags
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show todo.parse-tags --steps.
// fune: step todo.parse-tags after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| words separated by a space are two tags | home work | → | home, work |
| hashes and a comma with a space after it | #Home, #Work | → | home, work |
| runs of commas and spaces are one separator, and empty pieces vanish | home,work,, errands | → | home, work, errands |
| an empty box is no tags | → | ||
| only separators is no tags | , , | → | |
| the same tag typed three ways is one tag | Home HOME #home | → | home |
| a hyphen keeps two words in one tag | home-office Q3 | → | home-office, q3 |
| tabs and line breaks separate too; Latin-1 letters are transliterated | Café Straße urgent | → | cafe, strasse, urgent |
| a tag in another script normalises to nothing and is dropped | #日本 home | → | home |
| a semicolon is not a separator: it becomes a hyphen inside the slug | a_b;c | → | a-b-c |
Show the other 2 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| repeats keep their first position | work, Errands, #WORK, errands | → | work, errands |
| a lone hash between tags is dropped | home # work | → | home, work |
More from the author
It never throws and does not enforce the limits (10 tags, 30 characters): todo.item's `validateDraft` reports those, so the form can say what is wrong instead of silently cutting what was typed.
Files
| Path | Bytes |
|---|---|
| README.md | 1,001 |
| impl/python.py | 465 |
| impl/rust.rs | 615 |
| impl/typescript.ts | 472 |
| vectors.json | 1,371 |