Skip to content

Commit 58f42e6

Browse files
committed
fix(SquirrelInputController): boundary check to prevent crash
- make sure candidatePreview is trimmed only when it is legal - add clamped() util function to clamp out-of-bounds values - prevent index out-of-bounds when candidatePreview is empty
1 parent ea6720a commit 58f42e6

1 file changed

Lines changed: 17 additions & 3 deletions

File tree

sources/SquirrelInputController.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,12 +466,18 @@ private extension SquirrelInputController {
466466
caretPos: candidatePreview.utf16.count - max(0, preedit.utf16.distance(from: caretPos, to: preedit.endIndex)))
467467
} else {
468468
if end < caretPos && start < caretPos {
469-
candidatePreview = String(candidatePreview[..<candidatePreview.index(candidatePreview.endIndex, offsetBy: -max(0, preedit.distance(from: end, to: caretPos)))])
469+
let trimCount = preedit.distance(from: end, to: caretPos).clamped(to: 0..<candidatePreview.count)
470+
candidatePreview = String(candidatePreview[..<candidatePreview.index(candidatePreview.endIndex, offsetBy: -trimCount)])
470471
} else if end < preedit.endIndex && caretPos <= start {
471-
candidatePreview = String(candidatePreview[..<candidatePreview.index(candidatePreview.endIndex, offsetBy: -max(0, preedit.distance(from: end, to: preedit.endIndex)))])
472+
let trimCount = preedit.distance(from: end, to: preedit.endIndex).clamped(to: 0..<candidatePreview.count)
473+
candidatePreview = String(candidatePreview[..<candidatePreview.index(candidatePreview.endIndex, offsetBy: -trimCount)])
472474
}
475+
476+
let selStart = candidatePreview.isEmpty ? 0 : start.utf16Offset(in: candidatePreview)
477+
let selLength = candidatePreview.utf16.count - selStart
478+
473479
show(preedit: candidatePreview,
474-
selRange: NSRange(location: start.utf16Offset(in: candidatePreview), length: candidatePreview.utf16.distance(from: start, to: candidatePreview.endIndex)),
480+
selRange: NSRange(location: selStart, length: selLength),
475481
caretPos: candidatePreview.utf16.count)
476482
}
477483
} else {
@@ -565,3 +571,11 @@ private extension SquirrelInputController {
565571
}
566572
}
567573
}
574+
575+
// clamp value into half open interval
576+
extension BinaryInteger {
577+
func clamped(to limits: Range<Self>) -> Self {
578+
guard !limits.isEmpty else { return limits.lowerBound }
579+
return min(max(self, limits.lowerBound), limits.upperBound - 1)
580+
}
581+
}

0 commit comments

Comments
 (0)