| |
| |
| |
| |
|
|
| function escapeHtmlImpl(text: string): string { |
| const div = document.createElement('div'); |
| div.textContent = text; |
| return div.innerHTML; |
| } |
|
|
| function isWhitespaceChar(char: string): boolean { |
| return /\p{White_Space}/u.test(char); |
| } |
|
|
| function isPrintableChar(char: string): boolean { |
| if (isWhitespaceChar(char)) return false; |
| const codePoint = char.codePointAt(0); |
| if (codePoint === undefined) return false; |
| if (codePoint >= 32 && codePoint <= 126) return true; |
| if ( |
| (codePoint >= 0x00A0 && codePoint <= 0x00FF) || |
| (codePoint >= 0x0100 && codePoint <= 0x017F) || |
| (codePoint >= 0x0180 && codePoint <= 0x024F) || |
| (codePoint >= 0x2000 && codePoint <= 0x206F) || |
| (codePoint >= 0x2070 && codePoint <= 0x209F) || |
| (codePoint >= 0x20A0 && codePoint <= 0x20CF) || |
| (codePoint >= 0x2100 && codePoint <= 0x214F) || |
| (codePoint >= 0x2190 && codePoint <= 0x21FF) || |
| (codePoint >= 0x2200 && codePoint <= 0x22FF) || |
| (codePoint >= 0x2300 && codePoint <= 0x23FF) || |
| (codePoint >= 0x2400 && codePoint <= 0x243F) || |
| (codePoint >= 0x2E00 && codePoint <= 0x2E7F) || |
| (codePoint >= 0x3000 && codePoint <= 0x303F) || |
| (codePoint >= 0x3040 && codePoint <= 0x309F) || |
| (codePoint >= 0x30A0 && codePoint <= 0x30FF) || |
| (codePoint >= 0x4E00 && codePoint <= 0x9FFF) || |
| (codePoint >= 0xAC00 && codePoint <= 0xD7AF) || |
| (codePoint >= 0xF900 && codePoint <= 0xFAFF) || |
| (codePoint >= 0xFF00 && codePoint <= 0xFFEF) |
| ) return true; |
| return false; |
| } |
|
|
| |
| export type VisualizeSpecialCharsOptions = { |
| |
| |
| |
| |
| |
| spaceDotExceptBeforeAsciiLetterOrNumber?: boolean; |
| }; |
|
|
| function visualizeSpecialCharsImpl(text: string, options?: VisualizeSpecialCharsOptions): string { |
| let result = text |
| .replace(/\r\n/g, '[CRLF]') |
| .replace(/\n/g, '[LF]') |
| .replace(/\r/g, '[CR]') |
| .replace(/\t/g, '[TAB]') |
| .replace(/\u3000/g, '[FS]'); |
| if (options?.spaceDotExceptBeforeAsciiLetterOrNumber === true) { |
| |
| result = result.replace(/ (?![A-Za-z0-9])/g, '·'); |
| } else { |
| result = result.replace(/ /g, '·'); |
| } |
|
|
| const processed: string[] = []; |
| let inBracket = false; |
|
|
| for (let i = 0; i < result.length; i++) { |
| const char = result[i]; |
| if (char === '[') { |
| inBracket = true; |
| processed.push(char); |
| } else if (char === ']' && inBracket) { |
| processed.push(char); |
| inBracket = false; |
| } else if (inBracket) { |
| processed.push(char); |
| } else { |
| |
| if (char === ' ') { |
| processed.push(char); |
| } else if (isPrintableChar(char)) { |
| processed.push(char); |
| } else { |
| const codePoint = char.codePointAt(0); |
| if (codePoint !== undefined) { |
| const hexCode = codePoint.toString(16).toUpperCase().padStart(4, '0'); |
| processed.push(`[U+${hexCode}]`); |
| } else { |
| processed.push(char); |
| } |
| } |
| } |
| } |
| return processed.join(''); |
| } |
|
|
| |
| export function processCandidateText(text: string): string { |
| return escapeHtmlImpl(visualizeSpecialCharsImpl(text)); |
| } |
|
|
| |
| |
| |
| |
| export function tooltipTokenDisplayHtml(text: string): string { |
| return processCandidateText(text); |
| } |
|
|
| |
| export function escapeHtml(text: string): string { |
| return escapeHtmlImpl(text); |
| } |
|
|
| |
| export function visualizeSpecialChars(text: string, options?: VisualizeSpecialCharsOptions): string { |
| return visualizeSpecialCharsImpl(text, options); |
| } |
|
|