A note's name is one rule, pinned by the shared fixture

The server and the core each derived display_title and disagreed twice: the
server cut it at 200 characters and the core didn't, and the server split lines
with splitlines(), which also breaks on a lone \r or a U+2028, where the core and
every other reading of the grammar split on \n alone.

- grammar.json gains a display_titles section: blank lines, markers, an empty
  item, \r\n, a lone \r, U+2028, and a 201-character line of 'é' (the cut is
  characters, not bytes).
- derive::display_title and DISPLAY_TITLE_CAP are the core's half, moved next to
  strip_marker. The server splits on "\n". Both suites run the cases.

Behaviour: a device now names a note with a first line over 200 characters the
way the web always has, and the server names a note containing a lone \r or a
U+2028 the way devices always have.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 14:27:32 -04:00
co-authored by Claude Opus 5.5
parent 2809c26214
commit db9e9a2dfb
5 changed files with 63 additions and 27 deletions
+14 -1
View File
@@ -1,9 +1,10 @@
{
"_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), the web (markdown.ts, colors.ts) and, for the tint, Android (DerivedTint.kt, which pins the same values by hand).",
"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.",
"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."
],
@@ -68,6 +69,18 @@
{ "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": "éééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé" }
],
"tint": {
"keys": ["red", "orange", "yellow", "green", "teal", "blue", "purple", "pink", "gray"],
"hashes": [