Files
inkwell/core/testdata/grammar.json
T
bvandeusenandClaude Opus 5.5 1c4bf56058 A lone link's preview is looked up where the server filed it
The server unfurls what detect_urls finds, trailing .,;:!? trimmed, and files the
preview under that. The web and Android cards looked a lone link's preview up
under body.trim(), punctuation included, so a note reading
"https://example.com/a." never showed its card.

- grammar.json gains a urls section: what the server finds in a body, and the
  link a lone-link note is filed under.
- The web's rule moves out of NoteCard into notes/links.ts loneUrl(); Android's
  LinkPreviewRow gets the same loneUrl(); both trim like the server.
- The server and web suites run the cases; Android's JVM test pins them by hand,
  as it does the tint.

Fixes #5399. DRY pass #2, batch 3 (#5372).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:30:32 -04:00

120 lines
7.6 KiB
JSON

{
"_about": [
"The cases every implementation of the note grammar is tested against.",
"One file, read by four test suites: core (derive.rs), the server (checklist.py, tags.py, helpers.py), the web (markdown.ts, colors.ts) and, for the tint, Android (DerivedTint.kt, which pins the same values by hand).",
"A difference between any two implementations is a note that changes shape when it syncs. Add a case here, not to one suite.",
"Lives under core/ because core is the definition, and because core/ is in the desktop, Android and server file sets, so changing a case reruns every suite that reads it.",
"display_titles: a note's name is its first line that says anything once a task marker is stripped, cut to 200 characters (not bytes). Lines split on \\n alone, as the grammar does everywhere.",
"urls: the links in a body the server unfurls (found), and the one a note that is nothing but a link files its preview under (lone). Trailing .,;:!? is the sentence's, not the link's, on every surface.",
"recurrences: the repeat rules a reminder can carry, as stored. The server, the core and the web's editor each hold the list; Android's picker pins it by hand, as it does the tint.",
"Tag cases are plain text on purpose. The web's renderer lets `code` and **bold** claim a `#` first, which the derivers do not; that is a rendering choice, not grammar, and is tested where it lives."
],
"task_lines": [
{ "line": "- [ ] milk", "item": { "checked": false, "text": "milk" } },
{ "line": "- [x] milk", "item": { "checked": true, "text": "milk" } },
{ "line": "- [X] milk", "item": { "checked": true, "text": "milk" } },
{ "line": "* [ ] star bullet", "item": { "checked": false, "text": "star bullet" } },
{ "line": " - [ ] indented", "item": { "checked": false, "text": "indented" } },
{ "line": "\t- [ ] tab indent", "item": { "checked": false, "text": "tab indent" } },
{ "line": "- [ ] two spaces after the bullet", "item": { "checked": false, "text": "two spaces after the bullet" } },
{ "line": "- [ ]", "item": { "checked": false, "text": "" } },
{ "line": "- [ ] ", "item": { "checked": false, "text": "" } },
{ "line": "- [ ] trailing space kept ", "item": { "checked": false, "text": "trailing space kept " } },
{ "line": "- [ ]x", "item": null },
{ "line": "-[ ] x", "item": null },
{ "line": "+ [ ] plus bullet", "item": null },
{ "line": "- [y] x", "item": null },
{ "line": "- [ ] x", "item": null },
{ "line": "text - [ ] x", "item": null },
{ "line": "- milk", "item": null },
{ "line": "", "item": null }
],
"rendered_items": [
{ "text": "milk", "checked": false, "line": "- [ ] milk" },
{ "text": "milk", "checked": true, "line": "- [x] milk" },
{ "text": "", "checked": false, "line": "- [ ]" },
{ "text": "", "checked": true, "line": "- [x]" }
],
"tags": [
{ "body": "#todo buy milk", "tags": ["todo"] },
{ "body": "buy #milk and #eggs", "tags": ["milk", "eggs"] },
{ "body": "#Todo and #todo", "tags": ["Todo"] },
{ "body": "line one\n#two", "tags": ["two"] },
{ "body": "#tag, and #more.", "tags": ["tag", "more"] },
{ "body": "#x-y_z9", "tags": ["x-y_z9"] },
{ "body": "#café #über", "tags": ["café", "über"] },
{ "body": "(#todo)", "tags": [] },
{ "body": "end.#tag", "tags": [] },
{ "body": "https://x.com/#section", "tags": [] },
{ "body": "#tag#more", "tags": ["tag"] },
{ "body": "a#b", "tags": [] },
{ "body": "x-#tag", "tags": [] },
{ "body": "##x", "tags": [] },
{ "body": "#1st", "tags": [] },
{ "body": "#2024", "tags": [] },
{ "body": "#_x", "tags": [] },
{ "body": "# heading", "tags": [] },
{ "body": "see https://x.com/page#frag", "tags": [] },
{ "body": "", "tags": [] }
],
"lifts": [
{ "body": "#todo\nreorganize the homepage", "standalone": ["todo"], "inline": [], "lifted": "reorganize the homepage" },
{ "body": "buy milk #grocery", "standalone": [], "inline": ["grocery"], "lifted": "buy milk #grocery" },
{ "body": "first\n\n#todo\n\nsecond", "standalone": ["todo"], "inline": [], "lifted": "first\n\nsecond" },
{ "body": "#todo\ncall #todo later", "standalone": [], "inline": ["todo"], "lifted": "call #todo later" },
{ "body": "#a #b", "standalone": [], "inline": ["a", "b"], "lifted": "#a #b" },
{ "body": "```\n# comment #todo\n```", "standalone": [], "inline": ["todo"], "lifted": "```\n# comment #todo\n```" },
{ "body": "(#todo)\nnotes", "standalone": [], "inline": [], "lifted": "(#todo)\nnotes" }
],
"display_titles": [
{ "body": "", "title": "" },
{ "body": "\n\n Shopping \nmilk", "title": "Shopping" },
{ "body": "- [ ] milk\n- [x] eggs", "title": "milk" },
{ "body": "- [ ]\n- [ ] bread", "title": "bread" },
{ "body": " \n\t\nlate start", "title": "late start" },
{ "body": "one\r\ntwo", "title": "one" },
{ "body": "a\rb\nc", "title": "a\rb" },
{ "body": "a\u2028b\nc", "title": "a\u2028b" },
{ "body": "ééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé", "title": "éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé" }
],
"urls": [
{ "body": "https://example.com/a", "found": ["https://example.com/a"], "lone": "https://example.com/a" },
{ "body": " https://example.com/a \n", "found": ["https://example.com/a"], "lone": "https://example.com/a" },
{ "body": "https://example.com/a.", "found": ["https://example.com/a"], "lone": "https://example.com/a" },
{ "body": "https://example.com/a?q=1!", "found": ["https://example.com/a?q=1"], "lone": "https://example.com/a?q=1" },
{ "body": "see https://example.com/a, then https://example.com/b.", "found": ["https://example.com/a", "https://example.com/b"], "lone": null },
{ "body": "<https://example.com/a>", "found": ["https://example.com/a"], "lone": null },
{ "body": "https://example.com/a)", "found": ["https://example.com/a"], "lone": null },
{ "body": "https://example.com/a https://example.com/a", "found": ["https://example.com/a"], "lone": null },
{ "body": "ftp://example.com/a", "found": [], "lone": null },
{ "body": "", "found": [], "lone": null }
],
"recurrences": ["daily", "weekly", "monthly", "yearly"],
"tint": {
"keys": ["red", "orange", "yellow", "green", "teal", "blue", "purple", "pink", "gray"],
"hashes": [
{ "id": "00000000-0000-0000-0000-000000000000", "hash": 3192360657, "color": "purple" },
{ "id": "11111111-1111-1111-1111-111111111111", "hash": 1031130113, "color": "blue" },
{ "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "hash": 4043892016, "color": "orange" },
{ "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "hash": 1533351232, "color": "orange" }
],
"names": [
{ "name": "todo", "color": "pink" },
{ "name": "grocery", "color": "blue" },
{ "name": "work", "color": "green" },
{ "name": "home", "color": "gray" },
{ "name": "ideas", "color": "green" },
{ "name": "reading", "color": "gray" },
{ "name": "urgent", "color": "red" }
]
}
}