























@@ -13,11 +13,17 @@ const URL_PREFIX_RE = /^(https?:\/\/|file:\/\/)/i;
1313const WINDOWS_DRIVE_RE = /^[a-zA-Z]:[\\/]/;
1414const FILE_LIKE_RE = /^[a-zA-Z0-9._-]+$/;
1515const EDGE_PUNCTUATION_RE = /^[`"'([{<]+|[`"')\]}>.,:;!?]+$/g;
16+const ALPHANUMERIC_RE = /[A-Za-z0-9]/;
1617const TOKENISH_MIN_LENGTH = 24;
1718const RTL_SCRIPT_RE = /[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/;
1819const BIDI_CONTROL_RE = /[\u202a-\u202e\u2066-\u2069]/;
1920const RTL_ISOLATE_START = "\u2067";
2021const RTL_ISOLATE_END = "\u2069";
22+// Fenced code blocks (``` or ~~~). Lazy on content; tolerates info string after
23+// the opening fence. Closing fence must sit on its own line.
24+const FENCED_CODE_RE = /(```|~~~)[^\n]*\n[\s\S]*?\n\1[^\n]*/g;
25+// Inline code spans with balanced backtick run (`code`, ``co`de``, ...).
26+const INLINE_CODE_RE = /(`+)(?:(?!\1).)+?\1/g;
21272228function hasControlChars(text: string): boolean {
2329for (const char of text) {
@@ -62,24 +68,29 @@ function isCopySensitiveToken(token: string): boolean {
6268const coreToken = token.replace(EDGE_PUNCTUATION_RE, "");
6369const candidate = coreToken || token;
647065-if (URL_PREFIX_RE.test(token)) {
71+if (URL_PREFIX_RE.test(candidate)) {
6672return true;
6773}
6874if (
69-token.startsWith("/") ||
70-token.startsWith("~/") ||
71-token.startsWith("./") ||
72-token.startsWith("../")
75+candidate.startsWith("/") ||
76+candidate.startsWith("~/") ||
77+candidate.startsWith("./") ||
78+candidate.startsWith("../")
7379) {
7480return true;
7581}
76-if (WINDOWS_DRIVE_RE.test(token) || token.startsWith("\\\\")) {
82+if (WINDOWS_DRIVE_RE.test(candidate) || candidate.startsWith("\\\\")) {
7783return true;
7884}
79-if (token.includes("/") || token.includes("\\")) {
85+if (candidate.includes("/") || candidate.includes("\\")) {
8086return true;
8187}
82-if (token.includes("_") && FILE_LIKE_RE.test(token)) {
88+// Identifiers that look file-like, dotted, or hyphen/underscore-separated:
89+// package names, entity IDs, kebab/snake CLI flags, dotted module paths.
90+if (
91+FILE_LIKE_RE.test(candidate) &&
92+(candidate.includes("_") || candidate.includes("-") || candidate.includes("."))
93+) {
8394return true;
8495}
8596@@ -96,9 +107,50 @@ function normalizeLongTokenForDisplay(token: string): string {
96107if (isCopySensitiveToken(token)) {
97108return token;
98109}
110+// Pure symbol/punctuation runs (table borders made of `─`, `=`, `-`) carry
111+// no copyable identifier; chunking would corrupt the visible structure.
112+if (!ALPHANUMERIC_RE.test(token)) {
113+return token;
114+}
99115return chunkToken(token, MAX_TOKEN_CHARS).join(" ");
100116}
101117118+type Segment = { kind: "prose" | "code"; text: string };
119+120+function partitionByRegex(text: string, re: RegExp): Segment[] {
121+const parts: Segment[] = [];
122+let lastIndex = 0;
123+for (const match of text.matchAll(re)) {
124+const start = match.index ?? 0;
125+if (start > lastIndex) {
126+parts.push({ kind: "prose", text: text.slice(lastIndex, start) });
127+}
128+parts.push({ kind: "code", text: match[0] });
129+lastIndex = start + match[0].length;
130+}
131+if (lastIndex < text.length) {
132+parts.push({ kind: "prose", text: text.slice(lastIndex) });
133+}
134+return parts;
135+}
136+137+// Apply `transform` only to spans of `text` that are not inside fenced code
138+// blocks or inline code spans. Code regions pass through verbatim so long
139+// identifiers, dotted IDs, package names, and shell line-continuations the
140+// user may copy stay byte-for-byte intact.
141+function transformOutsideCode(text: string, transform: (segment: string) => string): string {
142+const fenced = partitionByRegex(text, FENCED_CODE_RE);
143+return fenced
144+.map((seg) => {
145+if (seg.kind === "code") {
146+return seg.text;
147+}
148+const inline = partitionByRegex(seg.text, INLINE_CODE_RE);
149+return inline.map((s) => (s.kind === "code" ? s.text : transform(s.text))).join("");
150+})
151+.join("");
152+}
153+102154function redactBinaryLikeLine(line: string): string {
103155const replacementCount = (line.match(REPLACEMENT_CHAR_RE) || []).length;
104156if (
@@ -149,7 +201,11 @@ export function sanitizeRenderableText(text: string): string {
149201.join("\n")
150202 : withoutControlChars;
151203const tokenSafe = LONG_TOKEN_TEST_RE.test(redacted)
152- ? redacted.replace(LONG_TOKEN_RE, normalizeLongTokenForDisplay)
204+ ? transformOutsideCode(redacted, (segment) =>
205+LONG_TOKEN_TEST_RE.test(segment)
206+ ? segment.replace(LONG_TOKEN_RE, normalizeLongTokenForDisplay)
207+ : segment,
208+)
153209 : redacted;
154210return applyRtlIsolation(tokenSafe);
155211}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。