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

単語登録中に単語登録できるようにする #170

Merged
merged 2 commits into from
Jun 9, 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
37 changes: 19 additions & 18 deletions macSKK/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ struct SelectingState: Equatable, MarkedTextProtocol {
}
/// 候補選択状態に遷移する前の状態。
let prev: PrevState

/**
* 辞書登録する際の読み。ただし数値変換エントリの場合は例外あり。
*
Expand Down Expand Up @@ -555,33 +556,33 @@ struct UnregisterState: SpecialStateProtocol {

/// 入力中に遷移する特別なモード
enum SpecialState: SpecialStateProtocol {
/// 単語登録
case register(RegisterState)
/// 単語登録状態。登録中に登録する再帰可能でその場合はprevの末尾に現在の登録状態を付けて新しい状態に遷移する。
case register(RegisterState, prev: [RegisterState])
/// 単語登録解除
case unregister(UnregisterState)

func appendText(_ text: String) -> Self {
switch self {
case .register(let registerState):
return .register(registerState.appendText(text))
case .register(let registerState, let prev):
return .register(registerState.appendText(text), prev: prev)
case .unregister(let unregisterState):
return .unregister(unregisterState.appendText(text))
}
}

func dropLast() -> Self {
switch self {
case .register(let registerState):
return .register(registerState.dropLast())
case .register(let registerState, let prev):
return .register(registerState.dropLast(), prev: prev)
case .unregister(let unregisterState):
return .unregister(unregisterState.dropLast())
}
}

func dropForward() -> Self {
switch self {
case .register(let registerState):
return .register(registerState.dropForward())
case .register(let registerState, let prev):
return .register(registerState.dropForward(), prev: prev)
case .unregister:
// unregister時はカーソル移動できないので無視
return self
Expand All @@ -591,35 +592,35 @@ enum SpecialState: SpecialStateProtocol {
// MARK: - CursorProtocol
func moveCursorLeft() -> Self {
switch self {
case .register(let registerState):
return .register(registerState.moveCursorLeft())
case .register(let registerState, let prev):
return .register(registerState.moveCursorLeft(), prev: prev)
case .unregister(let unregisterState):
return .unregister(unregisterState.moveCursorLeft())
}
}

func moveCursorRight() -> Self {
switch self {
case .register(let registerState):
return .register(registerState.moveCursorRight())
case .register(let registerState, let prev):
return .register(registerState.moveCursorRight(), prev: prev)
case .unregister(let unregisterState):
return .unregister(unregisterState.moveCursorRight())
}
}

func moveCursorFirst() -> Self {
switch self {
case .register(let registerState):
return .register(registerState.moveCursorFirst())
case .register(let registerState, let prev):
return .register(registerState.moveCursorFirst(), prev: prev)
case .unregister(let unregisterState):
return .unregister(unregisterState.moveCursorFirst())
}
}

func moveCursorLast() -> Self {
switch self {
case .register(let registerState):
return .register(registerState.moveCursorLast())
case .register(let registerState, let prev):
return .register(registerState.moveCursorLast(), prev: prev)
case .unregister(let unregisterState):
return .unregister(unregisterState.moveCursorLast())
}
Expand Down Expand Up @@ -655,14 +656,14 @@ struct IMEState {
var elements = [MarkedText.Element]()
if let specialState {
switch specialState {
case .register(let registerState):
case .register(let registerState, let prevRegisterStates):
let mode = registerState.prev.mode
let composing = registerState.prev.composing
var yomi = composing.subText().joined()
if let okuri = composing.okuri, !okuri.isEmpty {
yomi += "*" + okuri.map { $0.string(for: mode) }.joined()
}
elements.append(.plain("[登録:\(yomi)]"))
elements.append(.plain(String(repeating: "[", count: prevRegisterStates.count + 1) + "登録:\(yomi)" + String(repeating: "]", count: prevRegisterStates.count + 1)))
if let registerCursor = registerState.cursor {
let subtext = String(registerState.text.prefix(registerCursor))
if !subtext.isEmpty {
Expand Down
49 changes: 39 additions & 10 deletions macSKK/StateMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,23 @@ final class StateMachine {
}
case .enter:
if let specialState {
if case .register(let registerState) = specialState {
if case .register(let registerState, let prev) = specialState {
if registerState.text.isEmpty {
state.inputMode = registerState.prev.mode
state.inputMethod = .composing(registerState.prev.composing)
state.specialState = nil
if let last = prev.last {
state.specialState = .register(last, prev: prev.dropLast())
} else {
state.specialState = nil
}
updateMarkedText()
} else {
addWordToUserDict(yomi: registerState.yomi, okuri: registerState.okuri, candidate: Candidate(registerState.text))
state.specialState = nil
if let last = prev.last {
state.specialState = .register(last, prev: prev.dropLast())
} else {
state.specialState = nil
}
state.inputMode = registerState.prev.mode
if let okuri = registerState.okuri {
addFixedText(registerState.text + okuri)
Expand Down Expand Up @@ -223,15 +231,20 @@ final class StateMachine {
case .cancel:
if let specialState = state.specialState {
switch specialState {
case .register(let registerState):
case .register(let registerState, let prevRegisterStates):
state.inputMode = registerState.prev.mode
state.inputMethod = .composing(registerState.prev.composing)
if let prevRegisterState = prevRegisterStates.last {
state.specialState = .register(prevRegisterState, prev: prevRegisterStates.dropLast())
} else {
state.specialState = nil
}
case .unregister(let unregisterState):
state.inputMode = unregisterState.prev.mode
updateCandidates(selecting: unregisterState.prev.selecting)
state.inputMethod = .selecting(unregisterState.prev.selecting)
state.specialState = nil
}
state.specialState = nil
updateMarkedText()
return true
} else {
Expand Down Expand Up @@ -870,15 +883,23 @@ final class StateMachine {
candidateWords = candidates(for: yomiText, option: nil)
}
if candidateWords.isEmpty {
if specialState != nil {
// 登録中に変換不能な変換をした場合は空文字列に変換する
if case .register(let registerState, let prev) = specialState {
// 単語登録中に単語登録する
state.specialState = .register(
RegisterState(
prev: RegisterState.PrevState(mode: state.inputMode, composing: trimmedComposing),
yomi: yomiText),
prev: prev + [registerState])
state.inputMethod = .normal
state.inputMode = .hiragana
inputMethodEventSubject.send(.modeChanged(.hiragana, action.cursorPosition))
} else {
// 単語登録に遷移する
state.specialState = .register(
RegisterState(
prev: RegisterState.PrevState(mode: state.inputMode, composing: trimmedComposing),
yomi: yomiText))
yomi: yomiText),
prev: [])
state.inputMethod = .normal
state.inputMode = .hiragana
inputMethodEventSubject.send(.modeChanged(.hiragana, action.cursorPosition))
Expand Down Expand Up @@ -947,7 +968,14 @@ final class StateMachine {
state.inputMethod = .selecting(newSelectingState)
updateCandidates(selecting: newSelectingState)
} else {
if specialState != nil {
if case .register(let registerState, let prev) = specialState {
state.specialState = .register(RegisterState(
prev: RegisterState.PrevState(
mode: selecting.prev.mode,
composing: selecting.prev.composing),
yomi: selecting.yomi),
prev: prev + [registerState])
} else if specialState != nil {
state.inputMethod = .normal
state.inputMode = selecting.prev.mode
} else {
Expand All @@ -956,7 +984,8 @@ final class StateMachine {
prev: RegisterState.PrevState(
mode: selecting.prev.mode,
composing: selecting.prev.composing),
yomi: selecting.yomi))
yomi: selecting.yomi),
prev: [])
state.inputMethod = .normal
state.inputMode = .hiragana
inputMethodEventSubject.send(.modeChanged(.hiragana, action.cursorPosition))
Expand Down
58 changes: 58 additions & 0 deletions macSKKTests/StateMachineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,64 @@ final class StateMachineTests: XCTestCase {
wait(for: [expectation], timeout: 1.0)
}

@MainActor func testHandleRegisteringRecursive() {
let stateMachine = StateMachine(initialState: IMEState(inputMode: .hiragana))
let expectation = XCTestExpectation()
stateMachine.inputMethodEvent.collect(9).sink { events in
XCTAssertEqual(events[0], .markedText(MarkedText([.markerCompose, .plain("い")])))
XCTAssertEqual(events[1], .modeChanged(.hiragana, .zero))
XCTAssertEqual(events[2], .markedText(MarkedText([.plain("[登録:い]")])))
XCTAssertEqual(events[3], .markedText(MarkedText([.plain("[登録:い]"), .markerCompose, .plain("う")])))
XCTAssertEqual(events[4], .modeChanged(.hiragana, .zero))
XCTAssertEqual(events[5], .markedText(MarkedText([.plain("[[登録:う]]")])))
XCTAssertEqual(events[6], .markedText(MarkedText([.plain("[[登録:う]]"), .plain("え")])))
XCTAssertEqual(events[7], .markedText(MarkedText([.plain("[登録:い]"), .plain("え")])))
XCTAssertEqual(events[8], .fixedText("え"))
expectation.fulfill()
}.store(in: &cancellables)
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "i", withShift: true)))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: " ")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "u", withShift: true)))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: " ")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "e")))
XCTAssertTrue(stateMachine.handle(enterAction))
XCTAssertTrue(stateMachine.handle(enterAction))
wait(for: [expectation], timeout: 1.0)
XCTAssertEqual(Global.dictionary.refer("い"), [Word("え")])
XCTAssertEqual(Global.dictionary.refer("う"), [Word("え")])
}

@MainActor func testHandleRegisteringRecursiveCancel() {
let stateMachine = StateMachine(initialState: IMEState(inputMode: .hiragana))
let expectation = XCTestExpectation()
stateMachine.inputMethodEvent.collect(12).sink { events in
XCTAssertEqual(events[0], .markedText(MarkedText([.markerCompose, .plain("い")])))
XCTAssertEqual(events[1], .modeChanged(.hiragana, .zero))
XCTAssertEqual(events[2], .markedText(MarkedText([.plain("[登録:い]")])))
XCTAssertEqual(events[3], .markedText(MarkedText([.plain("[登録:い]"), .plain("う")])))
XCTAssertEqual(events[4], .markedText(MarkedText([.plain("[登録:い]"), .plain("う"), .markerCompose, .plain("え")])))
XCTAssertEqual(events[5], .modeChanged(.hiragana, .zero))
XCTAssertEqual(events[6], .markedText(MarkedText([.plain("[[登録:え*お]]")])))
XCTAssertEqual(events[7], .markedText(MarkedText([.plain("[[登録:え*お]]"), .plain("b")])))
XCTAssertEqual(events[8], .markedText(MarkedText([.plain("[[登録:え*お]]"), .plain("ば")])))
XCTAssertEqual(events[9], .markedText(MarkedText([.plain("[登録:い]"), .plain("う"), .markerCompose, .plain("え*お")])))
XCTAssertEqual(events[10], .markedText(MarkedText([.plain("[登録:い]"), .plain("う")])))
XCTAssertEqual(events[11], .markedText(MarkedText([.markerCompose, .plain("い")])))
expectation.fulfill()
}.store(in: &cancellables)
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "i", withShift: true)))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: " ")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "u")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "e", withShift: true)))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "o", withShift: true)))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "b")))
XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "a")))
XCTAssertTrue(stateMachine.handle(cancelAction))
XCTAssertTrue(stateMachine.handle(cancelAction))
XCTAssertTrue(stateMachine.handle(cancelAction))
wait(for: [expectation], timeout: 1.0)
}

@MainActor func testHandleRegisteringUnregisteredKeyEventWithModifiers() {
let stateMachine = StateMachine(initialState: IMEState(inputMode: .hiragana))
let expectation = XCTestExpectation()
Expand Down
30 changes: 28 additions & 2 deletions macSKKTests/StateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ final class StateTests: XCTestCase {
remain: nil)
let state = IMEState(inputMode: .hiragana,
inputMethod: .selecting(selectingState),
specialState: .register(registerState),
specialState: .register(registerState, prev: []),
candidates: [])
let displayText = state.displayText()
XCTAssertEqual(displayText.elements, [.plain("[登録:あいうえお]"), .plain("愛上"), .markerSelect, .emphasized("尾")])
Expand All @@ -437,12 +437,38 @@ final class StateTests: XCTestCase {
remain: nil)
let state = IMEState(inputMode: .hiragana,
inputMethod: .selecting(selectingState),
specialState: .register(registerState),
specialState: .register(registerState, prev: []),
candidates: [])
let displayText = state.displayText()
XCTAssertEqual(displayText.elements, [.plain("[登録:あいうえお]"), .plain("愛"), .markerSelect, .emphasized("尾"), .cursor, .plain("上")])
}

func testIMEStateDisplayTextRegisterRecursive() {
let firstComposingState = ComposingState(isShift: true, text: ["あいうえお"], romaji: "")
let prevRegisterState = RegisterState(prev: RegisterState.PrevState(mode: .hiragana, composing: firstComposingState),
yomi: "あいうえお",
text: "",
cursor: nil)
let prevComposingState = ComposingState(isShift: true, text: ["愛上"], romaji: "")
let registerState = RegisterState(prev: RegisterState.PrevState(mode: .hiragana, composing: prevComposingState),
yomi: "あいうえ",
text: "愛上",
cursor: nil)
let composingState = ComposingState(isShift: true, text: ["お"], romaji: "")
let selectingState = SelectingState(prev: SelectingState.PrevState(mode: .hiragana, composing: composingState),
yomi: "お",
candidates: [Candidate("尾")],
candidateIndex: 0,
cursorPosition: .zero,
remain: nil)
let state = IMEState(inputMode: .hiragana,
inputMethod: .selecting(selectingState),
specialState: .register(registerState, prev: [prevRegisterState]),
candidates: [])
let displayText = state.displayText()
XCTAssertEqual(displayText.elements, [.plain("[[登録:あいうえ]]"), .plain("愛上"), .markerSelect, .emphasized("尾")])
}

func testIMEStateDisplayTextUnregister() {
let prevSelectingState = SelectingState(
prev: SelectingState.PrevState(
Expand Down
Loading