Repo // Massive updates conforming to recent changes of Megrez.
This commit is contained in:
parent
9b78be6d3a
commit
2664509276
|
@ -229,14 +229,15 @@ extension vChewingLM {
|
|||
|
||||
/// 插入臨時資料。
|
||||
/// - Parameters:
|
||||
/// - key: 索引鍵。
|
||||
/// - key: 索引鍵陣列。
|
||||
/// - unigram: 要插入的單元圖。
|
||||
/// - isFiltering: 是否有在過濾內容。
|
||||
public func insertTemporaryData(key: String, unigram: Megrez.Unigram, isFiltering: Bool) {
|
||||
public func insertTemporaryData(keyArray: [String], unigram: Megrez.Unigram, isFiltering: Bool) {
|
||||
let keyChain = keyArray.joined(separator: "-")
|
||||
_ =
|
||||
isFiltering
|
||||
? lmFiltered.temporaryMap[key, default: []].append(unigram)
|
||||
: lmUserPhrases.temporaryMap[key, default: []].append(unigram)
|
||||
? lmFiltered.temporaryMap[keyChain, default: []].append(unigram)
|
||||
: lmUserPhrases.temporaryMap[keyChain, default: []].append(unigram)
|
||||
}
|
||||
|
||||
/// 自當前記憶體取得指定使用者子語言模組內的原始資料體。
|
||||
|
@ -279,60 +280,62 @@ extension vChewingLM {
|
|||
}
|
||||
|
||||
/// 根據給定的索引鍵來確認各個資料庫陣列內是否存在對應的資料。
|
||||
/// - Parameter key: 索引鍵。
|
||||
/// - Parameter key: 索引鍵陣列。
|
||||
/// - Returns: 是否在庫。
|
||||
public func hasUnigramsFor(key: String) -> Bool {
|
||||
key == " " || (!unigramsFor(key: key).isEmpty && !key.isEmpty)
|
||||
public func hasUnigramsFor(keyArray: [String]) -> Bool {
|
||||
let keyChain = keyArray.joined(separator: "-")
|
||||
return keyChain == " " || (!unigramsFor(keyArray: keyArray).isEmpty && !keyChain.isEmpty)
|
||||
}
|
||||
|
||||
/// 根據給定的索引鍵和資料值,確認是否有該具體的資料值在庫。
|
||||
/// - Parameters:
|
||||
/// - key: 索引鍵。
|
||||
/// - key: 索引鍵陣列。
|
||||
/// - value: 資料值。
|
||||
/// - Returns: 是否在庫。
|
||||
public func hasKeyValuePairFor(key: String, value: String) -> Bool {
|
||||
unigramsFor(key: key).map(\.value).contains(value)
|
||||
public func hasKeyValuePairFor(key: [String], value: String) -> Bool {
|
||||
unigramsFor(keyArray: key).map(\.value).contains(value)
|
||||
}
|
||||
|
||||
/// 給定讀音字串,讓 LMI 給出對應的經過處理的單元圖陣列。
|
||||
/// - Parameter key: 給定的讀音字串。
|
||||
/// - Returns: 對應的經過處理的單元圖陣列。
|
||||
public func unigramsFor(key: String) -> [Megrez.Unigram] {
|
||||
guard !key.isEmpty else { return [] }
|
||||
public func unigramsFor(keyArray: [String]) -> [Megrez.Unigram] {
|
||||
let keyChain = keyArray.joined(separator: "-")
|
||||
guard !keyChain.isEmpty else { return [] }
|
||||
/// 給空格鍵指定輸出值。
|
||||
if key == " " { return [.init(value: " ")] }
|
||||
if keyChain == " " { return [.init(value: " ")] }
|
||||
|
||||
/// 準備不同的語言模組容器,開始逐漸往容器陣列內塞入資料。
|
||||
var rawAllUnigrams: [Megrez.Unigram] = []
|
||||
|
||||
if isCassetteEnabled { rawAllUnigrams += Self.lmCassette.unigramsFor(key: key) }
|
||||
if isCassetteEnabled { rawAllUnigrams += Self.lmCassette.unigramsFor(key: keyChain) }
|
||||
|
||||
// 如果有檢測到使用者自訂逐字選字語料庫內的相關資料的話,在這裡先插入。
|
||||
if isSCPCEnabled {
|
||||
rawAllUnigrams += lmPlainBopomofo.valuesFor(key: key).map { Megrez.Unigram(value: $0, score: 0) }
|
||||
rawAllUnigrams += lmPlainBopomofo.valuesFor(key: keyChain).map { Megrez.Unigram(value: $0, score: 0) }
|
||||
}
|
||||
|
||||
// 用 reversed 指令讓使用者語彙檔案內的詞條優先順序隨著行數增加而逐漸增高。
|
||||
// 這樣一來就可以在就地新增語彙時徹底複寫優先權。
|
||||
// 將兩句差分也是為了讓 rawUserUnigrams 的類型不受可能的影響。
|
||||
rawAllUnigrams += lmUserPhrases.unigramsFor(key: key).reversed()
|
||||
rawAllUnigrams += lmUserPhrases.unigramsFor(key: keyChain).reversed()
|
||||
|
||||
if !isCassetteEnabled || isCassetteEnabled && key.charComponents[0] == "_" {
|
||||
if !isCassetteEnabled || isCassetteEnabled && keyChain.charComponents[0] == "_" {
|
||||
// LMMisc 與 LMCore 的 score 在 (-10.0, 0.0) 這個區間內。
|
||||
rawAllUnigrams += lmMisc.unigramsFor(key: key)
|
||||
rawAllUnigrams += lmCore.unigramsFor(key: key)
|
||||
if isCNSEnabled { rawAllUnigrams += Self.lmCNS.unigramsFor(key: key) }
|
||||
rawAllUnigrams += lmMisc.unigramsFor(key: keyChain)
|
||||
rawAllUnigrams += lmCore.unigramsFor(key: keyChain)
|
||||
if isCNSEnabled { rawAllUnigrams += Self.lmCNS.unigramsFor(key: keyChain) }
|
||||
}
|
||||
|
||||
if isSymbolEnabled {
|
||||
rawAllUnigrams += lmUserSymbols.unigramsFor(key: key)
|
||||
rawAllUnigrams += lmUserSymbols.unigramsFor(key: keyChain)
|
||||
if !isCassetteEnabled {
|
||||
rawAllUnigrams += Self.lmSymbols.unigramsFor(key: key)
|
||||
rawAllUnigrams += Self.lmSymbols.unigramsFor(key: keyChain)
|
||||
}
|
||||
}
|
||||
|
||||
// 新增與日期、時間、星期有關的單元圖資料
|
||||
rawAllUnigrams.append(contentsOf: queryDateTimeUnigrams(with: key))
|
||||
rawAllUnigrams.append(contentsOf: queryDateTimeUnigrams(with: keyChain))
|
||||
|
||||
// 提前處理語彙置換
|
||||
if isPhraseReplacementEnabled {
|
||||
|
@ -344,7 +347,7 @@ extension vChewingLM {
|
|||
}
|
||||
|
||||
// 讓單元圖陣列自我過濾。在此基礎之上,對於相同詞值的多個單元圖,僅保留權重最大者。
|
||||
rawAllUnigrams.consolidate(filter: .init(lmFiltered.unigramsFor(key: key).map(\.value)))
|
||||
rawAllUnigrams.consolidate(filter: .init(lmFiltered.unigramsFor(key: keyChain).map(\.value)))
|
||||
return rawAllUnigrams
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ extension vChewingLM {
|
|||
var headIndex = 0
|
||||
guard let nodeIter = currentWalk.findNode(at: cursor, target: &headIndex) else { return .init() }
|
||||
let key = vChewingLM.LMUserOverride.formObservationKey(walkedNodes: currentWalk, headIndex: headIndex)
|
||||
return getSuggestion(key: key, timestamp: timestamp, headReading: nodeIter.key)
|
||||
return getSuggestion(key: key, timestamp: timestamp, headReading: nodeIter.joinedKey())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,15 +329,15 @@ extension vChewingLM.LMUserOverride {
|
|||
arrNodes = Array(arrNodes.reversed())
|
||||
|
||||
let kvCurrent = arrNodes[0].currentPair
|
||||
guard !kvCurrent.key.contains("_") else {
|
||||
guard !kvCurrent.joinedKey().contains("_") else {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 字音數與字數不一致的內容會被拋棄。
|
||||
if kvCurrent.key.split(separator: "-").count != kvCurrent.value.count { return "" }
|
||||
if kvCurrent.keyArray.count != kvCurrent.value.count { return "" }
|
||||
|
||||
// 前置單元只記錄讀音,在其後的單元則同時記錄讀音與字詞
|
||||
let strCurrent = kvCurrent.key
|
||||
let strCurrent = kvCurrent.joinedKey()
|
||||
var kvPrevious = Megrez.Compositor.KeyValuePaired()
|
||||
var kvAnterior = Megrez.Compositor.KeyValuePaired()
|
||||
var readingStack = ""
|
||||
|
@ -354,19 +354,19 @@ extension vChewingLM.LMUserOverride {
|
|||
}
|
||||
|
||||
if arrNodes.count >= 2,
|
||||
!kvPrevious.key.contains("_"),
|
||||
kvPrevious.key.split(separator: "-").count == kvPrevious.value.count
|
||||
!kvPrevious.joinedKey().contains("_"),
|
||||
kvPrevious.joinedKey().split(separator: "-").count == kvPrevious.value.count
|
||||
{
|
||||
kvPrevious = arrNodes[1].currentPair
|
||||
readingStack = kvPrevious.key + readingStack
|
||||
readingStack = kvPrevious.joinedKey() + readingStack
|
||||
}
|
||||
|
||||
if arrNodes.count >= 3,
|
||||
!kvAnterior.key.contains("_"),
|
||||
kvAnterior.key.split(separator: "-").count == kvAnterior.value.count
|
||||
!kvAnterior.joinedKey().contains("_"),
|
||||
kvAnterior.joinedKey().split(separator: "-").count == kvAnterior.value.count
|
||||
{
|
||||
kvAnterior = arrNodes[2].currentPair
|
||||
readingStack = kvAnterior.key + readingStack
|
||||
readingStack = kvAnterior.joinedKey() + readingStack
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import Cocoa
|
||||
|
||||
public protocol CtlCandidateDelegate {
|
||||
func candidatePairs(conv: Bool) -> [(String, String)]
|
||||
func candidatePairs(conv: Bool) -> [([String], String)]
|
||||
func candidatePairSelected(at index: Int)
|
||||
func candidatePairRightClicked(at index: Int, action: CandidateContextMenuAction)
|
||||
func candidates(_ sender: Any!) -> [Any]!
|
||||
|
|
|
@ -15,7 +15,7 @@ public protocol IMEStateProtocol {
|
|||
var isASCIIMode: Bool { get set }
|
||||
var isVerticalTyping: Bool { get set }
|
||||
var isVerticalCandidateWindow: Bool { get set }
|
||||
var candidates: [(String, String)] { get set }
|
||||
var candidates: [([String], String)] { get set }
|
||||
var hasComposition: Bool { get }
|
||||
var isCandidateContainer: Bool { get }
|
||||
var displayedText: String { get }
|
||||
|
@ -49,7 +49,7 @@ public protocol IMEStateDataProtocol {
|
|||
var isFilterable: Bool { get }
|
||||
var isVerticalTyping: Bool { get set }
|
||||
var isMarkedLengthValid: Bool { get }
|
||||
var candidates: [(String, String)] { get set }
|
||||
var candidates: [([String], String)] { get set }
|
||||
var displayedText: String { get set }
|
||||
var displayedTextConverted: String { get }
|
||||
var tooltipBackupForInputting: String { get set }
|
||||
|
|
|
@ -84,7 +84,7 @@ public struct IMEState: IMEStateProtocol {
|
|||
self.data = data
|
||||
self.type = type
|
||||
self.node = node
|
||||
self.data.candidates = node.members.map { ("", $0.name) }
|
||||
self.data.candidates = node.members.map { ([""], $0.name) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ extension IMEState {
|
|||
return result
|
||||
}
|
||||
|
||||
public static func ofAssociates(candidates: [(String, String)]) -> IMEState {
|
||||
public static func ofAssociates(candidates: [([String], String)]) -> IMEState {
|
||||
var result = IMEState(type: .ofAssociates)
|
||||
result.candidates = candidates
|
||||
return result
|
||||
|
@ -132,7 +132,7 @@ extension IMEState {
|
|||
return result
|
||||
}
|
||||
|
||||
public static func ofCandidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int)
|
||||
public static func ofCandidates(candidates: [([String], String)], displayTextSegments: [String], cursor: Int)
|
||||
-> IMEState
|
||||
{
|
||||
var result = IMEState(displayTextSegments: displayTextSegments, cursor: cursor)
|
||||
|
@ -178,7 +178,7 @@ extension IMEState {
|
|||
return result
|
||||
}
|
||||
|
||||
public var candidates: [(String, String)] {
|
||||
public var candidates: [([String], String)] {
|
||||
get { data.candidates }
|
||||
set { data.candidates = newValue }
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ public struct IMEStateData: IMEStateDataProtocol {
|
|||
|
||||
public var reading: String = ""
|
||||
public var markedReadings = [String]()
|
||||
public var candidates = [(String, String)]()
|
||||
public var candidates = [([String], String)]()
|
||||
public var textToCommit: String = ""
|
||||
public var tooltip: String = ""
|
||||
public var tooltipDuration: Double = 1.0
|
||||
|
@ -172,7 +172,7 @@ public struct IMEStateData: IMEStateDataProtocol {
|
|||
extension IMEStateData {
|
||||
public var doesUserPhraseExist: Bool {
|
||||
let text = displayedText.charComponents[markedRange].joined()
|
||||
let joined = markedReadings.joined(separator: "-")
|
||||
let joined = markedReadings.joined(separator: InputHandler.keySeparator)
|
||||
return LMMgr.checkIfUserPhraseExist(
|
||||
userPhrase: text, mode: IMEApp.currentInputMode, key: joined
|
||||
)
|
||||
|
@ -205,7 +205,7 @@ extension IMEStateData {
|
|||
}
|
||||
|
||||
public var userPhraseKVPair: (String, String) {
|
||||
let key = markedReadings.joined(separator: "-")
|
||||
let key = markedReadings.joined(separator: InputHandler.keySeparator)
|
||||
let value = displayedText.charComponents[markedRange].joined()
|
||||
return (key, value)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ public protocol InputHandlerProtocol {
|
|||
var currentUOM: vChewingLM.LMUserOverride { get set }
|
||||
var delegate: InputHandlerDelegate? { get set }
|
||||
var composer: Tekkon.Composer { get set }
|
||||
var keySeparator: String { get }
|
||||
static var keySeparator: String { get }
|
||||
var isCompositorEmpty: Bool { get }
|
||||
var isComposerUsingPinyin: Bool { get }
|
||||
func clear()
|
||||
|
@ -31,7 +33,7 @@ public protocol InputHandlerProtocol {
|
|||
func generateStateOfCandidates() -> IMEStateProtocol
|
||||
func generateStateOfInputting(sansReading: Bool) -> IMEStateProtocol
|
||||
func generateStateOfAssociates(withPair pair: Megrez.Compositor.KeyValuePaired) -> IMEStateProtocol
|
||||
func consolidateNode(candidate: (String, String), respectCursorPushing: Bool, preConsolidate: Bool)
|
||||
func consolidateNode(candidate: ([String], String), respectCursorPushing: Bool, preConsolidate: Bool)
|
||||
func updateUnigramData() -> Bool
|
||||
}
|
||||
|
||||
|
@ -149,10 +151,10 @@ public class InputHandler: InputHandlerProtocol {
|
|||
/// - Parameter key: 給定的聯想詞的開頭字。
|
||||
/// - Returns: 抓取到的聯想詞陣列。
|
||||
/// 不會是 nil,但那些負責接收結果的函式會對空白陣列結果做出正確的處理。
|
||||
func generateArrayOfAssociates(withPair pair: Megrez.Compositor.KeyValuePaired) -> [(String, String)] {
|
||||
var arrResult: [(String, String)] = []
|
||||
func generateArrayOfAssociates(withPair pair: Megrez.Compositor.KeyValuePaired) -> [([String], String)] {
|
||||
var arrResult: [([String], String)] = []
|
||||
if currentLM.hasAssociatedPhrasesFor(pair: pair) {
|
||||
arrResult = currentLM.associatedPhrasesFor(pair: pair).map { ("", $0) }
|
||||
arrResult = currentLM.associatedPhrasesFor(pair: pair).map { ([""], $0) }
|
||||
}
|
||||
return arrResult
|
||||
}
|
||||
|
@ -223,7 +225,7 @@ public class InputHandler: InputHandlerProtocol {
|
|||
for (subPosition, key) in currentNode.keyArray.enumerated() {
|
||||
guard values.count > subPosition else { break } // 防呆,應該沒有發生的可能性
|
||||
let thePair = Megrez.Compositor.KeyValuePaired(
|
||||
key: key, value: values[subPosition]
|
||||
keyArray: [key], value: values[subPosition]
|
||||
)
|
||||
compositor.overrideCandidate(thePair, at: position)
|
||||
position += 1
|
||||
|
@ -241,9 +243,9 @@ public class InputHandler: InputHandlerProtocol {
|
|||
/// - respectCursorPushing: 若該選項為 true,則會在選字之後始終將游標推送至選字後的節錨的前方。
|
||||
/// - consolidate: 在固化節點之前,先鞏固上下文。該選項可能會破壞在內文組字區內就地輪替候選字詞時的體驗。
|
||||
public func consolidateNode(
|
||||
candidate: (String, String), respectCursorPushing: Bool = true, preConsolidate: Bool = false
|
||||
candidate: ([String], String), respectCursorPushing: Bool = true, preConsolidate: Bool = false
|
||||
) {
|
||||
let theCandidate: Megrez.Compositor.KeyValuePaired = .init(key: candidate.0, value: candidate.1)
|
||||
let theCandidate: Megrez.Compositor.KeyValuePaired = .init(keyArray: candidate.0, value: candidate.1)
|
||||
|
||||
/// 必須先鞏固當前組字器游標上下文、以消滅意料之外的影響,但在內文組字區內就地輪替候選字詞時除外。
|
||||
if preConsolidate { consolidateCursorContext(with: theCandidate) }
|
||||
|
@ -284,7 +286,7 @@ public class InputHandler: InputHandlerProtocol {
|
|||
}
|
||||
|
||||
/// 獲取候選字詞(包含讀音)陣列資料內容。
|
||||
func generateArrayOfCandidates(fixOrder: Bool = true) -> [(String, String)] {
|
||||
func generateArrayOfCandidates(fixOrder: Bool = true) -> [([String], String)] {
|
||||
/// 警告:不要對游標前置風格使用 nodesCrossing,否則會導致游標行為與 macOS 內建注音輸入法不一致。
|
||||
/// 微軟新注音輸入法的游標後置風格也是不允許 nodeCrossing 的。
|
||||
var arrCandidates: [Megrez.Compositor.KeyValuePaired] = {
|
||||
|
@ -301,7 +303,7 @@ public class InputHandler: InputHandlerProtocol {
|
|||
|
||||
// 決定是否根據半衰記憶模組的建議來調整候選字詞的順序。
|
||||
if !prefs.fetchSuggestionsFromUserOverrideModel || prefs.useSCPCTypingMode || fixOrder {
|
||||
return arrCandidates.map { ($0.key, $0.value) }
|
||||
return arrCandidates.map { ($0.keyArray, $0.value) }
|
||||
}
|
||||
|
||||
let arrSuggestedUnigrams: [(String, Megrez.Unigram)] = retrieveUOMSuggestions(apply: false)
|
||||
|
@ -310,8 +312,8 @@ public class InputHandler: InputHandlerProtocol {
|
|||
}
|
||||
arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates
|
||||
arrCandidates = arrCandidates.deduplicated
|
||||
arrCandidates = arrCandidates.stableSort { $0.key.split(separator: "-").count > $1.key.split(separator: "-").count }
|
||||
return arrCandidates.map { ($0.key, $0.value) }
|
||||
arrCandidates = arrCandidates.stableSort { $0.keyArray.count > $1.keyArray.count }
|
||||
return arrCandidates.map { ($0.keyArray, $0.value) }
|
||||
}
|
||||
|
||||
/// 向半衰引擎詢問可能的選字建議、且套用給組字器內的當前游標位置。
|
||||
|
@ -437,6 +439,10 @@ public class InputHandler: InputHandlerProtocol {
|
|||
|
||||
// MARK: - Extracted methods and functions (Megrez).
|
||||
|
||||
public var keySeparator: String { compositor.separator }
|
||||
|
||||
public static var keySeparator: String { Megrez.Compositor.theSeparator }
|
||||
|
||||
/// 就地增刪詞之後,需要就地更新游標上下文單元圖資料。
|
||||
public func updateUnigramData() -> Bool {
|
||||
let result = compositor.update(updateExisting: true)
|
||||
|
|
|
@ -173,12 +173,12 @@ extension InputHandler {
|
|||
? currentLM.isThisCassetteKeyAllowed(key: input.text) : composer.inputValidityCheck(key: input.charCode)
|
||||
|
||||
var shouldAutoSelectCandidate: Bool =
|
||||
isInputValid || currentLM.hasUnigramsFor(key: customPunctuation)
|
||||
|| currentLM.hasUnigramsFor(key: punctuation)
|
||||
isInputValid || currentLM.hasUnigramsFor(keyArray: [customPunctuation])
|
||||
|| currentLM.hasUnigramsFor(keyArray: [punctuation])
|
||||
|
||||
if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey {
|
||||
let letter = "_letter_\(input.text)"
|
||||
if currentLM.hasUnigramsFor(key: letter) { shouldAutoSelectCandidate = true }
|
||||
if currentLM.hasUnigramsFor(keyArray: [letter]) { shouldAutoSelectCandidate = true }
|
||||
}
|
||||
|
||||
if shouldAutoSelectCandidate {
|
||||
|
|
|
@ -53,7 +53,7 @@ extension InputHandler {
|
|||
theComposer.intonation.clear()
|
||||
// 檢查新的漢字字音是否在庫。
|
||||
let temporaryReadingKey = theComposer.getComposition()
|
||||
if currentLM.hasUnigramsFor(key: temporaryReadingKey) {
|
||||
if currentLM.hasUnigramsFor(keyArray: [temporaryReadingKey]) {
|
||||
compositor.dropKey(direction: .rear)
|
||||
walk() // 這裡必須 Walk 一次、來更新目前被 walk 的內容。
|
||||
composer = theComposer
|
||||
|
@ -92,7 +92,7 @@ extension InputHandler {
|
|||
return handleCtrlCommandEnter()
|
||||
}
|
||||
// 向語言模型詢問是否有對應的記錄。
|
||||
if !currentLM.hasUnigramsFor(key: readingKey) {
|
||||
if !currentLM.hasUnigramsFor(keyArray: [readingKey]) {
|
||||
delegate.callError("B49C0979:語彙庫內無「\(readingKey)」的匹配記錄。")
|
||||
|
||||
if prefs.keepReadingUponCompositionError {
|
||||
|
@ -140,14 +140,14 @@ extension InputHandler {
|
|||
case 2...: delegate.switchState(candidateState)
|
||||
case 1:
|
||||
let firstCandidate = candidateState.candidates.first! // 一定會有,所以強制拆包也無妨。
|
||||
let reading: String = firstCandidate.0
|
||||
let reading: String = firstCandidate.0.joined(separator: compositor.separator)
|
||||
let text: String = firstCandidate.1
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: text))
|
||||
|
||||
if !prefs.associatedPhrasesEnabled {
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
} else {
|
||||
let associatedPhrases = generateStateOfAssociates(withPair: .init(key: reading, value: text))
|
||||
let associatedPhrases = generateStateOfAssociates(withPair: .init(keyArray: [reading], value: text))
|
||||
delegate.switchState(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases)
|
||||
}
|
||||
default: break
|
||||
|
@ -240,7 +240,7 @@ extension InputHandler {
|
|||
return handleCtrlCommandEnter()
|
||||
}
|
||||
// 向語言模型詢問是否有對應的記錄。
|
||||
if !currentLM.hasUnigramsFor(key: calligrapher) {
|
||||
if !currentLM.hasUnigramsFor(keyArray: [calligrapher]) {
|
||||
delegate.callError("B49C0979_Cassette:語彙庫內無「\(calligrapher)」的匹配記錄。")
|
||||
|
||||
calligrapher.removeAll()
|
||||
|
@ -282,14 +282,14 @@ extension InputHandler {
|
|||
case 2...: delegate.switchState(candidateState)
|
||||
case 1:
|
||||
let firstCandidate = candidateState.candidates.first! // 一定會有,所以強制拆包也無妨。
|
||||
let reading: String = firstCandidate.0
|
||||
let reading: String = firstCandidate.0.joined(separator: compositor.separator)
|
||||
let text: String = firstCandidate.1
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: text))
|
||||
|
||||
if !prefs.associatedPhrasesEnabled {
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
} else {
|
||||
let associatedPhrases = generateStateOfAssociates(withPair: .init(key: reading, value: text))
|
||||
let associatedPhrases = generateStateOfAssociates(withPair: .init(keyArray: [reading], value: text))
|
||||
delegate.switchState(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases)
|
||||
}
|
||||
default: break
|
||||
|
|
|
@ -190,7 +190,7 @@ extension InputHandler {
|
|||
|
||||
if input.isSymbolMenuPhysicalKey, !input.isShiftHold, !input.isControlHold, state.type != .ofDeactivated {
|
||||
if input.isOptionHold {
|
||||
if currentLM.hasUnigramsFor(key: "_punctuation_list") {
|
||||
if currentLM.hasUnigramsFor(keyArray: ["_punctuation_list"]) {
|
||||
if isComposerOrCalligrapherEmpty, compositor.insertKey("_punctuation_list") {
|
||||
walk()
|
||||
// 一邊吃一邊屙(僅對位列黑名單的 App 用這招限制組字區長度)。
|
||||
|
|
|
@ -239,7 +239,7 @@ extension InputHandler {
|
|||
func handlePunctuation(_ customPunctuation: String) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
|
||||
if !currentLM.hasUnigramsFor(key: customPunctuation) {
|
||||
if !currentLM.hasUnigramsFor(keyArray: [customPunctuation]) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ extension InputHandler {
|
|||
case 2...: delegate.switchState(candidateState)
|
||||
case 1:
|
||||
clear() // 這句不要砍,因為下文可能會回呼 candidateState。
|
||||
if let candidateToCommit: (String, String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty {
|
||||
if let candidateToCommit: ([String], String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty {
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: candidateToCommit.1))
|
||||
} else {
|
||||
delegate.switchState(candidateState)
|
||||
|
@ -337,13 +337,13 @@ extension InputHandler {
|
|||
var composed = ""
|
||||
|
||||
for node in compositor.walkedNodes {
|
||||
var key = node.key
|
||||
var key = node.keyArray.joined(separator: "\t")
|
||||
if !prefs.cassetteEnabled {
|
||||
if prefs.inlineDumpPinyinInLieuOfZhuyin {
|
||||
key = Tekkon.restoreToneOneInZhuyinKey(target: key) // 恢復陰平標記
|
||||
key = Tekkon.cnvPhonaToHanyuPinyin(target: key) // 注音轉拼音
|
||||
key = Tekkon.cnvHanyuPinyinToTextbookStyle(target: key) // 轉教科書式標調
|
||||
key = key.replacingOccurrences(of: "-", with: " ")
|
||||
key = key.replacingOccurrences(of: "\t", with: " ")
|
||||
} else {
|
||||
key = Tekkon.cnvZhuyinChainToTextbookReading(target: key, newSeparator: " ")
|
||||
}
|
||||
|
@ -706,7 +706,7 @@ extension InputHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
let currentPaired = (currentNode.key, currentNode.value)
|
||||
let currentPaired = (currentNode.keyArray, currentNode.value)
|
||||
|
||||
var currentIndex = 0
|
||||
if !currentNode.isOverriden {
|
||||
|
|
|
@ -279,8 +279,8 @@ public class LMMgr {
|
|||
key unigramKey: String
|
||||
) -> Bool {
|
||||
switch mode {
|
||||
case .imeModeCHS: return lmCHS.hasKeyValuePairFor(key: unigramKey, value: userPhrase)
|
||||
case .imeModeCHT: return lmCHT.hasKeyValuePairFor(key: unigramKey, value: userPhrase)
|
||||
case .imeModeCHS: return lmCHS.hasKeyValuePairFor(key: [unigramKey], value: userPhrase)
|
||||
case .imeModeCHT: return lmCHT.hasKeyValuePairFor(key: [unigramKey], value: userPhrase)
|
||||
case .imeModeNULL: return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,8 @@ extension SessionCtl: InputHandlerDelegate {
|
|||
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
||||
let temporaryScore: Double = SessionCtl.areWeNerfing ? -114.514 : 0
|
||||
LMMgr.currentLM.insertTemporaryData(
|
||||
key: rawPair.0, unigram: .init(value: rawPair.1, score: temporaryScore), isFiltering: SessionCtl.areWeNerfing
|
||||
keyArray: [rawPair.0], unigram: .init(value: rawPair.1, score: temporaryScore),
|
||||
isFiltering: SessionCtl.areWeNerfing
|
||||
)
|
||||
// 開始針對使用者半衰模組的清詞處理
|
||||
LMMgr.bleachSpecifiedSuggestions(targets: [valueCurrent], mode: IMEApp.currentInputMode)
|
||||
|
@ -95,12 +96,12 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
PrefMgr.shared.useIMKCandidateWindow ? "123456789" : PrefMgr.shared.candidateKeys
|
||||
}
|
||||
|
||||
public func candidatePairs(conv: Bool = false) -> [(String, String)] {
|
||||
public func candidatePairs(conv: Bool = false) -> [([String], String)] {
|
||||
if !state.isCandidateContainer || state.candidates.isEmpty { return [] }
|
||||
if !conv || PrefMgr.shared.cns11643Enabled || state.candidates[0].0.contains("_punctuation") {
|
||||
if !conv || PrefMgr.shared.cns11643Enabled || state.candidates[0].0.joined().contains("_punctuation") {
|
||||
return state.candidates
|
||||
}
|
||||
let convertedCandidates: [(String, String)] = state.candidates.map { theCandidatePair -> (String, String) in
|
||||
let convertedCandidates: [([String], String)] = state.candidates.map { theCandidatePair -> ([String], String) in
|
||||
let theCandidate = theCandidatePair.1
|
||||
let theConverted = ChineseConverter.kanjiConversionIfRequired(theCandidate)
|
||||
let result = (theCandidate == theConverted) ? theCandidate : "\(theConverted)(\(theCandidate))"
|
||||
|
@ -139,7 +140,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
// 此時是逐字選字模式,所以「selectedValue.1」是單個字、不用追加處理。
|
||||
if PrefMgr.shared.associatedPhrasesEnabled {
|
||||
let associates = inputHandler.generateStateOfAssociates(
|
||||
withPair: .init(key: selectedValue.0, value: selectedValue.1)
|
||||
withPair: .init(keyArray: selectedValue.0, value: selectedValue.1)
|
||||
)
|
||||
switchState(associates.candidates.isEmpty ? IMEState.ofEmpty() : associates)
|
||||
} else {
|
||||
|
@ -162,7 +163,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
}
|
||||
if PrefMgr.shared.associatedPhrasesEnabled {
|
||||
let associates = inputHandler.generateStateOfAssociates(
|
||||
withPair: .init(key: selectedValue.0, value: String(valueKept))
|
||||
withPair: .init(keyArray: selectedValue.0, value: String(valueKept))
|
||||
)
|
||||
if !associates.candidates.isEmpty {
|
||||
switchState(associates)
|
||||
|
@ -204,7 +205,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
||||
let temporaryScore: Double = (action == .toNerf) ? -114.514 : 0
|
||||
LMMgr.currentLM.insertTemporaryData(
|
||||
key: rawPair.0, unigram: .init(value: rawPair.1, score: temporaryScore), isFiltering: action == .toFilter
|
||||
keyArray: rawPair.0, unigram: .init(value: rawPair.1, score: temporaryScore), isFiltering: action == .toFilter
|
||||
)
|
||||
|
||||
// 開始針對使用者半衰模組的清詞處理
|
||||
|
|
|
@ -21,17 +21,19 @@ extension SessionCtl {
|
|||
var arrResult = [String]()
|
||||
|
||||
// 注意:下文中的不可列印字元是用來方便在 IMEState 當中用來分割資料的。
|
||||
func handleIMKCandidatesPrepared(_ candidates: [(String, String)], prefix: String = "") {
|
||||
func handleIMKCandidatesPrepared(_ candidates: [([String], String)], prefix: String = "") {
|
||||
guard let separator = inputHandler?.keySeparator else { return }
|
||||
for theCandidate in candidates {
|
||||
let theConverted = ChineseConverter.kanjiConversionIfRequired(theCandidate.1)
|
||||
var result = (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)\u{1A}(\(theCandidate.1))"
|
||||
if arrResult.contains(result) {
|
||||
let reading: String =
|
||||
PrefMgr.shared.cassetteEnabled
|
||||
? theCandidate.0
|
||||
? theCandidate.0.joined(separator: separator)
|
||||
: (PrefMgr.shared.showHanyuPinyinInCompositionBuffer
|
||||
? Tekkon.cnvPhonaToHanyuPinyin(target: Tekkon.restoreToneOneInZhuyinKey(target: theCandidate.0))
|
||||
: theCandidate.0)
|
||||
? Tekkon.cnvPhonaToHanyuPinyin(
|
||||
target: Tekkon.restoreToneOneInZhuyinKey(target: theCandidate.0.joined(separator: separator)))
|
||||
: theCandidate.0.joined(separator: separator))
|
||||
result = "\(result)\u{17}(\(reading))"
|
||||
}
|
||||
arrResult.append(prefix + result)
|
||||
|
@ -82,15 +84,18 @@ extension SessionCtl {
|
|||
var indexDeducted = 0
|
||||
|
||||
// 注意:下文中的不可列印字元是用來方便在 IMEState 當中用來分割資料的。
|
||||
func handleIMKCandidatesSelected(_ candidates: [(String, String)], prefix: String = "") {
|
||||
func handleIMKCandidatesSelected(_ candidates: [([String], String)], prefix: String = "") {
|
||||
guard let separator = inputHandler?.keySeparator else { return }
|
||||
for (i, neta) in candidates.enumerated() {
|
||||
let theConverted = ChineseConverter.kanjiConversionIfRequired(neta.1)
|
||||
let netaShown = (neta.1 == theConverted) ? neta.1 : "\(theConverted)\u{1A}(\(neta.1))"
|
||||
let reading: String =
|
||||
PrefMgr.shared.cassetteEnabled
|
||||
? neta.0
|
||||
? neta.0.joined(separator: separator)
|
||||
: (PrefMgr.shared.showHanyuPinyinInCompositionBuffer
|
||||
? Tekkon.cnvPhonaToHanyuPinyin(target: Tekkon.restoreToneOneInZhuyinKey(target: neta.0)) : neta.0)
|
||||
? Tekkon.cnvPhonaToHanyuPinyin(
|
||||
target: Tekkon.restoreToneOneInZhuyinKey(target: neta.0.joined(separator: separator)))
|
||||
: neta.0.joined(separator: separator))
|
||||
let netaShownWithPronunciation = "\(netaShown)\u{17}(\(reading))"
|
||||
if candidateString == prefix + netaShownWithPronunciation {
|
||||
indexDeducted = i
|
||||
|
@ -104,7 +109,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
// 分類符號選單不會出現同符異音項、不需要康熙 / JIS 轉換,所以使用簡化過的處理方式。
|
||||
func handleSymbolCandidatesSelected(_ candidates: [(String, String)]) {
|
||||
func handleSymbolCandidatesSelected(_ candidates: [([String], String)]) {
|
||||
for (i, neta) in candidates.enumerated() {
|
||||
if candidateString == neta.1 {
|
||||
indexDeducted = i
|
||||
|
|
Loading…
Reference in New Issue