Skip to content

Commit c769b30

Browse files
committed
Fixes the regression in the ucs-detect test suite introduced when main
got merged into test-suites branch: * Use getCharacter(for:) for the VS base check so combined graphemes don’t get treated as invalid scalars in Terminal. * Add optional terminal/characterProvider plumbing to buffer string
1 parent ee2f3a0 commit c769b30

6 files changed

Lines changed: 108 additions & 19 deletions

File tree

Sources/SwiftTerm/Terminal.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@ open class Terminal {
13071307
let isVs15 = firstScalar.value == 0xFE0E
13081308
let needsEmojiVariationCheck = isVs16 || isVs15
13091309
if needsEmojiVariationCheck {
1310-
let baseScalar = cd.getCharacter().unicodeScalars.last
1310+
let baseScalar = getCharacter(for: cd).unicodeScalars.last
13111311
if baseScalar == nil || !UnicodeUtil.isEmojiVs16Base(rune: baseScalar!) {
13121312
continue
13131313
}

Tests/SwiftTermTests/ReflowPortedTests.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,33 @@ private func makeBuffer(cols: Int, rows: Int, scrollback: Int?) -> Buffer {
2222
return buffer
2323
}
2424

25-
private func lineText(_ buffer: Buffer, lineIndex: Int, trimRight: Bool = true) -> String {
25+
private func lineText(
26+
_ buffer: Buffer,
27+
lineIndex: Int,
28+
trimRight: Bool = true,
29+
characterProvider: ((CharData) -> Character)? = nil
30+
) -> String {
2631
buffer.translateBufferLineToString(
2732
lineIndex: lineIndex,
2833
trimRight: trimRight,
2934
startCol: 0,
3035
endCol: -1,
31-
skipNullCellsFollowingWide: true
36+
skipNullCellsFollowingWide: true,
37+
characterProvider: characterProvider
3238
).replacingOccurrences(of: "\u{0}", with: " ")
3339
}
3440

35-
private func lineString(_ line: BufferLine, trimRight: Bool = true) -> String {
41+
private func lineString(
42+
_ line: BufferLine,
43+
trimRight: Bool = true,
44+
characterProvider: ((CharData) -> Character)? = nil
45+
) -> String {
3646
line.translateToString(
3747
trimRight: trimRight,
3848
startCol: 0,
3949
endCol: -1,
40-
skipNullCellsFollowingWide: true
50+
skipNullCellsFollowingWide: true,
51+
characterProvider: characterProvider
4152
).replacingOccurrences(of: "\u{0}", with: " ")
4253
}
4354

Tests/SwiftTermTests/ScreenTests.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@ import Testing
22
@testable import SwiftTerm
33

44
final class ScreenTests {
5-
private func bufferLineText(_ buffer: Buffer, lineIndex: Int) -> String {
6-
buffer.translateBufferLineToString(
5+
private func bufferLineText(_ buffer: Buffer, lineIndex: Int, terminal: Terminal? = nil) -> String {
6+
let characterProvider: ((CharData) -> Character)?
7+
if let terminal {
8+
characterProvider = { terminal.getCharacter(for: $0) }
9+
} else {
10+
characterProvider = nil
11+
}
12+
return buffer.translateBufferLineToString(
713
lineIndex: lineIndex,
814
trimRight: true,
915
startCol: 0,
1016
endCol: -1,
11-
skipNullCellsFollowingWide: true
17+
skipNullCellsFollowingWide: true,
18+
characterProvider: characterProvider
1219
).replacingOccurrences(of: "\u{0}", with: " ")
1320
}
1421

Tests/SwiftTermTests/SynchronizedOutputTests.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,20 @@ final class SynchronizedOutputTests {
1616
func bell(source: Terminal) {}
1717
}
1818

19-
private func topLineText(from buffer: Buffer) -> String {
20-
buffer.translateBufferLineToString(
19+
private func topLineText(from buffer: Buffer, terminal: Terminal? = nil) -> String {
20+
let characterProvider: ((CharData) -> Character)?
21+
if let terminal {
22+
characterProvider = { terminal.getCharacter(for: $0) }
23+
} else {
24+
characterProvider = nil
25+
}
26+
return buffer.translateBufferLineToString(
2127
lineIndex: buffer.yDisp,
2228
trimRight: true,
2329
startCol: 0,
2430
endCol: -1,
25-
skipNullCellsFollowingWide: true
31+
skipNullCellsFollowingWide: true,
32+
characterProvider: characterProvider
2633
).replacingOccurrences(of: "\u{0}", with: " ")
2734
}
2835

Tests/SwiftTermTests/TerminalTestHarness.swift

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,26 @@ enum TerminalTestHarness {
2828
return (terminal, delegate)
2929
}
3030

31-
static func visibleLinesText(buffer: Buffer, trimRight: Bool = true) -> [String] {
31+
static func visibleLinesText(buffer: Buffer, terminal: Terminal? = nil, trimRight: Bool = true) -> [String] {
3232
let start = buffer.yDisp
3333
let end = min(buffer.yDisp + buffer.rows, buffer.lines.count)
3434
guard start < end else { return [] }
35-
return (start..<end).map { bufferLineText(buffer: buffer, lineIndex: $0, trimRight: trimRight) }
35+
let characterProvider = makeCharacterProvider(terminal)
36+
return (start..<end).map {
37+
bufferLineText(buffer: buffer, lineIndex: $0, trimRight: trimRight, characterProvider: characterProvider)
38+
}
3639
}
3740

38-
static func lineText(buffer: Buffer, row: Int, trimRight: Bool = true) -> String? {
41+
static func lineText(buffer: Buffer, terminal: Terminal? = nil, row: Int, trimRight: Bool = true) -> String? {
3942
guard row >= 0, row < buffer.rows else { return nil }
4043
let index = buffer.yDisp + row
4144
guard index >= 0, index < buffer.lines.count else { return nil }
42-
return bufferLineText(buffer: buffer, lineIndex: index, trimRight: trimRight)
45+
return bufferLineText(
46+
buffer: buffer,
47+
lineIndex: index,
48+
trimRight: trimRight,
49+
characterProvider: makeCharacterProvider(terminal)
50+
)
4351
}
4452

4553
static func charData(buffer: Buffer, row: Int, col: Int) -> CharData? {
@@ -65,18 +73,29 @@ enum TerminalTestHarness {
6573
#expect(buffer.y == row)
6674
}
6775

68-
static func assertLineText(_ buffer: Buffer, row: Int, equals expected: String) {
69-
let actual = lineText(buffer: buffer, row: row) ?? ""
76+
static func assertLineText(_ buffer: Buffer, terminal: Terminal? = nil, row: Int, equals expected: String) {
77+
let actual = lineText(buffer: buffer, terminal: terminal, row: row) ?? ""
7078
#expect(actual == expected)
7179
}
7280
}
7381

74-
private func bufferLineText(buffer: Buffer, lineIndex: Int, trimRight: Bool) -> String {
82+
private func bufferLineText(
83+
buffer: Buffer,
84+
lineIndex: Int,
85+
trimRight: Bool,
86+
characterProvider: ((CharData) -> Character)? = nil
87+
) -> String {
7588
return buffer.translateBufferLineToString(
7689
lineIndex: lineIndex,
7790
trimRight: trimRight,
7891
startCol: 0,
7992
endCol: -1,
80-
skipNullCellsFollowingWide: true
93+
skipNullCellsFollowingWide: true,
94+
characterProvider: characterProvider
8195
).replacingOccurrences(of: "\u{0}", with: " ")
8296
}
97+
98+
private func makeCharacterProvider(_ terminal: Terminal?) -> ((CharData) -> Character)? {
99+
guard let terminal else { return nil }
100+
return { terminal.getCharacter(for: $0) }
101+
}

Tests/SwiftTermTests/UnicodeTests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,50 @@ final class SwiftTermUnicode {
152152
#expect(char0_0 == "👩‍❤️‍👨")
153153
}
154154

155+
@Test func testZwJSequencePreservesVariationSelector16() {
156+
let h = HeadlessTerminal (queue: SwiftTermTests.queue) { exitCode in }
157+
let t = h.terminal!
158+
159+
let sequence = "👩‍❤\u{FE0F}"
160+
t.feed (text: "\(sequence)\r\n")
161+
162+
let cell = t.getCharacter (col:0, row: 0)
163+
#expect(cell != nil)
164+
let char0_0 = cell ?? " "
165+
#expect(char0_0.unicodeScalars.contains { $0.value == 0xFE0F })
166+
}
167+
168+
@Test func testZwJSequencePreservesVariationSelector15() {
169+
let h = HeadlessTerminal (queue: SwiftTermTests.queue) { exitCode in }
170+
let t = h.terminal!
171+
172+
let sequence = "👩‍❤\u{FE0E}"
173+
t.feed (text: "\(sequence)\r\n")
174+
175+
let cell = t.getCharacter (col:0, row: 0)
176+
#expect(cell != nil)
177+
let char0_0 = cell ?? " "
178+
#expect(char0_0.unicodeScalars.contains { $0.value == 0xFE0E })
179+
}
180+
181+
@Test func testBufferTranslationUsesCharacterProviderForExtendedGrapheme() {
182+
let h = HeadlessTerminal (queue: SwiftTermTests.queue) { exitCode in }
183+
let t = h.terminal!
184+
185+
let sequence = "👩‍👩‍👦‍👦"
186+
t.feed (text: "\(sequence)X")
187+
188+
let line = t.buffer.translateBufferLineToString(
189+
lineIndex: t.buffer.yDisp,
190+
trimRight: true,
191+
startCol: 0,
192+
endCol: -1,
193+
skipNullCellsFollowingWide: true,
194+
characterProvider: { t.getCharacter(for: $0) }
195+
).replacingOccurrences(of: "\u{0}", with: " ")
196+
197+
#expect(line == "\(sequence)X")
198+
}
199+
155200
}
156201
#endif

0 commit comments

Comments
 (0)