Repo // Refactor APIs related to LM access and configs.
This commit is contained in:
parent
23ef3124d4
commit
586822c981
|
@ -30,26 +30,36 @@ public extension vChewingLM {
|
|||
/// LMI 會根據需要分別載入原廠語言模組和其他個別的子語言模組。LMI 本身不會記錄這些
|
||||
/// 語言模組的相關資料的存放位置,僅藉由參數來讀取相關訊息。
|
||||
class LMInstantiator: LangModelProtocol {
|
||||
public struct Config {
|
||||
public var isCassetteEnabled = false
|
||||
public var isPhraseReplacementEnabled = false
|
||||
public var isCNSEnabled = false
|
||||
public var isSymbolEnabled = false
|
||||
public var isSCPCEnabled = false
|
||||
public var deltaOfCalendarYears: Int = -2000
|
||||
}
|
||||
|
||||
// SQLite 連線所在的記憶體位置。
|
||||
static var ptrSQL: OpaquePointer?
|
||||
|
||||
// SQLite 連線是否已經建立。
|
||||
public private(set) static var isSQLDBConnected: Bool = false
|
||||
|
||||
// 簡體中文模型?
|
||||
public let isCHS: Bool
|
||||
|
||||
// 在函式內部用以記錄狀態的開關。
|
||||
public var isCassetteEnabled = false
|
||||
public var isPhraseReplacementEnabled = false
|
||||
public var isCNSEnabled = false
|
||||
public var isSymbolEnabled = false
|
||||
public var isSCPCEnabled = false
|
||||
public var isCHS = false
|
||||
public var deltaOfCalendarYears: Int = -2000
|
||||
public var config = Config()
|
||||
|
||||
// 這句需要留著,不然無法被 package 外界存取。
|
||||
public init(isCHS: Bool = false) {
|
||||
self.isCHS = isCHS
|
||||
}
|
||||
|
||||
public func setOptions(handler: (inout Config) -> Void) {
|
||||
handler(&config)
|
||||
}
|
||||
|
||||
@discardableResult public static func connectSQLDB(dbPath: String, dropPreviousConnection: Bool = true) -> Bool {
|
||||
if dropPreviousConnection { disconnectSQLDB() }
|
||||
vCLog("Establishing SQLite connection to: \(dbPath)")
|
||||
|
@ -315,10 +325,10 @@ public extension vChewingLM {
|
|||
/// 準備不同的語言模組容器,開始逐漸往容器陣列內塞入資料。
|
||||
var rawAllUnigrams: [Megrez.Unigram] = []
|
||||
|
||||
if isCassetteEnabled { rawAllUnigrams += Self.lmCassette.unigramsFor(key: keyChain) }
|
||||
if config.isCassetteEnabled { rawAllUnigrams += Self.lmCassette.unigramsFor(key: keyChain) }
|
||||
|
||||
// 如果有檢測到使用者自訂逐字選字語料庫內的相關資料的話,在這裡先插入。
|
||||
if isSCPCEnabled {
|
||||
if config.isSCPCEnabled {
|
||||
rawAllUnigrams += lmPlainBopomofo.valuesFor(key: keyChain).map { Megrez.Unigram(value: $0, score: 0) }
|
||||
}
|
||||
|
||||
|
@ -327,18 +337,18 @@ public extension vChewingLM {
|
|||
// 將兩句差分也是為了讓 rawUserUnigrams 的類型不受可能的影響。
|
||||
rawAllUnigrams += lmUserPhrases.unigramsFor(key: keyChain).reversed()
|
||||
|
||||
if !isCassetteEnabled || isCassetteEnabled && keyChain.map(\.description)[0] == "_" {
|
||||
if !config.isCassetteEnabled || config.isCassetteEnabled && keyChain.map(\.description)[0] == "_" {
|
||||
// LMMisc 與 LMCore 的 score 在 (-10.0, 0.0) 這個區間內。
|
||||
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataCHEW)
|
||||
rawAllUnigrams += factoryCoreUnigramsFor(key: keyChain)
|
||||
if isCNSEnabled {
|
||||
if config.isCNSEnabled {
|
||||
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataCNS)
|
||||
}
|
||||
}
|
||||
|
||||
if isSymbolEnabled {
|
||||
if config.isSymbolEnabled {
|
||||
rawAllUnigrams += lmUserSymbols.unigramsFor(key: keyChain)
|
||||
if !isCassetteEnabled {
|
||||
if !config.isCassetteEnabled {
|
||||
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataSYMB)
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +373,7 @@ public extension vChewingLM {
|
|||
}
|
||||
|
||||
// 提前處理語彙置換。
|
||||
if isPhraseReplacementEnabled {
|
||||
if config.isPhraseReplacementEnabled {
|
||||
for i in 0 ..< rawAllUnigrams.count {
|
||||
let newValue = lmReplacements.valuesFor(key: rawAllUnigrams[i].value)
|
||||
guard !newValue.isEmpty else { continue }
|
||||
|
|
|
@ -19,14 +19,14 @@ extension vChewingLM.LMInstantiator {
|
|||
|
||||
func processDateWithDayDelta(_ delta: Int) {
|
||||
tokens = ["MACRO@DATE_DAYDELTA:\(delta)"]
|
||||
if deltaOfCalendarYears != 0 { tokens.append("MACRO@DATE_DAYDELTA:\(delta)_YEARDELTA:\(deltaOfCalendarYears)") }
|
||||
if config.deltaOfCalendarYears != 0 { tokens.append("MACRO@DATE_DAYDELTA:\(delta)_YEARDELTA:\(config.deltaOfCalendarYears)") }
|
||||
tokens.append("MACRO@DATE_DAYDELTA:\(delta)_SHORTENED")
|
||||
tokens.append("MACRO@DATE_DAYDELTA:\(delta)_LUNA")
|
||||
}
|
||||
|
||||
func processYearWithYearDelta(_ delta: Int) {
|
||||
tokens = ["MACRO@YEAR_YEARDELTA:\(delta)"]
|
||||
if deltaOfCalendarYears != 0 { tokens.append("MACRO@YEAR_YEARDELTA:\(delta + deltaOfCalendarYears)") }
|
||||
if config.deltaOfCalendarYears != 0 { tokens.append("MACRO@YEAR_YEARDELTA:\(delta + config.deltaOfCalendarYears)") }
|
||||
tokens.append("MACRO@YEAR_GANZHI_YEARDELTA:\(delta)")
|
||||
tokens.append("MACRO@YEAR_ZODIAC_YEARDELTA:\(delta)")
|
||||
}
|
||||
|
|
|
@ -59,8 +59,10 @@ final class InputTokenTests: XCTestCase {
|
|||
func testGeneratedResultsFromLMInstantiator() throws {
|
||||
let instance = vChewingLM.LMInstantiator(isCHS: true)
|
||||
XCTAssertTrue(vChewingLM.LMInstantiator.connectToTestSQLDB())
|
||||
instance.isCNSEnabled = false
|
||||
instance.isSymbolEnabled = false
|
||||
instance.setOptions { config in
|
||||
config.isCNSEnabled = false
|
||||
config.isSymbolEnabled = false
|
||||
}
|
||||
instance.insertTemporaryData(
|
||||
keyArray: ["ㄐㄧㄣ", "ㄊㄧㄢ", "ㄖˋ", "ㄑㄧˊ"],
|
||||
unigram: .init(value: "MACRO@DATE_YEARDELTA:-1945", score: -97.5),
|
||||
|
|
|
@ -24,14 +24,18 @@ final class LMInstantiatorSQLTests: XCTestCase {
|
|||
func testSQL() throws {
|
||||
let instance = vChewingLM.LMInstantiator(isCHS: true)
|
||||
XCTAssertTrue(vChewingLM.LMInstantiator.connectToTestSQLDB())
|
||||
instance.isCNSEnabled = false
|
||||
instance.isSymbolEnabled = false
|
||||
instance.setOptions { config in
|
||||
config.isCNSEnabled = false
|
||||
config.isSymbolEnabled = false
|
||||
}
|
||||
XCTAssertEqual(instance.unigramsFor(keyArray: strBloatingKey).description, "[(吹牛逼,-7.375), (吹牛屄,-7.399)]")
|
||||
XCTAssertEqual(instance.unigramsFor(keyArray: strHaninSymbolMenuKey)[1].description, "(,,-9.9)")
|
||||
XCTAssertEqual(instance.unigramsFor(keyArray: strRefutationKey).description, "[(㨃,-9.544)]")
|
||||
XCTAssertEqual(instance.unigramsFor(keyArray: strBoobsKey).description, "[(ㄋㄟㄋㄟ,-1.0)]")
|
||||
instance.isCNSEnabled = true
|
||||
instance.isSymbolEnabled = true
|
||||
instance.setOptions { config in
|
||||
config.isCNSEnabled = true
|
||||
config.isSymbolEnabled = true
|
||||
}
|
||||
XCTAssertEqual(instance.unigramsFor(keyArray: strBloatingKey).last?.description, "(🌳🆕🐝,-13.0)")
|
||||
XCTAssertEqual(instance.unigramsFor(keyArray: strHaninSymbolMenuKey)[1].description, "(,,-9.9)")
|
||||
XCTAssertEqual(instance.unigramsFor(keyArray: strRefutationKey).count, 10)
|
||||
|
|
|
@ -12,50 +12,42 @@ import NotifierUI
|
|||
import Shared
|
||||
import SwiftExtension
|
||||
|
||||
// MARK: - Input Mode Extension for Language Models
|
||||
|
||||
public extension Shared.InputMode {
|
||||
private static let lmCHS = vChewingLM.LMInstantiator(isCHS: true)
|
||||
private static let lmCHT = vChewingLM.LMInstantiator(isCHS: false)
|
||||
private static let uomCHS = vChewingLM.LMUserOverride(dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHS))
|
||||
private static let uomCHT = vChewingLM.LMUserOverride(dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHT))
|
||||
|
||||
var langModel: vChewingLM.LMInstantiator {
|
||||
switch self {
|
||||
case .imeModeCHS: return Self.lmCHS
|
||||
case .imeModeCHT: return Self.lmCHT
|
||||
case .imeModeNULL: return .init()
|
||||
}
|
||||
}
|
||||
|
||||
var uom: vChewingLM.LMUserOverride {
|
||||
switch self {
|
||||
case .imeModeCHS: return Self.uomCHS
|
||||
case .imeModeCHT: return Self.uomCHT
|
||||
case .imeModeNULL: return .init(dataURL: LMMgr.userOverrideModelDataURL(IMEApp.currentInputMode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Language Model Manager.
|
||||
|
||||
public class LMMgr {
|
||||
public static var shared = LMMgr()
|
||||
public private(set) static var lmCHS = vChewingLM.LMInstantiator(isCHS: true)
|
||||
public private(set) static var lmCHT = vChewingLM.LMInstantiator(isCHS: false)
|
||||
public private(set) static var uomCHS = vChewingLM.LMUserOverride(
|
||||
dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHS))
|
||||
public private(set) static var uomCHT = vChewingLM.LMUserOverride(
|
||||
dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHT))
|
||||
|
||||
public static var currentLM: vChewingLM.LMInstantiator {
|
||||
Self.getLM(mode: IMEApp.currentInputMode)
|
||||
}
|
||||
|
||||
public static var currentUOM: vChewingLM.LMUserOverride {
|
||||
Self.getUOM(mode: IMEApp.currentInputMode)
|
||||
}
|
||||
|
||||
public static func getLM(mode: Shared.InputMode) -> vChewingLM.LMInstantiator {
|
||||
switch mode {
|
||||
case .imeModeCHS:
|
||||
return Self.lmCHS
|
||||
case .imeModeCHT:
|
||||
return Self.lmCHT
|
||||
case .imeModeNULL:
|
||||
return .init()
|
||||
}
|
||||
}
|
||||
|
||||
public static func getUOM(mode: Shared.InputMode) -> vChewingLM.LMUserOverride {
|
||||
switch mode {
|
||||
case .imeModeCHS:
|
||||
return Self.uomCHS
|
||||
case .imeModeCHT:
|
||||
return Self.uomCHT
|
||||
case .imeModeNULL:
|
||||
return .init(dataURL: Self.userOverrideModelDataURL(IMEApp.currentInputMode))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions reacting directly with language models.
|
||||
|
||||
public static func initUserLangModels() {
|
||||
Self.chkUserLMFilesExist(.imeModeCHT)
|
||||
Self.chkUserLMFilesExist(.imeModeCHS)
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
Self.chkUserLMFilesExist(mode)
|
||||
}
|
||||
// LMMgr 的 loadUserPhrases 等函式在自動讀取 dataFolderPath 時,
|
||||
// 如果發現自訂目錄不可用,則會自動抹去自訂目錄設定、改採預設目錄。
|
||||
// 所以這裡不需要特別處理。
|
||||
|
@ -84,83 +76,69 @@ public class LMMgr {
|
|||
|
||||
public static func loadUserPhrasesData(type: vChewingLM.ReplacableUserDataType? = nil) {
|
||||
guard let type = type else {
|
||||
Self.lmCHT.loadUserPhrasesData(
|
||||
path: userDictDataURL(mode: .imeModeCHT, type: .thePhrases).path,
|
||||
filterPath: userDictDataURL(mode: .imeModeCHT, type: .theFilter).path
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
mode.langModel.loadUserPhrasesData(
|
||||
path: userDictDataURL(mode: mode, type: .thePhrases).path,
|
||||
filterPath: userDictDataURL(mode: mode, type: .theFilter).path
|
||||
)
|
||||
Self.lmCHS.loadUserPhrasesData(
|
||||
path: userDictDataURL(mode: .imeModeCHS, type: .thePhrases).path,
|
||||
filterPath: userDictDataURL(mode: .imeModeCHS, type: .theFilter).path
|
||||
)
|
||||
Self.lmCHT.loadUserSymbolData(path: userDictDataURL(mode: .imeModeCHT, type: .theSymbols).path)
|
||||
Self.lmCHS.loadUserSymbolData(path: userDictDataURL(mode: .imeModeCHS, type: .theSymbols).path)
|
||||
mode.langModel.loadUserSymbolData(path: userDictDataURL(mode: mode, type: .theSymbols).path)
|
||||
mode.uom.loadData(fromURL: userOverrideModelDataURL(mode))
|
||||
}
|
||||
|
||||
if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() }
|
||||
if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() }
|
||||
if PrefMgr.shared.useSCPCTypingMode { Self.loadSCPCSequencesData() }
|
||||
|
||||
Self.uomCHT.loadData(fromURL: userOverrideModelDataURL(.imeModeCHT))
|
||||
Self.uomCHS.loadData(fromURL: userOverrideModelDataURL(.imeModeCHS))
|
||||
|
||||
CandidateNode.load(url: Self.userSymbolMenuDataURL())
|
||||
return
|
||||
}
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
switch type {
|
||||
case .thePhrases:
|
||||
Self.lmCHT.loadUserPhrasesData(
|
||||
path: userDictDataURL(mode: .imeModeCHT, type: .thePhrases).path,
|
||||
filterPath: nil
|
||||
)
|
||||
Self.lmCHS.loadUserPhrasesData(
|
||||
path: userDictDataURL(mode: .imeModeCHS, type: .thePhrases).path,
|
||||
mode.langModel.loadUserPhrasesData(
|
||||
path: userDictDataURL(mode: mode, type: .thePhrases).path,
|
||||
filterPath: nil
|
||||
)
|
||||
case .theFilter:
|
||||
DispatchQueue.main.async {
|
||||
Self.reloadUserFilterDirectly(mode: IMEApp.currentInputMode)
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
Self.reloadUserFilterDirectly(mode: IMEApp.currentInputMode.reversed)
|
||||
Self.reloadUserFilterDirectly(mode: mode)
|
||||
}
|
||||
case .theReplacements:
|
||||
if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() }
|
||||
case .theAssociates:
|
||||
if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() }
|
||||
case .theSymbols:
|
||||
Self.lmCHT.loadUserSymbolData(
|
||||
path: Self.userDictDataURL(mode: .imeModeCHT, type: .theSymbols).path
|
||||
)
|
||||
Self.lmCHS.loadUserSymbolData(
|
||||
path: Self.userDictDataURL(mode: .imeModeCHS, type: .theSymbols).path
|
||||
mode.langModel.loadUserSymbolData(
|
||||
path: Self.userDictDataURL(mode: mode, type: .theSymbols).path
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func loadUserAssociatesData() {
|
||||
Self.lmCHT.loadUserAssociatesData(
|
||||
path: Self.userDictDataURL(mode: .imeModeCHT, type: .theAssociates).path
|
||||
)
|
||||
Self.lmCHS.loadUserAssociatesData(
|
||||
path: Self.userDictDataURL(mode: .imeModeCHS, type: .theAssociates).path
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
mode.langModel.loadUserAssociatesData(
|
||||
path: Self.userDictDataURL(mode: mode, type: .theAssociates).path
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public static func loadUserPhraseReplacement() {
|
||||
Self.lmCHT.loadReplacementsData(
|
||||
path: Self.userDictDataURL(mode: .imeModeCHT, type: .theReplacements).path
|
||||
)
|
||||
Self.lmCHS.loadReplacementsData(
|
||||
path: Self.userDictDataURL(mode: .imeModeCHS, type: .theReplacements).path
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
mode.langModel.loadReplacementsData(
|
||||
path: Self.userDictDataURL(mode: mode, type: .theReplacements).path
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public static func loadSCPCSequencesData() {
|
||||
Self.lmCHT.loadSCPCSequencesData()
|
||||
Self.lmCHS.loadSCPCSequencesData()
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
mode.langModel.loadSCPCSequencesData()
|
||||
}
|
||||
}
|
||||
|
||||
public static func reloadUserFilterDirectly(mode: Shared.InputMode) {
|
||||
Self.getLM(mode: mode).reloadUserFilterDirectly(path: userDictDataURL(mode: mode, type: .theFilter).path)
|
||||
mode.langModel.reloadUserFilterDirectly(path: userDictDataURL(mode: mode, type: .theFilter).path)
|
||||
}
|
||||
|
||||
public static func checkIfPhrasePairExists(
|
||||
|
@ -169,7 +147,7 @@ public class LMMgr {
|
|||
keyArray: [String],
|
||||
factoryDictionaryOnly: Bool = false
|
||||
) -> Bool {
|
||||
Self.getLM(mode: mode).hasKeyValuePairFor(
|
||||
mode.langModel.hasKeyValuePairFor(
|
||||
keyArray: keyArray, value: userPhrase, factoryDictionaryOnly: factoryDictionaryOnly
|
||||
)
|
||||
}
|
||||
|
@ -179,7 +157,7 @@ public class LMMgr {
|
|||
mode: Shared.InputMode,
|
||||
keyArray: [String]
|
||||
) -> Bool {
|
||||
Self.getLM(mode: mode).isPairFiltered(pair: .init(keyArray: keyArray, value: userPhrase))
|
||||
mode.langModel.isPairFiltered(pair: .init(keyArray: keyArray, value: userPhrase))
|
||||
}
|
||||
|
||||
public static func countPhrasePairs(
|
||||
|
@ -187,39 +165,22 @@ public class LMMgr {
|
|||
mode: Shared.InputMode,
|
||||
factoryDictionaryOnly: Bool = false
|
||||
) -> Int {
|
||||
Self.getLM(mode: mode).countKeyValuePairs(
|
||||
mode.langModel.countKeyValuePairs(
|
||||
keyArray: keyArray, factoryDictionaryOnly: factoryDictionaryOnly
|
||||
)
|
||||
}
|
||||
|
||||
public static func setPhraseReplacementEnabled(_ state: Bool) {
|
||||
Self.lmCHT.isPhraseReplacementEnabled = state
|
||||
Self.lmCHS.isPhraseReplacementEnabled = state
|
||||
public static func syncLMPrefs() {
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
mode.langModel.setOptions { config in
|
||||
config.isPhraseReplacementEnabled = PrefMgr.shared.phraseReplacementEnabled
|
||||
config.isCNSEnabled = PrefMgr.shared.cns11643Enabled
|
||||
config.isSymbolEnabled = PrefMgr.shared.symbolInputEnabled
|
||||
config.isSCPCEnabled = PrefMgr.shared.useSCPCTypingMode
|
||||
config.isCassetteEnabled = PrefMgr.shared.cassetteEnabled
|
||||
config.deltaOfCalendarYears = PrefMgr.shared.deltaOfCalendarYears
|
||||
}
|
||||
|
||||
public static func setCNSEnabled(_ state: Bool) {
|
||||
Self.lmCHT.isCNSEnabled = state
|
||||
Self.lmCHS.isCNSEnabled = state
|
||||
}
|
||||
|
||||
public static func setSymbolEnabled(_ state: Bool) {
|
||||
Self.lmCHT.isSymbolEnabled = state
|
||||
Self.lmCHS.isSymbolEnabled = state
|
||||
}
|
||||
|
||||
public static func setSCPCEnabled(_ state: Bool) {
|
||||
Self.lmCHT.isSCPCEnabled = state
|
||||
Self.lmCHS.isSCPCEnabled = state
|
||||
}
|
||||
|
||||
public static func setCassetteEnabled(_ state: Bool) {
|
||||
Self.lmCHT.isCassetteEnabled = state
|
||||
Self.lmCHS.isCassetteEnabled = state
|
||||
}
|
||||
|
||||
public static func setDeltaOfCalendarYears(_ delta: Int) {
|
||||
Self.lmCHT.deltaOfCalendarYears = delta
|
||||
Self.lmCHS.deltaOfCalendarYears = delta
|
||||
}
|
||||
|
||||
// MARK: UOM
|
||||
|
@ -227,26 +188,23 @@ public class LMMgr {
|
|||
public static func saveUserOverrideModelData() {
|
||||
let globalQueue = DispatchQueue(label: "vChewingLM_UOM", qos: .unspecified, attributes: .concurrent)
|
||||
let group = DispatchGroup()
|
||||
Shared.InputMode.validCases.forEach { mode in
|
||||
group.enter()
|
||||
globalQueue.async {
|
||||
Self.uomCHT.saveData(toURL: userOverrideModelDataURL(.imeModeCHT))
|
||||
mode.uom.saveData(toURL: userOverrideModelDataURL(mode))
|
||||
group.leave()
|
||||
}
|
||||
group.enter()
|
||||
globalQueue.async {
|
||||
Self.uomCHS.saveData(toURL: userOverrideModelDataURL(.imeModeCHS))
|
||||
group.leave()
|
||||
}
|
||||
_ = group.wait(timeout: .distantFuture)
|
||||
group.notify(queue: DispatchQueue.main) {}
|
||||
}
|
||||
|
||||
public static func bleachSpecifiedSuggestions(targets: [String], mode: Shared.InputMode) {
|
||||
Self.getUOM(mode: mode).bleachSpecifiedSuggestions(targets: targets, saveCallback: { Self.getUOM(mode: mode).saveData() })
|
||||
mode.uom.bleachSpecifiedSuggestions(targets: targets, saveCallback: { mode.uom.saveData() })
|
||||
}
|
||||
|
||||
public static func removeUnigramsFromUserOverrideModel(_ mode: Shared.InputMode) {
|
||||
Self.getUOM(mode: mode).bleachUnigrams(saveCallback: { Self.getUOM(mode: mode).saveData() })
|
||||
mode.uom.bleachUnigrams(saveCallback: { mode.uom.saveData() })
|
||||
}
|
||||
|
||||
public static func relocateWreckedUOMData() {
|
||||
|
@ -268,6 +226,6 @@ public class LMMgr {
|
|||
}
|
||||
|
||||
public static func clearUserOverrideModelData(_ mode: Shared.InputMode = .imeModeNULL) {
|
||||
Self.getUOM(mode: mode).clearData(withURL: userOverrideModelDataURL(mode))
|
||||
mode.uom.clearData(withURL: userOverrideModelDataURL(mode))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public extension LMMgr {
|
|||
}
|
||||
|
||||
public var isAlreadyFiltered: Bool {
|
||||
LMMgr.getLM(mode: inputMode).isPairFiltered(pair: .init(keyArray: keyArray, value: value))
|
||||
inputMode.langModel.isPairFiltered(pair: .init(keyArray: keyArray, value: value))
|
||||
}
|
||||
|
||||
public func write(toFilter: Bool) -> Bool {
|
||||
|
|
|
@ -230,21 +230,21 @@ import SwiftExtension
|
|||
@AppProperty(key: UserDef.kCNS11643Enabled.rawValue, defaultValue: false)
|
||||
public dynamic var cns11643Enabled: Bool {
|
||||
didSet {
|
||||
LMMgr.setCNSEnabled(cns11643Enabled) // 很重要
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
}
|
||||
|
||||
@AppProperty(key: UserDef.kSymbolInputEnabled.rawValue, defaultValue: true)
|
||||
public dynamic var symbolInputEnabled: Bool {
|
||||
didSet {
|
||||
LMMgr.setSymbolEnabled(symbolInputEnabled) // 很重要
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
}
|
||||
|
||||
@AppProperty(key: UserDef.kCassetteEnabled.rawValue, defaultValue: false)
|
||||
public dynamic var cassetteEnabled: Bool {
|
||||
didSet {
|
||||
LMMgr.setCassetteEnabled(cassetteEnabled) // 很重要
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,7 +325,7 @@ import SwiftExtension
|
|||
willSet {
|
||||
if newValue {
|
||||
LMMgr.loadSCPCSequencesData()
|
||||
LMMgr.setSCPCEnabled(true)
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ import SwiftExtension
|
|||
@AppProperty(key: UserDef.kPhraseReplacementEnabled.rawValue, defaultValue: false)
|
||||
public dynamic var phraseReplacementEnabled: Bool {
|
||||
willSet {
|
||||
LMMgr.setPhraseReplacementEnabled(newValue)
|
||||
LMMgr.syncLMPrefs()
|
||||
if newValue {
|
||||
LMMgr.loadUserPhraseReplacement()
|
||||
}
|
||||
|
|
|
@ -177,8 +177,8 @@ public class SessionCtl: IMKInputController {
|
|||
resetInputHandler()
|
||||
// ----------------------------
|
||||
/// 重設所有語言模組。這裡不需要做按需重設,因為對運算量沒有影響。
|
||||
inputHandler?.currentLM = LMMgr.currentLM // 會自動更新組字引擎內的模組。
|
||||
inputHandler?.currentUOM = LMMgr.currentUOM
|
||||
inputHandler?.currentLM = inputMode.langModel // 會自動更新組字引擎內的模組。
|
||||
inputHandler?.currentUOM = inputMode.uom
|
||||
/// 清空注拼槽+同步最新的注拼槽排列設定。
|
||||
inputHandler?.ensureKeyboardParser()
|
||||
/// 將輸入法偏好設定同步至語言模組內。
|
||||
|
@ -215,7 +215,7 @@ public class SessionCtl: IMKInputController {
|
|||
Self.current?.hidePalettes()
|
||||
Self.current = self
|
||||
self.inputHandler = InputHandler(
|
||||
lm: LMMgr.currentLM, uom: LMMgr.currentUOM, pref: PrefMgr.shared
|
||||
lm: self.inputMode.langModel, uom: self.inputMode.uom, pref: PrefMgr.shared
|
||||
)
|
||||
self.inputHandler?.delegate = self
|
||||
self.syncBaseLMPrefs()
|
||||
|
@ -314,7 +314,7 @@ public extension SessionCtl {
|
|||
|
||||
// 這裡不需要 setValue(),因為 IMK 會在自動呼叫 activateServer() 之後自動執行 setValue()。
|
||||
self.inputHandler = InputHandler(
|
||||
lm: LMMgr.currentLM, uom: LMMgr.currentUOM, pref: PrefMgr.shared
|
||||
lm: self.inputMode.langModel, uom: self.inputMode.uom, pref: PrefMgr.shared
|
||||
)
|
||||
self.inputHandler?.delegate = self
|
||||
self.syncBaseLMPrefs()
|
||||
|
@ -408,12 +408,7 @@ public extension SessionCtl {
|
|||
|
||||
/// 將輸入法偏好設定同步至語言模組內。
|
||||
func syncBaseLMPrefs() {
|
||||
LMMgr.currentLM.isPhraseReplacementEnabled = PrefMgr.shared.phraseReplacementEnabled
|
||||
LMMgr.currentLM.isCNSEnabled = PrefMgr.shared.cns11643Enabled
|
||||
LMMgr.currentLM.isSymbolEnabled = PrefMgr.shared.symbolInputEnabled
|
||||
LMMgr.currentLM.isSCPCEnabled = PrefMgr.shared.useSCPCTypingMode
|
||||
LMMgr.currentLM.isCassetteEnabled = PrefMgr.shared.cassetteEnabled
|
||||
LMMgr.currentLM.deltaOfCalendarYears = PrefMgr.shared.deltaOfCalendarYears
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ extension SessionCtl: InputHandlerDelegate {
|
|||
|
||||
// 因為上述操作不會立即生效(除非遞交組字區),所以暫時塞入臨時資料記錄。
|
||||
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
||||
LMMgr.currentLM.insertTemporaryData(
|
||||
inputMode.langModel.insertTemporaryData(
|
||||
keyArray: userPhrase.keyArray,
|
||||
unigram: .init(value: userPhrase.value, score: userPhrase.weight ?? 0),
|
||||
isFiltering: addToFilter
|
||||
|
@ -119,7 +119,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
if state.type == .ofAssociates {
|
||||
return shortened ? "⇧" : NSLocalizedString("Hold ⇧ to choose associates.", comment: "")
|
||||
} else if state.type == .ofInputting, state.isCandidateContainer {
|
||||
let useShift = LMMgr.currentLM.areCassetteCandidateKeysShiftHeld
|
||||
let useShift = inputMode.langModel.areCassetteCandidateKeysShiftHeld
|
||||
let theEmoji = useShift ? "⬆️" : "⚡️"
|
||||
return shortened ? theEmoji : "\(theEmoji) " + NSLocalizedString("Quick Candidates", comment: "")
|
||||
} else if PrefMgr.shared.cassetteEnabled {
|
||||
|
@ -141,14 +141,14 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
if value.isEmpty { return blankResult } // 空字串沒有需要反查的東西。
|
||||
if value.contains("_") { return blankResult }
|
||||
// 因為威注音輸入法的反查結果僅由磁帶模組負責,所以相關運算挪至 LMInstantiator 內處理。
|
||||
return LMMgr.currentLM.cassetteReverseLookup(for: value)
|
||||
return inputMode.langModel.cassetteReverseLookup(for: value)
|
||||
}
|
||||
|
||||
public var selectionKeys: String {
|
||||
// 磁帶模式的 `%quick` 有單獨的選字鍵判定,會在資料不合規時使用 1234567890 選字鍵。
|
||||
cassetteQuick: if state.type == .ofInputting, state.isCandidateContainer {
|
||||
guard PrefMgr.shared.cassetteEnabled else { break cassetteQuick }
|
||||
guard let cinCandidateKey = LMMgr.currentLM.cassetteSelectionKey,
|
||||
guard let cinCandidateKey = inputMode.langModel.cassetteSelectionKey,
|
||||
CandidateKey.validate(keys: cinCandidateKey) == nil
|
||||
else {
|
||||
return "1234567890"
|
||||
|
@ -267,7 +267,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
|
||||
// 因為上述操作不會立即生效(除非遞交組字區),所以暫時塞入臨時資料記錄。
|
||||
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
||||
LMMgr.currentLM.insertTemporaryData(
|
||||
inputMode.langModel.insertTemporaryData(
|
||||
keyArray: userPhrase.keyArray,
|
||||
unigram: .init(value: userPhrase.value, score: userPhrase.weight ?? 0),
|
||||
isFiltering: action == .toFilter
|
||||
|
|
|
@ -140,7 +140,7 @@ public struct VwrSettingsPaneCassette: View {
|
|||
} else {
|
||||
LMMgr.loadCassetteData()
|
||||
}
|
||||
LMMgr.setCassetteEnabled(cassetteEnabled)
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -177,13 +177,13 @@ public struct VwrSettingsPaneDictionary: View {
|
|||
Toggle(
|
||||
LocalizedStringKey("Enable CNS11643 Support (2023-11-06)"),
|
||||
isOn: $cns11643Enabled.onChange {
|
||||
LMMgr.setCNSEnabled(cns11643Enabled)
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
)
|
||||
Toggle(
|
||||
LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"),
|
||||
isOn: $symbolInputEnabled.onChange {
|
||||
LMMgr.setSymbolEnabled(symbolInputEnabled)
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
)
|
||||
VStack(alignment: .leading) {
|
||||
|
@ -200,7 +200,7 @@ public struct VwrSettingsPaneDictionary: View {
|
|||
Toggle(
|
||||
LocalizedStringKey("Enable phrase replacement table"),
|
||||
isOn: $phraseReplacementEnabled.onChange {
|
||||
LMMgr.setPhraseReplacementEnabled(phraseReplacementEnabled)
|
||||
LMMgr.syncLMPrefs()
|
||||
if phraseReplacementEnabled {
|
||||
LMMgr.loadUserPhraseReplacement()
|
||||
}
|
||||
|
|
|
@ -226,6 +226,10 @@ public enum Shared {
|
|||
}
|
||||
}
|
||||
|
||||
public static var validCases: [InputMode] {
|
||||
[.imeModeCHS, .imeModeCHT]
|
||||
}
|
||||
|
||||
public var localizedDescription: String { NSLocalizedString(description, comment: "") }
|
||||
public var description: String {
|
||||
switch self {
|
||||
|
|
|
@ -272,7 +272,7 @@ public extension SessionCtl {
|
|||
? "NotificationSwitchON".localized
|
||||
: "NotificationSwitchOFF".localized)
|
||||
)
|
||||
if !LMMgr.currentLM.isCassetteDataLoaded {
|
||||
if !inputMode.langModel.isCassetteDataLoaded {
|
||||
LMMgr.loadCassetteData()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,11 +233,11 @@ class CtlPrefWindow: NSWindowController, NSWindowDelegate {
|
|||
// 這裡有必要加上這段處理,用來確保藉由偏好設定介面動過的 CNS 開關能夠立刻生效。
|
||||
// 所有涉及到語言模型開關的內容均需要這樣處理。
|
||||
@IBAction func toggleCNSSupport(_: Any) {
|
||||
LMMgr.setCNSEnabled(PrefMgr.shared.cns11643Enabled)
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
|
||||
@IBAction func toggleSymbolInputEnabled(_: Any) {
|
||||
LMMgr.setSymbolEnabled(PrefMgr.shared.symbolInputEnabled)
|
||||
LMMgr.syncLMPrefs()
|
||||
}
|
||||
|
||||
@IBAction func toggleTrad2KangXiAction(_: Any) {
|
||||
|
|
Loading…
Reference in New Issue