mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-22 05:59:31 +08:00
83 lines
3.3 KiB
JavaScript
83 lines
3.3 KiB
JavaScript
#!/usr/bin/env node
|
|
// Display widths for node:readline's terminal mode (scr_readline_tty.c),
|
|
// derived from the pinned Node release: getStringWidth (internal/util/inspect,
|
|
// backed by ICU) of each code point on its own, and the Emoji_Presentation
|
|
// and Emoji_Modifier code points, which getStringWidth skips after a ZWJ.
|
|
// kGetDisplayPos measures one code point at a time and the completion
|
|
// listing measures whole completions.
|
|
//
|
|
// node scripts/gen-readline-width-table.mjs regenerate the table
|
|
// node scripts/gen-readline-width-table.mjs --check verify only
|
|
import { spawnSync } from "node:child_process";
|
|
import { readFileSync } from "node:fs";
|
|
import { writeFile } from "node:fs/promises";
|
|
import { createRequire } from "node:module";
|
|
import { fileURLToPath } from "node:url";
|
|
import { join } from "node:path";
|
|
|
|
if (!process.execArgv.includes("--expose-internals")) {
|
|
const self = fileURLToPath(import.meta.url);
|
|
const run = spawnSync(process.execPath, ["--expose-internals", self, ...process.argv.slice(2)], {
|
|
stdio: "inherit",
|
|
});
|
|
process.exit(run.status ?? 1);
|
|
}
|
|
|
|
const root = fileURLToPath(new URL("..", import.meta.url));
|
|
const out = join(root, "packages/runtime/src/scr_readline_width_data.h");
|
|
const checkOnly = process.argv.includes("--check");
|
|
const pinned = readFileSync(join(root, ".node-version"), "utf8").trim();
|
|
if (process.version !== `v${pinned}`) {
|
|
throw new Error(`run with Node v${pinned} (this is ${process.version})`);
|
|
}
|
|
const { getStringWidth } = createRequire(import.meta.url)("internal/util/inspect");
|
|
|
|
// [start, end, width] for the code points whose width is not 1
|
|
const widths = [];
|
|
for (let cp = 0; cp <= 0x10ffff; cp++) {
|
|
const w = getStringWidth(String.fromCodePoint(cp), false);
|
|
const last = widths.at(-1);
|
|
if (w === 1) continue;
|
|
if (last && last[2] === w && last[1] === cp - 1) last[1] = cp;
|
|
else widths.push([cp, cp, w]);
|
|
}
|
|
const emoji = [];
|
|
const isEmoji = /^(?:\p{Emoji_Presentation}|\p{Emoji_Modifier})$/u;
|
|
for (let cp = 0; cp <= 0x10ffff; cp++) {
|
|
if (cp >= 0xd800 && cp <= 0xdfff) continue;
|
|
if (!isEmoji.test(String.fromCodePoint(cp))) continue;
|
|
const last = emoji.at(-1);
|
|
if (last && last[1] === cp - 1) last[1] = cp;
|
|
else emoji.push([cp, cp]);
|
|
}
|
|
|
|
const hex = (n) => `0x${n.toString(16)}`;
|
|
const lines = [
|
|
`/* Generated by scripts/gen-readline-width-table.mjs from Node ${process.version}`,
|
|
` * (ICU ${process.versions.icu}, Unicode ${process.versions.unicode}). Do not edit. */`,
|
|
"#ifndef SCR_READLINE_WIDTH_DATA_H",
|
|
"#define SCR_READLINE_WIDTH_DATA_H",
|
|
"/* getStringWidth(String.fromCodePoint(cp), false) where it is not 1 */",
|
|
"typedef struct { uint32_t start, end; uint8_t width; } ScrRlWidthRange;",
|
|
"static const ScrRlWidthRange scr_rl_width_ranges[] = {",
|
|
...widths.map(([a, b, w]) => ` { ${hex(a)}, ${hex(b)}, ${w} },`),
|
|
"};",
|
|
"/* Emoji_Presentation and Emoji_Modifier */",
|
|
"typedef struct { uint32_t start, end; } ScrRlEmojiRange;",
|
|
"static const ScrRlEmojiRange scr_rl_emoji_ranges[] = {",
|
|
...emoji.map(([a, b]) => ` { ${hex(a)}, ${hex(b)} },`),
|
|
"};",
|
|
"#endif",
|
|
];
|
|
const text = lines.join("\n") + "\n";
|
|
if (checkOnly) {
|
|
if (readFileSync(out, "utf8") !== text) {
|
|
console.error(`${out} is stale: run node scripts/gen-readline-width-table.mjs`);
|
|
process.exit(1);
|
|
}
|
|
console.log(`${out} is current`);
|
|
} else {
|
|
await writeFile(out, text);
|
|
console.log(`wrote ${out}`);
|
|
}
|