Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/jsc/bindings/wrapAnsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ static std::optional<uint32_t> getCloseCode(uint32_t code)
return 28;
case 9:
return 29;
case 53:
return 55;
}

if (code >= 30 && code <= 37)
Expand Down Expand Up @@ -511,8 +513,9 @@ static void joinRowsWithAnsiPreservation(const Vector<Row<Char>>& rows, StringBu
}
}
} else if (c == '\n') {
// Restore styles after newline
if (escapeCode) {
// Restore styles after newline (only open codes; close/unknown codes
// have no close mapping and are not re-emitted, matching npm wrap-ansi)
if (escapeCode && getCloseCode(*escapeCode)) {
Comment thread
robobun marked this conversation as resolved.
result.append("\x1b["_s);
result.append(String::number(*escapeCode));
result.append('m');
Expand Down
25 changes: 25 additions & 0 deletions test/js/bun/util/wrapAnsi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,31 @@ describe("Bun.wrapAnsi", () => {
// "ab" is 2 chars, should fit in width 2
expect(Bun.wrapAnsi(input, 2)).toBe(input);
});

// SGR close codes (22-29, 49, 55) and unknown codes have no close mapping
// in ansi-styles' codes map, so npm wrap-ansi never re-emits them after a
// line break. Only open codes with a known close code are closed-then-reopened.
test.each([22, 23, 24, 25, 27, 28, 29, 49, 55, 39, 0, 200])(
"does not re-open SGR close/unknown code %p after line break",
code => {
expect(Bun.wrapAnsi(`\x1b[${code}mabc def`, 3)).toBe(`\x1b[${code}mabc\ndef`);
},
);

test.each([
[1, 22],
[4, 24],
[31, 39],
[42, 49],
[53, 55],
[100, 49],
])("re-opens SGR open code %p after line break", (open, close) => {
expect(Bun.wrapAnsi(`\x1b[${open}mabc def`, 3)).toBe(`\x1b[${open}mabc\x1b[${close}m\n\x1b[${open}mdef`);
});

test("close code following an open code is not carried across line break", () => {
expect(Bun.wrapAnsi("\x1b[42mab\x1b[49mcd ef", 4)).toBe("\x1b[42mab\x1b[49mcd\nef");
});
});

describe("Unicode support", () => {
Expand Down
Loading