Repository navigation
foam-core: read tasks into Resource - #1734
riccardoferretti wants to merge 3 commits into
Conversation
A task line is a GFM list item, at any depth and inside block quotes, whose checkbox has text after it on the same line, read by the block rules of core's parser (remark-parse 8 with GFM, frontmatter): code, frontmatter, HTML blocks, tables and link definitions hold none. Moved from the iPhone app's task model, where it was written to move here unchanged; only its imports change. A randomized test checks it against the parser on 3000 generated notes. Exported from the barrel: reading task lines and list items, the day links a task carries, and the edits that tick, schedule and unschedule a task. The app's test that the model imports only @foam/core and its own files stays behind: inside core that rule has no meaning, and the portability gate already rejects Node built-ins reachable from the barrel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finding inline code and the whitespace that ends a line used regexes that backtrack: a line of 100,000 backticks, or of tabs followed by a word, took seconds (CodeQL: polynomial regular expression). Inline code is now read by its backtick runs, as remark-parse 8 reads it: a run closes only at a run of exactly as many backticks, and a run nothing closes is read again from its next backtick. The regex let a longer run close a shorter one, hiding a day link that the parser shows. Trailing whitespace is found by walking back from the end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
riccardoferretti
left a comment
There was a problem hiding this comment.
here are my comments. in general i feel we could use more the model and less string operations. also we can add tasks to the Resource object and follow the same pattern we have for e.g. tags.
Also use the same pattern for utility fns that we use for Tags, Links, Resource, ... with the static fns
| /** The text after the checkbox and the space or tab that follows it. */ | ||
| text: string; | ||
| /** The days it links, as `YYYY-MM-DD`, each once, in the order written. */ | ||
| days: string[]; |
There was a problem hiding this comment.
why strings and not Dates? genuine question
There was a problem hiding this comment.
Deliberate, and unchanged. A day here is a calendar date, not a moment in time, and a Date is a moment. The same Date falls on different days in different time zones, and two Dates for the same day are not ===. Resources also go through JSON in the parser cache, where a Date comes back as a string anyway. YYYY-MM-DD is exactly what the link says and what daily notes are named, so it compares, sorts and is written back as it is.
🤖 Addressed by Claude Code
Tasks are now part of the parsed note, like tags: a parser plugin adds each list item with a GFM checkbox and text after it on its line to `Resource.tasks`, with its range (checkbox to end of line), status and text. The status is a `TaskStatus` enum, open or done for now. `Task` follows the model's pattern of an interface with static functions: `createStatusEdit`, `getDays`, `createScheduleEdits` and `createRemoveDayEdits`. Days come from the note's links; moving a day link goes through `MarkdownLink.createUpdateLinkEdit`. The line scanner that re-implemented remark's block rules goes, with its code-fence, frontmatter and day-link string helpers and the test comparing it to the parser. A wikilink the parser reads over an unclosed `[[` is not taken for a day link, so removing a day can't remove the text before it. The VS Code parser cache version goes to 8 for the new field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foam/corenow reads GFM tasks into the parsed note asResource.tasks, the same way it reads tags.Taskhas static functions that change a task's status and schedule it for a day by linking that day's note.What a task is
A task is a list item that the parser (remark-parse 8 with GFM, as core runs it) reads with a checkbox:
[ ],[x]or[X], then a space or a tab, then text on the same line. A tab between the brackets also reads as open. Tasks can be nested at any depth and can sit inside block quotes, and the parser already leaves out frontmatter, code and HTML blocks.- [ ]on its own is not a task.API
Resource.tasks: Task[]range(from its checkbox to the end of its line), itsstatus, and itstext(what follows the checkbox on that line)TaskStatusOpenorDone, stored as the strings'open'and'done'Task.createStatusEdit(task, status)Task.getDays(resource, task)YYYY-MM-DDdateTask.createScheduleEdits(resource, task, from, to)MarkdownLink.createUpdateLinkEdit), or adds[[to]]after its textTask.createRemoveDayEdits(resource, task, day)Also in this PR
Resourcenow setstasks: [], and the VS Code parser cache version goes from 7 to 8.[[2026-10-09# [[2026-10-09]]as a single link. A link like that, which runs across an unclosed[[, is not treated as a day link, so removing a day can't delete the text before it.Why
The iPhone app (
packages/foam-mobile, not on main yet) has its own task code. Once this is merged, the app will read tasks fromResource.tasksand edit them with these functions. Its live editor still has to find list items in text that hasn't been parsed yet, so it keeps its own line reader for that.Nothing else in core uses tasks yet. A workspace-wide
FoamTasksindex, likeFoamTags, can be added when something needs it.Testing
yarn build,yarn test:unitin every package, andyarn lintall pass. Core's benchmarks, run against a baseline taken before this change, show nothing slower.🤖 Generated with Claude Code