feat(typing): add English autocomplete and Chinese candidate expand panel
Offline English lexicon suggestions with autocapitalization, plus a same-height Chinese more-candidates grid over the key area.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
// TypingAutocapitalization.swift
|
||||
// OSGKeyboard · Shared
|
||||
//
|
||||
// Decides when the English letter page should arm Shift once for
|
||||
// automatic capitalization (mirrors UITextAutocapitalizationType).
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum TypingAutocapitalizationMode: String, Sendable, Equatable {
|
||||
case none
|
||||
case words
|
||||
case sentences
|
||||
case allCharacters
|
||||
}
|
||||
|
||||
public enum TypingAutocapitalization: Sendable {
|
||||
/// Whether Shift should be armed for the next Latin letter.
|
||||
public static func shouldCapitalize(
|
||||
precedingText: String?,
|
||||
mode: TypingAutocapitalizationMode
|
||||
) -> Bool {
|
||||
switch mode {
|
||||
case .none:
|
||||
return false
|
||||
case .allCharacters:
|
||||
return true
|
||||
case .words:
|
||||
return needsWordCapitalization(precedingText)
|
||||
case .sentences:
|
||||
return needsSentenceCapitalization(precedingText)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func needsWordCapitalization(_ preceding: String?) -> Bool {
|
||||
guard let preceding, !preceding.isEmpty else { return true }
|
||||
guard let last = preceding.last else { return true }
|
||||
return last.isWhitespace || last.isNewline
|
||||
}
|
||||
|
||||
private static func needsSentenceCapitalization(_ preceding: String?) -> Bool {
|
||||
guard let preceding, !preceding.isEmpty else { return true }
|
||||
|
||||
// Walk backward past trailing whitespace / newlines; capitalize when
|
||||
// the field is empty or the previous visible character ends a sentence.
|
||||
var index = preceding.endIndex
|
||||
var sawContent = false
|
||||
while index > preceding.startIndex {
|
||||
index = preceding.index(before: index)
|
||||
let character = preceding[index]
|
||||
if character.isWhitespace || character.isNewline {
|
||||
continue
|
||||
}
|
||||
sawContent = true
|
||||
return isSentenceTerminator(character)
|
||||
}
|
||||
return !sawContent
|
||||
}
|
||||
|
||||
private static func isSentenceTerminator(_ character: Character) -> Bool {
|
||||
switch character {
|
||||
case ".", "!", "?", "…", "。", "!", "?":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user