context simplification, legacy app determination integrated into TextCompatibilityCheck

This commit is contained in:
sgschantz 2023-09-26 10:48:43 +07:00
parent cc1c7605df
commit 682d99c2cb
18 changed files with 249 additions and 231 deletions

View file

@ -116,7 +116,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
if (type == NX_KEYDOWN) {
// Key down event
NSEvent *mEvent = [NSEvent eventWithCGEvent:event];
KMEngine *kme = [[KMEngine alloc] initWithKMX:kmx contextBuffer:contextBuffer];
KMEngine *kme = [[KMEngine alloc] initWithKMX:kmx context:contextBuffer];
NSArray *actions = [kme processEvent:mEvent];
//if (debugMode)
NSLog(@"%@", actions);

View file

@ -38,13 +38,13 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
-(instancetype)initWithString:(NSString*)initialContext {
self = [self init];
if (self) {
[_context setString:[self trimToMaximumContext:initialContext]];
[_context setString:[KMContext trimToMaximumContext:initialContext]];
_invalid = NO;
}
return self;
}
-(NSString*)trimToMaximumContext:(NSString*)initialContext {
+(NSString*)trimToMaximumContext:(NSString*)initialContext {
NSString *maximumContext = nil;
// copy up to MAXIMUM_CONTEXT_LENGTH from the end of the context
@ -59,8 +59,9 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
}
-(void)resetContext:(NSString*)newContext {
[_context setString:[self trimToMaximumContext:newContext]];
[_context setString:[KMContext trimToMaximumContext:newContext]];
self.invalid = NO;
NSLog(@" ***CachedContext resetContext, resulting context = %@", _context);
}
-(NSString*)currentContext {
@ -78,12 +79,14 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
-(void)clearContext {
_context.string = @"";
NSLog(@" ***CachedContext clearContext, resulting context = %@", _context);
}
-(void)appendMarker {
#define UC_NONCHARACTER 0xFFFF
NSString *markerString = [NSString stringWithFormat:@"%C", UC_NONCHARACTER];
[self.context appendString:markerString];
NSLog(@" ***CachedContext appendMarker, resulting context = %@", _context);
}
/**
@ -100,6 +103,7 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
NSLog(@"KMContext deleteLastUnicodeCharacter, index = %lu, rangeOfLastCharacter = %@\n", characterIndex, NSStringFromRange(characterRange));
[self.context deleteCharactersInRange:characterRange];
}
NSLog(@" ***CachedContext deleteLastCodePoint, resulting context = %@", _context);
}
-(void)replaceSubstring:(NSString*)newText count:(int)count {
@ -113,10 +117,12 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
if (newText.length > 0) {
[self addSubtring:newText];
}
NSLog(@" ***CachedContext replaceSubstring, resulting context = %@", _context);
}
-(void)addSubtring:(NSString*)string {
[self.context appendString:string];
NSLog(@" ***CachedContext addSubtring, resulting context = %@", _context);
}
-(void)applyAction:(CoreAction*)action keyDownEvent:(nonnull NSEvent *)event {
@ -145,6 +151,7 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
NSLog(@"CachedContext applyAction, action not applied to context %@\n", action.typeName.description);
break;
}
NSLog(@" ***CachedContext applyAction, resulting context = %@", _context);
}
// TODO: see handleDefaultKeymanEngineActions
@ -189,6 +196,7 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
}
break;
}
NSLog(@" ***CachedContext applyUnhandledEvent, resulting context = %@", _context);
}
@end

View file

@ -129,42 +129,24 @@ typedef enum {BackspacesOnly,
switch (self.pattern) {
case CharactersOnly:
result = [self buildResultForCharactersOnly];
//handledEvent = [self applyCharacterActions:0];
break;
// TODO: create separate pattern for single backspace? and for EmitKeystroke action?
case BackspacesOnly:
if ([self isSinglePassThroughBackspace]) {
result = [self buildResultForSinglePassThroughBackspaceNoText];
} else {
result = [self buildResultForMultipleBackspacesNoText];
//handledEvent = [self applyIsolatedBackspaceActions:self.backspaceCount];
}
break;
case BackspaceBeforeCharacter:
result = [self buildResultForBackspacesBeforeText];
//handledEvent = [self applyCharacterActions:self.backspaceCount];
break;
case None:
result = [self buildResultForNoCharactersOrBackspaces];
break;
default:
NSLog(@"Unimplemented pattern***");
NSLog(@"KMCoreActionHandler handleActions(), Unimplemented pattern***");
}
// TODO: delete
/**
* this event is passed through and will be sent to the client
* but we need to update the context for it -- no, actually we don't:
* the context is updated for every action, and we should still get an action
* for a backspace, even if the event is unhandled by Core
* if there is a need for this, it would be while processing the Emit Keystroke action
*/
/*
if(!result.handledEvent) {
[self.context applyUnhandledEvent:self.event];
}
*/
return result;
}
@ -185,7 +167,7 @@ typedef enum {BackspacesOnly,
}
/**
* For multiple backspaces with no text, new events must be generated for each backspace
* Multiple backspaces with no text will be handled by generating events
*/
-(KMActionHandlerResult*)buildResultForMultipleBackspacesNoText {
NSLog(@"buildResultForMultipleBackspacesNoText");
@ -196,7 +178,6 @@ typedef enum {BackspacesOnly,
* For backspaces needed before text, insert with replace is possible, but some clients do not support replace
*/
-(KMActionHandlerResult*)buildResultForBackspacesBeforeText {
// TODO: make backspaces events if replacement does not work with insertText
NSLog(@"buildResultForBackspacesBeforeText");
return [[KMActionHandlerResult alloc] initForActions:self.actions handledEvent:YES backspaceCount:self.backspaceCount textToInsert:[self collectOutputText]];
}

View file

@ -110,7 +110,6 @@ typedef struct {
- (void)postKeyboardEventWithSource: (CGEventSourceRef)source code:(CGKeyCode) virtualKey postCallback:(PostEventCallback)postEvent;
- (KeymanVersionInfo)versionInfo;
- (NSString *)keymanDataPath;
- (NSArray *)legacyAppsUserDefaults;
- (void)registerConfigurationWindow:(NSWindowController *)window;
@end

View file

@ -39,7 +39,6 @@ NSString *const kKMDeprecatedPersistedOptionsKey = @"KMSavedStoresKey";
NSString *const kKMPersistedOptionsKey = @"KMPersistedOptionsKey";
NSString *const kKMAlwaysShowOSKKey = @"KMAlwaysShowOSKKey";
NSString *const kKMUseVerboseLogging = @"KMUseVerboseLogging";
NSString *const kKMLegacyApps = @"KMLegacyApps";
NSString *const kKeymanKeyboardDownloadCompletedNotification = @"kKeymanKeyboardDownloadCompletedNotification";
@ -393,7 +392,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
- (KMEngine *)kme {
if (_kme == nil) {
//TODO: verify that initial context is correct; this is passed to core
_kme = [[KMEngine alloc] initWithKMX:nil contextBuffer:self.contextBuffer];
_kme = [[KMEngine alloc] initWithKMX:nil context:self.contextBuffer];
[_kme setDebugMode:self.debugMode];
}
@ -530,14 +529,6 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
return _keymanDataPath;
}
/**
* Returns the list of user-default legacy apps
*/
- (NSArray *)legacyAppsUserDefaults {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
return [userData arrayForKey:kKMLegacyApps];
}
/**
* Returns the root folder where keyboards are stored; currently the same
* as the keymanDataPath, but may diverge in future versions (possibly a sub-folder)
@ -773,7 +764,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
_contextBuffer = [contextBuffer mutableCopy];
if (_contextBuffer.length)
[_contextBuffer replaceOccurrencesOfString:@"\0" withString:[NSString nullChar] options:0 range:NSMakeRange(0, 1)];
[self.kme setContextBuffer:self.contextBuffer];
[self.kme setCoreContext:self.contextBuffer];
}
- (void)awakeFromNib {

View file

@ -59,72 +59,6 @@ NSRange _previousSelRange;
return self;
}
/**
* Checks if the client app requires legacy input mode, first by checking the user defaults, if they exist,
* then, by our hard-coded list.
*/
- (BOOL)isClientAppLegacy:(NSString *)clientAppId {
NSArray *legacyAppsUserDefaults = [self.AppDelegate legacyAppsUserDefaults];
BOOL result = NO;
if(legacyAppsUserDefaults != nil) {
result = [self isClientAppLegacy:clientAppId fromArray:legacyAppsUserDefaults];
}
if(!result) {
// TODO: Pages and Keynote (and possibly lots of other undiscovered apps that are otherwise compliant
// with Apple's IM framework) have a problem in that if the user selects a different font (or other
// formatting) and then types a sequence that causes characters to be added to the document and then
// subsequently replaced, the replacement causes the formatting decision to be forgotten. This can be
// "fixed" by treating them as legacy apps, but it causes other problems.
result = ([clientAppId isEqual: @"com.github.atom"] ||
[clientAppId isEqual: @"com.collabora.libreoffice-free"] ||
[clientAppId isEqual: @"org.libreoffice.script"] ||
[clientAppId isEqual: @"com.axosoft.gitkraken"] ||
[clientAppId isEqual: @"org.sil.app.builder.scripture.ScriptureAppBuilder"] ||
[clientAppId isEqual: @"org.sil.app.builder.reading.ReadingAppBuilder"] ||
[clientAppId isEqual: @"org.sil.app.builder.dictionary.DictionaryAppBuilder"] ||
//[clientAppId isEqual: @"com.microsoft.Word"] || // 2020-11-24[mcd]: Appears to work well in Word 16.43, disable legacy by default
[clientAppId isEqual: @"org.openoffice.script"] ||
[clientAppId isEqual: @"com.adobe.illustrator"] ||
[clientAppId isEqual: @"com.adobe.InDesign"] ||
[clientAppId isEqual: @"com.adobe.Photoshop"] ||
[clientAppId isEqual: @"com.adobe.AfterEffects"] ||
[clientAppId isEqual: @"com.microsoft.VSCode"] ||
[clientAppId isEqual: @"com.google.Chrome"] ||
[clientAppId hasPrefix: @"net.java"] ||
[clientAppId isEqual: @"com.Keyman.test.legacyInput"]
/*||[clientAppId isEqual: @"ro.sync.exml.Oxygen"] - Oxygen has worse problems */
);
}
return result;
}
/**
* Checks user defaults array for a list of possible regexes to match a client app id
*/
- (BOOL)isClientAppLegacy:(NSString *)clientAppId fromArray:(NSArray *)legacyApps {
for(id legacyApp in legacyApps) {
if(![legacyApp isKindOfClass:[NSString class]]) {
NSLog(@"isClientAppLegacy:fromArray: LegacyApps user defaults array should contain only strings");
} else {
NSError *error = nil;
NSRange range = NSMakeRange(0, clientAppId.length);
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: (NSString *) legacyApp options: 0 error: &error];
NSArray *matchesArray = [regex matchesInString:clientAppId options:0 range:range];
if(matchesArray.count>0) {
NSLog(@"isClientAppLegacy: found match for legacy app %@: ", clientAppId);
return YES;
}
}
}
return NO;
}
// This is the public initializer.
- (instancetype)initWithClient:(NSString *)clientAppId client:(id) sender {
_textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:sender applicationId:clientAppId];
@ -136,7 +70,7 @@ NSRange _previousSelRange;
_generatedBackspaceCount = 0;
BOOL legacy = [self isClientAppLegacy:clientAppId];
//BOOL legacy = [self isClientAppLegacy:clientAppId];
// In Xcode, if Keyman is the active IM and is in "debugMode" and "English plus Spanish" is
// the current keyboard and you type "Sentry force now", it will force a simulated crash to
@ -148,10 +82,12 @@ NSRange _previousSelRange;
else
_easterEggForSentry = nil;
//TODO: move to TextCompatibilityCheck
// For the Atom editor, this isn't really true (the context CAN change unexpectedly), but we can't get
// the context, so we pretend/hope it won't.
BOOL selectionCanChangeUnexpectedly = (![clientAppId isEqual: @"com.github.atom"]);
return [self initWithLegacyMode:legacy clientSelectionCanChangeUnexpectedly:selectionCanChangeUnexpectedly];
// TODO: this equates 'legacyMode' with being forced to send events
return [self initWithLegacyMode:self.textCompatibility.mustBackspaceUsingEvents clientSelectionCanChangeUnexpectedly:selectionCanChangeUnexpectedly];
}
- (void)switchToLegacyMode {
@ -967,7 +903,7 @@ NSRange _previousSelRange;
//MARK: Core-related key processing
// replacement handleEvent implementation for core event processing
- (BOOL)handleEvent:(NSEvent *)event client:(id)sender {
NSLog(@"SGS handleEvent event = %@", event);
NSLog(@"handleEvent event = %@", event);
// mouse movement requires that the context be invalidated
@ -1053,13 +989,14 @@ NSRange _previousSelRange;
}
}
/*
-(void)checkClientTextCompatibility:(id) client {
if(!self.textCompatibility) {
_textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:client applicationId:self.clientApplicationId];
NSLog(@"KMInputMethodHandler checkClientTextCompatibility: %@", self.textCompatibility);
}
}
*/
/*
-(BOOL)containsKeymanCoreActions:(NSArray*)actions {
BOOL containsCoreActions = NO;
@ -1110,7 +1047,7 @@ NSRange _previousSelRange;
NSUInteger realLengthAfter =
[_cachedContext.currentContext lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
NSLog(@"applyActions, contextBefore = '%@', length = %lu, real length = %lu; contextAfter = '%@', length = %lu, real length = %lu", contextBefore, lengthBefore, realLengthBefore, contextAfter, lengthAfter, realLengthAfter);
NSLog(@"applyKeymanCoreActions, contextBefore = '%@', length = %lu, real length = %lu; contextAfter = '%@', length = %lu, real length = %lu", contextBefore, lengthBefore, realLengthBefore, contextAfter, lengthAfter, realLengthAfter);
return result.handledEvent;
}

View file

@ -2,7 +2,7 @@
* Keyman is copyright (C) SIL International. MIT License.
*
* KeySender.h
* KeyTest
* Keyman
*
* Created by Shawn Schantz on 2023-04-17.
*

View file

@ -2,7 +2,7 @@
* Keyman is copyright (C) SIL International. MIT License.
*
* KeySender.m
* KeyTest
* Keyman
*
* Created by Shawn Schantz on 2023-04-17.
*

View file

@ -2,7 +2,7 @@
* Keyman is copyright (C) SIL International. MIT License.
*
* TextCompatibilityCheck.h
* KeyTest
* Keyman
*
* Created by Shawn Schantz on 2023-05-05.
*
@ -21,7 +21,6 @@ NS_ASSUME_NONNULL_BEGIN
-(BOOL)canInsertText;
-(BOOL)canReplaceText;
-(BOOL)mustBackspaceUsingEvents;
@end
NS_ASSUME_NONNULL_END

View file

@ -2,7 +2,7 @@
* Keyman is copyright (C) SIL International. MIT License.
*
* TextCompatibilityCheck.m
* KeyTest
* Keyman
*
* Created by Shawn Schantz on 2023-05-05.
*
@ -12,6 +12,8 @@
#import "TextCompatibilityCheck.h"
#import <InputMethodKit/InputMethodKit.h>
NSString *const kKMLegacyApps = @"KMLegacyApps";
@interface TextCompatibilityCheck()
@property (readonly) id client;
@ -22,7 +24,6 @@
@end
@implementation TextCompatibilityCheck
-(instancetype)initWithClient:(id) client applicationId:(NSString *)appId {
@ -47,20 +48,16 @@ return [NSString stringWithFormat:@"hasSelectionAPI: %d, hasReadAPI: %d, hasInse
-(BOOL) checkSelectionApi:(id) client applicationId:(NSString *)clientAppId {
BOOL workingSelectionApi = NO;
// if the selector exists, then call the API and see if it returns a valid value
workingSelectionApi = [client respondsToSelector:@selector(selectedRange)];
if ([client respondsToSelector:@selector(selectedRange)]) {
/*
// if the selector exists, then call the API and see if it returns a valid value
if (workingSelectionApi) {
NSRange selectionRange = [self.client selectedRange];
NSLog(@"TextCompatibilityCheck checkSelectionApi, location = %lu, length = %lu", selectionRange.location, selectionRange.length);
NSRange notFoundRange = NSMakeRange(NSNotFound, NSNotFound);
/*
if (selectionRange.location == 0) {
canGetSelection = NO;
NSLog(@"checkCanGetSelection, selectionRange.location == 0 ");
} else
*/
if (NSEqualRanges(selectionRange, notFoundRange)) {
workingSelectionApi = YES;
// no current selection, but API is working
@ -69,12 +66,39 @@ return [NSString stringWithFormat:@"hasSelectionAPI: %d, hasReadAPI: %d, hasInse
workingSelectionApi = YES;
}
}
// TODO: integrate with existing legacy code
*/
// if the selection API appears to work, it may still be broken
// getting the selection does not work for anything in the legacy app list
// getting the selection does not work for anything in the noncompliant app lists
if (workingSelectionApi) {
BOOL isLegacy = ([clientAppId isEqual: @"com.github.atom"] ||
workingSelectionApi = ![self containedInNoncompliantAppLists:clientAppId];
}
NSLog(@"hasWorkingSelectionApi for app %@: set to %@", clientAppId, workingSelectionApi?@"yes":@"no");
return workingSelectionApi;
}
/**
* Checks if the client app is known to be non-complian, first by checking the hard-coded non-compliant app list
* and then by checking the user-managed (via user defaults) non-compliant app list
*/
- (BOOL)containedInNoncompliantAppLists:(NSString *)clientAppId {
BOOL isAppNonCompliant = [self containedInHardCodedNoncompliantAppList:clientAppId];
if (!isAppNonCompliant) {
isAppNonCompliant = [self containedInUserManagedNoncompliantAppList:clientAppId];
}
NSLog(@"containedInNoncompliantAppLists: for app %@: %@", clientAppId, isAppNonCompliant?@"yes":@"no");
return isAppNonCompliant;
}
/** Check this hard-coded list to see if the application ID is among those
* that are known to not implement selectionRange correctly.
* This was formerly called the legacy app list, renamed to improve clarity.
*/
- (BOOL)containedInHardCodedNoncompliantAppList:(NSString *)clientAppId {
BOOL isAppNonCompliant = ([clientAppId isEqual: @"com.github.atom"] ||
[clientAppId isEqual: @"com.collabora.libreoffice-free"] ||
[clientAppId isEqual: @"org.libreoffice.script"] ||
[clientAppId isEqual: @"com.axosoft.gitkraken"] ||
@ -93,11 +117,105 @@ return [NSString stringWithFormat:@"hasSelectionAPI: %d, hasReadAPI: %d, hasInse
[clientAppId isEqual: @"com.Keyman.test.legacyInput"]
/*||[clientAppId isEqual: @"ro.sync.exml.Oxygen"] - Oxygen has worse problems */
);
workingSelectionApi = !isLegacy;
NSLog(@"containedInHardCodedNoncompliantAppList: for app %@: %@", clientAppId, isAppNonCompliant?@"yes":@"no");
return isAppNonCompliant;
}
return workingSelectionApi;
/**
* Returns the list of user-default legacy apps
*/
- (NSArray *)legacyAppsUserDefaults {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
return [userData arrayForKey:kKMLegacyApps];
}
/** Check this user-managed list to see if the application ID is among those
* that are known to not implement selectionRange correctly.
* This was formerly called the legacy app list, renamed to improve clarity.
*/
- (BOOL)containedInUserManagedNoncompliantAppList:(NSString *)clientAppId {
BOOL isAppNonCompliant = false;
NSArray *legacyAppsUserDefaults = self.legacyAppsUserDefaults;
if(legacyAppsUserDefaults != nil) {
isAppNonCompliant = [self isClientAppLegacy:clientAppId fromArray:legacyAppsUserDefaults];
}
NSLog(@"containedInUserManagedNoncompliantAppList: for app %@: %@", clientAppId, isAppNonCompliant?@"yes":@"no");
return isAppNonCompliant;
}
/**
* Checks if the client app requires legacy input mode, first by checking the user defaults, if they exist,
* then, by our hard-coded list.
*/
/*
- (BOOL)isClientAppLegacy:(NSString *)clientAppId {
BOOL isAppNonCompliant = false;
NSArray *legacyAppsUserDefaults = self.legacyAppsUserDefaults;
BOOL result = NO;
if(legacyAppsUserDefaults != nil) {
result = [self isClientAppLegacy:clientAppId fromArray:legacyAppsUserDefaults];
}
if(!result) {
// TODO: Pages and Keynote (and possibly lots of other undiscovered apps that are otherwise compliant
// with Apple's IM framework) have a problem in that if the user selects a different font (or other
// formatting) and then types a sequence that causes characters to be added to the document and then
// subsequently replaced, the replacement causes the formatting decision to be forgotten. This can be
// "fixed" by treating them as legacy apps, but it causes other problems.
result = ([clientAppId isEqual: @"com.github.atom"] ||
[clientAppId isEqual: @"com.collabora.libreoffice-free"] ||
[clientAppId isEqual: @"org.libreoffice.script"] ||
[clientAppId isEqual: @"com.axosoft.gitkraken"] ||
[clientAppId isEqual: @"org.sil.app.builder.scripture.ScriptureAppBuilder"] ||
[clientAppId isEqual: @"org.sil.app.builder.reading.ReadingAppBuilder"] ||
[clientAppId isEqual: @"org.sil.app.builder.dictionary.DictionaryAppBuilder"] ||
//[clientAppId isEqual: @"com.microsoft.Word"] || // 2020-11-24[mcd]: Appears to work well in Word 16.43, disable legacy by default
[clientAppId isEqual: @"org.openoffice.script"] ||
[clientAppId isEqual: @"com.adobe.illustrator"] ||
[clientAppId isEqual: @"com.adobe.InDesign"] ||
[clientAppId isEqual: @"com.adobe.Photoshop"] ||
[clientAppId isEqual: @"com.adobe.AfterEffects"] ||
[clientAppId isEqual: @"com.microsoft.VSCode"] ||
[clientAppId isEqual: @"com.google.Chrome"] ||
[clientAppId hasPrefix: @"net.java"] ||
[clientAppId isEqual: @"com.Keyman.test.legacyInput"]
//||[clientAppId isEqual: @"ro.sync.exml.Oxygen"] - Oxygen has worse problems
);
}
return result;
}
*/
/**
* Checks array for a list of possible regexes to match a client app id
*/
- (BOOL)isClientAppLegacy:(NSString *)clientAppId fromArray:(NSArray *)legacyApps {
for(id legacyApp in legacyApps) {
if(![legacyApp isKindOfClass:[NSString class]]) {
NSLog(@"isClientAppLegacy:fromArray: LegacyApps user defaults array should contain only strings");
} else {
NSError *error = nil;
NSRange range = NSMakeRange(0, clientAppId.length);
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: (NSString *) legacyApp options: 0 error: &error];
NSArray *matchesArray = [regex matchesInString:clientAppId options:0 range:range];
if(matchesArray.count>0) {
NSLog(@"isClientAppLegacy: found match for legacy app %@: ", clientAppId);
return YES;
}
}
}
return NO;
}
-(BOOL) canGetSelection {
return self.hasWorkingSelectionApi;
}

View file

@ -14,11 +14,19 @@
#import "KMInputMethodEventHandlerProtected.h"
#import "LegacyTestClient.h"
#import "AppleCompliantTestClient.h"
#import "TextCompatibilityCheck.h"
@interface InputMethodTests : XCTestCase
@end
// included following interface that we can see and test private methods of TextCompatibilityCheck
@interface TextCompatibilityCheck (Testing)
- (BOOL)isClientAppLegacy:(NSString *)clientAppId fromArray:(NSArray *)legacyApps;
@end
@implementation InputMethodTests
- (void)setUp {
@ -30,25 +38,25 @@
}
- (void)testIsClientAppLegacy_unlistedClientAppId_returnsNo {
KMInputMethodEventHandler * inputMethod = [[KMInputMethodEventHandler alloc] initWithLegacyMode:NO clientSelectionCanChangeUnexpectedly:NO];
id client = [[AppleCompliantTestClient alloc] init];
NSString *clientAppId = @"com.compliant.app";
TextCompatibilityCheck *textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:client applicationId:clientAppId];
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.microsoft.VSCode",@"com.adobe.Photoshop",nil];
BOOL isLegacy = [inputMethod isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
BOOL isLegacy = [textCompatibility isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
NSLog(@"isLegacy = %@", isLegacy?@"yes":@"no");
XCTAssert(isLegacy == NO, @"App not expected to be in legacy list");
}
- (void)testIsClientAppLegacy_listedClientAppId_returnsYes {
KMInputMethodEventHandler * inputMethod = [[KMInputMethodEventHandler alloc] initWithLegacyMode:NO clientSelectionCanChangeUnexpectedly:NO];
id client = [[AppleCompliantTestClient alloc] init];
NSString *clientAppId = @"com.microsoft.VSCode";
TextCompatibilityCheck *textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:client applicationId:clientAppId];
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.adobe.Photoshop",@"com.microsoft.VSCode",nil];
BOOL isLegacy = [inputMethod isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
BOOL isLegacy = [textCompatibility isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
NSLog(@"isLegacy = %@", isLegacy?@"yes":@"no");
XCTAssert(isLegacy == YES, @"App expected to be in legacy list");
}

View file

@ -16,7 +16,7 @@
- (KMEngine *)kme {
if (_kme == nil) {
_kme = [[KMEngine alloc] initWithKMX:nil contextBuffer:self.contextBuffer];
_kme = [[KMEngine alloc] initWithKMX:nil context:self.contextBuffer];
[_kme setDebugMode:self.debugMode];
}
@ -40,7 +40,7 @@
_contextBuffer = [contextBuffer mutableCopy];
if (_contextBuffer.length)
[_contextBuffer replaceOccurrencesOfString:@"\0" withString:[NSString nullChar] options:0 range:NSMakeRange(0, 1)];
[self.kme setContextBuffer:self.contextBuffer];
[self.kme setCoreContext:self.contextBuffer];
}
- (BOOL)debugMode {

View file

@ -81,34 +81,26 @@ UInt32 VirtualKeyMap[VIRTUAL_KEY_ARRAY_SIZE];
return optimizedActionArray;
}
// TODO: remove excessive debug statements above and below
-(UInt32)macToKeymanModifier:(NSEventModifierFlags)modifiers {
UInt32 keymanModifiers = 0;
if ([self isShiftKey:modifiers]) {
keymanModifiers |= KM_KBP_MODIFIER_SHIFT;
NSLog(@"macToKeymanModifier setting shift bit");
}
if([self isLeftOptionKey:modifiers]) {
keymanModifiers |= KM_KBP_MODIFIER_LALT;
NSLog(@"macToKeymanModifier setting left alt bit");
} else if([self isRightOptionKey:modifiers]) {
keymanModifiers |= KM_KBP_MODIFIER_RALT;
NSLog(@"macToKeymanModifier setting right alt bit");
} else if ([self isOptionKey:modifiers]) {
keymanModifiers |= KM_KBP_MODIFIER_ALT;
NSLog(@"macToKeymanModifier setting alt bit");
}
if ([self isLeftControlKey:modifiers]) {
keymanModifiers |= KM_KBP_MODIFIER_LCTRL;
NSLog(@"macToKeymanModifier setting left control bit");
} else if ([self isRightControlKey:modifiers]) {
keymanModifiers |= KM_KBP_MODIFIER_RCTRL;
NSLog(@"macToKeymanModifier setting right control bit");
} else if ([self isControlKey:modifiers]) {
keymanModifiers |= KM_KBP_MODIFIER_CTRL;
NSLog(@"macToKeymanModifier setting control bit");
}
if ([self isCapsLockKey:modifiers]) {

View file

@ -16,9 +16,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface CoreWrapper : NSObject
@property (strong, nonatomic, readonly) NSString * keyboardId;
@property (strong, nonatomic, readonly) NSString * keyboardVersion;
@property (strong, nonatomic) NSString * context;
@property (strong, nonatomic, readonly) NSString *keyboardId;
@property (strong, nonatomic, readonly) NSString *keyboardVersion;
@property (strong, nonatomic) NSString *context;
-(instancetype)initWithHelper:(CoreHelper*)helper kmxFilePath:(nullable NSString*)path;
-(BOOL)setOptionsForCore: (NSString *) key value:(NSString *) value;

View file

@ -47,6 +47,9 @@
[self setContextUsingCore:newValue];
}
-(void)clearContext {
[self clearContextUsingCore];
}
-(void)changeKeyboardWithKmxFilePath:(NSString*) path {
if (path != nil) {
@try {
@ -299,19 +302,28 @@
return immutableString;
}
-(void)setContextUsingCore:(NSString*)context {
char const *coreString = [context cStringUsingEncoding:NSUTF8StringEncoding];
km_kbp_context_item *contextItemArray;
// create array of context items
km_kbp_status result = km_kbp_context_items_from_utf8(coreString, &contextItemArray);
NSLog(@"km_kbp_context_items_from_utf8, result=%i", result);
// set the context in core using the array
-(void)clearContextUsingCore {
km_kbp_context * coreContext = km_kbp_state_context(self.keyboardState);
km_kbp_context_set(coreContext, contextItemArray);
// dispose
km_kbp_context_items_dispose(contextItemArray);
km_kbp_context_clear(coreContext);
}
-(void)setContextUsingCore:(NSString*)context {
if (context.length == 0) {
[self clearContextUsingCore];
} else {
char const *coreString = [context cStringUsingEncoding:NSUTF8StringEncoding];
km_kbp_context_item *contextItemArray;
// create array of context items
km_kbp_status result = km_kbp_context_items_from_utf8(coreString, &contextItemArray);
NSLog(@"km_kbp_context_items_from_utf8, result=%i", result);
// set the context in core using the array
km_kbp_context * coreContext = km_kbp_state_context(self.keyboardState);
km_kbp_context_set(coreContext, contextItemArray);
// dispose
km_kbp_context_items_dispose(contextItemArray);
}
}
//TODO: create and save as static

View file

@ -19,11 +19,16 @@
@property (weak, nonatomic) KMXFile *kmx;
@property (assign, nonatomic) BOOL debugMode;
- (id)initWithKMX:(KMXFile *)kmx contextBuffer:(NSString *)ctxBuf;
- (id)initWithKMX:(KMXFile *)kmx context:(NSString *)ctxBuf;
- (NSString *)getCoreContext;
- (void)clearCoreContext;
- (void)setCoreContext:(NSString *)context;
//TODO: remove additional context methods
/*
- (void)setContextBuffer:(NSString *)ctxBuf;
- (NSString *)contextBuffer;
*/
- (void)setCoreOptions:(NSString *)key withValue:(NSString *)value;
- (NSArray *)processEvent:(NSEvent *)event;
- (void)setUseVerboseLogging:(BOOL)useVerboseLogging;

View file

@ -20,7 +20,6 @@
//DWORD VKMap[0x80];
@interface KMEngine ()
@property (strong, nonatomic) NSMutableString *tmpCtxBuf;
@property (readonly) CoreHelper *coreHelper;
@property (nonatomic, retain) CoreWrapper * keymanCore;
@end
@ -31,7 +30,7 @@ NSMutableString* _easterEggForSentry = nil;
const NSString* kEasterEggText = @"Sentrycrash#KME";
const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
- (id)initWithKMX:(KMXFile *)kmx contextBuffer:(NSString *)ctxBuf {
- (id)initWithKMX:(KMXFile *)kmx context:(NSString *)contextString {
self = [super init];
if (self) {
#ifdef DEBUG
@ -44,14 +43,8 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
if (kmx) {
[self loadCoreWrapperFromKmxFile:self.kmx.filePath];
[self.keymanCore setContext:ctxBuf];
[self.keymanCore setContext:contextString];
}
_tmpCtxBuf = [[NSMutableString alloc] initWithString:ctxBuf];
[_tmpCtxBuf removeAllNullChars];
if (_tmpCtxBuf.length)
[_tmpCtxBuf replaceOccurrencesOfString:@"\0" withString:@"" options:0 range:NSMakeRange(0, 1)];
//[self setVKMapping];
}
return self;
@ -97,36 +90,15 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
NSLog(@"KME - Turning verbose logging off");
}
- (NSString *)getCoreContext {
return self.keymanCore.context;
}
- (void)setCoreContext:(NSString *)context {
[self.keymanCore setContext:context];
}
- (void)setContextBuffer:(NSString *)ctxBuf {
_tmpCtxBuf = [[NSMutableString alloc] initWithString:ctxBuf];
[_tmpCtxBuf removeAllNullChars];
if (_tmpCtxBuf.length)
[_tmpCtxBuf replaceOccurrencesOfString:@"\0" withString:@"" options:0 range:NSMakeRange(0, 1)];
}
- (NSString *)contextBuffer {
return [NSString stringWithString:self.tmpCtxBuf];
}
- (NSMutableString *)tmpCtxBuf {
if (_tmpCtxBuf == nil) {
_tmpCtxBuf = [[NSMutableString alloc] initWithString:@""];
}
return _tmpCtxBuf;
}
- (void)setCoreOptions:(NSString *)key withValue:(NSString *)value {
/*
KMCompStore *store = [self.kmx.store objectAtIndex:storeID];
KMCompStore *storeSaved = [self.kmx.storeSaved objectAtIndex:storeID];
store.string = [[NSString alloc] initWithString:value];
storeSaved.string = [[NSString alloc] initWithString:value];
*/
BOOL success = [self.keymanCore setOptionsForCore:key value:value];
NSLog(@"setCoreOptions for key: %@, value: %@ succeeded = %@", key, value, success ? @"YES" : @"NO");
}
@ -144,11 +116,7 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
if ([coreActions count] == 0) {
return nil;
} else {
// update context from core
[self.tmpCtxBuf setString:self.keymanCore.context];
return coreActions;
// TODO: integrate the different contexts
// -- probably ignore/trash tmpCtxBuf because we update local context using actions
}
}

View file

@ -31,7 +31,7 @@ NSString * names[nCombinations];
}
- (void)testinitWithKMX_NilKmx_ProcessEventReturnsNil {
KMEngine *engine = [[KMEngine alloc] initWithKMX:nil contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:nil context:@""];
NSEvent *event = [[NSEvent alloc] init];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions == nil, @"Expected processEvent to return nil for nil kmx");
@ -39,28 +39,28 @@ NSString * names[nCombinations];
- (void)testinitWithKMX_ValidKmxEmptyContext_InitializedWithEmptyContext {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
XCTAssert(engine != nil, @"Expected non-nil engine");
XCTAssert(engine.contextBuffer.length == 0, @"Expected empty context buffer");
XCTAssert(engine.getCoreContext.length == 0, @"Expected empty context buffer");
}
- (void)testinitWithKMX_ValidKmxNonEmptyContext_InitializedWithContext {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"abc"];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"abc"];
XCTAssert(engine != nil, @"Expected non-nil engine");
XCTAssert([engine.contextBuffer isEqualToString:@"abc"], @"Expected 'abc' in context buffer");
XCTAssert([engine.getCoreContext isEqualToString:@"abc"], @"Expected 'abc' in context buffer");
}
- (void)testsetContextBuffer_NonEmptyContext_InitializedWithContext {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
[engine setContextBuffer:@"xyz"];
XCTAssert([engine.contextBuffer isEqualToString:@"xyz"], @"Expected 'xyz' in context buffer");
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
[engine setCoreContext:@"xyz"];
XCTAssert([engine.getCoreContext isEqualToString:@"xyz"], @"Expected 'xyz' in context buffer");
}
- (void)testprocessEvent_eventForCommandKey_ReturnsNilActionsArray {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:NSEventModifierFlagCommand timestamp:0 windowNumber:0 context:nil characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:kVK_ANSI_A];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions == nil, @"Expected nil array of actions");
@ -68,7 +68,7 @@ NSString * names[nCombinations];
- (void)testprocessEvent_eventWithoutKeycode_ReturnsNilActionsArray {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSEvent *event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved location:NSMakePoint(29, 21) modifierFlags:NSEventModifierFlagShift timestamp:0 windowNumber:0 context:nil eventNumber:23 clickCount:0 pressure:0];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions == nil, @"Expected nil array of actions");
@ -77,7 +77,7 @@ NSString * names[nCombinations];
// TODO:
- (void)testprocessEvent_eventForUnmappedKey_ReturnsNoActions {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"z" charactersIgnoringModifiers:@"z" isARepeat:NO keyCode:kVK_ANSI_Z];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions == nil, @"Expected nil array of actions");
@ -85,7 +85,7 @@ NSString * names[nCombinations];
- (void)testprocessEvent_eventForLowercaseA_ReturnsCharacterActionWithExpectedCharacterBasedOnKmx {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:kVK_ANSI_A];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 1, @"Expected 1 action");
@ -127,14 +127,14 @@ NSString * names[nCombinations];
XCTAssert([actionType isEqualToString:Q_STR], @"Expected Q_STR action");
NSString *output = [action objectForKey:actionType];
XCTAssert([output isEqualToString:expectedOutput], @"Output incorrect");
[engine setContextBuffer:@""];
[engine clearContext];
}
}
*/
- (void)testprocessEvent_eventsForOpenCurlyBraceWithCipherMusicKmx_ReturnsCharacterActionForStartSlide {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
UTF32Char expectedUtf32Char = 0x1D177;
NSString * expectedStartSlideSurrogatePair = [[NSString alloc] initWithBytes:&expectedUtf32Char length:4 encoding:NSUTF32LittleEndianStringEncoding];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:NSEventModifierFlagShift timestamp:0 windowNumber:0 context:nil characters:@"{" charactersIgnoringModifiers:@"[" isARepeat:NO keyCode:kVK_ANSI_LeftBracket];
@ -161,7 +161,7 @@ NSString * names[nCombinations];
CoreAction *action = actions[0];
XCTAssert([action isCharacter], @"Expected CharacterAction");
XCTAssert([action.content isEqualToString:numeral], @"Output incorrect");
[engine setContextBuffer:@""];
[engine clearContext];
}
}
*/
@ -169,7 +169,7 @@ NSString * names[nCombinations];
// TODO: fails with core, returns CharacterAction
- (void)testprocessEvent_eventForShiftNumeralsWithoutRulesInCipherMusicKmx_ReturnsNoAction {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
for (int i = 0; i <= 9; i++)
{
if (i == 6)
@ -212,7 +212,7 @@ NSString * names[nCombinations];
// TODO: fails with core, returns CharacterAction
- (void)testprocessEvent_eventForPeriodWithCipherMusicKmx_ReturnsNoAction {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"." charactersIgnoringModifiers:@"." isARepeat:NO keyCode:kVK_ANSI_Period];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 0, @"Expected no actions");
@ -221,7 +221,7 @@ NSString * names[nCombinations];
// TODO: fails with core, returns CharacterAction
- (void)testprocessEvent_eventForCtrl8WithCipherMusicKmx_ReturnsNoAction {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:NSEventModifierFlagControl timestamp:0 windowNumber:0 context:nil characters:@"8" charactersIgnoringModifiers:@"8" isARepeat:NO keyCode:kVK_ANSI_8];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 0, @"Expected no actions");
@ -264,10 +264,10 @@ NSString * names[nCombinations];
[KMEngineTests fillInNamesAndModifiersForAllChiralCombinations];
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
for (i = 0; i < nCombinations; i++) {
[engine setContextBuffer:@""];
[engine clearCoreContext];
NSString *charactersIgnoringModifiers = (modifiers[i] & (LEFT_SHIFT_FLAG | RIGHT_SHIFT_FLAG)) ? @"A" : @"a";
NSString * characters = charactersIgnoringModifiers;
if (modifiers[i] & (LEFT_ALT_FLAG | RIGHT_ALT_FLAG))
@ -333,10 +333,10 @@ NSString * names[nCombinations];
[KMEngineTests fillInNamesAndModifiersForAllChiralCombinations];
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
for (i = 0; i < nCombinations; i++) {
[engine setContextBuffer:@""];
[engine clearCoreContext];
NSString *charactersIgnoringModifiers = (modifiers[i] & (LEFT_SHIFT_FLAG | RIGHT_SHIFT_FLAG)) ? @"S" : @"s";
NSString * characters = charactersIgnoringModifiers;
if (modifiers[i] & (LEFT_ALT_FLAG | RIGHT_ALT_FLAG)) {
@ -409,7 +409,7 @@ NSString * names[nCombinations];
- (NSString *)checkPlatform_getOutputForKeystroke: (NSString*) character modifierFlags: (NSEventModifierFlags) flag keyCode:(unsigned short)code {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForPlatformTest];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSString *lcChar = [character lowercaseString];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:flag timestamp:0 windowNumber:0 context:nil characters:character charactersIgnoringModifiers:lcChar isARepeat:NO keyCode:code];
NSArray *actions = [engine processEvent:event];
@ -512,7 +512,7 @@ NSString * names[nCombinations];
// KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"a"];
// NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"=" charactersIgnoringModifiers:@"=" isARepeat:NO keyCode:kVK_ANSI_Equal];
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForIndexOffsetTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"z"];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"z"];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:kVK_ANSI_A];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 2, @"Expected 2 actions");
@ -541,7 +541,7 @@ NSString * names[nCombinations];
- (void)testCoreProcessEvent_eventForFWithElNuerKmx_ReturnsCorrectCharacter {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"f" charactersIgnoringModifiers:@"f" isARepeat:NO keyCode:kVK_ANSI_F];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 1, @"Expected one action");
@ -552,13 +552,13 @@ NSString * names[nCombinations];
- (void)testCoreProcessEvent_eventDeleteWithElNuerKmx_EmptiesContextReturnsDelete {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"ɣ"];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"ɣ"];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"\b" charactersIgnoringModifiers:@"\b" isARepeat:NO keyCode:kVK_Delete];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 1, @"Expected one action");
CoreAction *action = actions[0];
XCTAssert([action isCharacterBackspace], @"Expected CharacterBackspace action");
NSString *context = engine.contextBuffer;
NSString *context = engine.getCoreContext;
XCTAssert([context isEqualToString:@""], @"Context should be empty.");
}
@ -577,13 +577,13 @@ NSString * names[nCombinations];
- (void)testCoreProcessEvent_eventReturnWithElNuerKmx_ContextUnchangedReturnsReturn {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"ɣ"];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"ɣ"];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"\n" charactersIgnoringModifiers:@"\n" isARepeat:NO keyCode:kVK_Return];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 2, @"Expected one action");
CoreAction *action = actions[1];
XCTAssert(action.actionType == EmitKeystrokeAction, @"Expected EmitKeystrokeAction");
NSString *context = engine.contextBuffer;
NSString *context = engine.getCoreContext;
XCTAssert([context isEqualToString:@"ɣ"], @"Context should be unchanged.");
}
@ -601,13 +601,13 @@ NSString * names[nCombinations];
- (void)testCoreProcessEvent_eventTabWithElNuerKmx_ContextUnchangedReturnsTab {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"ɣ"];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"ɣ"];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"\t" charactersIgnoringModifiers:@"\t" isARepeat:NO keyCode:kVK_Tab];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 2, @"Expected two actions");
CoreAction *action = actions[1];
XCTAssert(action.actionType == EmitKeystrokeAction, @"Expected EmitKeystrokeAction");
NSString *context = engine.contextBuffer;
NSString *context = engine.getCoreContext;
XCTAssert([context isEqualToString:@"ɣ"], @"Context should be unchanged.");
}
@ -626,13 +626,13 @@ NSString * names[nCombinations];
- (void)testCoreProcessEvent_eventSingleQuoteWithElNuerKmx_ReturnsDiacritic {
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
NSString *context = @"ɛ";
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:context];
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:context];
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"'" charactersIgnoringModifiers:@"'" isARepeat:NO keyCode:kVK_ANSI_Quote];
NSArray *actions = [engine processEvent:event];
XCTAssert(actions.count == 1, @"Expected one action");
CoreAction *action = actions[0];
XCTAssert([action isCharacter], @"Expected CharacterAction");
context = engine.contextBuffer;
context = engine.getCoreContext;
XCTAssert([context isEqualToString:@"\u025B\u0308"], @"Context updated with diacritic.");
}