Skip to content

calternal_notes_core::tasks::line

Inline checkbox lines: read-only parser and the byte-stable status writer. A Markdown checkbox has one open in-progress marker, so Blocked and Waiting fold to Doing for inline Tasks (#1092, DESIGN §§31, 40). Task Notes keep those statuses in frontmatter.

  • parse_task_line parses a single text line (from a day-file body or a tasks/ child line) into a TaskLine. It is pure and read-only — it never mutates the line.
  • set_task_line_status is the write-side counterpart: it flips ONLY the [.] checkbox marker of the line addressed by a block-id and preserves every other byte of the file.
  • task_root_checkbox_ordinal finds a rich Task mirror by its persisted reminder block ID, then its legacy unanchored title. This keeps an anchored same-title child independent (#659, DESIGN §9).
  • set_task_root_checkbox_status writes that mirror and leaves legacy child-only files unchanged.

The two share one checkbox-detection helper (checkbox_marker_span) so the reader and writer ALWAYS agree on which lines are checkbox tasks and where the marker window lives — a line the reader treats as a task is exactly the set the writer counts for ordinals and matches for ^id.

The projector (extractTaskIndex, plan 6 / T6) calls parse_task_line to turn every checkbox line in a day/body file or a tasks/ file’s body into a TaskLine, then assembles TaskIndexEntry rows from the results. Anchors use the Markdown block-ID grammar (ASCII letters, digits, - and _) so link and Task projections keep the same identity (#531).

Priority fold (4-level mapping from five emoji signifiers)

Section titled “Priority fold (4-level mapping from five emoji signifiers)”

The inline Task format defines five priority emoji; calternal tracks 4 levels. The fold:

  • 🔺 → Urgent
  • ⏫ → High
  • 🔼 → Medium
  • 🔽 → Low
  • ⏬ → Low ← the format’s “Lowest” is folded into our Low tier because we have no “Lowest” concept and it is semantically closest to Low.

Root mirror readers and writers match literal frontmatter titles through one predicate; Task sigils in a Note title must not create a second Task (#940; DESIGN §40).

The title field is the residual after removing ALL of:

  • the leading \s*- [.] checkbox prefix
  • every emoji metadata token (📅, 🛫, ⏳, ➕, ✅, ❌, 🔁) and their following date/rule value
  • priority emoji (🔺, ⏫, 🔼, 🔽, ⏬)
  • a reference link to tasks/<file>, arrow-prefixed or bare, in EITHER accepted spelling (#27 T11, dual-form read rule): [[tasks/<file>]] (legacy wikilink) or [<text>](tasks/<file>) (the standard form the app now WRITES — spec A §C.1)
  • ^blockid anchors
  • #tag tokens Surrounding whitespace is trimmed from what remains.

Tags keep their original source span for title stripping and are returned in NFC form so decomposed and composed spellings share one name (#473).

Source: crates/calternal-notes-core/src/tasks/line.rs

pub struct TaskLinePatch

Byte-stable edits to the supported scalar fields of one anchored checkbox. Each Some(None) removes that field; None leaves it unchanged.

Fields

  • pub title: Option<String>
  • pub due: Option<Option<CivilDate>>
  • pub scheduled: Option<Option<CivilDate>>
  • pub start: Option<Option<CivilDate>>
  • pub completed: Option<Option<CivilDate>>
  • pub priority: Option<Option<TaskPriority>>

Implements: Clone, Debug, Default, PartialEq, Eq

Source: crates/calternal-notes-core/src/tasks/line.rs:590

pub fn remove_task_line(text: &str, block_id: &str) -> Result<String, String>

Remove exactly one checkbox line addressed by its explicit block ID.

The writer rejects missing or duplicate IDs so a stale CalDAV resource cannot delete an arbitrary sibling. It preserves the line endings and every byte outside the selected line.

Source: crates/calternal-notes-core/src/tasks/line.rs:642

pub fn set_task_line_properties(
text: &str,
block_id: &str,
patch: &TaskLinePatch,
) -> Result<String, String>

Update fields on the checkbox with this explicit block ID. The writer does not accept ordinal fallbacks because CalDAV resource identities must remain stable when a sibling checkbox is inserted or removed.

Source: crates/calternal-notes-core/src/tasks/line.rs:602

pub fn set_task_line_status(
text: &str,
block_id: &str,
status: TaskStatus,
) -> Result<String, String>

Flip ONLY the checkbox marker of the task line addressed by block_id, preserving every other byte of text.

This is the write-side counterpart to [parse_task_line]: the UI calls it to tick/untick an inline checkbox by its block-id. It round-trips with the byte-stable frontmatter writer.

Todo→[ ], Doing/Blocked/Waiting→[/], Done→[x], and Cancelled→[-]. Inline checkboxes have no separate Blocked or Waiting marker, so those states fold to the Markdown in-progress marker. Task Notes retain the exact status in their frontmatter (#1092, DESIGN §§31, 40).

block_id is matched against a checkbox line’s trailing ^<id>. If no line carries that literal id AND block_id has the form L<n>, it addresses the n-th checkbox line, 0-based — the ${source}#L${ordinal} taskId fallback (contract §3). A literal ^L<n> always wins over the ordinal interpretation. No match at all → Ok(text.to_string()) unchanged (a no-op; the caller logs).

We splice: unchanged prefix + the new marker + unchanged suffix, using the located byte range [open, close) — never a hardcoded width. For the ASCII markers this writer emits ([ ], [x], [/], [-]) that window is 3 bytes, but the splice is driven by the detected range, not the constant. Only the [.] window on the matched line is touched — emoji, dates, tags, the block-id, the line’s EOL, and every other line are preserved verbatim. Idempotent: if the window already equals the target marker we return the input unchanged (byte-identical, no splice).

Source: crates/calternal-notes-core/src/tasks/line.rs:397

pub fn set_task_root_checkbox_status(
text: &str,
root_title: &str,
status: TaskStatus,
) -> Result<String, String>

Set the checkbox marker for a rich Task’s generated root mirror.

The explicit root reminder ID identifies an anchored mirror; legacy files fall back to an unanchored title match. If neither exists, a child-only Task file stays unchanged (#659, DESIGN §§9, 41).

Source: crates/calternal-notes-core/src/tasks/line.rs:576

pub fn set_task_root_checkbox_title(
text: &str,
old_title: &str,
new_title: &str,
) -> Result<String, String>

Rename the root checkbox that mirrors a rich Task’s frontmatter title.

Prefer the persisted root reminder ID, then fall back to an unanchored title match. A same-title anchored child stays unchanged. The splice keeps line endings and task metadata intact.

Source: crates/calternal-notes-core/src/tasks/line.rs:527

pub fn task_root_checkbox_status(text: &str, root_title: &str) -> Option<TaskStatus>

Read the marker from the explicit root checkbox or its legacy unanchored mirror.

Source: crates/calternal-notes-core/src/tasks/line.rs:514