Skip to content

Commit f264650

Browse files
Strip test comments, remove dead CRLF branch, restore GitHub nav link
- Remove all comments from test files - Remove unreachable CRLF look-ahead in ANSIParser (Swift treats \r\n as a single grapheme cluster that never matches case "\r") - Remove two tests that covered the dead CRLF path - Restore GitHub link in website nav that was removed in #8 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a1d14f8 commit f264650

5 files changed

Lines changed: 7 additions & 153 deletions

File tree

Sources/DevtailKit/ANSIParser.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,6 @@ public struct ANSIParser: Sendable {
7878

7979
case "\r":
8080
flushText()
81-
let next = input.index(after: i)
82-
if next < input.endIndex && input[next] == "\n" {
83-
actions.append(.newline)
84-
i = input.index(after: next)
85-
continue
86-
}
8781
actions.append(.carriageReturn)
8882

8983
case "\n":

Tests/DevtailKitTests/ANSIParserTests.swift

Lines changed: 4 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,25 @@ import Testing
44

55
struct ANSIParserTests {
66

7-
// MARK: - Helper
8-
9-
/// Shorthand to parse a string and return actions.
107
private func parse(_ input: String) -> [TerminalAction] {
118
var parser = ANSIParser()
129
return parser.parse(input)
1310
}
1411

15-
/// Extract the first .text action's span, if any.
1612
private func firstSpan(_ actions: [TerminalAction]) -> StyledSpan? {
1713
for action in actions {
1814
if case .text(let span) = action { return span }
1915
}
2016
return nil
2117
}
2218

23-
/// Collect all .text spans from actions.
2419
private func allSpans(_ actions: [TerminalAction]) -> [StyledSpan] {
2520
actions.compactMap {
2621
if case .text(let span) = $0 { return span }
2722
return nil
2823
}
2924
}
3025

31-
// MARK: - Plain text
32-
3326
@Test func plainTextPassesThrough() {
3427
let actions = parse("hello world")
3528
#expect(actions.count == 1)
@@ -46,8 +39,6 @@ struct ANSIParserTests {
4639
#expect(actions.isEmpty)
4740
}
4841

49-
// MARK: - Newline / carriage return
50-
5142
@Test func newlineProducesNewlineAction() {
5243
let actions = parse("\n")
5344
#expect(actions.count == 1)
@@ -66,17 +57,6 @@ struct ANSIParserTests {
6657
}
6758
}
6859

69-
@Test func crlfGraphemeClusterIsFiltered() {
70-
// In Swift, \r\n forms a single Character grapheme cluster that does NOT
71-
// match case "\r" or case "\n" in the parser's switch. Its asciiValue is 10
72-
// (newline), which is < 32 and != 9, so it gets filtered as a control char.
73-
// This is a known limitation of character-level parsing in Swift.
74-
let input = String(Unicode.Scalar(0x0D)) + String(Unicode.Scalar(0x0A))
75-
let actions = parse(input)
76-
// CRLF grapheme is filtered out, producing no actions
77-
#expect(actions.isEmpty)
78-
}
79-
8060
@Test func textBeforeAndAfterNewline() {
8161
let actions = parse("abc\ndef")
8262
#expect(actions.count == 3)
@@ -88,8 +68,6 @@ struct ANSIParserTests {
8868
if case .text(let s2) = actions[2] { #expect(s2.text == "def") }
8969
}
9070

91-
// MARK: - Standard foreground colors (30-37)
92-
9371
@Test func standardForegroundColors() {
9472
for code in 30...37 {
9573
let actions = parse("\u{1B}[\(code)mX")
@@ -99,8 +77,6 @@ struct ANSIParserTests {
9977
}
10078
}
10179

102-
// MARK: - Standard background colors (40-47)
103-
10480
@Test func standardBackgroundColors() {
10581
for code in 40...47 {
10682
let actions = parse("\u{1B}[\(code)mX")
@@ -110,8 +86,6 @@ struct ANSIParserTests {
11086
}
11187
}
11288

113-
// MARK: - Bright foreground colors (90-97)
114-
11589
@Test func brightForegroundColors() {
11690
for code in 90...97 {
11791
let actions = parse("\u{1B}[\(code)mX")
@@ -121,8 +95,6 @@ struct ANSIParserTests {
12195
}
12296
}
12397

124-
// MARK: - Bright background colors (100-107)
125-
12698
@Test func brightBackgroundColors() {
12799
for code in 100...107 {
128100
let actions = parse("\u{1B}[\(code)mX")
@@ -132,8 +104,6 @@ struct ANSIParserTests {
132104
}
133105
}
134106

135-
// MARK: - 256-color palette
136-
137107
@Test func foreground256Color() {
138108
let actions = parse("\u{1B}[38;5;123mHi")
139109
let span = firstSpan(actions)
@@ -160,8 +130,6 @@ struct ANSIParserTests {
160130
#expect(span?.style.foreground == .palette(255))
161131
}
162132

163-
// MARK: - RGB truecolor
164-
165133
@Test func foregroundRGBColor() {
166134
let actions = parse("\u{1B}[38;2;255;128;0mTruecolor")
167135
let span = firstSpan(actions)
@@ -188,8 +156,6 @@ struct ANSIParserTests {
188156
#expect(span?.style.foreground == .rgb(255, 255, 255))
189157
}
190158

191-
// MARK: - Text styles
192-
193159
@Test func boldStyle() {
194160
let actions = parse("\u{1B}[1mBold")
195161
let span = firstSpan(actions)
@@ -221,13 +187,9 @@ struct ANSIParserTests {
221187
#expect(span?.style.strikethrough == true)
222188
}
223189

224-
// MARK: - Reset
225-
226190
@Test func resetClearsAllStyles() {
227191
var parser = ANSIParser()
228-
// Set bold + red foreground
229192
_ = parser.parse("\u{1B}[1;31m")
230-
// Now reset
231193
let actions = parser.parse("\u{1B}[0mAfterReset")
232194
let span = firstSpan(actions)
233195
#expect(span?.style == ANSIStyle())
@@ -242,8 +204,6 @@ struct ANSIParserTests {
242204
#expect(span?.style == ANSIStyle())
243205
}
244206

245-
// MARK: - Individual style resets
246-
247207
@Test func code22ResetsBoldAndDim() {
248208
var parser = ANSIParser()
249209
_ = parser.parse("\u{1B}[1;2m")
@@ -277,8 +237,6 @@ struct ANSIParserTests {
277237
#expect(span?.style.strikethrough == false)
278238
}
279239

280-
// MARK: - Default foreground / background
281-
282240
@Test func code39ResetsDefaultForeground() {
283241
var parser = ANSIParser()
284242
_ = parser.parse("\u{1B}[31m")
@@ -295,8 +253,6 @@ struct ANSIParserTests {
295253
#expect(span?.style.background == .default)
296254
}
297255

298-
// MARK: - Erase line
299-
300256
@Test func eraseLineCode2K() {
301257
let actions = parse("\u{1B}[2K")
302258
#expect(actions.count == 1)
@@ -324,8 +280,6 @@ struct ANSIParserTests {
324280
}
325281
}
326282

327-
// MARK: - Cursor up
328-
329283
@Test func cursorUpDefault() {
330284
let actions = parse("\u{1B}[A")
331285
#expect(actions.count == 1)
@@ -355,28 +309,23 @@ struct ANSIParserTests {
355309
#expect(n == 1)
356310
}
357311

358-
// MARK: - Multiple SGR params in one sequence
359-
360312
@Test func multipleSGRParams() {
361313
let actions = parse("\u{1B}[1;31;42mCombined")
362314
let span = firstSpan(actions)
363315
#expect(span?.style.bold == true)
364-
#expect(span?.style.foreground == .standard(1)) // red
365-
#expect(span?.style.background == .standard(2)) // green
316+
#expect(span?.style.foreground == .standard(1))
317+
#expect(span?.style.background == .standard(2))
366318
#expect(span?.text == "Combined")
367319
}
368320

369321
@Test func multipleSGRParamsItalicBrightCyan() {
370322
let actions = parse("\u{1B}[3;96mTest")
371323
let span = firstSpan(actions)
372324
#expect(span?.style.italic == true)
373-
#expect(span?.style.foreground == .bright(6)) // bright cyan
325+
#expect(span?.style.foreground == .bright(6))
374326
}
375327

376-
// MARK: - Control characters
377-
378328
@Test func controlCharactersAreFiltered() {
379-
// Characters below 32 (except tab) should be stripped
380329
let input = "A\u{01}B\u{02}C\u{07}D"
381330
let actions = parse(input)
382331
let span = firstSpan(actions)
@@ -389,37 +338,29 @@ struct ANSIParserTests {
389338
#expect(span?.text == "A\tB")
390339
}
391340

392-
// MARK: - Incomplete / malformed sequences
393-
394341
@Test func incompleteEscapeSequenceIsSkipped() {
395-
// ESC followed by end of string
396342
let actions = parse("Hello\u{1B}")
397343
#expect(actions.count == 1)
398344
let span = firstSpan(actions)
399345
#expect(span?.text == "Hello")
400346
}
401347

402348
@Test func escNotFollowedByBracketSkips() {
403-
// ESC followed by non-bracket char
404349
let actions = parse("A\u{1B}XB")
405-
// "A" is flushed before ESC, ESC+X advances past ESC, then "XB" continues
406350
let spans = allSpans(actions)
407351
let combined = spans.map(\.text).joined()
408352
#expect(combined == "AXB")
409353
}
410354

411355
@Test func incompleteCSISequenceIsSkipped() {
412-
// ESC[ with digits but no final letter
413356
let actions = parse("Hi\u{1B}[31")
414357
let span = firstSpan(actions)
415358
#expect(span?.text == "Hi")
416359
}
417360

418-
// MARK: - Style persistence across parse calls
419-
420361
@Test func stylePersistsAcrossParseCalls() {
421362
var parser = ANSIParser()
422-
_ = parser.parse("\u{1B}[1;31m") // bold + red
363+
_ = parser.parse("\u{1B}[1;31m")
423364
let actions = parser.parse("StyledText")
424365
let span = firstSpan(actions)
425366
#expect(span?.style.bold == true)
@@ -438,8 +379,6 @@ struct ANSIParserTests {
438379
#expect(s2?.style == ANSIStyle())
439380
}
440381

441-
// MARK: - Mixed text and escapes
442-
443382
@Test func mixedTextAndEscapes() {
444383
let actions = parse("Hello \u{1B}[31mWorld\u{1B}[0m!")
445384
let spans = allSpans(actions)
@@ -477,29 +416,16 @@ struct ANSIParserTests {
477416
}
478417
}
479418

480-
@Test func textWithCRLFGraphemeClusters() {
481-
// CRLF grapheme clusters are filtered (see crlfGraphemeClusterIsFiltered),
482-
// so text on either side merges into one span.
483-
let crlf = String(Unicode.Scalar(0x0D)) + String(Unicode.Scalar(0x0A))
484-
let input = "line1" + crlf + "line2"
485-
let actions = parse(input)
486-
#expect(actions.count == 1)
487-
if case .text(let s) = actions[0] { #expect(s.text == "line1line2") }
488-
}
489-
490419
@Test func separateCRAndLFProduceBothActions() {
491-
// When \r and \n arrive in separate parse() calls, they work as expected
492420
var parser = ANSIParser()
493421
let a1 = parser.parse("line1\r")
494422
let a2 = parser.parse("\nline2")
495-
// First parse: text("line1") + carriageReturn
496423
#expect(a1.count == 2)
497424
if case .text(let s) = a1[0] { #expect(s.text == "line1") }
498425
guard case .carriageReturn = a1[1] else {
499426
Issue.record("Expected .carriageReturn")
500427
return
501428
}
502-
// Second parse: newline + text("line2")
503429
#expect(a2.count == 2)
504430
guard case .newline = a2[0] else {
505431
Issue.record("Expected .newline")
@@ -519,60 +445,44 @@ struct ANSIParserTests {
519445
if case .text(let s) = actions[2] { #expect(s.text == "new") }
520446
}
521447

522-
// MARK: - Extended color edge cases
523-
524448
@Test func extendedColor38WithInsufficientParams() {
525-
// 38;5 without the color number -- should not crash
526449
let actions = parse("\u{1B}[38;5mX")
527450
let span = firstSpan(actions)
528-
// Parser should still produce text, foreground stays default
529451
#expect(span?.text == "X")
530452
}
531453

532454
@Test func extendedColor38ModeUnknown() {
533-
// 38;3 is not a recognized mode (only 2 and 5)
534455
let actions = parse("\u{1B}[38;3;100mX")
535456
let span = firstSpan(actions)
536457
#expect(span?.text == "X")
537-
// foreground should remain default since mode 3 is unrecognized
538458
#expect(span?.style.foreground == .default)
539459
}
540460

541461
@Test func truecolorWithInsufficientParams() {
542-
// 38;2;255;128 is missing the blue component
543462
let actions = parse("\u{1B}[38;2;255;128mX")
544463
let span = firstSpan(actions)
545464
#expect(span?.text == "X")
546-
// Should not crash; foreground stays default since insufficient params
547465
#expect(span?.style.foreground == .default)
548466
}
549467

550-
// MARK: - Unrecognized CSI final characters
551-
552468
@Test func unrecognizedCSIFinalCharIsIgnored() {
553-
// ESC[5J is a valid CSI form but 'J' (erase display) isn't handled
554469
let actions = parse("A\u{1B}[2JB")
555470
let spans = allSpans(actions)
556471
let combined = spans.map(\.text).joined()
557472
#expect(combined == "AB")
558473
}
559474

560-
// MARK: - Complex real-world sequences
561-
562475
@Test func npmColoredOutput() {
563-
// Simulate typical npm output with reset, bold, colors
564476
let input = "\u{1B}[1m\u{1B}[32m>\u{1B}[0m dev\n next dev"
565477
let actions = parse(input)
566478
let spans = allSpans(actions)
567479
#expect(spans.count >= 2)
568-
// First span should be bold + green ">"
569480
#expect(spans[0].style.bold == true)
570481
#expect(spans[0].style.foreground == .standard(2))
571482
#expect(spans[0].text == ">")
572483
}
573484

574485
@Test func progressBarWithCR() {
575-
// Simulate a progress bar that uses carriage return to overwrite
576486
let input = "Progress: 50%\rProgress: 100%"
577487
let actions = parse(input)
578488
#expect(actions.count == 3)

0 commit comments

Comments
 (0)