Files
AzooKeyKanaKanjiConverter/Sources/KanaKanjiConverterModule/Candidate.swift
2023-08-02 18:29:33 +09:00

164 lines
5.6 KiB
Swift

//
// Candidate.swift
// Keyboard
//
// Created by ensan on 2020/10/26.
// Copyright © 2020 ensan. All rights reserved.
//
import Foundation
/// Data of clause.
final class ClauseDataUnit {
/// The MID of the clause.
var mid: Int = MIDData.EOS.mid
/// The LCID in the next clause.
var nextLcid = CIDData.EOS.cid
/// The text of the unit.
var text: String = ""
/// The range of the unit in input text.
var inputRange: Range<Int> = 0 ..< 0
/// Merge the given unit to this unit.
/// - Parameter:
/// - unit: The unit to merge.
func merge(with unit: ClauseDataUnit) {
self.text.append(unit.text)
self.inputRange = self.inputRange.startIndex ..< unit.inputRange.endIndex
self.nextLcid = unit.nextLcid
}
}
extension ClauseDataUnit: Equatable {
static func == (lhs: ClauseDataUnit, rhs: ClauseDataUnit) -> Bool {
lhs.mid == rhs.mid && lhs.nextLcid == rhs.nextLcid && lhs.text == rhs.text && lhs.inputRange == rhs.inputRange
}
}
#if DEBUG
extension ClauseDataUnit: CustomDebugStringConvertible {
var debugDescription: String {
"ClauseDataUnit(mid: \(mid), nextLcid: \(nextLcid), text: \(text), inputRange: \(inputRange))"
}
}
#endif
struct CandidateData {
typealias ClausesUnit = (clause: ClauseDataUnit, value: PValue)
var clauses: [ClausesUnit]
var data: [DicdataElement]
init(clauses: [ClausesUnit], data: [DicdataElement]) {
self.clauses = clauses
self.data = data
}
var lastClause: ClauseDataUnit? {
self.clauses.last?.clause
}
var isEmpty: Bool {
clauses.isEmpty
}
}
public enum CompleteAction: Sendable {
/// 調
case moveCursor(Int)
}
///
public struct Candidate: Sendable {
///
public var text: String
///
public let value: PValue
/// composingText.input
public var correspondingCount: Int
/// mid()
public let lastMid: Int
/// DicdataElement
public let data: [DicdataElement]
/// `action`
/// - note:
public var actions: [CompleteAction]
///
/// - note:
public let inputable: Bool
public init(text: String, value: PValue, correspondingCount: Int, lastMid: Int, data: [DicdataElement], actions: [CompleteAction] = [], inputable: Bool = true) {
self.text = text
self.value = value
self.correspondingCount = correspondingCount
self.lastMid = lastMid
self.data = data
self.actions = actions
self.inputable = inputable
}
/// `action`
/// - parameters:
/// - actions: `action`
@inlinable public mutating func withActions(_ actions: [CompleteAction]) {
self.actions = actions
}
private static let dateExpression = "<date format=\".*?\" type=\".*?\" language=\".*?\" delta=\".*?\" deltaunit=\".*?\">"
private static let randomExpression = "<random type=\".*?\" value=\".*?\">"
///
public static func parseTemplate(_ text: String) -> String {
var newText = text
while let range = newText.range(of: Self.dateExpression, options: .regularExpression) {
let templateString = String(newText[range])
let template = DateTemplateLiteral.import(from: templateString)
let value = template.previewString()
newText.replaceSubrange(range, with: value)
}
while let range = newText.range(of: Self.randomExpression, options: .regularExpression) {
let templateString = String(newText[range])
let template = RandomTemplateLiteral.import(from: templateString)
let value = template.previewString()
newText.replaceSubrange(range, with: value)
}
return newText.unescaped()
}
///
@inlinable public mutating func parseTemplate() {
// Candidate.textdata.map(\.word).join("")
// data
self.text = Self.parseTemplate(text)
}
/// prefixCandidate
public static func makePrefixClauseCandidate(data: some Collection<DicdataElement>) -> Candidate {
var text = ""
var correspondingCount = 0
var lastRcid = CIDData.BOS.cid
var lastMid = 501
var candidateData: [DicdataElement] = []
for item in data {
//
if DicdataStore.isClause(lastRcid, item.lcid) {
break
}
text.append(item.word)
correspondingCount += item.ruby.count
lastRcid = item.rcid
//
if item.mid != 500 && DicdataStore.includeMMValueCalculation(item) {
lastMid = item.mid
}
candidateData.append(item)
}
return Candidate(
text: text,
value: -5,
correspondingCount: correspondingCount,
lastMid: lastMid,
data: candidateData
)
}
}