fix: bad width calculations when characters are wide (#212)

* fix: bad width calculations when characters are wide

Signed-off-by: Chapman Pendery <cpendery@vt.edu>

* fix: typo

Signed-off-by: Chapman Pendery <cpendery@vt.edu>

---------

Signed-off-by: Chapman Pendery <cpendery@vt.edu>
This commit is contained in:
Chapman Pendery
2024-03-18 20:28:58 -07:00
committed by GitHub
parent 054bd00510
commit 9db5beea23
2 changed files with 38 additions and 5 deletions
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { truncateText } from "../../ui/utils";
describe("truncateText", () => {
test("handling chinese wide characters", () => {
expect(truncateText("美国人", 10)).toBe("美国人 ");
});
test("truncates too long wide characters when exact", () => {
expect(truncateText("美国人 人人", 10)).toBe("美国人 人…");
});
test("truncates too long wide characters when split", () => {
expect(truncateText("美国人美国人", 10)).toBe("美国人美… ");
});
});
+20 -5
View File
@@ -4,7 +4,7 @@
import ansi from "ansi-escapes";
import wrapAnsi from "wrap-ansi";
import chalk from "chalk";
import wcwdith from "wcwidth";
import wcwidth from "wcwidth";
/**
* Renders a box around the given rows
@@ -36,12 +36,27 @@ export const truncateMultilineText = (description: string, width: number, maxHei
return truncatedLines.map((line) => line.padEnd(width));
};
const wcPadEnd = (text: string, width: number, char = " "): string => text + char.repeat(Math.max(width - wcwidth(text), 0));
const wcPoints = (text: string, length: number): [string, boolean] => {
const points = [...text];
const accPoints = [];
let accWidth = 0;
for (const point of points) {
const width = wcwidth(point);
if (width + accWidth > length) {
return wcwidth(accPoints.join("")) < length ? [accPoints.join(""), true] : [accPoints.slice(0, -1).join(""), true];
}
accPoints.push(point);
accWidth += width;
}
return [accPoints.join(""), false];
};
/**
* Truncates the text to the given width
*/
export const truncateText = (text: string, width: number) => {
const textPoints = [...text];
const wcOffset = Math.max(wcwdith(text) - textPoints.length, 0);
const slicedText = textPoints.slice(0, width - 1 - wcOffset);
return slicedText.length == textPoints.length ? text.padEnd(width) : (slicedText.join("") + "…").padEnd(width);
const [points, truncated] = wcPoints(text, width);
return !truncated ? wcPadEnd(text, width) : wcPadEnd(points + "…", width);
};