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) ?? [];
}