mirror of
https://github.com/morluto/rea.git
synced 2026-11-03 13:26:10 +00:00
63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
import { analysisViewInputError } from "./analysisViewErrors.js";
|
|
import type { AnalysisError } from "../analysisErrorBase.js";
|
|
import { err, ok, type Result } from "../result.js";
|
|
import { z } from "zod";
|
|
import {
|
|
pageViewCoverage,
|
|
type AnalysisViewCoverage,
|
|
} from "./analysisViewCoverage.js";
|
|
|
|
/** Exact text pages use UTF-16 code-unit offsets without splitting surrogate pairs. */
|
|
export const analysisTextPageItemSchema = z.strictObject({
|
|
text: z.string(),
|
|
unit: z.literal("utf16-code-units"),
|
|
});
|
|
|
|
const viewError = (message: string): AnalysisError =>
|
|
analysisViewInputError([
|
|
{ path: ["view"], reason: "invalid_value", message },
|
|
]);
|
|
|
|
/** Slice text by UTF-16 code units without losing a partial Unicode character. */
|
|
export const analysisTextPage = (
|
|
body: string,
|
|
offset: number,
|
|
limit: number,
|
|
label = "Pseudocode",
|
|
): Result<
|
|
{
|
|
readonly item: Readonly<z.output<typeof analysisTextPageItemSchema>>;
|
|
readonly coverage: AnalysisViewCoverage;
|
|
},
|
|
AnalysisError
|
|
> => {
|
|
const start = Math.min(offset, body.length);
|
|
const high = (n: number) => n >= 0xd800 && n <= 0xdbff;
|
|
const low = (n: number) => n >= 0xdc00 && n <= 0xdfff;
|
|
if (
|
|
start > 0 &&
|
|
start < body.length &&
|
|
high(body.charCodeAt(start - 1)) &&
|
|
low(body.charCodeAt(start))
|
|
)
|
|
return err(viewError(`${label} offset splits a UTF-16 surrogate pair.`));
|
|
let end = Math.min(start + limit, body.length);
|
|
if (
|
|
end > start &&
|
|
end < body.length &&
|
|
high(body.charCodeAt(end - 1)) &&
|
|
low(body.charCodeAt(end))
|
|
)
|
|
end--;
|
|
if (end === start && start < body.length)
|
|
return err(
|
|
viewError(
|
|
`Increase ${label.toLowerCase()} limit to include the next complete Unicode character.`,
|
|
),
|
|
);
|
|
return ok({
|
|
item: { text: body.slice(start, end), unit: "utf16-code-units" },
|
|
coverage: pageViewCoverage(start, end - start, body.length),
|
|
});
|
|
};
|