Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abbrevモードから抜けたときに入る前のモードに戻す #128

Merged
merged 2 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 34 additions & 20 deletions macSKK/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,28 @@ protocol SpecialStateProtocol: CursorProtocol {
/// 入力中の未確定文字列の定義
struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
/// (Sticky)Shiftによる未確定入力中かどうか。先頭に▽ついてる状態。
var isShift: Bool
let isShift: Bool
/// かな/カナならかなになっているひらがなの文字列、abbrevなら入力した文字列.
var text: [String]
let text: [String]
/// (Sticky)Shiftが押されたあとに入力されてかなになっている文字列。送り仮名モードになってなければnil
/// StickyShiftが押されたり送り仮名をバックスペースで削除して送り仮名モードだけど送り仮名が空のときは空配列
var okuri: [Romaji.Moji]?
let okuri: [Romaji.Moji]?
/// ローマ字モードで未確定部分。"k" や "ky" など最低あと1文字でかなに変換できる文字列。
var romaji: String
let romaji: String
/// カーソル位置。末尾のときはnil。先頭の▽分は含まないので非nilのときは[0, text.count)の範囲を取る。
var cursor: Int?
let cursor: Int?
/// 未確定文字列の入力開始前のモード。
/// スラッシュでAbbrevモードに入るときにその前のモードを設定しておき、Abbrevモードから抜けるときにそのモードに戻す
let prevMode: InputMode?

init(isShift: Bool, text: [String], okuri: [Romaji.Moji]? = nil, romaji: String, cursor: Int? = nil, prevMode: InputMode? = nil) {
self.isShift = isShift
self.text = text
self.okuri = okuri
self.romaji = romaji
self.cursor = cursor
self.prevMode = prevMode
}

/**
* 現在の状態で別の状態に移行するための処理を行った結果を返す
Expand All @@ -108,14 +120,16 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
text: text,
okuri: okuri + [Romaji.n],
romaji: "",
cursor: cursor)
cursor: cursor,
prevMode: prevMode)
} else {
return ComposingState(
isShift: isShift,
text: text + [Romaji.n.kana],
okuri: nil,
romaji: "",
cursor: cursor)
cursor: cursor,
prevMode: prevMode)
}
} else {
return self
Expand Down Expand Up @@ -150,7 +164,7 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
newText = text + moji.kana.map({ String($0) })
newCursor = nil
}
return ComposingState(isShift: isShift, text: newText, okuri: okuri, romaji: romaji, cursor: newCursor)
return ComposingState(isShift: isShift, text: newText, okuri: okuri, romaji: romaji, cursor: newCursor, prevMode: prevMode)
}

/**
Expand All @@ -163,23 +177,23 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
if newRomaji.isEmpty && text.isEmpty {
return nil
}
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: String(newRomaji), cursor: cursor)
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: String(newRomaji), cursor: cursor, prevMode: prevMode)
} else if let okuri {
return ComposingState(
isShift: isShift, text: text, okuri: okuri.isEmpty ? nil : okuri.dropLast(), romaji: romaji,
cursor: cursor)
cursor: cursor, prevMode: prevMode)
} else if text.isEmpty {
return nil
} else if let cursor = cursor {
if cursor > 0 {
var newText = text
newText.remove(at: cursor - 1)
return ComposingState(isShift: isShift, text: newText, okuri: okuri, romaji: romaji, cursor: cursor - 1)
return ComposingState(isShift: isShift, text: newText, okuri: okuri, romaji: romaji, cursor: cursor - 1, prevMode: prevMode)
} else {
return self
}
} else {
return ComposingState(isShift: isShift, text: text.dropLast(), okuri: okuri, romaji: romaji, cursor: cursor)
return ComposingState(isShift: isShift, text: text.dropLast(), okuri: okuri, romaji: romaji, cursor: cursor, prevMode: prevMode)
}
}

Expand All @@ -190,18 +204,18 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
if let cursor = cursor, cursor < text.count {
var newText = text
newText.remove(at: text.index(text.startIndex, offsetBy: cursor))
return ComposingState(isShift: isShift, text: newText, okuri: okuri, romaji: romaji, cursor: cursor == text.count - 1 ? nil : cursor)
return ComposingState(isShift: isShift, text: newText, okuri: okuri, romaji: romaji, cursor: cursor == text.count - 1 ? nil : cursor, prevMode: prevMode)
} else {
return self
}
}

func resetRomaji() -> Self {
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: "", cursor: cursor)
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: "", cursor: cursor, prevMode: prevMode)
}

func with(isShift: Bool) -> Self {
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: "", cursor: cursor)
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: "", cursor: cursor, prevMode: prevMode)
}

/// カーソルより左のtext部分を返す。
Expand Down Expand Up @@ -249,7 +263,7 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
} else {
newCursor = max(text.count - 1, 0)
}
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: newCursor)
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: newCursor, prevMode: prevMode)
} else {
return self
}
Expand All @@ -261,10 +275,10 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
return self
} else if let cursor, isShift {
if cursor + 1 == text.count {
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: nil)
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: nil, prevMode: prevMode)
} else {
return ComposingState(
isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: min(cursor + 1, text.count))
isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: min(cursor + 1, text.count), prevMode: prevMode)
}
} else {
return self
Expand All @@ -275,7 +289,7 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
if text.isEmpty {
return self
} else if isShift {
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: 0)
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: 0, prevMode: prevMode)
} else {
return self
}
Expand All @@ -285,7 +299,7 @@ struct ComposingState: Equatable, MarkedTextProtocol, CursorProtocol {
if text.isEmpty {
return self
} else if isShift {
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: nil)
return ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: nil, prevMode: prevMode)
} else {
return self
}
Expand Down
16 changes: 15 additions & 1 deletion macSKK/StateMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ class StateMachine {
} else if input == "/" && !action.shiftIsPressed() {
switch state.inputMode {
case .hiragana, .katakana, .hankaku:
state.inputMethod = .composing(ComposingState(isShift: true, text: [], okuri: nil, romaji: "", prevMode: state.inputMode))
state.inputMode = .direct
state.inputMethod = .composing(ComposingState(isShift: true, text: [], okuri: nil, romaji: ""))
inputMethodEventSubject.send(.modeChanged(.direct, action.cursorPosition))
updateMarkedText()
return true
Expand Down Expand Up @@ -364,12 +364,20 @@ class StateMachine {
let okuri = composing.okuri
let romaji = composing.romaji

func updateModeIfPrevModeExists() {
if let prevMode = composing.prevMode {
state.inputMode = prevMode
inputMethodEventSubject.send(.modeChanged(prevMode, action.cursorPosition))
}
}

switch action.keyEvent {
case .enter:
// 未確定ローマ字はn以外は入力されずに削除される. nだけは"ん"として変換する
let fixedText = composing.string(for: state.inputMode, convertHatsuon: true)
state.inputMethod = .normal
addFixedText(fixedText)
updateModeIfPrevModeExists()
return true
case .backspace:
if let newComposingState = composing.dropLast() {
Expand All @@ -393,6 +401,7 @@ class StateMachine {
if text.isEmpty {
addFixedText(" ")
state.inputMethod = .normal
updateModeIfPrevModeExists()
return true
} else if composing.cursor == 0 {
state.inputMethod = .normal
Expand Down Expand Up @@ -491,6 +500,7 @@ class StateMachine {
if text.isEmpty || romaji.isEmpty {
// 下線テキストをリセットする
state.inputMethod = .normal
updateModeIfPrevModeExists()
} else {
state.inputMethod = .composing(ComposingState(isShift: isShift, text: text, okuri: nil, romaji: ""))
}
Expand Down Expand Up @@ -882,6 +892,10 @@ class StateMachine {
} else {
state.inputMethod = .normal
addFixedText(selecting.fixedText)
if let prevMode = selecting.prev.composing.prevMode {
state.inputMode = prevMode
inputMethodEventSubject.send(.modeChanged(prevMode, action.cursorPosition))
}
}
}

Expand Down
33 changes: 32 additions & 1 deletion macSKKTests/StateMachineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,34 @@ final class StateMachineTests: XCTestCase {
wait(for: [expectation], timeout: 1.0)
}

func testHandleNormalAbbrevPrevMode() {
let expectation = XCTestExpectation()
stateMachine.inputMethodEvent.collect(12).sink { events in
XCTAssertEqual(events[0], .modeChanged(.katakana, .zero))
XCTAssertEqual(events[1], .modeChanged(.direct, .zero))
XCTAssertEqual(events[2], .markedText(MarkedText([.markerCompose])))
XCTAssertEqual(events[3], .markedText(MarkedText([.markerCompose, .plain("a")])))
XCTAssertEqual(events[4], .fixedText("a"))
XCTAssertEqual(events[5], .modeChanged(.katakana, .zero))
XCTAssertEqual(events[6], .modeChanged(.hankaku, .zero))
XCTAssertEqual(events[7], .modeChanged(.direct, .zero))
XCTAssertEqual(events[8], .markedText(MarkedText([.markerCompose])))
XCTAssertEqual(events[9], .markedText(MarkedText([.markerCompose, .plain("b")])))
XCTAssertEqual(events[10], .modeChanged(.hankaku, .zero))
XCTAssertEqual(events[11], .markedText(MarkedText([])))
expectation.fulfill()
}.store(in: &cancellables)
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "q"))) // カタカナモードにしておく
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "/")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "a")))
XCTAssertTrue(stateMachine.handle(Action(keyEvent: .enter, originalEvent: nil, cursorPosition: .zero)))
XCTAssertTrue(stateMachine.handle(Action(keyEvent: .ctrlQ, originalEvent: nil, cursorPosition: .zero))) // 半角カナモード
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "/")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "b")))
XCTAssertTrue(stateMachine.handle(Action(keyEvent: .cancel, originalEvent: nil, cursorPosition: .zero)))
wait(for: [expectation], timeout: 1.0)
}

func testHandleNormalOptionModifier() {
let expectation = XCTestExpectation()
stateMachine.inputMethodEvent.collect(1).sink { events in
Expand Down Expand Up @@ -1472,16 +1500,19 @@ final class StateMachineTests: XCTestCase {
dictionary.setEntries(["n": [Word("美")]])

let expectation = XCTestExpectation()
stateMachine.inputMethodEvent.collect(4).sink { events in
stateMachine.inputMethodEvent.collect(6).sink { events in
XCTAssertEqual(events[0], .modeChanged(.direct, .zero))
XCTAssertEqual(events[1], .markedText(MarkedText([.markerCompose])))
XCTAssertEqual(events[2], .markedText(MarkedText([.markerCompose, .plain("n")])))
XCTAssertEqual(events[3], .markedText(MarkedText([.markerSelect, .emphasized("美")])))
XCTAssertEqual(events[4], .fixedText("美"))
XCTAssertEqual(events[5], .modeChanged(.hiragana, .zero))
expectation.fulfill()
}.store(in: &cancellables)
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "/")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "n")))
XCTAssertTrue(stateMachine.handle(Action(keyEvent: .space, originalEvent: nil, cursorPosition: .zero)))
XCTAssertTrue(stateMachine.handle(Action(keyEvent: .enter, originalEvent: nil, cursorPosition: .zero)))
wait(for: [expectation], timeout: 1.0)
}

Expand Down
19 changes: 14 additions & 5 deletions macSKKTests/StateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ final class StateTests: XCTestCase {
var state = ComposingState(
isShift: true, text: ["あ", "い"], okuri: nil, romaji: "", cursor: 1)
XCTAssertEqual(state.subText(), ["あ"])
state.cursor = nil
state = state.with(cursor: nil)
XCTAssertEqual(state.subText(), ["あ", "い"])
}

Expand Down Expand Up @@ -109,10 +109,9 @@ final class StateTests: XCTestCase {
XCTAssertEqual(state.yomi(for: .katakana), "あい")
XCTAssertEqual(state.yomi(for: .hankaku), "あい")
XCTAssertEqual(state.yomi(for: .direct), "あい")
state.cursor = 1
state = state.with(cursor: 1)
XCTAssertEqual(state.yomi(for: .hiragana), "あ")
state.okuri = [Romaji.Moji(firstRomaji: "u", kana: "う")]
state.cursor = nil
state = state.with(okuri: [Romaji.Moji(firstRomaji: "u", kana: "う")]).with(cursor: nil)
XCTAssertEqual(state.yomi(for: .katakana), "あいu")
}

Expand All @@ -124,7 +123,7 @@ final class StateTests: XCTestCase {
romaji: "",
cursor: nil)
XCTAssertEqual(state.yomi(for: .direct), "ab")
state.cursor = 1
state = state.with(cursor: 1)
XCTAssertEqual(state.yomi(for: .direct), "a")
}

Expand Down Expand Up @@ -468,3 +467,13 @@ final class StateTests: XCTestCase {
XCTAssertEqual(displayText.elements, [.plain("だい# /第#/ を削除します(yes/no)"), .plain("yes")])
}
}

extension ComposingState {
func with(cursor : Int?) -> Self {
ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: cursor, prevMode: prevMode)
}

func with(okuri: [Romaji.Moji]) -> Self {
ComposingState(isShift: isShift, text: text, okuri: okuri, romaji: romaji, cursor: cursor, prevMode: prevMode)
}
}
Loading