Repo // Use KeyValuePaired as candidate unit.

This commit is contained in:
ShikiSuen 2022-07-11 23:03:41 +08:00
parent 5f654c842e
commit 828575a08a
11 changed files with 49 additions and 50 deletions

View File

@ -139,9 +139,9 @@ enum InputState {
/// 西 .NotEmpty /// 西 .NotEmpty
class AssociatedPhrases: InputStateProtocol { class AssociatedPhrases: InputStateProtocol {
public var type: StateType { .ofAssociatedPhrases } public var type: StateType { .ofAssociatedPhrases }
private(set) var candidates: [String] = [] private(set) var candidates: [(String, String)] = []
private(set) var isTypingVertical: Bool = false private(set) var isTypingVertical: Bool = false
init(candidates: [String], isTypingVertical: Bool) { init(candidates: [(String, String)], isTypingVertical: Bool) {
self.candidates = candidates self.candidates = candidates
self.isTypingVertical = isTypingVertical self.isTypingVertical = isTypingVertical
} }
@ -407,10 +407,10 @@ enum InputState {
/// .ChoosingCandidate: 使 /// .ChoosingCandidate: 使
class ChoosingCandidate: NotEmpty { class ChoosingCandidate: NotEmpty {
override public var type: StateType { .ofChooseCandidate } override public var type: StateType { .ofChooseCandidate }
private(set) var candidates: [String] private(set) var candidates: [(String, String)]
private(set) var isTypingVertical: Bool private(set) var isTypingVertical: Bool
init(composingBuffer: String, cursorIndex: Int, candidates: [String], isTypingVertical: Bool) { init(composingBuffer: String, cursorIndex: Int, candidates: [(String, String)], isTypingVertical: Bool) {
self.candidates = candidates self.candidates = candidates
self.isTypingVertical = isTypingVertical self.isTypingVertical = isTypingVertical
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
@ -432,7 +432,7 @@ enum InputState {
self.node = node self.node = node
let candidates = node.children?.map(\.title) ?? [String]() let candidates = node.children?.map(\.title) ?? [String]()
super.init( super.init(
composingBuffer: "", cursorIndex: 0, candidates: candidates, composingBuffer: "", cursorIndex: 0, candidates: candidates.map { ("", $0) },
isTypingVertical: isTypingVertical isTypingVertical: isTypingVertical
) )
} }

View File

@ -149,10 +149,10 @@ class KeyHandler {
/// - Parameter key: /// - Parameter key:
/// - Returns: /// - Returns:
/// nil /// nil
func buildAssociatePhraseArray(withKey key: String) -> [String] { func buildAssociatePhraseArray(withKey key: String) -> [(String, String)] {
var arrResult: [String] = [] var arrResult: [(String, String)] = []
if currentLM.hasAssociatedPhrasesFor(key: key) { if currentLM.hasAssociatedPhrasesFor(key: key) {
arrResult.append(contentsOf: currentLM.associatedPhrasesFor(key: key)) arrResult = currentLM.associatedPhrasesFor(key: key).map { ("", $0) }
} }
return arrResult return arrResult
} }
@ -162,21 +162,22 @@ class KeyHandler {
/// - Parameters: /// - Parameters:
/// - value: /// - value:
/// - respectCursorPushing: true /// - respectCursorPushing: true
func fixNode(value: String, respectCursorPushing: Bool = true) { func fixNode(candidate: (String, String), respectCursorPushing: Bool = true) {
let theCandidate: Megrez.KeyValuePaired = .init(key: candidate.0, value: candidate.1)
let adjustedCursor = max(0, min(actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength)) let adjustedCursor = max(0, min(actualCandidateCursor + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength))
// //
let selectedNode: Megrez.NodeAnchor = compositor.fixNodeSelectedCandidate(value, at: adjustedCursor) let selectedNode: Megrez.NodeAnchor = compositor.fixNodeWithCandidate(theCandidate, at: adjustedCursor)
// //
if !mgrPrefs.useSCPCTypingMode { if !mgrPrefs.useSCPCTypingMode {
var addToUserOverrideModel = true var addToUserOverrideModel = true
// //
if selectedNode.spanLength != value.count { if selectedNode.spanLength != theCandidate.value.count {
IME.prtDebugIntel("UOM: SpanningLength != value.count, dismissing.") IME.prtDebugIntel("UOM: SpanningLength != value.count, dismissing.")
addToUserOverrideModel = false addToUserOverrideModel = false
} }
if addToUserOverrideModel { if addToUserOverrideModel {
// SymbolLM Score -12 // SymbolLM Score -12
if selectedNode.node.scoreFor(candidate: value) <= -12 { if selectedNode.node.scoreForPaired(candidate: theCandidate) <= -12 {
IME.prtDebugIntel("UOM: Score <= -12, dismissing.") IME.prtDebugIntel("UOM: Score <= -12, dismissing.")
addToUserOverrideModel = false addToUserOverrideModel = false
} }
@ -186,7 +187,7 @@ class KeyHandler {
// //
// //
currentUOM.observe( currentUOM.observe(
walkedAnchors: walkedAnchors, cursorIndex: adjustedCursor, candidate: value, walkedAnchors: walkedAnchors, cursorIndex: adjustedCursor, candidate: theCandidate.value,
timestamp: NSDate().timeIntervalSince1970 timestamp: NSDate().timeIntervalSince1970
) )
} }
@ -211,21 +212,21 @@ class KeyHandler {
for anchor in walkedAnchors { for anchor in walkedAnchors {
if index >= width - kMaxComposingBufferNeedsToWalkSize { break } if index >= width - kMaxComposingBufferNeedsToWalkSize { break }
if anchor.node.score < Megrez.Node.kSelectedCandidateScore { if anchor.node.score < Megrez.Node.kSelectedCandidateScore {
compositor.fixNodeSelectedCandidate(anchor.node.currentPair.value, at: index + anchor.spanLength) compositor.fixNodeWithCandidate(anchor.node.currentPair, at: index + anchor.spanLength)
} }
index += anchor.spanLength index += anchor.spanLength
} }
} }
/// ///
func getCandidatesArray(fixOrder: Bool = true) -> [String] { func getCandidatesArray(fixOrder: Bool = true) -> [(String, String)] {
var arrAnchors: [Megrez.NodeAnchor] = rawAnchorsOfNodes var arrAnchors: [Megrez.NodeAnchor] = rawAnchorsOfNodes
var arrCandidates: [String] = [] var arrCandidates: [Megrez.KeyValuePaired] = .init()
/// nodes /// nodes
/// ///
/// ///
if arrAnchors.isEmpty { return arrCandidates } if arrAnchors.isEmpty { return .init() }
// //
arrAnchors = arrAnchors.stableSort { $0.keyLength > $1.keyLength } arrAnchors = arrAnchors.stableSort { $0.keyLength > $1.keyLength }
@ -235,19 +236,19 @@ class KeyHandler {
// / JIS // / JIS
// //
// //
arrCandidates.append(currentCandidate.value) arrCandidates.append(.init(key: currentCandidate.key, value: currentCandidate.value))
} }
// 調 // 調
if !mgrPrefs.fetchSuggestionsFromUserOverrideModel || mgrPrefs.useSCPCTypingMode || fixOrder { if !mgrPrefs.fetchSuggestionsFromUserOverrideModel || mgrPrefs.useSCPCTypingMode || fixOrder {
return arrCandidates return arrCandidates.map { ($0.key, $0.value) }
} }
let arrSuggestedUnigrams: [Megrez.Unigram] = fetchSuggestedCandidates().stableSort { $0.score > $1.score } let arrSuggestedUnigrams: [Megrez.Unigram] = fetchSuggestedCandidates().stableSort { $0.score > $1.score }
let arrSuggestedCandidates: [String] = arrSuggestedUnigrams.map(\.keyValue.value) let arrSuggestedCandidates: [Megrez.KeyValuePaired] = arrSuggestedUnigrams.map(\.keyValue)
arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates
arrCandidates = arrCandidates.deduplicate arrCandidates = arrCandidates.deduplicate
arrCandidates = arrCandidates.stableSort { $0.count > $1.count } arrCandidates = arrCandidates.stableSort { $0.key.split(separator: "-").count > $1.key.split(separator: "-").count }
return arrCandidates return arrCandidates.map { ($0.key, $0.value) }
} }
/// ///

View File

@ -274,7 +274,7 @@ extension KeyHandler {
// MARK: End Key // MARK: End Key
var candidates: [String]! var candidates: [(String, String)]!
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
candidates = state.candidates candidates = state.candidates

View File

@ -225,7 +225,7 @@ extension KeyHandler {
) )
if choosingCandidates.candidates.count == 1 { if choosingCandidates.candidates.count == 1 {
clear() clear()
let text: String = choosingCandidates.candidates.first ?? "" let text: String = choosingCandidates.candidates.first?.1 ?? ""
stateCallback(InputState.Committing(textToCommit: text)) stateCallback(InputState.Committing(textToCommit: text))
if !mgrPrefs.associatedPhrasesEnabled { if !mgrPrefs.associatedPhrasesEnabled {

View File

@ -330,8 +330,8 @@ extension KeyHandler {
) )
if candidateState.candidates.count == 1 { if candidateState.candidates.count == 1 {
clear() clear()
if let strtextToCommit: String = candidateState.candidates.first { if let candidateToCommit: (String, String) = candidateState.candidates.first {
stateCallback(InputState.Committing(textToCommit: strtextToCommit)) stateCallback(InputState.Committing(textToCommit: candidateToCommit.1))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
} else { } else {
stateCallback(candidateState) stateCallback(candidateState)
@ -802,7 +802,7 @@ extension KeyHandler {
} }
let currentNode = currentAnchor.node let currentNode = currentAnchor.node
let currentValue = currentNode.currentPair.value let currentPaired: Megrez.KeyValuePaired = currentNode.currentPair
var currentIndex = 0 var currentIndex = 0
if currentNode.score < Megrez.Node.kSelectedCandidateScore { if currentNode.score < Megrez.Node.kSelectedCandidateScore {
@ -814,14 +814,14 @@ extension KeyHandler {
/// 使 /// 使
/// (Shift+)Tab () /// (Shift+)Tab ()
/// Shift(+CMD)+Space Tab /// Shift(+CMD)+Space Tab
if candidates[0] == currentValue { if candidates[0].0 == currentPaired.key, candidates[0].1 == currentPaired.value {
/// ///
/// ///
currentIndex = reverseModifier ? candidates.count - 1 : 1 currentIndex = reverseModifier ? candidates.count - 1 : 1
} }
} else { } else {
for candidate in candidates { for candidate in candidates {
if candidate == currentValue { if candidate.0 == currentPaired.key, candidate.1 == currentPaired.value {
if reverseModifier { if reverseModifier {
if currentIndex == 0 { if currentIndex == 0 {
currentIndex = candidates.count - 1 currentIndex = candidates.count - 1
@ -841,7 +841,7 @@ extension KeyHandler {
currentIndex = 0 currentIndex = 0
} }
fixNode(value: candidates[currentIndex], respectCursorPushing: false) fixNode(candidate: candidates[currentIndex], respectCursorPushing: false)
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
return true return true

View File

@ -81,7 +81,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
} }
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int) func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
-> String -> (String, String)
{ {
_ = controller // _ = controller //
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
@ -89,7 +89,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
} else if let state = state as? InputState.AssociatedPhrases { } else if let state = state as? InputState.AssociatedPhrases {
return state.candidates[index] return state.candidates[index]
} }
return "" return ("", "")
} }
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) { func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) {
@ -112,7 +112,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
let selectedValue = state.candidates[index] let selectedValue = state.candidates[index]
keyHandler.fixNode(value: selectedValue, respectCursorPushing: true) keyHandler.fixNode(candidate: selectedValue, respectCursorPushing: true)
let inputting = keyHandler.buildInputtingState let inputting = keyHandler.buildInputtingState
@ -137,10 +137,10 @@ extension ctlInputMethod: ctlCandidateDelegate {
if let state = state as? InputState.AssociatedPhrases { if let state = state as? InputState.AssociatedPhrases {
let selectedValue = state.candidates[index] let selectedValue = state.candidates[index]
handle(state: InputState.Committing(textToCommit: selectedValue)) handle(state: InputState.Committing(textToCommit: selectedValue.1))
if mgrPrefs.associatedPhrasesEnabled, if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState( let associatePhrases = keyHandler.buildAssociatePhraseState(
withKey: selectedValue, isTypingVertical: state.isTypingVertical withKey: selectedValue.1, isTypingVertical: state.isTypingVertical
), !associatePhrases.candidates.isEmpty ), !associatePhrases.candidates.isEmpty
{ {
handle(state: associatePhrases) handle(state: associatePhrases)

View File

@ -55,7 +55,7 @@ extension ctlInputMethod {
return false return false
} }
var isCandidateWindowVertical: Bool { var isCandidateWindowVertical: Bool {
var candidates: [String] = [] var candidates: [(String, String)] = .init()
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
candidates = state.candidates candidates = state.candidates
} else if let state = state as? InputState.AssociatedPhrases { } else if let state = state as? InputState.AssociatedPhrases {
@ -63,13 +63,11 @@ extension ctlInputMethod {
} }
if isTypingVertical { return true } if isTypingVertical { return true }
// 使 // 使
candidates.sort { //
$0.count > $1.count
}
// 使 // 使
// Beer emoji // Beer emoji
let maxCandidatesPerPage = mgrPrefs.candidateKeys.count let maxCandidatesPerPage = mgrPrefs.candidateKeys.count
let firstPageCandidates = candidates[0..<min(maxCandidatesPerPage, candidates.count)] let firstPageCandidates = candidates[0..<min(maxCandidatesPerPage, candidates.count)].map(\.1)
return firstPageCandidates.joined().count > Int(round(Double(maxCandidatesPerPage) * 1.8)) return firstPageCandidates.joined().count > Int(round(Double(maxCandidatesPerPage) * 1.8))
// true // true
} }

View File

@ -40,7 +40,7 @@ public class CandidateKeyLabel: NSObject {
public protocol ctlCandidateDelegate: AnyObject { public protocol ctlCandidateDelegate: AnyObject {
func candidateCountForController(_ controller: ctlCandidate) -> Int func candidateCountForController(_ controller: ctlCandidate) -> Int
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int) func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
-> String -> (String, String)
func ctlCandidate( func ctlCandidate(
_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int _ controller: ctlCandidate, didSelectCandidateAtIndex index: Int
) )

View File

@ -559,7 +559,7 @@ extension ctlCandidateUniversal {
} }
candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont) candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont)
var candidates = [String]() var candidates = [(String, String)]()
let count = delegate.candidateCountForController(self) let count = delegate.candidateCountForController(self)
let keyLabelCount = keyLabels.count let keyLabelCount = keyLabels.count
@ -569,7 +569,7 @@ extension ctlCandidateUniversal {
candidates.append(candidate) candidates.append(candidate)
} }
candidateView.set( candidateView.set(
keyLabels: keyLabels.map(\.displayedText), displayedCandidates: candidates keyLabels: keyLabels.map(\.displayedText), displayedCandidates: candidates.map(\.1)
) )
var newSize = candidateView.sizeForView var newSize = candidateView.sizeForView
var frameRect = candidateView.frame var frameRect = candidateView.frame

View File

@ -365,7 +365,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase {
XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)") XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)")
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
XCTAssertTrue(state.candidates.contains("")) XCTAssertTrue(state.candidates.map(\.1).contains(""))
} }
mgrPrefs.halfWidthPunctuationEnabled = enabled mgrPrefs.halfWidthPunctuationEnabled = enabled
} }
@ -993,7 +993,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase {
XCTAssertEqual(state.composingBuffer, "") XCTAssertEqual(state.composingBuffer, "")
XCTAssertEqual(state.cursorIndex, 1) XCTAssertEqual(state.cursorIndex, 1)
let candidates = state.candidates let candidates = state.candidates
XCTAssertTrue(candidates.contains("")) XCTAssertTrue(candidates.map(\.1).contains(""))
} }
} }
@ -1030,7 +1030,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase {
XCTAssertEqual(state.composingBuffer, "") XCTAssertEqual(state.composingBuffer, "")
XCTAssertEqual(state.cursorIndex, 1) XCTAssertEqual(state.cursorIndex, 1)
let candidates = state.candidates let candidates = state.candidates
XCTAssertTrue(candidates.contains("")) XCTAssertTrue(candidates.map(\.1).contains(""))
} }
mgrPrefs.chooseCandidateUsingSpace = enabled mgrPrefs.chooseCandidateUsingSpace = enabled
} }

View File

@ -85,7 +85,7 @@ class KeyHandlerTestsSCPCCHT: XCTestCase {
XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)") XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)")
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
XCTAssertTrue(state.candidates.contains("")) XCTAssertTrue(state.candidates.map(\.1).contains(""))
} }
} }
@ -238,7 +238,7 @@ class KeyHandlerTestsSCPCCHT: XCTestCase {
XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)") XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)")
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
XCTAssertTrue(state.candidates.contains("")) XCTAssertTrue(state.candidates.map(\.1).contains(""))
} }
} }
@ -315,7 +315,7 @@ class KeyHandlerTestsSCPCCHT: XCTestCase {
print("\(state)") print("\(state)")
// XCTAssertTrue(state is InputState.AssociatedPhrases, "\(state)") // XCTAssertTrue(state is InputState.AssociatedPhrases, "\(state)")
// if let state = state as? InputState.AssociatedPhrases { // if let state = state as? InputState.AssociatedPhrases {
// XCTAssertTrue(state.candidates.contains("")) // XCTAssertTrue(state.candidates.map(\.1).contains(""))
// } // }
mgrPrefs.associatedPhrasesEnabled = enabled mgrPrefs.associatedPhrasesEnabled = enabled
} }