refactor(natives): removed sanitizeText from pi-natives, use JS impl
- Moved @oh-my-pi/pi-natives from devDependencies to dependencies in pi-utils. - Deleted Rust sanitizeText implementation and its JS bindings/types. - Updated sanitize benchmark to use pi-utils implementation as baseline.
This commit is contained in:
@@ -180,13 +180,13 @@
|
||||
"name": "@oh-my-pi/pi-utils",
|
||||
"version": "15.1.2",
|
||||
"dependencies": {
|
||||
"@oh-my-pi/pi-natives": "catalog:",
|
||||
"beautiful-mermaid": "catalog:",
|
||||
"handlebars": "catalog:",
|
||||
"winston": "catalog:",
|
||||
"winston-daily-rotate-file": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@oh-my-pi/pi-natives": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1263,89 +1263,6 @@ pub fn extract_segments(
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// sanitizeText
|
||||
// ============================================================================
|
||||
|
||||
/// Strip ANSI escape sequences, remove control characters / lone surrogates,
|
||||
/// and normalize line endings.
|
||||
#[napi]
|
||||
pub fn sanitize_text(text: JsString<'_>) -> Result<Either<JsString<'_>, Utf16String>> {
|
||||
let original = text;
|
||||
let text_u16 = text.into_utf16()?;
|
||||
let data = text_u16.as_slice();
|
||||
|
||||
let mut did_change = false;
|
||||
let mut out: Vec<u16> = Vec::new();
|
||||
let mut last = 0usize;
|
||||
let mut i = 0usize;
|
||||
let len = data.len();
|
||||
|
||||
while i < len {
|
||||
let u = data[i];
|
||||
|
||||
// Allow tab + newline; normalize CR by removing it.
|
||||
if u == 0x09 || u == 0x0a {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut remove_len = if u == ESC
|
||||
&& let Some(seq_len) = ansi_seq_len_u16(data, i)
|
||||
{
|
||||
seq_len
|
||||
} else {
|
||||
0usize
|
||||
};
|
||||
|
||||
if remove_len == 0 {
|
||||
// Drop CR to normalize line endings.
|
||||
if u == 0x0d {
|
||||
remove_len = 1;
|
||||
} else if u <= 0x1f || u == 0x7f || (0x80..=0x9f).contains(&u) {
|
||||
// C0 + DEL + C1 controls.
|
||||
remove_len = 1;
|
||||
} else if (0xd800..=0xdbff).contains(&u) {
|
||||
// High surrogate: keep only if followed by a valid low surrogate.
|
||||
if i + 1 < len {
|
||||
let lo = data[i + 1];
|
||||
if (0xdc00..=0xdfff).contains(&lo) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
remove_len = 1;
|
||||
} else if (0xdc00..=0xdfff).contains(&u) {
|
||||
// Lone low surrogate.
|
||||
remove_len = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if remove_len == 0 {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if !did_change {
|
||||
did_change = true;
|
||||
out = Vec::with_capacity(len);
|
||||
}
|
||||
if last != i {
|
||||
out.extend_from_slice(&data[last..i]);
|
||||
}
|
||||
i += remove_len;
|
||||
last = i;
|
||||
}
|
||||
|
||||
if !did_change {
|
||||
return Ok(Either::A(original));
|
||||
}
|
||||
if last < len {
|
||||
out.extend_from_slice(&data[last..]);
|
||||
}
|
||||
Ok(Either::B(build_utf16_string(out)))
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// visibleWidth
|
||||
// ============================================================================
|
||||
|
||||
Vendored
-6
@@ -1154,12 +1154,6 @@ export interface PtyStartOptions {
|
||||
*/
|
||||
export declare function readImageFromClipboard(): Promise<ClipboardImage | undefined | null>
|
||||
|
||||
/**
|
||||
* Strip ANSI escape sequences, remove control characters / lone surrogates,
|
||||
* and normalize line endings.
|
||||
*/
|
||||
export declare function sanitizeText(text: string): string
|
||||
|
||||
/**
|
||||
* Search content for a pattern (one-shot, compiles pattern each time).
|
||||
* For repeated searches with the same pattern, use [`grep`] with file filters.
|
||||
|
||||
@@ -56,7 +56,6 @@ export const matchesLegacySequence = nativeBindings.matchesLegacySequence;
|
||||
export const parseKey = nativeBindings.parseKey;
|
||||
export const parseKittySequence = nativeBindings.parseKittySequence;
|
||||
export const readImageFromClipboard = nativeBindings.readImageFromClipboard;
|
||||
export const sanitizeText = nativeBindings.sanitizeText;
|
||||
export const search = nativeBindings.search;
|
||||
export const sliceWithWidth = nativeBindings.sliceWithWidth;
|
||||
export const summarizeCode = nativeBindings.summarizeCode;
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
listWorkspace,
|
||||
MacOSPowerAssertion,
|
||||
PtySession,
|
||||
sanitizeText,
|
||||
summarizeCode,
|
||||
truncateToWidth,
|
||||
visibleWidth,
|
||||
@@ -584,28 +583,11 @@ describe("pi-natives", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("sanitizeText", () => {
|
||||
it("should strip ANSI, remove control chars and normalize CR", () => {
|
||||
const input = "\x1b[31mred\x1b[0m\ra\u0000b\tline\ncarriage\r\u0001\u0085";
|
||||
expect(sanitizeText(input)).toBe("redab\tline\ncarriage");
|
||||
});
|
||||
|
||||
it("should remove lone surrogates but keep valid pairs", () => {
|
||||
expect(sanitizeText(`a\ud800b\udc00c`)).toBe("abc");
|
||||
const validPair = "a\u{1f600}b";
|
||||
expect(sanitizeText(validPair)).toBe(validPair);
|
||||
});
|
||||
|
||||
it("should strip OSC sequences", () => {
|
||||
const input = "\x1b]0;title\x07hello";
|
||||
expect(sanitizeText(input)).toBe("hello");
|
||||
});
|
||||
describe("MacOSPowerAssertion", () => {
|
||||
it("should create a stoppable power assertion handle", () => {
|
||||
const assertion = MacOSPowerAssertion.start({ reason: "pi-natives test" });
|
||||
assertion.stop();
|
||||
assertion.stop();
|
||||
});
|
||||
describe("MacOSPowerAssertion", () => {
|
||||
it("should create a stoppable power assertion handle", () => {
|
||||
const assertion = MacOSPowerAssertion.start({ reason: "pi-natives test" });
|
||||
assertion.stop();
|
||||
assertion.stop();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { sanitizeText as nativeSanitizeText } from "@oh-my-pi/pi-natives";
|
||||
import { sanitizeText as currentSanitizeText } from "@oh-my-pi/pi-utils/sanitize-text";
|
||||
|
||||
const STRIP_RE = new RegExp(
|
||||
@@ -313,39 +312,35 @@ console.log(`Text layout benchmark (${ITERATIONS} iterations)\n`);
|
||||
|
||||
for (const name in samples) {
|
||||
const text = samples[name as keyof typeof samples];
|
||||
const baseline = currentSanitizeText(text);
|
||||
const jsResult = jsSanitizeText(text);
|
||||
const nativeResult = nativeSanitizeText(text);
|
||||
const currentResult = currentSanitizeText(text);
|
||||
const regexResult = regexSanitizeText(text);
|
||||
if (jsResult !== nativeResult) {
|
||||
console.log(`MISMATCH js/native ${name}`);
|
||||
if (jsResult !== baseline) {
|
||||
console.log(`MISMATCH js/current ${name}`);
|
||||
}
|
||||
if (currentResult !== nativeResult) {
|
||||
console.log(`MISMATCH current/native ${name}: current=${JSON.stringify(currentResult)} native=${JSON.stringify(nativeResult)}`);
|
||||
}
|
||||
if (regexResult !== nativeResult) {
|
||||
console.log(`MISMATCH regex/native ${name}: regex=${JSON.stringify(regexResult)} native=${JSON.stringify(nativeResult)}`);
|
||||
if (regexResult !== baseline) {
|
||||
console.log(`MISMATCH regex/current ${name}: regex=${JSON.stringify(regexResult)} baseline=${JSON.stringify(baseline)}`);
|
||||
}
|
||||
const gatedResult = gatedSanitizeText(text);
|
||||
const skipResult = skipRunSanitizeText(text);
|
||||
const removalStartResult = removalStartSanitizeText(text);
|
||||
const wellFormedControlResult = wellFormedControlSanitizeText(text);
|
||||
const lazyWellFormedResult = lazyWellFormedSanitizeText(text);
|
||||
if (gatedResult !== nativeResult) {
|
||||
console.log(`MISMATCH gated/native ${name}`);
|
||||
const removalStartResult = removalStartSanitizeText(text);
|
||||
const wellFormedControlResult = wellFormedControlSanitizeText(text);
|
||||
const lazyWellFormedResult = lazyWellFormedSanitizeText(text);
|
||||
if (gatedResult !== baseline) {
|
||||
console.log(`MISMATCH gated/current ${name}`);
|
||||
}
|
||||
if (skipResult !== nativeResult) {
|
||||
console.log(`MISMATCH skip/native ${name}: skip=${JSON.stringify(skipResult)} native=${JSON.stringify(nativeResult)}`);
|
||||
if (skipResult !== baseline) {
|
||||
console.log(`MISMATCH skip/current ${name}: skip=${JSON.stringify(skipResult)} baseline=${JSON.stringify(baseline)}`);
|
||||
}
|
||||
if (removalStartResult !== baseline) {
|
||||
console.log(`MISMATCH removalStart/current ${name}: removalStart=${JSON.stringify(removalStartResult)} baseline=${JSON.stringify(baseline)}`);
|
||||
}
|
||||
if (wellFormedControlResult !== baseline) {
|
||||
console.log(`MISMATCH wellFormedControl/current ${name}: wellFormedControl=${JSON.stringify(wellFormedControlResult)} baseline=${JSON.stringify(baseline)}`);
|
||||
}
|
||||
if (lazyWellFormedResult !== baseline) {
|
||||
console.log(`MISMATCH lazyWellFormed/current ${name}: lazyWellFormed=${JSON.stringify(lazyWellFormedResult)} baseline=${JSON.stringify(baseline)}`);
|
||||
}
|
||||
if (removalStartResult !== nativeResult) {
|
||||
console.log(`MISMATCH removalStart/native ${name}: removalStart=${JSON.stringify(removalStartResult)} native=${JSON.stringify(nativeResult)}`);
|
||||
}
|
||||
if (wellFormedControlResult !== nativeResult) {
|
||||
console.log(`MISMATCH wellFormedControl/native ${name}: wellFormedControl=${JSON.stringify(wellFormedControlResult)} native=${JSON.stringify(nativeResult)}`);
|
||||
}
|
||||
if (lazyWellFormedResult !== nativeResult) {
|
||||
console.log(`MISMATCH lazyWellFormed/native ${name}: lazyWellFormed=${JSON.stringify(lazyWellFormedResult)} native=${JSON.stringify(nativeResult)}`);
|
||||
}
|
||||
|
||||
bench(`jsSanitizeText/${name}`, () => {
|
||||
jsSanitizeText(text);
|
||||
@@ -362,17 +357,14 @@ for (const name in samples) {
|
||||
bench(`skipRunSanitizeText/${name}`, () => {
|
||||
skipRunSanitizeText(text);
|
||||
});
|
||||
bench(`removalStartSanitizeText/${name}`, () => {
|
||||
removalStartSanitizeText(text);
|
||||
});
|
||||
bench(`wellFormedControlSanitizeText/${name}`, () => {
|
||||
wellFormedControlSanitizeText(text);
|
||||
});
|
||||
bench(`lazyWellFormedSanitizeText/${name}`, () => {
|
||||
lazyWellFormedSanitizeText(text);
|
||||
});
|
||||
bench(`nativeSanitizeText/${name}`, () => {
|
||||
nativeSanitizeText(text);
|
||||
bench(`removalStartSanitizeText/${name}`, () => {
|
||||
removalStartSanitizeText(text);
|
||||
});
|
||||
bench(`wellFormedControlSanitizeText/${name}`, () => {
|
||||
wellFormedControlSanitizeText(text);
|
||||
});
|
||||
bench(`lazyWellFormedSanitizeText/${name}`, () => {
|
||||
lazyWellFormedSanitizeText(text);
|
||||
});
|
||||
console.log();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user