Skip to content

Commit 2bc74c0

Browse files
committed
feat(Phase A): expand decoder rules (vowel insert, neighbors), tie-break ranking, merge norm+best suggestions with DEBUG timing; add tests
1 parent 7bcb2c0 commit 2bc74c0

File tree

4 files changed

+49
-10
lines changed

4 files changed

+49
-10
lines changed

AvroKeyboardController.m

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,29 @@ - (void)findCurrentCandidates {
6060
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"ForgivingTypingEnabled"]) {
6161
BOOL enabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"ForgivingTypingEnabled"];
6262
if (enabled) {
63-
termForLookup = [RomanNormalizer normalize:termForLookup];
63+
CFAbsoluteTime t0 = CFAbsoluteTimeGetCurrent();
64+
NSString *norm = [RomanNormalizer normalize:termForLookup];
6465
// Choose best tolerant correction (includes original)
65-
termForLookup = [TolerantDecoder bestFor:termForLookup];
66+
NSString *best = [TolerantDecoder bestFor:norm];
67+
// Optionally, merge original+best lookups for broader suggestions
68+
if ([best isEqualToString:norm]) {
69+
termForLookup = best;
70+
#ifdef DEBUG
71+
NSLog(@"[FT] norm/best=%@ (%.2f ms)", best, (CFAbsoluteTimeGetCurrent()-t0)*1000.0);
72+
#endif
73+
} else {
74+
NSMutableOrderedSet *merged = [NSMutableOrderedSet orderedSet];
75+
NSArray *a = [[Suggestion sharedInstance] getList:norm];
76+
NSArray *b = [[Suggestion sharedInstance] getList:best];
77+
if (a) [merged addObjectsFromArray:a];
78+
if (b) [merged addObjectsFromArray:b];
79+
[_currentCandidates release];
80+
_currentCandidates = [[merged array] mutableCopy];
81+
#ifdef DEBUG
82+
NSLog(@"[FT] norm=%@ best=%@ merged=%lu (%.2f ms)", norm, best, (unsigned long)[_currentCandidates count], (CFAbsoluteTimeGetCurrent()-t0)*1000.0);
83+
#endif
84+
return; // Already set _currentCandidates
85+
}
6686
}
6787
}
6888
_currentCandidates = [[[Suggestion sharedInstance] getList:termForLookup] retain];

Ranking.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ @implementation Ranking
1515
NSUInteger db = [roman levenshteinDistanceTo:b];
1616
if (da < db) return NSOrderedAscending;
1717
if (da > db) return NSOrderedDescending;
18+
// Tie-breakers: prefer exact input if present, then lexical
19+
if ([a isEqualToString:roman] && ![b isEqualToString:roman]) return NSOrderedAscending;
20+
if ([b isEqualToString:roman] && ![a isEqualToString:roman]) return NSOrderedDescending;
1821
return [a compare:b];
1922
}];
2023
}
2124

2225
@end
23-

Tests/TolerantDecoderTests.m

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,17 @@ - (void)testRankingOrdersByEditDistance {
3131
XCTAssertEqualObjects(ranked.firstObject, @"teh");
3232
}
3333

34-
@end
34+
- (void)testNeighborSubstitution {
35+
// 'tezt' -> neighbor substitution z->s should allow 'test'
36+
NSString *best = [TolerantDecoder bestFor:@"tezt"];
37+
// Depending on distance, 'tezt' (d1) vs 'test' (d1) tie-break keeps input; ensure 'test' is in candidates
38+
NSArray *c = [TolerantDecoder candidatesFor:@"tezt" max:10];
39+
XCTAssertTrue([c containsObject:@"test"]);
40+
}
3541

42+
- (void)testMissingVowelInsertion {
43+
NSString *best = [TolerantDecoder bestFor:@"bng"]; // expect 'bang'
44+
XCTAssertEqualObjects(best, @"bang");
45+
}
46+
47+
@end

TolerantDecoder.m

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ @implementation TolerantDecoder
2727
}
2828
}
2929

30-
// Missing vowel insertion (insert 'a' between consonant clusters)
30+
// Missing vowel insertion (insert a common vowel between consonant clusters)
3131
NSCharacterSet *vowels = [NSCharacterSet characterSetWithCharactersInString:@"aeiou"]; // lowercase expected
3232
for (NSUInteger i = 0; i <= len && out.count < MAX(2, maxCount); i++) {
3333
if (i > 0 && i < len) {
@@ -38,19 +38,25 @@ @implementation TolerantDecoder
3838
continue;
3939
}
4040
if (![vowels characterIsMember:p] && ![vowels characterIsMember:c]) {
41-
NSMutableString *m = [roman mutableCopy];
42-
[m insertString:@"a" atIndex:i];
43-
[out addObject:m];
41+
// Try a small set of vowels; add until max reached
42+
for (NSString *v in @[@"a", @"e"]) {
43+
NSMutableString *m = [roman mutableCopy];
44+
[m insertString:v atIndex:i];
45+
[out addObject:m];
46+
if (out.count >= maxCount && maxCount > 0) break;
47+
}
4448
}
4549
}
4650
}
4751

48-
// Keyboard neighbor substitution (very small, safe map)
52+
// Keyboard neighbor substitution (small, safe map)
4953
NSDictionary<NSString*, NSArray<NSString*>*> *near = @{
5054
@"t": @[@"r", @"y"],
5155
@"e": @[@"w", @"r"],
5256
@"h": @[@"g", @"j"],
5357
@"n": @[@"b", @"m"],
58+
@"s": @[@"a", @"w", @"x", @"z"],
59+
@"z": @[@"s", @"x"],
5460
};
5561
for (NSUInteger i = 0; i < len && out.count < MAX(3, maxCount); i++) {
5662
NSString *ch = [[roman substringWithRange:NSMakeRange(i, 1)] lowercaseString];
@@ -88,4 +94,3 @@ + (NSString *)bestFor:(NSString *)roman {
8894
}
8995

9096
@end
91-

0 commit comments

Comments
 (0)