feat: add SstData with DictionaryFile and word knowledge
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import Foundation
|
||||
|
||||
public struct DictionaryEntry: Identifiable, Equatable, Sendable {
|
||||
public enum Kind: Equatable, Sendable {
|
||||
case term(String)
|
||||
case correction(heard: String, wrote: String)
|
||||
}
|
||||
|
||||
public let id: UUID
|
||||
public var kind: Kind
|
||||
|
||||
public init(id: UUID = UUID(), kind: Kind) {
|
||||
self.id = id
|
||||
self.kind = kind
|
||||
}
|
||||
|
||||
/// Equality ignores `id`: two entries with the same content are the same entry.
|
||||
public static func == (lhs: DictionaryEntry, rhs: DictionaryEntry) -> Bool { lhs.kind == rhs.kind }
|
||||
|
||||
/// The text the correction pass listens for (a term listens for itself).
|
||||
public var source: String {
|
||||
switch kind {
|
||||
case .term(let term): term
|
||||
case .correction(let heard, _): heard
|
||||
}
|
||||
}
|
||||
|
||||
/// The text that ends up in the transcript.
|
||||
public var wrote: String {
|
||||
switch kind {
|
||||
case .term(let term): term
|
||||
case .correction(_, let wrote): wrote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum DictionaryLine: Equatable, Sendable {
|
||||
case entry(DictionaryEntry)
|
||||
case comment(String)
|
||||
case blank
|
||||
case invalid(String)
|
||||
}
|
||||
|
||||
/// The dictionary as a list of lines, so comments, blank lines and order survive an edit.
|
||||
public struct DictionaryFile: Equatable, Sendable {
|
||||
public var lines: [DictionaryLine]
|
||||
|
||||
public init(lines: [DictionaryLine] = []) {
|
||||
self.lines = lines
|
||||
}
|
||||
|
||||
public init(parsing text: String) {
|
||||
var raw = text.replacingOccurrences(of: "\r\n", with: "\n")
|
||||
.split(separator: "\n", omittingEmptySubsequences: false)
|
||||
.map(String.init)
|
||||
if raw.last == "" { raw.removeLast() }
|
||||
lines = raw.map(Self.parseLine)
|
||||
}
|
||||
|
||||
static func parseLine(_ line: String) -> DictionaryLine {
|
||||
let trimmed = line.trimmingCharacters(in: .whitespaces)
|
||||
if trimmed.isEmpty { return .blank }
|
||||
if trimmed.hasPrefix("#") { return .comment(line) }
|
||||
if let arrow = trimmed.range(of: "=>") {
|
||||
let heard = trimmed[..<arrow.lowerBound].trimmingCharacters(in: .whitespaces)
|
||||
let wrote = trimmed[arrow.upperBound...].trimmingCharacters(in: .whitespaces)
|
||||
guard !heard.isEmpty, !wrote.isEmpty else { return .invalid(line) }
|
||||
return .entry(DictionaryEntry(kind: .correction(heard: heard, wrote: wrote)))
|
||||
}
|
||||
return .entry(DictionaryEntry(kind: .term(trimmed)))
|
||||
}
|
||||
|
||||
public func serialized() -> String {
|
||||
let out = lines.map { line -> String in
|
||||
switch line {
|
||||
case .entry(let entry):
|
||||
switch entry.kind {
|
||||
case .term(let term): return term
|
||||
case .correction(let heard, let wrote): return "\(heard) => \(wrote)"
|
||||
}
|
||||
case .comment(let text), .invalid(let text): return text
|
||||
case .blank: return ""
|
||||
}
|
||||
}
|
||||
return out.isEmpty ? "" : out.joined(separator: "\n") + "\n"
|
||||
}
|
||||
|
||||
public var entries: [DictionaryEntry] {
|
||||
lines.compactMap { line in
|
||||
if case .entry(let entry) = line { return entry }
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public mutating func append(_ kind: DictionaryEntry.Kind) -> UUID {
|
||||
let entry = DictionaryEntry(kind: kind)
|
||||
lines.append(.entry(entry))
|
||||
return entry.id
|
||||
}
|
||||
|
||||
public mutating func update(id: UUID, kind: DictionaryEntry.Kind) {
|
||||
guard let index = lines.firstIndex(where: { if case .entry(let e) = $0 { return e.id == id } else { return false } }) else { return }
|
||||
lines[index] = .entry(DictionaryEntry(id: id, kind: kind))
|
||||
}
|
||||
|
||||
public mutating func remove(id: UUID) {
|
||||
lines.removeAll { if case .entry(let e) = $0 { return e.id == id } else { return false } }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user