Skip to content

Commit ed8e254

Browse files
committed
Add support for spacing combining marks.
A spacing combining mark is a character that combines (somehow) with a preceding base character. For example, TAMIL VOWEL SIGN E. They might combine in funny ways, like to the left of the base character. This commit does more or less what Terminal does, which is to give such a character its own cell but draw it in conjunction with the preceding character, provided the predecessor is drawable non-ascii. It is guarded by the advanced pref 'Detect base unicode characters with lookup table' which is a bit of a misnomer. I am not courageous enough to turn this on for 3.3, but perhaps a later dot release can have it by default.
1 parent e5a4e17 commit ed8e254

17 files changed

+486
-143
lines changed

iTerm2XCTests/VT100GridTest.m

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,37 +2542,6 @@ - (void)testCoordinateBefore {
25422542
XCTAssert(coord.x == 1);
25432543
XCTAssert(coord.y == 0);
25442544
}
2545-
2546-
- (void)testAddCombiningCharToCoord {
2547-
const unichar kCombiningAcuteAccent = 0x301;
2548-
const unichar kCombiningCedilla = 0x327;
2549-
const unichar kCombiningEnclosingCircle = 0x20dd;
2550-
2551-
VT100Grid *grid = [self gridFromCompactLines:@"abcd"];
2552-
XCTAssert([grid addCombiningChar:kCombiningEnclosingCircle
2553-
toCoord:VT100GridCoordMake(0, 0)]);
2554-
screen_char_t *line = [grid screenCharsAtLineNumber:0];
2555-
XCTAssert(line[0].complexChar);
2556-
NSString *str = ScreenCharToStr(&line[0]);
2557-
XCTAssert([[str decomposedStringWithCanonicalMapping] isEqualToString:[@"a⃝" decomposedStringWithCanonicalMapping]]);
2558-
2559-
// Fail to modify null character
2560-
grid = [self gridFromCompactLines:@".bcd"];
2561-
XCTAssert(![grid addCombiningChar:kCombiningAcuteAccent
2562-
toCoord:VT100GridCoordMake(0, 0)]);
2563-
2564-
// Add two combining marks
2565-
grid = [self gridFromCompactLines:@"abcd"];
2566-
XCTAssert([grid addCombiningChar:kCombiningAcuteAccent
2567-
toCoord:VT100GridCoordMake(0, 0)]);
2568-
XCTAssert([grid addCombiningChar:kCombiningCedilla
2569-
toCoord:VT100GridCoordMake(0, 0)]);
2570-
line = [grid screenCharsAtLineNumber:0];
2571-
XCTAssert(line[0].complexChar);
2572-
str = ScreenCharToStr(&line[0]);
2573-
XCTAssert([[str decomposedStringWithCanonicalMapping] isEqualToString:[@"á̧" decomposedStringWithCanonicalMapping]]);
2574-
}
2575-
25762545
- (void)testDeleteChars {
25772546
// Base case
25782547
VT100Grid *grid = [self gridFromCompactLinesWithContinuationMarks:

iTerm2XCTests/iTermAutomaticProfileSwitcherTest.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ - (void)testSwitchesOnUserName {
140140
_profile = self.profileUserX;
141141
_allProfiles = @[ self.profileUserX, self.profileUserY ];
142142
[_aps setHostname:@"whatever" username:@"y" path:@"whatever" job:@"whatever"];
143-
XCTAssert([_profile isEqualToProfile:self.profileUserY]);CSSMERR_CSP_INVALID_KEY_FORMAT
143+
XCTAssert([_profile isEqualToProfile:self.profileUserY]);
144144
}
145145

146146
- (void)testSwitchesOnPath {

sources/Metal/Infrastructure/GlyphKey.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace iTerm2 {
3434

3535
inline bool operator==(const GlyphKey &other) const {
3636
return (_repr.code == other._repr.code &&
37+
_repr.combiningSuccessor == other._repr.combiningSuccessor &&
3738
_repr.isComplex == other._repr.isComplex &&
3839
_repr.boxDrawing == other._repr.boxDrawing &&
3940
_repr.thinStrokes == other._repr.thinStrokes &&
@@ -45,8 +46,9 @@ namespace iTerm2 {
4546
}
4647

4748
NSString *description() const {
48-
return [NSString stringWithFormat:@"[GlyphKey: code=%@ complex=%@ boxdrawing=%@ thinstrokes=%@ drawable=%@ typeface=%@]",
49+
return [NSString stringWithFormat:@"[GlyphKey: code=%@ combiningSuccessor=%@ complex=%@ boxdrawing=%@ thinstrokes=%@ drawable=%@ typeface=%@]",
4950
@(_repr.code),
51+
@(_repr.combiningSuccessor),
5052
@(_repr.isComplex),
5153
@(_repr.boxDrawing),
5254
@(_repr.thinStrokes),
@@ -59,6 +61,7 @@ namespace iTerm2 {
5961
std::size_t seed = 0;
6062

6163
hash_combine(seed, _repr.code);
64+
hash_combine(seed, _repr.combiningSuccessor);
6265
hash_combine(seed, _repr.isComplex);
6366
hash_combine(seed, _repr.boxDrawing);
6467
hash_combine(seed, _repr.thinStrokes);

sources/Metal/iTermMetalDriver.m

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,8 @@ - (void)updateTextRendererForFrameData:(iTermMetalFrameData *)frameData {
867867
CGSize glyphSize = frameData.glyphSize;
868868
CGFloat scale = frameData.scale;
869869
__weak iTermMetalFrameData *weakFrameData = frameData;
870+
871+
// Set up the ASCII fast path
870872
[_textRenderer setASCIICellSize:cellSize
871873
offset:frameData.asciiOffset
872874
descriptor:[frameData.perFrameState characterSourceDescriptorForASCIIWithGlyphSize:glyphSize
@@ -876,32 +878,34 @@ - (void)updateTextRendererForFrameData:(iTermMetalFrameData *)frameData {
876878
__typeof(self) strongSelf = weakSelf;
877879
iTermMetalFrameData *strongFrameData = weakFrameData;
878880
if (strongSelf && strongFrameData) {
879-
return [strongSelf dictionaryForCharacter:c
880-
withAttributes:attributes
881-
frameData:strongFrameData
882-
glyphSize:glyphSize
883-
scale:scale];
881+
return [strongSelf dictionaryForASCIICharacter:c
882+
withAttributes:attributes
883+
frameData:strongFrameData
884+
glyphSize:glyphSize
885+
scale:scale];
884886
} else {
885887
return nil;
886888
}
887889
}];
888890
}
889891

890-
- (NSDictionary<NSNumber *, iTermCharacterBitmap *> *)dictionaryForCharacter:(char)c
891-
withAttributes:(iTermASCIITextureAttributes)attributes
892-
frameData:(iTermMetalFrameData *)frameData
893-
glyphSize:(CGSize)glyphSize
894-
scale:(CGFloat)scale {
892+
- (NSDictionary<NSNumber *, iTermCharacterBitmap *> *)dictionaryForASCIICharacter:(char)c
893+
withAttributes:(iTermASCIITextureAttributes)attributes
894+
frameData:(iTermMetalFrameData *)frameData
895+
glyphSize:(CGSize)glyphSize
896+
scale:(CGFloat)scale {
895897
static const int typefaceMask = ((1 << iTermMetalGlyphKeyTypefaceNumberOfBitsNeeded) - 1);
896898
iTermMetalGlyphKey glyphKey = {
897899
.code = c,
900+
.combiningSuccessor = 0,
898901
.isComplex = NO,
899902
.boxDrawing = NO,
900903
.thinStrokes = !!(attributes & iTermASCIITextureAttributesThinStrokes),
901904
.drawable = YES,
902905
.typeface = (attributes & typefaceMask),
903906
};
904907
BOOL emoji = NO;
908+
// Don't need to pass predecessor or successor because ASCII never has combining spacing marks.
905909
return [frameData.perFrameState metalImagesForGlyphKey:&glyphKey
906910
asciiOffset:frameData.asciiOffset
907911
size:glyphSize

sources/NSCharacterSet+iTerm.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
// Zero-width spaces.
2222
+ (instancetype)zeroWidthSpaceCharacterSetForUnicodeVersion:(NSInteger)version;
2323

24-
+ (instancetype)baseCharactersForUnicodeVersion:(NSInteger)version;
24+
+ (instancetype)spacingCombiningMarksForUnicodeVersion:(int)version;
25+
26+
+ (instancetype)codePointsWithOwnCell;
2527

2628
+ (NSCharacterSet *)urlCharacterSet;
2729
+ (NSCharacterSet *)filenameCharacterSet;

0 commit comments

Comments
 (0)