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