chore(typing): snapshot local pinyin WIP before syncing cloud branch

Preserve the in-progress local typing/pinyin implementation so feat/pinyin
can safely reset to origin/feat/pinyin (cloud English + Chinese typing).
This commit is contained in:
Rocky
2026-08-03 13:14:01 +08:00
parent c037be3654
commit d1e5fed964
58 changed files with 370539 additions and 121 deletions
+250
View File
@@ -0,0 +1,250 @@
#import "include/OSGRimeBridge.h"
@import RimeStatic;
const int OSGRimeKeyBackSpace = 0xff08;
const int OSGRimeKeyReturn = 0xff0d;
static NSString *const OSGRimeErrorDomain = @"com.osgkeyboard.rime";
static RimeApi_stdbool *OSGRimeAPI(void) {
return rime_get_api_stdbool();
}
@implementation OSGRimeCandidate
- (instancetype)initWithText:(NSString *)text comment:(NSString *)comment {
self = [super init];
if (self) {
_text = [text copy];
_comment = [comment copy];
}
return self;
}
@end
@implementation OSGRimeSnapshot
- (instancetype)initWithPreedit:(NSString *)preedit
candidates:(NSArray<OSGRimeCandidate *> *)candidates
commitText:(NSString *)commitText
composing:(BOOL)composing {
self = [super init];
if (self) {
_preedit = [preedit copy];
_candidates = [candidates copy];
_commitText = [commitText copy];
_composing = composing;
}
return self;
}
@end
@interface OSGRimeBridge ()
@property(nonatomic, copy) NSString *sharedDataDirectory;
@property(nonatomic, copy) NSString *userDataDirectory;
@property(nonatomic, copy) NSString *distributionVersion;
@property(nonatomic) RimeSessionId session;
@end
@implementation OSGRimeBridge
static BOOL sSetupComplete = NO;
static BOOL sInitialized = NO;
- (instancetype)initWithSharedDataDirectory:(NSString *)sharedDataDirectory
userDataDirectory:(NSString *)userDataDirectory
distributionVersion:(NSString *)distributionVersion {
self = [super init];
if (self) {
_sharedDataDirectory = [sharedDataDirectory copy];
_userDataDirectory = [userDataDirectory copy];
_distributionVersion = [distributionVersion copy];
_session = 0;
}
return self;
}
- (BOOL)running {
return self.session != 0 && OSGRimeAPI()->find_session(self.session);
}
- (void)populateTraits:(RimeTraits *)traits {
traits->shared_data_dir = self.sharedDataDirectory.UTF8String;
traits->user_data_dir = self.userDataDirectory.UTF8String;
traits->distribution_name = "OSGKeyboard";
traits->distribution_code_name = "OSGKeyboard";
traits->distribution_version = self.distributionVersion.UTF8String;
traits->app_name = "rime.OSGKeyboard";
traits->min_log_level = 2;
}
- (BOOL)prepareRuntime:(NSError **)error {
RimeApi_stdbool *api = OSGRimeAPI();
if (api == NULL) {
if (error) {
*error = [NSError errorWithDomain:OSGRimeErrorDomain
code:1
userInfo:@{NSLocalizedDescriptionKey: @"librime API unavailable"}];
}
return NO;
}
@synchronized([OSGRimeBridge class]) {
RIME_STRUCT(RimeTraits, traits);
[self populateTraits:&traits];
if (!sSetupComplete) {
api->setup(&traits);
sSetupComplete = YES;
}
if (!sInitialized) {
api->initialize(&traits);
sInitialized = YES;
}
}
return YES;
}
- (BOOL)start:(NSError **)error {
if (![self prepareRuntime:error]) {
return NO;
}
if ([self running]) {
return YES;
}
self.session = OSGRimeAPI()->create_session();
if (self.session == 0) {
if (error) {
*error = [NSError errorWithDomain:OSGRimeErrorDomain
code:2
userInfo:@{NSLocalizedDescriptionKey: @"Unable to create Rime session"}];
}
return NO;
}
return YES;
}
- (void)stopSession {
if (self.session != 0) {
OSGRimeAPI()->destroy_session(self.session);
self.session = 0;
}
}
- (void)finalizeRuntime {
[self stopSession];
@synchronized([OSGRimeBridge class]) {
if (sInitialized) {
OSGRimeAPI()->finalize();
sInitialized = NO;
}
}
}
- (BOOL)deployWithFullCheck:(BOOL)fullCheck error:(NSError **)error {
if (![self prepareRuntime:error]) {
return NO;
}
RimeApi_stdbool *api = OSGRimeAPI();
Bool started = api->start_maintenance(fullCheck ? True : False);
if (started && api->is_maintenance_mode()) {
api->join_maintenance_thread();
}
return YES;
}
- (BOOL)selectSchema:(NSString *)schemaIdentifier {
if (![self running]) {
return NO;
}
return OSGRimeAPI()->select_schema(self.session, schemaIdentifier.UTF8String);
}
- (BOOL)setASCIIMode:(BOOL)enabled {
if (![self running]) {
return NO;
}
OSGRimeAPI()->set_option(self.session, "ascii_mode", enabled ? True : False);
return YES;
}
- (BOOL)processKeyCode:(int)keyCode modifiers:(int)modifiers {
if (![self running]) {
return NO;
}
return OSGRimeAPI()->process_key(self.session, keyCode, modifiers);
}
- (BOOL)selectCandidateAtIndex:(NSInteger)index {
if (![self running] || index < 0) {
return NO;
}
return OSGRimeAPI()->select_candidate(self.session, (size_t)index);
}
- (BOOL)commitComposition {
if (![self running]) {
return NO;
}
return OSGRimeAPI()->commit_composition(self.session);
}
- (void)clearComposition {
if ([self running]) {
OSGRimeAPI()->clear_composition(self.session);
}
}
- (NSString *)rawInput {
if (![self running]) {
return @"";
}
const char *input = OSGRimeAPI()->get_input(self.session);
return input ? [NSString stringWithUTF8String:input] : @"";
}
- (OSGRimeSnapshot *)snapshotWithCandidateLimit:(NSInteger)limit {
if (![self running]) {
return [[OSGRimeSnapshot alloc] initWithPreedit:@""
candidates:@[]
commitText:@""
composing:NO];
}
RimeApi_stdbool *api = OSGRimeAPI();
NSString *commitText = @"";
RIME_STRUCT(RimeCommit, commit);
if (api->get_commit(self.session, &commit)) {
if (commit.text) {
commitText = [NSString stringWithUTF8String:commit.text];
}
api->free_commit(&commit);
}
NSString *preedit = @"";
BOOL composing = NO;
NSMutableArray<OSGRimeCandidate *> *candidates = [NSMutableArray array];
RIME_STRUCT(RimeContext_stdbool, context);
if (api->get_context(self.session, &context)) {
composing = context.composition.length > 0;
if (context.composition.preedit) {
preedit = [NSString stringWithUTF8String:context.composition.preedit];
}
NSInteger count = MIN((NSInteger)context.menu.num_candidates, MAX((NSInteger)0, limit));
for (NSInteger index = 0; index < count; index++) {
RimeCandidate item = context.menu.candidates[index];
NSString *text = item.text ? [NSString stringWithUTF8String:item.text] : @"";
NSString *comment = item.comment ? [NSString stringWithUTF8String:item.comment] : @"";
[candidates addObject:[[OSGRimeCandidate alloc] initWithText:text comment:comment]];
}
api->free_context(&context);
}
return [[OSGRimeSnapshot alloc] initWithPreedit:preedit
candidates:candidates
commitText:commitText
composing:composing];
}
@end
+57
View File
@@ -0,0 +1,57 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// Immutable candidate copied out of librime-owned memory.
@interface OSGRimeCandidate : NSObject
@property(nonatomic, copy, readonly) NSString *text;
@property(nonatomic, copy, readonly) NSString *comment;
- (instancetype)initWithText:(NSString *)text comment:(NSString *)comment;
@end
/// Immutable runtime snapshot consumed by SwiftUI.
@interface OSGRimeSnapshot : NSObject
@property(nonatomic, copy, readonly) NSString *preedit;
@property(nonatomic, copy, readonly) NSArray<OSGRimeCandidate *> *candidates;
@property(nonatomic, copy, readonly) NSString *commitText;
@property(nonatomic, readonly) BOOL composing;
- (instancetype)initWithPreedit:(NSString *)preedit
candidates:(NSArray<OSGRimeCandidate *> *)candidates
commitText:(NSString *)commitText
composing:(BOOL)composing;
@end
/// Minimal Objective-C++ façade over librime's C API.
///
/// Callers must serialize access. A bridge owns one session; setup and
/// initialization are process-global inside librime.
@interface OSGRimeBridge : NSObject
@property(nonatomic, readonly) BOOL running;
- (instancetype)initWithSharedDataDirectory:(NSString *)sharedDataDirectory
userDataDirectory:(NSString *)userDataDirectory
distributionVersion:(NSString *)distributionVersion;
- (BOOL)start:(NSError * _Nullable * _Nullable)error;
- (void)stopSession;
- (void)finalizeRuntime;
- (BOOL)deployWithFullCheck:(BOOL)fullCheck
error:(NSError * _Nullable * _Nullable)error;
- (BOOL)selectSchema:(NSString *)schemaIdentifier;
- (BOOL)setASCIIMode:(BOOL)enabled;
- (BOOL)processKeyCode:(int)keyCode modifiers:(int)modifiers;
- (BOOL)selectCandidateAtIndex:(NSInteger)index;
- (BOOL)commitComposition;
- (void)clearComposition;
- (NSString *)rawInput;
- (OSGRimeSnapshot *)snapshotWithCandidateLimit:(NSInteger)limit;
@end
/// X11-compatible key symbols used by librime.
FOUNDATION_EXPORT const int OSGRimeKeyBackSpace;
FOUNDATION_EXPORT const int OSGRimeKeyReturn;
NS_ASSUME_NONNULL_END