Skip to content

Commit baaa9a7

Browse files
committed
terminal/sagas: fix handling multibyte unicode
Enable the stream option on the text decode for the stdout and uart stream handlers that pipe data to the terminal. This fixes improperly printing multibyte unicode characters that get split across multiple data packets. A new decode is added for each different stream in case both are used at the same time. Fixes: pybricks/support#1743
1 parent a308303 commit baaa9a7

File tree

3 files changed

+18
-6
lines changed

3 files changed

+18
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66

77
### Fixed
88
- Fixed Bluetooth firmware updates sometimes failing on macOS. ([support#1787])
9+
- Fixed multibyte unicode characters not printing correctly in terminal when
10+
split across Bluetooth packets. ([support#1743])
911

10-
[[support#1787]]: https://github.com/pybricks/support/issues/1787
12+
[support#1743]: https://github.com/pybricks/support/issues/1743
13+
[support#1787]: https://github.com/pybricks/support/issues/1787
1114

1215
## [2.3.0-beta.1] - 2023-11-24
1316

src/terminal/sagas.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2020-2023 The Pybricks Authors
2+
// Copyright (c) 2020-2024 The Pybricks Authors
33

44
import PushStream from 'zen-push';
55
import { AsyncSaga, delay } from '../../test';
@@ -86,6 +86,14 @@ describe('receiving stdout from hub', () => {
8686

8787
await expect(saga.take()).resolves.toEqual(sendData(' '));
8888

89+
// ensure that unicode characters are handled correctly when split
90+
// across buffers
91+
92+
saga.put(didReceiveWriteStdout(new Uint8Array([0xe4]).buffer));
93+
await expect(saga.take()).resolves.toEqual(sendData(''));
94+
saga.put(didReceiveWriteStdout(new Uint8Array([0xb8, 0xad]).buffer));
95+
await expect(saga.take()).resolves.toEqual(sendData('中'));
96+
8997
await saga.end();
9098
});
9199
});

src/terminal/sagas.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2020-2023 The Pybricks Authors
2+
// Copyright (c) 2020-2024 The Pybricks Authors
33

44
import { AnyAction } from 'redux';
55
import {
@@ -39,7 +39,8 @@ import { receiveData, sendData } from './actions';
3939
export type TerminalSagaContext = { terminal: TerminalContextValue };
4040

4141
const encoder = new TextEncoder();
42-
const decoder = new TextDecoder();
42+
const uartDecoder = new TextDecoder();
43+
const stdoutDecoder = new TextDecoder();
4344

4445
function* receiveUartData(action: ReturnType<typeof didNotify>): Generator {
4546
const { runtime: hubState, useLegacyStdio } = yield* select(
@@ -56,14 +57,14 @@ function* receiveUartData(action: ReturnType<typeof didNotify>): Generator {
5657
return;
5758
}
5859

59-
const value = decoder.decode(action.value.buffer);
60+
const value = uartDecoder.decode(action.value.buffer, { stream: true });
6061
yield* put(sendData(value));
6162
}
6263

6364
function* handleReceiveWriteStdout(
6465
action: ReturnType<typeof didReceiveWriteStdout>,
6566
): Generator {
66-
const value = decoder.decode(action.payload);
67+
const value = stdoutDecoder.decode(action.payload, { stream: true });
6768
yield* put(sendData(value));
6869
}
6970

0 commit comments

Comments
 (0)