Skip to content
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
62 changes: 48 additions & 14 deletions Core/Sources/Core/InputUtils/SegmentsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public final class SegmentsManager {
private var backspaceAdjustedPredictionCandidate: PredictionCandidate?
private var backspaceTypoCorrectionLock: BackspaceTypoCorrectionLock?

public struct PredictionCandidate: Sendable {
public struct PredictionCandidate: Sendable, Equatable {
public var displayText: String
public var appendText: String
public var deleteCount: Int = 0
Expand Down Expand Up @@ -336,13 +336,13 @@ public final class SegmentsManager {

@MainActor
public func deleteBackwardFromCursorPosition(count: Int = 1) {
var beforeComposingText = self.composingText.prefixToCursorPosition()
var previousComposingText = self.composingText.prefixToCursorPosition()
if !self.composingText.isAtEndIndex {
// 右端に持っていく
_ = self.composingText.moveCursorFromCursorPosition(count: self.composingText.convertTarget.count - self.composingText.convertTargetCursorPosition)
// 一度segmentの編集状態もリセットにする
self.didExperienceSegmentEdition = false
beforeComposingText = self.composingText.prefixToCursorPosition()
previousComposingText = self.composingText.prefixToCursorPosition()
}
self.composingText.deleteBackwardFromCursorPosition(count: count)
self.lastOperation = .delete
Expand All @@ -354,15 +354,15 @@ public final class SegmentsManager {
self.backspaceTypoCorrectionLock = nil
return
}
let currentInput = self.composingText.convertTarget
let currentConvertTarget = self.composingText.convertTarget
guard count == 1 else {
self.backspaceAdjustedPredictionCandidate = nil
self.backspaceTypoCorrectionLock = nil
return
}
if let lock = self.backspaceTypoCorrectionLock {
self.backspaceAdjustedPredictionCandidate = Self.makeBackspaceTypoCorrectionPredictionCandidate(
currentInput: currentInput,
currentConvertTarget: currentConvertTarget,
targetReading: lock.targetReading,
displayText: lock.displayText
)
Expand All @@ -371,10 +371,10 @@ public final class SegmentsManager {
}
return
}
self.backspaceTypoCorrectionLock = self.lmBasedBackspaceTypoCorrectionLock(previousComposingText: beforeComposingText)
self.backspaceTypoCorrectionLock = self.lmBasedBackspaceTypoCorrectionLock(previousComposingText: previousComposingText)
if let lock = self.backspaceTypoCorrectionLock {
self.backspaceAdjustedPredictionCandidate = Self.makeBackspaceTypoCorrectionPredictionCandidate(
currentInput: currentInput,
currentConvertTarget: currentConvertTarget,
targetReading: lock.targetReading,
displayText: lock.displayText
)
Expand Down Expand Up @@ -809,6 +809,34 @@ public final class SegmentsManager {
suggestSelectionIndex = nil
}

public func requestTypoCorrectionPredictionCandidates() -> [PredictionCandidate] {
guard Config.DebugTypoCorrection().value else {
return []
}
guard let backspaceAdjustedPredictionCandidate else {
return []
}
return [backspaceAdjustedPredictionCandidate]
}

public static func preferredPredictionCandidates(
typoCorrectionCandidates: [PredictionCandidate],
predictionCandidates: [PredictionCandidate]
) -> [PredictionCandidate] {
if !typoCorrectionCandidates.isEmpty {
return typoCorrectionCandidates
}
return predictionCandidates
}

public static func shouldPresentTypoCorrectionPredictionCandidate(
candidateDisplayText: String,
previousComposingDisplayText: String
) -> Bool {
// 削除前の previousComposingText と同じ表示候補は、訂正候補としては提示しない。
candidateDisplayText != previousComposingDisplayText
}

public func requestPredictionCandidates() -> [PredictionCandidate] {
guard Config.DebugPredictiveTyping().value else {
return []
Expand All @@ -819,10 +847,6 @@ public final class SegmentsManager {
return []
}

if let backspaceAdjustedPredictionCandidate {
return [backspaceAdjustedPredictionCandidate]
}

guard let rawCandidates else {
return []
}
Expand Down Expand Up @@ -946,17 +970,27 @@ public final class SegmentsManager {
reading: correctedReading,
leftSideContext: self.getCleanLeftSideContext(maxCount: 30)
) ?? correctedReading
let previousComposingDisplayText = self.convertedText(
reading: previousComposingText.convertTarget,
leftSideContext: self.getCleanLeftSideContext(maxCount: 30)
) ?? previousComposingText.convertTarget
guard Self.shouldPresentTypoCorrectionPredictionCandidate(
candidateDisplayText: correctedDisplayText,
previousComposingDisplayText: previousComposingDisplayText
) else {
return nil
}

return .init(displayText: correctedDisplayText, targetReading: correctedReading)
}

static func makeBackspaceTypoCorrectionPredictionCandidate(
currentInput: String,
currentConvertTarget: String,
targetReading: String,
displayText: String
) -> PredictionCandidate? {
let operation = Self.makeSuffixEditOperation(from: currentInput, to: targetReading)
?? Self.makeSuffixEditOperation(from: currentInput.toHiragana(), to: targetReading)
let operation = Self.makeSuffixEditOperation(from: currentConvertTarget, to: targetReading)
?? Self.makeSuffixEditOperation(from: currentConvertTarget.toHiragana(), to: targetReading)
guard let operation else {
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Testing

@Test func testMakeBackspaceTypoCorrectionPredictionCandidateRecalculatesEditOperationForCurrentInput() async throws {
let candidate = SegmentsManager.makeBackspaceTypoCorrectionPredictionCandidate(
currentInput: "くだし",
currentConvertTarget: "くだし",
targetReading: "ください",
displayText: "下さい"
)
Expand All @@ -15,7 +15,7 @@ import Testing

@Test func testMakeBackspaceTypoCorrectionPredictionCandidateKeepsDisplayTextAndUpdatesAppendTextOnFurtherDelete() async throws {
let candidate = SegmentsManager.makeBackspaceTypoCorrectionPredictionCandidate(
currentInput: "くだ",
currentConvertTarget: "くだ",
targetReading: "ください",
displayText: "下さい"
)
Expand All @@ -24,3 +24,56 @@ import Testing
#expect(candidate?.appendText == "さい")
#expect(candidate?.deleteCount == 0)
}

@Test func testPreferredPredictionCandidatesPreferTypoCorrectionCandidates() async throws {
let typoCorrection = SegmentsManager.PredictionCandidate(
displayText: "下さい",
appendText: "さい",
deleteCount: 1
)
let prediction = SegmentsManager.PredictionCandidate(
displayText: "くださいました",
appendText: "ました"
)

let candidates = SegmentsManager.preferredPredictionCandidates(
typoCorrectionCandidates: [typoCorrection],
predictionCandidates: [prediction]
)

#expect(candidates == [typoCorrection])
}

@Test func testPreferredPredictionCandidatesFallbackToPredictionCandidates() async throws {
let prediction = SegmentsManager.PredictionCandidate(
displayText: "くださいました",
appendText: "ました"
)

let candidates = SegmentsManager.preferredPredictionCandidates(
typoCorrectionCandidates: [],
predictionCandidates: [prediction]
)

#expect(candidates == [prediction])
}

@Test func testShouldPresentTypoCorrectionPredictionCandidateReturnsFalseForMatchingPreviousComposingDisplay() async throws {
// 削除前の previousComposingText と同じ表示候補は、訂正候補として出さない。
let shouldPresent = SegmentsManager.shouldPresentTypoCorrectionPredictionCandidate(
candidateDisplayText: "下さい",
previousComposingDisplayText: "下さい"
)

#expect(shouldPresent == false)
}

@Test func testShouldPresentTypoCorrectionPredictionCandidateReturnsTrueForDifferentPreviousComposingDisplay() async throws {
// 削除前の previousComposingText と異なる表示候補だけを、訂正候補として出す。
let shouldPresent = SegmentsManager.shouldPresentTypoCorrectionPredictionCandidate(
candidateDisplayText: "下さい",
previousComposingDisplayText: "ください"
)

#expect(shouldPresent == true)
}
11 changes: 9 additions & 2 deletions azooKeyMac/InputController/azooKeyMacInputController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ class azooKeyMacInputController: IMKInputController, NSMenuItemValidation { // s
return
}

let predictions = self.segmentsManager.requestPredictionCandidates()
let predictions = self.requestPreferredPredictionCandidates()
if predictions.isEmpty {
let now = Date().timeIntervalSince1970
let elapsed = now - self.lastPredictionUpdateTime
Expand Down Expand Up @@ -709,7 +709,7 @@ class azooKeyMacInputController: IMKInputController, NSMenuItemValidation { // s

@MainActor
private func acceptPredictionCandidate() {
let predictions = self.segmentsManager.requestPredictionCandidates()
let predictions = self.requestPreferredPredictionCandidates()
guard let prediction = predictions.first else {
return
}
Expand All @@ -726,6 +726,13 @@ class azooKeyMacInputController: IMKInputController, NSMenuItemValidation { // s
self.segmentsManager.insertAtCursorPosition(appendText, inputStyle: .direct)
}

private func requestPreferredPredictionCandidates() -> [SegmentsManager.PredictionCandidate] {
SegmentsManager.preferredPredictionCandidates(
typoCorrectionCandidates: self.segmentsManager.requestTypoCorrectionPredictionCandidates(),
predictionCandidates: self.segmentsManager.requestPredictionCandidates()
)
}

var retryCount = 0
let maxRetries = 3

Expand Down