function escapeHtml(value: string): string { return value .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function splitTrailingComment(line: string): { body: string; comment: string | null } { let inSingle = false; let inDouble = false; for (let i = 0; i < line.length; i += 1) { const char = line[i]; const prev = i > 0 ? line[i - 1] : ''; if (char === "'" && !inDouble && prev !== '\\') inSingle = !inSingle; if (char === '"' && !inSingle && prev !== '\\') inDouble = !inDouble; if (char === '#' && !inSingle && !inDouble) { return { body: line.slice(0, i), comment: line.slice(i), }; } } return { body: line, comment: null }; } export function highlightYamlLine(line: string): string { const trimmed = line.trimStart(); if (trimmed.startsWith('#')) { return `${escapeHtml(line)}`; } const { body, comment } = splitTrailingComment(line); const bodyMatch = body.match(/^(\s*)(-\s+)?([^:#][^:]*?)(\s*:\s*)(.*)$/); let bodyHtml = escapeHtml(body); if (bodyMatch) { const [, indent = '', dash = '', key = '', separator = '', value = ''] = bodyMatch; bodyHtml = [ escapeHtml(indent), dash ? `${escapeHtml(dash)}` : '', `${escapeHtml(key)}`, `${escapeHtml(separator)}`, escapeHtml(value), ].join(''); } if (!comment) return bodyHtml; return `${bodyHtml}${escapeHtml(comment)}`; } export function highlightYamlLines(content: string | null): string[] { return content?.split('\n').map(highlightYamlLine) ?? []; }