eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1import Foundation
2
3// The format-as-you-type brain (issue #890) — the native twin of the input
4// rules in web/static/composer-pm.js. Pure string-in, decision-out, so the
5// regex semantics are headless-testable; the editors (ComposerField /
6// MacComposerField) own WHEN to consult it (single typed characters only,
7// never during IME composition, never inside code) and how to apply the edit.
8//
9// The regex literals are transliterated from composer-pm.js and drift-gated:
10// ComposerRulesTests greps them out of the web file (the ComposerTypeaheadTests
11// pattern), so a web-side rule change fails the native build until mirrored.
12
13public enum RuleMark: Equatable {
14 case bold, italic, code, strike
15}
16
17public struct InlineCompletion: Equatable {
18 public let replaceRange: Range<Int> // UTF-16 offsets, relative to the given line
19 public let content: String
20 public let mark: RuleMark
21}
22
23// Order matters: strong before em, so the ** closer never half-fires * (the
24// composer-pm.js comment, kept). Underscores italicize too — never intraword
25// (the lookbehind keeps snake_case whole). Block conversion was deliberately
26// removed (composers stick to the inline basics; blocks stay render-only).
27private let inlineRules: [(NSRegularExpression, RuleMark)] = [
28 (try! NSRegularExpression(pattern: "\\*\\*([^*\\s](?:[^*]*[^*\\s])?)\\*\\*$"), .bold),
29 (try! NSRegularExpression(pattern: "(?<!\\*)\\*([^*\\s](?:[^*]*[^*\\s])?)\\*$"), .italic),
30 (try! NSRegularExpression(pattern: "(?<![\\w_])_([^_\\s](?:[^_]*[^_\\s])?)_$"), .italic),
31 (try! NSRegularExpression(pattern: "`([^`\\s](?:[^`]*[^`\\s])?)`$"), .code),
32 (try! NSRegularExpression(pattern: "(?<!~)~([^~\\s](?:[^~]*[^~\\s])?)~$"), .strike),
33]
34
35// The text from the start of the caret's line up to the caret. Returns the
36// completion to apply (replace the marker span with styled content) or nil.
37public func inlineCompletion(lineUpToCaret line: String) -> InlineCompletion? {
38 let ns = line as NSString
39 let full = NSRange(location: 0, length: ns.length)
40 for (re, mark) in inlineRules {
41 guard let m = re.firstMatch(in: line, range: full), m.range.location != NSNotFound else { continue }
42 let content = ns.substring(with: m.range(at: 1))
43 return InlineCompletion(replaceRange: m.range.location..<(m.range.location + m.range.length), content: content, mark: mark)
44 }
45 return nil
46}
47
48
49