KeyHandler // Refactoring with modern Swift expressions.
This commit is contained in:
parent
e77d67370b
commit
45c877b3f9
|
@ -289,7 +289,7 @@ class InputState {
|
||||||
"<InputState.Marking, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), markedRange:\(markedRange)>"
|
"<InputState.Marking, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), markedRange:\(markedRange)>"
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertToInputting() -> Inputting {
|
var convertedToInputting: Inputting {
|
||||||
let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||||
state.tooltip = tooltipForInputting
|
state.tooltip = tooltipForInputting
|
||||||
return state
|
return state
|
||||||
|
|
|
@ -68,13 +68,35 @@ class KeyHandler {
|
||||||
return InputMode.imeModeNULL
|
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() {
|
public init() {
|
||||||
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-")
|
_builder = Megrez.BlockReadingBuilder(lm: _languageModel, separator: "-")
|
||||||
ensureParser()
|
ensureParser()
|
||||||
setInputMode(ctlInputMethod.currentInputMode)
|
inputMode = InputMode(rawValue: ctlInputMethod.currentInputMode) ?? InputMode.imeModeNULL
|
||||||
}
|
}
|
||||||
|
|
||||||
func clear() {
|
func clear() {
|
||||||
|
@ -83,34 +105,6 @@ class KeyHandler {
|
||||||
_walkedNodes.removeAll()
|
_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.
|
// MARK: - Functions dealing with Megrez.
|
||||||
|
|
||||||
func walk() {
|
func walk() {
|
||||||
|
@ -121,7 +115,7 @@ class KeyHandler {
|
||||||
_walkedNodes = _builder.walk(at: _builder.grid.width, nodesLimit: 10, balanced: true)
|
_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.
|
// 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
|
// 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
|
// become slower as the number of nodes increase. Therefore, we need to
|
||||||
|
@ -153,7 +147,7 @@ class KeyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixNode(value: String) {
|
func fixNode(value: String) {
|
||||||
let cursorIndex: Int = getActualCandidateCursorIndex()
|
let cursorIndex: Int = actualCandidateCursorIndex
|
||||||
let selectedNode: Megrez.NodeAnchor = _builder.grid.fixNodeSelectedCandidate(
|
let selectedNode: Megrez.NodeAnchor = _builder.grid.fixNodeSelectedCandidate(
|
||||||
location: cursorIndex, value: value
|
location: cursorIndex, value: value
|
||||||
)
|
)
|
||||||
|
@ -192,16 +186,16 @@ class KeyHandler {
|
||||||
if nextPosition >= cursorIndex { break }
|
if nextPosition >= cursorIndex { break }
|
||||||
nextPosition += node.spanningLength
|
nextPosition += node.spanningLength
|
||||||
}
|
}
|
||||||
if nextPosition <= getBuilderLength() {
|
if nextPosition <= builderLength {
|
||||||
setBuilderCursorIndex(value: nextPosition)
|
builderCursorIndex = nextPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCandidatesArray() -> [String] {
|
var candidatesArray: [String] {
|
||||||
var arrCandidates: [String] = []
|
var arrCandidates: [String] = []
|
||||||
var arrNodes: [Megrez.NodeAnchor] = []
|
var arrNodes: [Megrez.NodeAnchor] = []
|
||||||
arrNodes.append(contentsOf: getRawNodes())
|
arrNodes.append(contentsOf: rawNodes)
|
||||||
|
|
||||||
/// 原理:nodes 這個回饋結果包含一堆子陣列,分別對應不同詞長的候選字。
|
/// 原理:nodes 這個回饋結果包含一堆子陣列,分別對應不同詞長的候選字。
|
||||||
/// 這裡先對陣列排序、讓最長候選字的子陣列的優先權最高。
|
/// 這裡先對陣列排序、讓最長候選字的子陣列的優先權最高。
|
||||||
|
@ -228,7 +222,7 @@ class KeyHandler {
|
||||||
mgrPrefs.useSCPCTypingMode
|
mgrPrefs.useSCPCTypingMode
|
||||||
? ""
|
? ""
|
||||||
: _userOverrideModel.suggest(
|
: _userOverrideModel.suggest(
|
||||||
walkedNodes: _walkedNodes, cursorIndex: getBuilderCursorIndex(),
|
walkedNodes: _walkedNodes, cursorIndex: builderCursorIndex,
|
||||||
timestamp: NSDate().timeIntervalSince1970
|
timestamp: NSDate().timeIntervalSince1970
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -236,9 +230,9 @@ class KeyHandler {
|
||||||
IME.prtDebugIntel(
|
IME.prtDebugIntel(
|
||||||
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
|
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
|
||||||
_builder.grid.overrideNodeScoreForSelectedCandidate(
|
_builder.grid.overrideNodeScoreForSelectedCandidate(
|
||||||
location: getActualCandidateCursorIndex(),
|
location: actualCandidateCursorIndex,
|
||||||
value: overrideValue,
|
value: overrideValue,
|
||||||
overridingScore: findHighestScore(nodes: getRawNodes(), epsilon: kEpsilon)
|
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("UOM: Blank suggestion retrieved, dismissing.")
|
IME.prtDebugIntel("UOM: Blank suggestion retrieved, dismissing.")
|
||||||
|
@ -258,69 +252,6 @@ class KeyHandler {
|
||||||
return highestScore + epsilon
|
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).
|
// MARK: - Extracted methods and functions (Tekkon).
|
||||||
|
|
||||||
func ensureParser() {
|
func ensureParser() {
|
||||||
|
@ -357,4 +288,59 @@ class KeyHandler {
|
||||||
}
|
}
|
||||||
_composer.clear()
|
_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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,16 +47,16 @@ extension KeyHandler {
|
||||||
if cancelCandidateKey {
|
if cancelCandidateKey {
|
||||||
if (state is InputState.AssociatedPhrases)
|
if (state is InputState.AssociatedPhrases)
|
||||||
|| mgrPrefs.useSCPCTypingMode
|
|| mgrPrefs.useSCPCTypingMode
|
||||||
|| isBuilderEmpty()
|
|| isBuilderEmpty
|
||||||
{
|
{
|
||||||
// 如果此時發現當前組字緩衝區為真空的情況的話,
|
// 如果此時發現當前組字緩衝區為真空的情況的話,
|
||||||
// 就將當前的組字緩衝區析構處理、強制重設輸入狀態。
|
// 就將當前的組字緩衝區析構處理、強制重設輸入狀態。
|
||||||
// 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。
|
// 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。
|
||||||
// 所以這裡需要對 isBuilderEmpty() 做判定。
|
// 所以這裡需要對 isBuilderEmpty 做判定。
|
||||||
clear()
|
clear()
|
||||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||||
} else {
|
} else {
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -320,7 +320,7 @@ extension KeyHandler {
|
||||||
punctuationNamePrefix = "_punctuation_"
|
punctuationNamePrefix = "_punctuation_"
|
||||||
}
|
}
|
||||||
|
|
||||||
let parser = getCurrentMandarinParser()
|
let parser = currentMandarinParser
|
||||||
|
|
||||||
let arrCustomPunctuations: [String] = [
|
let arrCustomPunctuations: [String] = [
|
||||||
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),
|
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),
|
||||||
|
|
|
@ -135,7 +135,7 @@ extension KeyHandler {
|
||||||
) {
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
state = marking.convertToInputting()
|
state = marking.convertedToInputting
|
||||||
stateCallback(state)
|
stateCallback(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ extension KeyHandler {
|
||||||
// update the composing buffer.
|
// update the composing buffer.
|
||||||
let composeReading = _composer.hasToneMarker()
|
let composeReading = _composer.hasToneMarker()
|
||||||
if !composeReading {
|
if !composeReading {
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ extension KeyHandler {
|
||||||
// However, Swift does not support "|=".
|
// However, Swift does not support "|=".
|
||||||
composeReading = composeReading || (!_composer.isEmpty && (input.isSpace || input.isEnter))
|
composeReading = composeReading || (!_composer.isEmpty && (input.isSpace || input.isEnter))
|
||||||
if composeReading {
|
if composeReading {
|
||||||
if input.isSpace && !_composer.hasToneMarker() {
|
if input.isSpace, !_composer.hasToneMarker() {
|
||||||
_composer.receiveKey(fromString: " ") // 補上空格。
|
_composer.receiveKey(fromString: " ") // 補上空格。
|
||||||
}
|
}
|
||||||
let reading = _composer.getComposition()
|
let reading = _composer.getComposition()
|
||||||
|
@ -176,7 +176,7 @@ extension KeyHandler {
|
||||||
IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。")
|
IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
_composer.clear()
|
_composer.clear()
|
||||||
stateCallback((getBuilderLength() == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState())
|
stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ extension KeyHandler {
|
||||||
insertReadingToBuilderAtCursor(reading: reading)
|
insertReadingToBuilderAtCursor(reading: reading)
|
||||||
|
|
||||||
// ... then walk the grid...
|
// ... then walk the grid...
|
||||||
let poppedText = popOverflowComposingTextAndWalk()
|
let poppedText = popOverflowComposingTextAndWalk
|
||||||
|
|
||||||
// ... get and tweak override model suggestion if possible...
|
// ... get and tweak override model suggestion if possible...
|
||||||
dealWithOverrideModelSuggestions()
|
dealWithOverrideModelSuggestions()
|
||||||
|
@ -192,7 +192,7 @@ extension KeyHandler {
|
||||||
// ... then update the text.
|
// ... then update the text.
|
||||||
_composer.clear()
|
_composer.clear()
|
||||||
|
|
||||||
let inputting = buildInputtingState()
|
let inputting = buildInputtingState
|
||||||
inputting.poppedText = poppedText
|
inputting.poppedText = poppedText
|
||||||
stateCallback(inputting)
|
stateCallback(inputting)
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ extension KeyHandler {
|
||||||
// but does not compose. Only sequences such as "ㄧˊ", "ˊㄧˊ", "ˊㄧˇ", or "ˊㄧ "
|
// but does not compose. Only sequences such as "ㄧˊ", "ˊㄧˊ", "ˊㄧˇ", or "ˊㄧ "
|
||||||
// would compose.
|
// would compose.
|
||||||
if keyConsumedByReading {
|
if keyConsumedByReading {
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ extension KeyHandler {
|
||||||
if input.isSpace {
|
if input.isSpace {
|
||||||
// If the Space key is NOT set to be a selection key
|
// If the Space key is NOT set to be a selection key
|
||||||
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
|
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
|
||||||
if getBuilderCursorIndex() >= getBuilderLength() {
|
if builderCursorIndex >= builderLength {
|
||||||
let composingBuffer = currentState.composingBuffer
|
let composingBuffer = currentState.composingBuffer
|
||||||
if !composingBuffer.isEmpty {
|
if !composingBuffer.isEmpty {
|
||||||
stateCallback(InputState.Committing(poppedText: composingBuffer))
|
stateCallback(InputState.Committing(poppedText: composingBuffer))
|
||||||
|
@ -257,8 +257,8 @@ extension KeyHandler {
|
||||||
stateCallback(InputState.Empty())
|
stateCallback(InputState.Empty())
|
||||||
} else if ifLangModelHasUnigrams(forKey: " ") {
|
} else if ifLangModelHasUnigrams(forKey: " ") {
|
||||||
insertReadingToBuilderAtCursor(reading: " ")
|
insertReadingToBuilderAtCursor(reading: " ")
|
||||||
let poppedText = popOverflowComposingTextAndWalk()
|
let poppedText = popOverflowComposingTextAndWalk
|
||||||
let inputting = buildInputtingState()
|
let inputting = buildInputtingState
|
||||||
inputting.poppedText = poppedText
|
inputting.poppedText = poppedText
|
||||||
stateCallback(inputting)
|
stateCallback(inputting)
|
||||||
}
|
}
|
||||||
|
@ -355,8 +355,8 @@ extension KeyHandler {
|
||||||
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
|
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
|
||||||
if _composer.isEmpty {
|
if _composer.isEmpty {
|
||||||
insertReadingToBuilderAtCursor(reading: "_punctuation_list")
|
insertReadingToBuilderAtCursor(reading: "_punctuation_list")
|
||||||
let poppedText: String! = popOverflowComposingTextAndWalk()
|
let poppedText: String! = popOverflowComposingTextAndWalk
|
||||||
let inputting = buildInputtingState()
|
let inputting = buildInputtingState
|
||||||
inputting.poppedText = poppedText
|
inputting.poppedText = poppedText
|
||||||
stateCallback(inputting)
|
stateCallback(inputting)
|
||||||
stateCallback(buildCandidate(state: inputting, useVerticalMode: input.useVerticalMode))
|
stateCallback(buildCandidate(state: inputting, useVerticalMode: input.useVerticalMode))
|
||||||
|
@ -392,7 +392,7 @@ extension KeyHandler {
|
||||||
punctuationNamePrefix = "_punctuation_"
|
punctuationNamePrefix = "_punctuation_"
|
||||||
}
|
}
|
||||||
|
|
||||||
let parser = getCurrentMandarinParser()
|
let parser = currentMandarinParser
|
||||||
let arrCustomPunctuations: [String] = [
|
let arrCustomPunctuations: [String] = [
|
||||||
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),
|
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -29,22 +29,22 @@ import Cocoa
|
||||||
// MARK: - § Misc functions.
|
// MARK: - § Misc functions.
|
||||||
|
|
||||||
extension KeyHandler {
|
extension KeyHandler {
|
||||||
func getCurrentMandarinParser() -> String {
|
var currentMandarinParser: String {
|
||||||
mgrPrefs.mandarinParserName + "_"
|
mgrPrefs.mandarinParserName + "_"
|
||||||
}
|
}
|
||||||
|
|
||||||
func getActualCandidateCursorIndex() -> Int {
|
var actualCandidateCursorIndex: Int {
|
||||||
var cursorIndex = getBuilderCursorIndex()
|
var cursorIndex = builderCursorIndex
|
||||||
// Windows Yahoo Kimo IME style, phrase is *at the rear of* the cursor.
|
// Windows Yahoo Kimo IME style, phrase is *at the rear of* the cursor.
|
||||||
// (i.e. the cursor is always *before* the phrase.)
|
// (i.e. the cursor is always *before* the phrase.)
|
||||||
// This is different from MS Phonetics IME style ...
|
// This is different from MS Phonetics IME style ...
|
||||||
// ... since Windows Yahoo Kimo allows "node crossing".
|
// ... since Windows Yahoo Kimo allows "node crossing".
|
||||||
if (mgrPrefs.setRearCursorMode
|
if (mgrPrefs.setRearCursorMode
|
||||||
&& (cursorIndex < getBuilderLength()))
|
&& (cursorIndex < builderLength))
|
||||||
|| cursorIndex == 0
|
|| cursorIndex == 0
|
||||||
{
|
{
|
||||||
if cursorIndex == 0, !mgrPrefs.setRearCursorMode {
|
if cursorIndex == 0, !mgrPrefs.setRearCursorMode {
|
||||||
cursorIndex += getKeyLengthAtIndexZero()
|
cursorIndex += keyLengthAtIndexZero
|
||||||
} else {
|
} else {
|
||||||
cursorIndex += 1
|
cursorIndex += 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,14 @@ import Cocoa
|
||||||
extension KeyHandler {
|
extension KeyHandler {
|
||||||
// MARK: - 構築狀態(State Building)
|
// MARK: - 構築狀態(State Building)
|
||||||
|
|
||||||
func buildInputtingState() -> InputState.Inputting {
|
var buildInputtingState: InputState.Inputting {
|
||||||
// "Updating the composing buffer" means to request the client
|
// "Updating the composing buffer" means to request the client
|
||||||
// to "refresh" the text input buffer with our "composing text"
|
// to "refresh" the text input buffer with our "composing text"
|
||||||
var composingBuffer = ""
|
var composingBuffer = ""
|
||||||
var composedStringCursorIndex = 0
|
var composedStringCursorIndex = 0
|
||||||
|
|
||||||
var readingCursorIndex = 0
|
var readingCursorIndex = 0
|
||||||
let builderCursorIndex = getBuilderCursorIndex()
|
let builderCursorIndex = builderCursorIndex
|
||||||
|
|
||||||
for theAnchor in _walkedNodes {
|
for theAnchor in _walkedNodes {
|
||||||
guard let node = theAnchor.node else {
|
guard let node = theAnchor.node else {
|
||||||
|
@ -106,7 +106,7 @@ extension KeyHandler {
|
||||||
InputState.ChoosingCandidate(
|
InputState.ChoosingCandidate(
|
||||||
composingBuffer: currentState.composingBuffer,
|
composingBuffer: currentState.composingBuffer,
|
||||||
cursorIndex: currentState.cursorIndex,
|
cursorIndex: currentState.cursorIndex,
|
||||||
candidates: getCandidatesArray(),
|
candidates: candidatesArray,
|
||||||
useVerticalMode: useVerticalMode
|
useVerticalMode: useVerticalMode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ extension KeyHandler {
|
||||||
|
|
||||||
// 這次重寫時,針對「buildAssociatePhraseStateWithKey」這個(用以生成帶有
|
// 這次重寫時,針對「buildAssociatePhraseStateWithKey」這個(用以生成帶有
|
||||||
// 聯想詞候選清單的結果的狀態回呼的)函數進行了小幅度的重構處理,使其始終
|
// 聯想詞候選清單的結果的狀態回呼的)函數進行了小幅度的重構處理,使其始終
|
||||||
// 可以從 ObjC 部分的「buildAssociatePhraseArray」函數獲取到一個內容類型
|
// 可以從 Core 部分的「buildAssociatePhraseArray」函數獲取到一個內容類型
|
||||||
// 為「String」的標準 Swift 陣列。這樣一來,該聯想詞狀態回呼函數將始終能
|
// 為「String」的標準 Swift 陣列。這樣一來,該聯想詞狀態回呼函數將始終能
|
||||||
// 夠傳回正確的結果形態、永遠也無法傳回 nil。於是,所有在用到該函數時以
|
// 夠傳回正確的結果形態、永遠也無法傳回 nil。於是,所有在用到該函數時以
|
||||||
// 回傳結果類型判斷作為合法性判斷依據的函數,全都將依據改為檢查傳回的陣列
|
// 回傳結果類型判斷作為合法性判斷依據的函數,全都將依據改為檢查傳回的陣列
|
||||||
|
@ -139,7 +139,7 @@ extension KeyHandler {
|
||||||
errorCallback: @escaping () -> Void
|
errorCallback: @escaping () -> Void
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
if input.isESC {
|
if input.isESC {
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ extension KeyHandler {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ extension KeyHandler {
|
||||||
readings: state.readings
|
readings: state.readings
|
||||||
)
|
)
|
||||||
marking.tooltipForInputting = state.tooltipForInputting
|
marking.tooltipForInputting = state.tooltipForInputting
|
||||||
stateCallback(marking.markedRange.length == 0 ? marking.convertToInputting() : marking)
|
stateCallback(marking.markedRange.length == 0 ? marking.convertedToInputting : marking)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("1149908D")
|
IME.prtDebugIntel("1149908D")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
|
@ -191,7 +191,7 @@ extension KeyHandler {
|
||||||
readings: state.readings
|
readings: state.readings
|
||||||
)
|
)
|
||||||
marking.tooltipForInputting = state.tooltipForInputting
|
marking.tooltipForInputting = state.tooltipForInputting
|
||||||
stateCallback(marking.markedRange.length == 0 ? marking.convertToInputting() : marking)
|
stateCallback(marking.markedRange.length == 0 ? marking.convertedToInputting : marking)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("9B51408D")
|
IME.prtDebugIntel("9B51408D")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
|
@ -217,8 +217,8 @@ extension KeyHandler {
|
||||||
|
|
||||||
if _composer.isEmpty {
|
if _composer.isEmpty {
|
||||||
insertReadingToBuilderAtCursor(reading: customPunctuation)
|
insertReadingToBuilderAtCursor(reading: customPunctuation)
|
||||||
let poppedText = popOverflowComposingTextAndWalk()
|
let poppedText = popOverflowComposingTextAndWalk
|
||||||
let inputting = buildInputtingState()
|
let inputting = buildInputtingState
|
||||||
inputting.poppedText = poppedText
|
inputting.poppedText = poppedText
|
||||||
stateCallback(inputting)
|
stateCallback(inputting)
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ extension KeyHandler {
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
guard state is InputState.Inputting else { return false }
|
guard state is InputState.Inputting else { return false }
|
||||||
|
|
||||||
var composingBuffer = currentReadings().joined(separator: "-")
|
var composingBuffer = currentReadings.joined(separator: "-")
|
||||||
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
|
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
|
||||||
composingBuffer = restoreToneOneInZhuyinKey(target: composingBuffer) // 恢復陰平標記
|
composingBuffer = restoreToneOneInZhuyinKey(target: composingBuffer) // 恢復陰平標記
|
||||||
composingBuffer = Tekkon.cnvPhonaToHanyuPinyin(target: composingBuffer) // 注音轉拼音
|
composingBuffer = Tekkon.cnvPhonaToHanyuPinyin(target: composingBuffer) // 注音轉拼音
|
||||||
|
@ -341,7 +341,7 @@ extension KeyHandler {
|
||||||
if _composer.hasToneMarker(withNothingElse: true) {
|
if _composer.hasToneMarker(withNothingElse: true) {
|
||||||
_composer.clear()
|
_composer.clear()
|
||||||
} else if _composer.isEmpty {
|
} else if _composer.isEmpty {
|
||||||
if getBuilderCursorIndex() >= 0 {
|
if builderCursorIndex >= 0 {
|
||||||
deleteBuilderReadingInFrontOfCursor()
|
deleteBuilderReadingInFrontOfCursor()
|
||||||
walk()
|
walk()
|
||||||
} else {
|
} else {
|
||||||
|
@ -354,10 +354,10 @@ extension KeyHandler {
|
||||||
_composer.doBackSpace()
|
_composer.doBackSpace()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _composer.isEmpty, getBuilderLength() == 0 {
|
if _composer.isEmpty, builderLength == 0 {
|
||||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||||
} else {
|
} else {
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -372,10 +372,10 @@ extension KeyHandler {
|
||||||
guard state is InputState.Inputting else { return false }
|
guard state is InputState.Inputting else { return false }
|
||||||
|
|
||||||
if _composer.isEmpty {
|
if _composer.isEmpty {
|
||||||
if getBuilderCursorIndex() != getBuilderLength() {
|
if builderCursorIndex != builderLength {
|
||||||
deleteBuilderReadingToTheFrontOfCursor()
|
deleteBuilderReadingToTheFrontOfCursor()
|
||||||
walk()
|
walk()
|
||||||
let inputting = buildInputtingState()
|
let inputting = buildInputtingState
|
||||||
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||||
if inputting.composingBuffer.isEmpty {
|
if inputting.composingBuffer.isEmpty {
|
||||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||||
|
@ -428,9 +428,9 @@ extension KeyHandler {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if getBuilderCursorIndex() != 0 {
|
if builderCursorIndex != 0 {
|
||||||
setBuilderCursorIndex(value: 0)
|
builderCursorIndex = 0
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("66D97F90")
|
IME.prtDebugIntel("66D97F90")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
|
@ -456,9 +456,9 @@ extension KeyHandler {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if getBuilderCursorIndex() != getBuilderLength() {
|
if builderCursorIndex != builderLength {
|
||||||
setBuilderCursorIndex(value: getBuilderLength())
|
builderCursorIndex = builderLength
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("9B69908E")
|
IME.prtDebugIntel("9B69908E")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
|
@ -490,10 +490,10 @@ extension KeyHandler {
|
||||||
// If reading is not empty, we cancel the reading.
|
// If reading is not empty, we cancel the reading.
|
||||||
if !_composer.isEmpty {
|
if !_composer.isEmpty {
|
||||||
_composer.clear()
|
_composer.clear()
|
||||||
if getBuilderLength() == 0 {
|
if builderLength == 0 {
|
||||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||||
} else {
|
} else {
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,7 +526,7 @@ extension KeyHandler {
|
||||||
composingBuffer: currentState.composingBuffer,
|
composingBuffer: currentState.composingBuffer,
|
||||||
cursorIndex: currentState.cursorIndex,
|
cursorIndex: currentState.cursorIndex,
|
||||||
markerIndex: UInt(nextPosition),
|
markerIndex: UInt(nextPosition),
|
||||||
readings: currentReadings()
|
readings: currentReadings
|
||||||
)
|
)
|
||||||
marking.tooltipForInputting = currentState.tooltip
|
marking.tooltipForInputting = currentState.tooltip
|
||||||
stateCallback(marking)
|
stateCallback(marking)
|
||||||
|
@ -536,9 +536,9 @@ extension KeyHandler {
|
||||||
stateCallback(state)
|
stateCallback(state)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if getBuilderCursorIndex() < getBuilderLength() {
|
if builderCursorIndex < builderLength {
|
||||||
setBuilderCursorIndex(value: getBuilderCursorIndex() + 1)
|
builderCursorIndex += 1
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("A96AAD58")
|
IME.prtDebugIntel("A96AAD58")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
|
@ -575,7 +575,7 @@ extension KeyHandler {
|
||||||
composingBuffer: currentState.composingBuffer,
|
composingBuffer: currentState.composingBuffer,
|
||||||
cursorIndex: currentState.cursorIndex,
|
cursorIndex: currentState.cursorIndex,
|
||||||
markerIndex: UInt(previousPosition),
|
markerIndex: UInt(previousPosition),
|
||||||
readings: currentReadings()
|
readings: currentReadings
|
||||||
)
|
)
|
||||||
marking.tooltipForInputting = currentState.tooltip
|
marking.tooltipForInputting = currentState.tooltip
|
||||||
stateCallback(marking)
|
stateCallback(marking)
|
||||||
|
@ -585,9 +585,9 @@ extension KeyHandler {
|
||||||
stateCallback(state)
|
stateCallback(state)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if getBuilderCursorIndex() > 0 {
|
if builderCursorIndex > 0 {
|
||||||
setBuilderCursorIndex(value: getBuilderCursorIndex() - 1)
|
builderCursorIndex -= 1
|
||||||
stateCallback(buildInputtingState())
|
stateCallback(buildInputtingState)
|
||||||
} else {
|
} else {
|
||||||
IME.prtDebugIntel("7045E6F3")
|
IME.prtDebugIntel("7045E6F3")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
|
|
|
@ -648,7 +648,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
||||||
let selectedValue = state.candidates[Int(index)]
|
let selectedValue = state.candidates[Int(index)]
|
||||||
keyHandler.fixNode(value: selectedValue)
|
keyHandler.fixNode(value: selectedValue)
|
||||||
|
|
||||||
let inputting = keyHandler.buildInputtingState()
|
let inputting = keyHandler.buildInputtingState
|
||||||
|
|
||||||
if mgrPrefs.useSCPCTypingMode {
|
if mgrPrefs.useSCPCTypingMode {
|
||||||
keyHandler.clear()
|
keyHandler.clear()
|
||||||
|
|
Loading…
Reference in New Issue