diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-16 15:22:08 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-16 15:22:08 +0200 |
| commit | e5e024a5f4c8709cf2a28dc3bfab79d8037516e1 (patch) | |
| tree | b458cc77b4bbd0580ed5248e90e78148b697199c /Sources/NorgKeyboardToolbar/EditorSnippet.swift | |
| parent | 3402f5764f794a60f85a152a13c4eacaa2a719f6 (diff) | |
Support all bare modifiers1.0.0
Diffstat (limited to 'Sources/NorgKeyboardToolbar/EditorSnippet.swift')
| -rw-r--r-- | Sources/NorgKeyboardToolbar/EditorSnippet.swift | 29 |
1 files changed, 28 insertions, 1 deletions
diff --git a/Sources/NorgKeyboardToolbar/EditorSnippet.swift b/Sources/NorgKeyboardToolbar/EditorSnippet.swift index b87d2b1..4773eac 100644 --- a/Sources/NorgKeyboardToolbar/EditorSnippet.swift +++ b/Sources/NorgKeyboardToolbar/EditorSnippet.swift @@ -29,7 +29,7 @@ enum EditorSnippet: String, CaseIterable, Identifiable, Sendable { switch self { case .heading where isInBareMarker(characters, at: position, markers: ["*"]): return promoteHeading(characters, at: position) - case .task where isInBareMarker(characters, at: position, markers: ["*", "~"]): + case .task where isInBareDetachedModifier(characters, at: position): return addTask(toMarker: characters, at: position) default: return insertOnNewLine(in: characters, cursor: position) @@ -38,6 +38,11 @@ enum EditorSnippet: String, CaseIterable, Identifiable, Sendable { // MARK: - Marker-aware behaviours + /// The detached-modifier markers that can carry a task status, matching + /// NorgKit's task scanner. Any of these — headings, un/ordered lists, + /// quotes, definitions, footnotes — may be turned into a task in place. + private static let detachedModifierMarkers: Set<Character> = ["*", "-", "~", ">", "$", "^", ":"] + /// Whether the is at a position with only `markers` and spaces, and contains /// at least one marker. private func isInBareMarker(_ characters: [Character], at position: Int, markers: Set<Character>) @@ -48,6 +53,28 @@ enum EditorSnippet: String, CaseIterable, Identifiable, Sendable { && prefix.allSatisfy { markers.contains($0) || $0 == " " } } + /// Whether the cursor sits just past a bare detached modifier: leading + /// spaces, a run of a single marker, then at least one space, with nothing + /// else before the cursor. The trailing space requirement keeps delimiters + /// such as `---` (which carry no whitespace) from being treated as tasks. + private func isInBareDetachedModifier(_ characters: [Character], at position: Int) -> Bool { + let lineStart = LineScanner.lineStart(in: characters, at: position) + var index = lineStart + while index < position, characters[index] == " " { index += 1 } + + guard index < position, Self.detachedModifierMarkers.contains(characters[index]) else { + return false + } + let marker = characters[index] + while index < position, characters[index] == marker { index += 1 } + + // A space must separate the marker run from the cursor, and the cursor may + // only sit within that run of separating spaces (i.e. before any content). + guard index < position, characters[index] == " " else { return false } + while index < position, characters[index] == " " { index += 1 } + return index == position + } + /// Adds a `*` after the leading run of spaces and stars on the cursor's line. private func promoteHeading(_ characters: [Character], at position: Int) -> ( text: String, cursor: Int |