KeyHandler // Refactoring with modern Swift expressions.

This commit is contained in:
ShikiSuen 2022-05-20 23:03:50 +08:00
parent e77d67370b
commit 45c877b3f9
7 changed files with 144 additions and 158 deletions

View File

@ -289,7 +289,7 @@ class InputState {
"<InputState.Marking, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), markedRange:\(markedRange)>"
}
func convertToInputting() -> Inputting {
var convertedToInputting: Inputting {
let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
state.tooltip = tooltipForInputting
return state

View File

@ -68,13 +68,35 @@ class KeyHandler {
return InputMode.imeModeNULL
}
}
set { setInputMode(newValue.rawValue) }
set {
let isCHS: Bool = (newValue == InputMode.imeModeCHS)
// ctlInputMethod:
ctlInputMethod.currentInputMode = isCHS ? InputMode.imeModeCHS.rawValue : InputMode.imeModeCHT.rawValue
mgrPrefs.mostRecentInputMode = ctlInputMethod.currentInputMode
// _inputMode ctlInputMethod
if _inputMode != ctlInputMethod.currentInputMode {
// Reinitiate language models if necessary
_languageModel = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT
_userOverrideModel = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT
// Synchronize the sub-languageModel state settings to the new LM.
syncBaseLMPrefs()
// Create new grid builder and clear the composer.
createNewBuilder()
_composer.clear()
}
//
_inputMode = ctlInputMethod.currentInputMode
}
}
public init() {
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-")
ensureParser()
setInputMode(ctlInputMethod.currentInputMode)
inputMode = InputMode(rawValue: ctlInputMethod.currentInputMode) ?? InputMode.imeModeNULL
}
func clear() {
@ -83,34 +105,6 @@ class KeyHandler {
_walkedNodes.removeAll()
}
func setInputMode(_ value: String) {
// isKindOfClass
// plist
let isCHS: Bool = (value == InputMode.imeModeCHS.rawValue)
// ctlInputMethod:
ctlInputMethod.currentInputMode = isCHS ? InputMode.imeModeCHS.rawValue : InputMode.imeModeCHT.rawValue
mgrPrefs.mostRecentInputMode = ctlInputMethod.currentInputMode
// _inputMode ctlInputMethod
if _inputMode != ctlInputMethod.currentInputMode {
// Reinitiate language models if necessary
setInputModesToLM(isCHS: isCHS)
// Synchronize the sub-languageModel state settings to the new LM.
syncBaseLMPrefs()
// Create new grid builder.
createNewBuilder()
if !_composer.isEmpty {
_composer.clear()
}
}
//
_inputMode = ctlInputMethod.currentInputMode
}
// MARK: - Functions dealing with Megrez.
func walk() {
@ -121,7 +115,7 @@ class KeyHandler {
_walkedNodes = _builder.walk(at: _builder.grid.width, nodesLimit: 10, balanced: true)
}
func popOverflowComposingTextAndWalk() -> String {
var popOverflowComposingTextAndWalk: String {
// In ideal situations we can allow users to type infinitely in a buffer.
// However, Viberti algorithm has a complexity of O(N^2), the walk will
// become slower as the number of nodes increase. Therefore, we need to
@ -153,7 +147,7 @@ class KeyHandler {
}
func fixNode(value: String) {
let cursorIndex: Int = getActualCandidateCursorIndex()
let cursorIndex: Int = actualCandidateCursorIndex
let selectedNode: Megrez.NodeAnchor = _builder.grid.fixNodeSelectedCandidate(
location: cursorIndex, value: value
)
@ -192,16 +186,16 @@ class KeyHandler {
if nextPosition >= cursorIndex { break }
nextPosition += node.spanningLength
}
if nextPosition <= getBuilderLength() {
setBuilderCursorIndex(value: nextPosition)
if nextPosition <= builderLength {
builderCursorIndex = nextPosition
}
}
}
func getCandidatesArray() -> [String] {
var candidatesArray: [String] {
var arrCandidates: [String] = []
var arrNodes: [Megrez.NodeAnchor] = []
arrNodes.append(contentsOf: getRawNodes())
arrNodes.append(contentsOf: rawNodes)
/// nodes
///
@ -228,7 +222,7 @@ class KeyHandler {
mgrPrefs.useSCPCTypingMode
? ""
: _userOverrideModel.suggest(
walkedNodes: _walkedNodes, cursorIndex: getBuilderCursorIndex(),
walkedNodes: _walkedNodes, cursorIndex: builderCursorIndex,
timestamp: NSDate().timeIntervalSince1970
)
@ -236,9 +230,9 @@ class KeyHandler {
IME.prtDebugIntel(
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
_builder.grid.overrideNodeScoreForSelectedCandidate(
location: getActualCandidateCursorIndex(),
location: actualCandidateCursorIndex,
value: overrideValue,
overridingScore: findHighestScore(nodes: getRawNodes(), epsilon: kEpsilon)
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
)
} else {
IME.prtDebugIntel("UOM: Blank suggestion retrieved, dismissing.")
@ -258,69 +252,6 @@ class KeyHandler {
return highestScore + epsilon
}
// MARK: - Extracted methods and functions (Megrez).
func isBuilderEmpty() -> Bool { _builder.grid.width == 0 }
func getRawNodes() -> [Megrez.NodeAnchor] {
/// 使 nodesCrossing macOS
/// nodeCrossing Megrez
/// Windows
mgrPrefs.setRearCursorMode
? _builder.grid.nodesCrossingOrEndingAt(location: getActualCandidateCursorIndex())
: _builder.grid.nodesEndingAt(location: getActualCandidateCursorIndex())
}
func setInputModesToLM(isCHS: Bool) {
_languageModel = isCHS ? mgrLangModel.lmCHS : mgrLangModel.lmCHT
_userOverrideModel = isCHS ? mgrLangModel.uomCHS : mgrLangModel.uomCHT
}
func syncBaseLMPrefs() {
_languageModel.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled
_languageModel.isCNSEnabled = mgrPrefs.cns11643Enabled
_languageModel.isSymbolEnabled = mgrPrefs.symbolInputEnabled
}
func createNewBuilder() {
// Each Mandarin syllable is separated by a hyphen.
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-")
}
func currentReadings() -> [String] { _builder.readings }
func ifLangModelHasUnigrams(forKey reading: String) -> Bool {
_languageModel.hasUnigramsFor(key: reading)
}
func insertReadingToBuilderAtCursor(reading: String) {
_builder.insertReadingAtCursor(reading: reading)
}
func setBuilderCursorIndex(value: Int) {
_builder.cursorIndex = value
}
func getBuilderCursorIndex() -> Int {
_builder.cursorIndex
}
func getBuilderLength() -> Int {
_builder.length
}
func deleteBuilderReadingInFrontOfCursor() {
_builder.deleteReadingAtTheRearOfCursor()
}
func deleteBuilderReadingToTheFrontOfCursor() {
_builder.deleteReadingToTheFrontOfCursor()
}
func getKeyLengthAtIndexZero() -> Int {
_walkedNodes[0].node?.currentKeyValue.value.count ?? 0
}
// MARK: - Extracted methods and functions (Tekkon).
func ensureParser() {
@ -357,4 +288,59 @@ class KeyHandler {
}
_composer.clear()
}
// MARK: - Extracted methods and functions (Megrez).
var isBuilderEmpty: Bool { _builder.grid.width == 0 }
var rawNodes: [Megrez.NodeAnchor] {
/// 使 nodesCrossing macOS
/// nodeCrossing Megrez
/// Windows
mgrPrefs.setRearCursorMode
? _builder.grid.nodesCrossingOrEndingAt(location: actualCandidateCursorIndex)
: _builder.grid.nodesEndingAt(location: actualCandidateCursorIndex)
}
func syncBaseLMPrefs() {
_languageModel.isPhraseReplacementEnabled = mgrPrefs.phraseReplacementEnabled
_languageModel.isCNSEnabled = mgrPrefs.cns11643Enabled
_languageModel.isSymbolEnabled = mgrPrefs.symbolInputEnabled
}
func createNewBuilder() {
// Each Mandarin syllable is separated by a hyphen.
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-")
}
var currentReadings: [String] { _builder.readings }
func ifLangModelHasUnigrams(forKey reading: String) -> Bool {
_languageModel.hasUnigramsFor(key: reading)
}
func insertReadingToBuilderAtCursor(reading: String) {
_builder.insertReadingAtCursor(reading: reading)
}
var builderCursorIndex: Int {
get { _builder.cursorIndex }
set { _builder.cursorIndex = newValue }
}
var builderLength: Int {
_builder.length
}
func deleteBuilderReadingInFrontOfCursor() {
_builder.deleteReadingAtTheRearOfCursor()
}
func deleteBuilderReadingToTheFrontOfCursor() {
_builder.deleteReadingToTheFrontOfCursor()
}
var keyLengthAtIndexZero: Int {
_walkedNodes[0].node?.currentKeyValue.value.count ?? 0
}
}

View File

@ -47,16 +47,16 @@ extension KeyHandler {
if cancelCandidateKey {
if (state is InputState.AssociatedPhrases)
|| mgrPrefs.useSCPCTypingMode
|| isBuilderEmpty()
|| isBuilderEmpty
{
//
//
// 使 BackSpace
// isBuilderEmpty()
// isBuilderEmpty
clear()
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
stateCallback(buildInputtingState())
stateCallback(buildInputtingState)
}
return true
}
@ -320,7 +320,7 @@ extension KeyHandler {
punctuationNamePrefix = "_punctuation_"
}
let parser = getCurrentMandarinParser()
let parser = currentMandarinParser
let arrCustomPunctuations: [String] = [
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),

View File

@ -135,7 +135,7 @@ extension KeyHandler {
) {
return true
}
state = marking.convertToInputting()
state = marking.convertedToInputting
stateCallback(state)
}
@ -154,7 +154,7 @@ extension KeyHandler {
// update the composing buffer.
let composeReading = _composer.hasToneMarker()
if !composeReading {
stateCallback(buildInputtingState())
stateCallback(buildInputtingState)
return true
}
}
@ -166,7 +166,7 @@ extension KeyHandler {
// However, Swift does not support "|=".
composeReading = composeReading || (!_composer.isEmpty && (input.isSpace || input.isEnter))
if composeReading {
if input.isSpace && !_composer.hasToneMarker() {
if input.isSpace, !_composer.hasToneMarker() {
_composer.receiveKey(fromString: " ") //
}
let reading = _composer.getComposition()
@ -176,7 +176,7 @@ extension KeyHandler {
IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。")
errorCallback()
_composer.clear()
stateCallback((getBuilderLength() == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState())
stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
return true
}
@ -184,7 +184,7 @@ extension KeyHandler {
insertReadingToBuilderAtCursor(reading: reading)
// ... then walk the grid...
let poppedText = popOverflowComposingTextAndWalk()
let poppedText = popOverflowComposingTextAndWalk
// ... get and tweak override model suggestion if possible...
dealWithOverrideModelSuggestions()
@ -192,7 +192,7 @@ extension KeyHandler {
// ... then update the text.
_composer.clear()
let inputting = buildInputtingState()
let inputting = buildInputtingState
inputting.poppedText = poppedText
stateCallback(inputting)
@ -233,7 +233,7 @@ extension KeyHandler {
// but does not compose. Only sequences such as "ˊ", "ˊˊ", "ˊˇ", or "ˊ "
// would compose.
if keyConsumedByReading {
stateCallback(buildInputtingState())
stateCallback(buildInputtingState)
return true
}
@ -247,7 +247,7 @@ extension KeyHandler {
if input.isSpace {
// If the Space key is NOT set to be a selection key
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
if getBuilderCursorIndex() >= getBuilderLength() {
if builderCursorIndex >= builderLength {
let composingBuffer = currentState.composingBuffer
if !composingBuffer.isEmpty {
stateCallback(InputState.Committing(poppedText: composingBuffer))
@ -257,8 +257,8 @@ extension KeyHandler {
stateCallback(InputState.Empty())
} else if ifLangModelHasUnigrams(forKey: " ") {
insertReadingToBuilderAtCursor(reading: " ")
let poppedText = popOverflowComposingTextAndWalk()
let inputting = buildInputtingState()
let poppedText = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
stateCallback(inputting)
}
@ -355,8 +355,8 @@ extension KeyHandler {
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
if _composer.isEmpty {
insertReadingToBuilderAtCursor(reading: "_punctuation_list")
let poppedText: String! = popOverflowComposingTextAndWalk()
let inputting = buildInputtingState()
let poppedText: String! = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
stateCallback(inputting)
stateCallback(buildCandidate(state: inputting, useVerticalMode: input.useVerticalMode))
@ -392,7 +392,7 @@ extension KeyHandler {
punctuationNamePrefix = "_punctuation_"
}
let parser = getCurrentMandarinParser()
let parser = currentMandarinParser
let arrCustomPunctuations: [String] = [
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),
]

View File

@ -29,22 +29,22 @@ import Cocoa
// MARK: - § Misc functions.
extension KeyHandler {
func getCurrentMandarinParser() -> String {
var currentMandarinParser: String {
mgrPrefs.mandarinParserName + "_"
}
func getActualCandidateCursorIndex() -> Int {
var cursorIndex = getBuilderCursorIndex()
var actualCandidateCursorIndex: Int {
var cursorIndex = builderCursorIndex
// Windows Yahoo Kimo IME style, phrase is *at the rear of* the cursor.
// (i.e. the cursor is always *before* the phrase.)
// This is different from MS Phonetics IME style ...
// ... since Windows Yahoo Kimo allows "node crossing".
if (mgrPrefs.setRearCursorMode
&& (cursorIndex < getBuilderLength()))
&& (cursorIndex < builderLength))
|| cursorIndex == 0
{
if cursorIndex == 0, !mgrPrefs.setRearCursorMode {
cursorIndex += getKeyLengthAtIndexZero()
cursorIndex += keyLengthAtIndexZero
} else {
cursorIndex += 1
}

View File

@ -31,14 +31,14 @@ import Cocoa
extension KeyHandler {
// MARK: - State Building
func buildInputtingState() -> InputState.Inputting {
var buildInputtingState: InputState.Inputting {
// "Updating the composing buffer" means to request the client
// to "refresh" the text input buffer with our "composing text"
var composingBuffer = ""
var composedStringCursorIndex = 0
var readingCursorIndex = 0
let builderCursorIndex = getBuilderCursorIndex()
let builderCursorIndex = builderCursorIndex
for theAnchor in _walkedNodes {
guard let node = theAnchor.node else {
@ -106,7 +106,7 @@ extension KeyHandler {
InputState.ChoosingCandidate(
composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex,
candidates: getCandidatesArray(),
candidates: candidatesArray,
useVerticalMode: useVerticalMode
)
}
@ -115,7 +115,7 @@ extension KeyHandler {
// buildAssociatePhraseStateWithKey
// 使
// ObjC buildAssociatePhraseArray
// Core buildAssociatePhraseArray
// String Swift
// nil
//
@ -139,7 +139,7 @@ extension KeyHandler {
errorCallback: @escaping () -> Void
) -> Bool {
if input.isESC {
stateCallback(buildInputtingState())
stateCallback(buildInputtingState)
return true
}
@ -152,7 +152,7 @@ extension KeyHandler {
return true
}
}
stateCallback(buildInputtingState())
stateCallback(buildInputtingState)
return true
}
@ -168,7 +168,7 @@ extension KeyHandler {
readings: state.readings
)
marking.tooltipForInputting = state.tooltipForInputting
stateCallback(marking.markedRange.length == 0 ? marking.convertToInputting() : marking)
stateCallback(marking.markedRange.length == 0 ? marking.convertedToInputting : marking)
} else {
IME.prtDebugIntel("1149908D")
errorCallback()
@ -191,7 +191,7 @@ extension KeyHandler {
readings: state.readings
)
marking.tooltipForInputting = state.tooltipForInputting
stateCallback(marking.markedRange.length == 0 ? marking.convertToInputting() : marking)
stateCallback(marking.markedRange.length == 0 ? marking.convertedToInputting : marking)
} else {
IME.prtDebugIntel("9B51408D")
errorCallback()
@ -217,8 +217,8 @@ extension KeyHandler {
if _composer.isEmpty {
insertReadingToBuilderAtCursor(reading: customPunctuation)
let poppedText = popOverflowComposingTextAndWalk()
let inputting = buildInputtingState()
let poppedText = popOverflowComposingTextAndWalk
let inputting = buildInputtingState
inputting.poppedText = poppedText
stateCallback(inputting)
@ -273,7 +273,7 @@ extension KeyHandler {
) -> Bool {
guard state is InputState.Inputting else { return false }
var composingBuffer = currentReadings().joined(separator: "-")
var composingBuffer = currentReadings.joined(separator: "-")
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
composingBuffer = restoreToneOneInZhuyinKey(target: composingBuffer) //
composingBuffer = Tekkon.cnvPhonaToHanyuPinyin(target: composingBuffer) //
@ -341,7 +341,7 @@ extension KeyHandler {
if _composer.hasToneMarker(withNothingElse: true) {
_composer.clear()
} else if _composer.isEmpty {
if getBuilderCursorIndex() >= 0 {
if builderCursorIndex >= 0 {
deleteBuilderReadingInFrontOfCursor()
walk()
} else {
@ -354,10 +354,10 @@ extension KeyHandler {
_composer.doBackSpace()
}
if _composer.isEmpty, getBuilderLength() == 0 {
if _composer.isEmpty, builderLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
stateCallback(buildInputtingState())
stateCallback(buildInputtingState)
}
return true
}
@ -372,10 +372,10 @@ extension KeyHandler {
guard state is InputState.Inputting else { return false }
if _composer.isEmpty {
if getBuilderCursorIndex() != getBuilderLength() {
if builderCursorIndex != builderLength {
deleteBuilderReadingToTheFrontOfCursor()
walk()
let inputting = buildInputtingState()
let inputting = buildInputtingState
// count > 0!isEmpty滿
if inputting.composingBuffer.isEmpty {
stateCallback(InputState.EmptyIgnoringPreviousState())
@ -428,9 +428,9 @@ extension KeyHandler {
return true
}
if getBuilderCursorIndex() != 0 {
setBuilderCursorIndex(value: 0)
stateCallback(buildInputtingState())
if builderCursorIndex != 0 {
builderCursorIndex = 0
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("66D97F90")
errorCallback()
@ -456,9 +456,9 @@ extension KeyHandler {
return true
}
if getBuilderCursorIndex() != getBuilderLength() {
setBuilderCursorIndex(value: getBuilderLength())
stateCallback(buildInputtingState())
if builderCursorIndex != builderLength {
builderCursorIndex = builderLength
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("9B69908E")
errorCallback()
@ -490,10 +490,10 @@ extension KeyHandler {
// If reading is not empty, we cancel the reading.
if !_composer.isEmpty {
_composer.clear()
if getBuilderLength() == 0 {
if builderLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState())
} else {
stateCallback(buildInputtingState())
stateCallback(buildInputtingState)
}
}
}
@ -526,7 +526,7 @@ extension KeyHandler {
composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex,
markerIndex: UInt(nextPosition),
readings: currentReadings()
readings: currentReadings
)
marking.tooltipForInputting = currentState.tooltip
stateCallback(marking)
@ -536,9 +536,9 @@ extension KeyHandler {
stateCallback(state)
}
} else {
if getBuilderCursorIndex() < getBuilderLength() {
setBuilderCursorIndex(value: getBuilderCursorIndex() + 1)
stateCallback(buildInputtingState())
if builderCursorIndex < builderLength {
builderCursorIndex += 1
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("A96AAD58")
errorCallback()
@ -575,7 +575,7 @@ extension KeyHandler {
composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex,
markerIndex: UInt(previousPosition),
readings: currentReadings()
readings: currentReadings
)
marking.tooltipForInputting = currentState.tooltip
stateCallback(marking)
@ -585,9 +585,9 @@ extension KeyHandler {
stateCallback(state)
}
} else {
if getBuilderCursorIndex() > 0 {
setBuilderCursorIndex(value: getBuilderCursorIndex() - 1)
stateCallback(buildInputtingState())
if builderCursorIndex > 0 {
builderCursorIndex -= 1
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("7045E6F3")
errorCallback()

View File

@ -648,7 +648,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
let selectedValue = state.candidates[Int(index)]
keyHandler.fixNode(value: selectedValue)
let inputting = keyHandler.buildInputtingState()
let inputting = keyHandler.buildInputtingState
if mgrPrefs.useSCPCTypingMode {
keyHandler.clear()