vChewing-macOS/Source/Modules/InputHandler_HandleStates.s...

862 lines
32 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
/// 調調
import Megrez
import Shared
import Tekkon
// MARK: - § 調 (Functions Interact With States).
extension InputHandler {
// MARK: - State Building
///
public func generateStateOfInputting(sansReading: Bool = false) -> IMEStateProtocol {
let cpInput = isCodePointInputMode && !sansReading
/// (Update the composing buffer)
/// IMEStateData NSAttributeString
var displayTextSegments: [String] = cpInput
? [strCodePointBuffer]
: compositor.walkedNodes.values
var cursor = cpInput
? displayTextSegments.joined().count
: convertCursorForDisplay(compositor.cursor)
let reading: String = (sansReading || isCodePointInputMode) ? "" : readingForDisplay //
if !reading.isEmpty {
var newDisplayTextSegments = [String]()
var temporaryNode = ""
var charCounter = 0
for node in displayTextSegments {
for char in node {
if charCounter == cursor {
newDisplayTextSegments.append(temporaryNode)
temporaryNode = ""
newDisplayTextSegments.append(reading)
}
temporaryNode += String(char)
charCounter += 1
}
newDisplayTextSegments.append(temporaryNode)
temporaryNode = ""
}
if newDisplayTextSegments == displayTextSegments { newDisplayTextSegments.append(reading) }
displayTextSegments = newDisplayTextSegments
cursor += reading.count
}
for i in 0 ..< displayTextSegments.count {
displayTextSegments[i] = displayTextSegments[i].trimmingCharacters(in: .newlines)
}
///
return IMEState.ofInputting(displayTextSegments: displayTextSegments, cursor: cursor)
}
///
/// - Parameter rawCursor:
/// - Returns:
func convertCursorForDisplay(_ rawCursor: Int) -> Int {
var composedStringCursorIndex = 0
var readingCursorIndex = 0
for theNode in compositor.walkedNodes {
let strNodeValue = theNode.value
///
/// NodeAnchorspanningLength
///
let spanningLength: Int = theNode.keyArray.count
if readingCursorIndex + spanningLength <= rawCursor {
composedStringCursorIndex += strNodeValue.count
readingCursorIndex += spanningLength
continue
}
if !theNode.isReadingMismatched {
strNodeValue.forEach { _ in
if readingCursorIndex < rawCursor {
composedStringCursorIndex += 1
readingCursorIndex += 1
}
}
continue
}
guard readingCursorIndex < rawCursor else { continue }
composedStringCursorIndex += strNodeValue.count
readingCursorIndex += spanningLength
readingCursorIndex = min(readingCursorIndex, rawCursor)
}
return composedStringCursorIndex
}
// MARK: -
///
/// - Returns:
public func generateStateOfCandidates() -> IMEStateProtocol {
IMEState.ofCandidates(
candidates: generateArrayOfCandidates(fixOrder: prefs.useFixecCandidateOrderOnSelection),
displayTextSegments: compositor.walkedNodes.values,
cursor: delegate?.state.cursor ?? generateStateOfInputting().cursor
)
}
// MARK: -
///
///
/// generateStateOfAssociates
/// 使
/// Core generateArrayOfAssociates
/// String Swift
/// nil
///
///
/// - Parameters:
/// - key:
/// - Returns:
public func generateStateOfAssociates(withPair pair: Megrez.Compositor.KeyValuePaired) -> IMEStateProtocol {
IMEState.ofAssociates(
candidates: generateArrayOfAssociates(withPair: pair))
}
// MARK: -
///
/// - Parameters:
/// - input:
/// - Returns: SessionCtl IMK
func handleMarkingState(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if input.isEsc {
delegate.switchState(generateStateOfInputting())
return true
}
//
if input.isControlHold, input.isCommandHold, input.isEnter {
delegate.callError("1198E3E5")
return true
}
// Enter
if input.isEnter {
var tooltipMessage = "+ Succeeded in adding / boosting a user phrase."
var tooltipColorState: TooltipColorState = .normal
//
if input.isShiftHold, input.isCommandHold {
tooltipMessage = "- Succeeded in nerfing a user phrase."
tooltipColorState = .succeeded
if !state.isFilterable {
delegate.callError("2EAC1F7A")
return true
}
}
if !state.isMarkedLengthValid {
delegate.callError("9AAFAC00")
return true
}
if !delegate.performUserPhraseOperation(addToFilter: false) {
delegate.callError("5B69CC8D")
return true
}
var newState = generateStateOfInputting()
newState.tooltip = NSLocalizedString(tooltipMessage, comment: "") + "  "
newState.data.tooltipColorState = tooltipColorState
newState.tooltipDuration = 1.85
delegate.switchState(newState)
return true
}
// BackSpace & Delete
if input.isBackSpace || input.isDelete {
let tooltipMessage = "! Succeeded in filtering a user phrase."
if !state.isFilterable {
delegate.callError("1F88B191")
return true
}
if !delegate.performUserPhraseOperation(addToFilter: true) {
delegate.callError("68D3C6C8")
return true
}
var newState = generateStateOfInputting()
newState.tooltip = NSLocalizedString(tooltipMessage, comment: "") + "  "
newState.data.tooltipColorState = .warning
newState.tooltipDuration = 1.85
delegate.switchState(newState)
return true
}
// Shift + Left
if input.isCursorBackward, input.isShiftHold {
if compositor.marker > 0 {
compositor.marker -= 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .rear, isMarker: true)
}
var marking = IMEState.ofMarking(
displayTextSegments: state.displayTextSegments,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
delegate.switchState(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else {
delegate.callError("1149908D")
}
return true
}
// Shift + Right
if input.isCursorForward, input.isShiftHold {
if compositor.marker < compositor.length {
compositor.marker += 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .front, isMarker: true)
}
var marking = IMEState.ofMarking(
displayTextSegments: state.displayTextSegments,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
delegate.switchState(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else {
delegate.callError("9B51408D")
}
return true
}
return false
}
// MARK: -
///
/// - Parameters:
/// - customPunctuation:
/// - Returns: SessionCtl IMK
func handlePunctuation(_ customPunctuation: String) -> Bool {
guard let delegate = delegate else { return false }
if !currentLM.hasUnigramsFor(keyArray: [customPunctuation]) {
return false
}
guard isComposerOrCalligrapherEmpty else {
//
delegate.callError("A9B69908D")
return true
}
guard compositor.insertKey(customPunctuation) else {
delegate.callError("C0793A6D: 得檢查對應的語言模組的 hasUnigramsFor() 是否有誤判之情形。")
return true
}
walk()
// App
let textToCommit = commitOverflownComposition
var inputting = generateStateOfInputting()
inputting.textToCommit = textToCommit
delegate.switchState(inputting)
//
guard prefs.useSCPCTypingMode, isComposerOrCalligrapherEmpty else { return true }
let candidateState = generateStateOfCandidates()
switch candidateState.candidates.count {
case 2...: delegate.switchState(candidateState)
case 1:
clear() // candidateState
if let candidateToCommit: ([String], String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty {
delegate.switchState(IMEState.ofCommitting(textToCommit: candidateToCommit.1))
} else {
delegate.switchState(candidateState)
}
default: delegate.callError("8DA4096E")
}
return true
}
// MARK: - Enter
/// Enter
/// - Parameter input:
/// - Returns: SessionCtl IMK
@discardableResult func handleEnter(input: InputSignalProtocol, readingOnly: Bool = false) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if isCodePointInputMode { return handleCodePointInputToggle() }
guard state.type == .ofInputting else { return false }
var displayedText = state.displayedText
if input.modifierFlags == [.option, .shift] {
displayedText = displayedText.map(\.description).joined(separator: " ")
} else if readingOnly {
displayedText = commissionByCtrlCommandEnter()
} else if input.isCommandHold, input.isControlHold {
displayedText =
input.isOptionHold
? commissionByCtrlOptionCommandEnter(isShiftPressed: input.isShiftHold)
: commissionByCtrlCommandEnter(isShiftPressed: input.isShiftHold)
}
delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText))
return true
}
// MARK: - Command+Enter
/// Command+Enter
/// - Parameter isShiftPressed: Shift
/// - Returns: SessionCtl IMK
private func commissionByCtrlCommandEnter(isShiftPressed: Bool = false) -> String {
var displayedText = compositor.keys.joined(separator: "\t")
if compositor.isEmpty {
displayedText = readingForDisplay
}
if !prefs.cassetteEnabled {
if prefs.inlineDumpPinyinInLieuOfZhuyin {
if !compositor.isEmpty {
var arrDisplayedTextElements = [String]()
compositor.keys.forEach { key in
arrDisplayedTextElements.append(Tekkon.restoreToneOneInPhona(target: key)) //
}
displayedText = arrDisplayedTextElements.joined(separator: "\t")
}
displayedText = Tekkon.cnvPhonaToHanyuPinyin(targetJoined: displayedText) //
}
if prefs.showHanyuPinyinInCompositionBuffer {
if compositor.isEmpty {
displayedText = displayedText.replacingOccurrences(of: "1", with: "")
}
}
}
displayedText = displayedText.replacingOccurrences(of: "\t", with: isShiftPressed ? "-" : " ")
return displayedText
}
// MARK: - Command+Option+Enter Ruby
/// Command+Option+Enter Ruby
/// - Parameter isShiftPressed: Shift
/// - Returns: SessionCtl IMK
private func commissionByCtrlOptionCommandEnter(isShiftPressed: Bool = false) -> String {
var composed = ""
compositor.walkedNodes.smashedPairs.forEach { key, value in
var key = key
if !prefs.cassetteEnabled {
key =
prefs.inlineDumpPinyinInLieuOfZhuyin
? Tekkon.restoreToneOneInPhona(target: key) //
: Tekkon.cnvPhonaToTextbookReading(target: key) //
if prefs.inlineDumpPinyinInLieuOfZhuyin {
key = Tekkon.cnvPhonaToHanyuPinyin(targetJoined: key) //
key = Tekkon.cnvHanyuPinyinToTextbookStyle(targetJoined: key) // 調
}
}
key = key.replacingOccurrences(of: "\t", with: " ")
if isShiftPressed {
if !composed.isEmpty { composed += " " }
composed += key.contains("_") ? "??" : key
return
}
//
composed += key.contains("_") ? value : "<ruby>\(value)<rp>(</rp><rt>\(key)</rt><rp>)</rp></ruby>"
}
return composed
}
// MARK: - BackSpace (macOS Delete)
/// BackSpace (macOS Delete)
/// - Parameters:
/// - input:
/// - Returns: SessionCtl IMK
func handleBackSpace(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else {
isCodePointInputMode = false
return false
}
if isCodePointInputMode {
if !strCodePointBuffer.isEmpty {
strCodePointBuffer = strCodePointBuffer.dropLast(1).description
if !strCodePointBuffer.isEmpty {
var updatedState = generateStateOfInputting()
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
delegate.switchState(updatedState)
return true
}
}
return handleCodePointInputToggle()
}
// macOS Shift+BackSpace
shiftBksp: switch prefs.specifyShiftBackSpaceKeyBehavior {
case 0:
if prefs.cassetteEnabled {
guard input.isShiftHold, calligrapher.isEmpty else { break shiftBksp }
guard let prevReading = previousParsableCalligraph else { break shiftBksp }
compositor.dropKey(direction: .rear)
walk() // Walk walk
calligrapher = prevReading
} else {
guard input.isShiftHold, isComposerOrCalligrapherEmpty else { break shiftBksp }
guard let prevReading = previousParsableReading else { break shiftBksp }
// prevReading 調調
compositor.dropKey(direction: .rear)
walk() // Walk walk
prevReading.1.map(\.description).forEach { composer.receiveKey(fromPhonabet: $0) }
}
delegate.switchState(generateStateOfInputting())
return true
case 1:
delegate.switchState(IMEState.ofAbortion())
return true
default: break
}
if input.isShiftHold, input.isOptionHold {
delegate.switchState(IMEState.ofAbortion())
return true
}
let isConfirm: Bool = prefs.cassetteEnabled ? input.isSpace : composer.hasIntonation(withNothingElse: true)
if isConfirm {
clearComposerAndCalligrapher()
} else if isComposerOrCalligrapherEmpty {
if compositor.cursor > 0 {
compositor.dropKey(direction: .rear)
walk()
} else {
delegate.callError("9D69908D")
return true
}
} else {
letComposerAndCalligrapherDoBackSpace()
}
switch isComposerOrCalligrapherEmpty && compositor.isEmpty {
case false: delegate.switchState(generateStateOfInputting())
case true: delegate.switchState(IMEState.ofAbortion())
}
return true
}
// MARK: - PC Delete (macOS Fn+BackSpace)
/// PC Delete (macOS Fn+BackSpace)
/// - Parameters:
/// - input:
/// - Returns: SessionCtl IMK
func handleDelete(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if isCodePointInputMode { return handleCodePointInputToggle() }
guard state.type == .ofInputting else { return false }
if input.isShiftHold {
delegate.switchState(IMEState.ofAbortion())
return true
}
if compositor.cursor == compositor.length, isComposerOrCalligrapherEmpty {
delegate.callError("9B69938D")
return true
}
if isComposerOrCalligrapherEmpty {
compositor.dropKey(direction: .front)
walk()
} else {
clearComposerAndCalligrapher()
}
let inputting = generateStateOfInputting()
// count > 0!isEmpty滿
switch inputting.displayedText.isEmpty {
case false: delegate.switchState(inputting)
case true: delegate.switchState(IMEState.ofAbortion())
}
return true
}
// MARK: - 90
/// 90
/// - Returns: SessionCtl IMK
func handleClockKey() -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else { return false }
if !isComposerOrCalligrapherEmpty { delegate.callError("9B6F908D") }
return true
}
// MARK: - Home
/// Home
/// - Returns: SessionCtl IMK
func handleHome() -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else { return false }
if !isComposerOrCalligrapherEmpty {
delegate.callError("ABC44080")
return true
}
if compositor.cursor != 0 {
compositor.cursor = 0
delegate.switchState(generateStateOfInputting())
} else {
delegate.callError("66D97F90")
}
return true
}
// MARK: - End
/// End
/// - Returns: SessionCtl IMK
func handleEnd() -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else { return false }
if !isComposerOrCalligrapherEmpty {
delegate.callError("9B69908D")
return true
}
if compositor.cursor != compositor.length {
compositor.cursor = compositor.length
delegate.switchState(generateStateOfInputting())
} else {
delegate.callError("9B69908E")
}
return true
}
// MARK: - Esc
/// Esc
/// - Returns: SessionCtl IMK
func handleEsc() -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if isCodePointInputMode { return handleCodePointInputToggle() }
guard state.type == .ofInputting else { return false }
if prefs.escToCleanInputBuffer {
///
/// macOS Windows 使
delegate.switchState(IMEState.ofAbortion())
} else {
if isComposerOrCalligrapherEmpty { return true }
///
clearComposerAndCalligrapher()
switch compositor.isEmpty {
case false: delegate.switchState(generateStateOfInputting())
case true: delegate.switchState(IMEState.ofAbortion())
}
}
return true
}
// MARK: -
///
/// - Parameters:
/// - input:
/// - Returns: SessionCtl IMK
func handleForward(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else { return false }
if !isComposerOrCalligrapherEmpty {
delegate.callError("B3BA5257")
return true
}
if input.isShiftHold {
// Shift + Right
if compositor.cursor < compositor.length {
compositor.marker = compositor.cursor + 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .front, isMarker: true)
}
var marking = IMEState.ofMarking(
displayTextSegments: compositor.walkedNodes.values,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = state.tooltip
delegate.switchState(marking)
} else {
delegate.callError("BB7F6DB9")
}
} else if input.isOptionHold {
if input.isControlHold {
return handleEnd()
}
//
if !compositor.jumpCursorBySpan(to: .front) {
delegate.callError("33C3B580")
return true
}
delegate.switchState(generateStateOfInputting())
} else {
if compositor.cursor < compositor.length {
compositor.cursor += 1
if isCursorCuttingChar() {
compositor.jumpCursorBySpan(to: .front)
}
delegate.switchState(generateStateOfInputting())
} else {
delegate.callError("A96AAD58")
}
}
return true
}
// MARK: -
///
/// - Parameters:
/// - input:
/// - Returns: SessionCtl IMK
func handleBackward(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else { return false }
if !isComposerOrCalligrapherEmpty {
delegate.callError("6ED95318")
return true
}
if input.isShiftHold {
// Shift + left
if compositor.cursor > 0 {
compositor.marker = compositor.cursor - 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .rear, isMarker: true)
}
var marking = IMEState.ofMarking(
displayTextSegments: compositor.walkedNodes.values,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = state.tooltip
delegate.switchState(marking)
} else {
delegate.callError("D326DEA3")
}
} else if input.isOptionHold {
if input.isControlHold { return handleHome() }
//
if !compositor.jumpCursorBySpan(to: .rear) {
delegate.callError("8D50DD9E")
return true
}
delegate.switchState(generateStateOfInputting())
} else {
if compositor.cursor > 0 {
compositor.cursor -= 1
if isCursorCuttingChar() {
compositor.jumpCursorBySpan(to: .rear)
}
delegate.switchState(generateStateOfInputting())
} else {
delegate.callError("7045E6F3")
}
}
return true
}
// MARK: - Tab Shift+Space
///
/// - Parameters:
/// - reverseOrder:
/// - Returns: SessionCtl IMK
func revolveCandidate(reverseOrder: Bool) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if isComposerOrCalligrapherEmpty, compositor.isEmpty || compositor.walkedNodes.isEmpty { return false }
guard state.type == .ofInputting else {
guard state.type == .ofEmpty else {
delegate.callError("6044F081")
return true
}
// 使 Tab
return false
}
guard isComposerOrCalligrapherEmpty else {
delegate.callError("A2DAF7BC")
return true
}
let candidates = generateArrayOfCandidates(fixOrder: true)
guard !candidates.isEmpty else {
delegate.callError("3378A6DF")
return true
}
guard let region = compositor.walkedNodes.cursorRegionMap[cursorForCandidate],
compositor.walkedNodes.count > region
else {
delegate.callError("1CE6FFBD")
return true
}
let currentNode = compositor.walkedNodes[region]
let currentPaired = (currentNode.keyArray, currentNode.value)
//
let newIndex: Int = {
if candidates.count == 1 { return 0 }
var result = 0
theLoop: for candidate in candidates {
if !currentNode.isOverridden {
if candidates[0] == currentPaired { result = reverseOrder ? candidates.count - 1 : 1 }
break theLoop
}
result.revolveAsIndex(with: candidates, clockwise: !(candidate == currentPaired && reverseOrder))
if candidate == currentPaired { break }
}
return (0 ..< candidates.count).contains(result) ? result : 0
}()
if candidates.count > 1 {
consolidateNode(
candidate: candidates[newIndex], respectCursorPushing: false,
preConsolidate: false, skipObservation: true
)
}
//
func isContextVertical() -> Bool {
delegate.updateVerticalTypingStatus()
return delegate.isVerticalTyping
}
var newState = generateStateOfInputting()
let locID = Bundle.main.preferredLocalizations[0]
let newTooltip = NSMutableString()
newTooltip.insert(" " + candidates[newIndex].1, at: 0)
if #available(macOS 10.13, *), isContextVertical(), locID != "en" {
newTooltip.insert((newIndex + 1).i18n(loc: locID) + "" + candidates.count.i18n(loc: locID), at: 0)
} else {
newTooltip.insert((newIndex + 1).description + " / " + candidates.count.description, at: 0)
}
newTooltip.append("  ")
newState.tooltip = newTooltip.description
vCLog(newState.tooltip)
newState.tooltipDuration = 0
delegate.switchState(newState)
return true
}
// MARK: -
@discardableResult func handleCodePointInputToggle() -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
if isCodePointInputMode {
isCodePointInputMode = false
delegate.switchState(IMEState.ofAbortion())
return true
}
var updatedState = generateStateOfInputting(sansReading: true)
delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText))
updatedState = IMEState.ofEmpty()
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
delegate.switchState(updatedState)
isCodePointInputMode = true
return true
}
// MARK: -
///
/// - Parameters:
/// - alternative: 使
/// - JIS: JIS
/// - Returns: SessionCtl IMK
func handlePunctuationList(alternative: Bool, isJIS: Bool = false) -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
if alternative {
if currentLM.hasUnigramsFor(keyArray: ["_punctuation_list"]) {
if isComposerOrCalligrapherEmpty, compositor.insertKey("_punctuation_list") {
walk()
// App
let textToCommit = commitOverflownComposition
var inputting = generateStateOfInputting()
inputting.textToCommit = textToCommit
delegate.switchState(inputting)
//
let newState = generateStateOfCandidates()
_ = newState.candidates.isEmpty ? delegate.callError("B5127D8A") : delegate.switchState(newState)
} else { //
delegate.callError("17446655")
}
return true
} else {
let errorMessage =
NSLocalizedString(
"Please manually implement the symbols of this menu \nin the user phrase file with “_punctuation_list” key.",
comment: ""
)
vCLog("8EB3FB1A: " + errorMessage)
let textToCommit = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: textToCommit))
delegate.switchState(IMEState.ofCommitting(textToCommit: isJIS ? "_" : "`"))
return true
}
} else {
// commit buffer ESC
let textToCommit = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: textToCommit))
delegate.switchState(IMEState.ofSymbolTable(node: CandidateNode.root))
return true
}
}
}