fix(tools): make fence-language fixer aware of outer fences (avoid touching nested triple backticks) and add safety for unmatched fences
This commit is contained in:
parent
15c37fd120
commit
9dfe22151c
|
|
@ -89,8 +89,14 @@ function fixFile(filePath) {
|
||||||
|
|
||||||
const fixes = [];
|
const fixes = [];
|
||||||
let modified = false;
|
let modified = false;
|
||||||
let inFence = false;
|
|
||||||
let fenceStart = -1;
|
// Track any outer fence (of any backtick length >=3) to avoid touching nested content
|
||||||
|
const fenceStack = [];
|
||||||
|
|
||||||
|
// State for a target triple-backtick fence without language that we intend to fix
|
||||||
|
let fixing = false;
|
||||||
|
let fixFenceStart = -1;
|
||||||
|
let fixOpenIndent = '';
|
||||||
let fenceContent = [];
|
let fenceContent = [];
|
||||||
|
|
||||||
const newLines = [];
|
const newLines = [];
|
||||||
|
|
@ -98,54 +104,91 @@ function fixFile(filePath) {
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
|
|
||||||
// Detect fence start
|
// If we are currently fixing a fence (collecting content until closing ```)
|
||||||
if (/^\s*```\s*$/.test(line)) {
|
if (fixing) {
|
||||||
if (!inFence) {
|
const closeMatch = line.match(/^(\s*)(`{3})(\s*)$/);
|
||||||
// Opening fence without language
|
if (closeMatch) {
|
||||||
inFence = true;
|
// Closing the target fence
|
||||||
fenceStart = i;
|
|
||||||
fenceContent = [];
|
|
||||||
|
|
||||||
// We'll hold this line and potentially modify it
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
// Closing fence
|
|
||||||
const language = detectLanguage(fenceContent.join('\n'));
|
const language = detectLanguage(fenceContent.join('\n'));
|
||||||
|
const fixedOpenLine = `${fixOpenIndent}\`\`\`${language}`;
|
||||||
|
|
||||||
// Add the opening fence with detected language
|
|
||||||
const indent = lines[fenceStart].match(/^(\s*)/)[1];
|
|
||||||
const fixedOpenLine = `${indent}\`\`\`${language}`;
|
|
||||||
newLines.push(fixedOpenLine);
|
newLines.push(fixedOpenLine);
|
||||||
|
|
||||||
// Add the content
|
|
||||||
newLines.push(...fenceContent);
|
newLines.push(...fenceContent);
|
||||||
|
|
||||||
// Add the closing fence
|
|
||||||
newLines.push(line);
|
newLines.push(line);
|
||||||
|
|
||||||
fixes.push({
|
fixes.push({
|
||||||
line: fenceStart + 1,
|
line: fixFenceStart + 1,
|
||||||
original: lines[fenceStart],
|
original: '```',
|
||||||
fixed: fixedOpenLine,
|
fixed: fixedOpenLine,
|
||||||
detectedLanguage: language,
|
detectedLanguage: language,
|
||||||
contentPreview: fenceContent.slice(0, 2).join('\n').substring(0, 60) + '...'
|
contentPreview: fenceContent.slice(0, 2).join('\n').substring(0, 60) + '...'
|
||||||
});
|
});
|
||||||
|
|
||||||
modified = true;
|
modified = true;
|
||||||
inFence = false;
|
fixing = false;
|
||||||
fenceStart = -1;
|
fixFenceStart = -1;
|
||||||
|
fixOpenIndent = '';
|
||||||
fenceContent = [];
|
fenceContent = [];
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
fenceContent.push(line);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inFence) {
|
// Not currently fixing; detect any fence line (opening or closing)
|
||||||
// Collecting fence content
|
const fenceLineMatch = line.match(/^(\s*)(`{3,})(.*)$/);
|
||||||
fenceContent.push(line);
|
if (fenceLineMatch) {
|
||||||
} else {
|
const indent = fenceLineMatch[1] || '';
|
||||||
// Regular line outside fence
|
const ticks = fenceLineMatch[2] || '';
|
||||||
|
const rest = (fenceLineMatch[3] || '').trim();
|
||||||
|
const hasLanguage = rest.length > 0; // simplistic but effective for our cases
|
||||||
|
|
||||||
|
// Determine if this is a closing fence for the current outer fence
|
||||||
|
if (fenceStack.length > 0 && fenceStack[fenceStack.length - 1].ticks === ticks) {
|
||||||
|
// Closing existing fence scope
|
||||||
|
fenceStack.pop();
|
||||||
|
newLines.push(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If inside any outer fence, don't attempt to fix nested fences
|
||||||
|
if (fenceStack.length > 0) {
|
||||||
|
newLines.push(line);
|
||||||
|
// Start a nested fence scope if this appears to be an opening fence
|
||||||
|
fenceStack.push({ ticks });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outside any fence
|
||||||
|
if (ticks === '```' && !hasLanguage) {
|
||||||
|
// Target: opening triple backtick without language; begin fixing mode
|
||||||
|
fixing = true;
|
||||||
|
fixFenceStart = i;
|
||||||
|
fixOpenIndent = indent;
|
||||||
|
fenceContent = [];
|
||||||
|
// Do not push the original opening line; we'll emit the fixed one at close
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any other fence (with language or more backticks): treat as an outer fence start
|
||||||
|
fenceStack.push({ ticks });
|
||||||
|
newLines.push(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular non-fence line
|
||||||
newLines.push(line);
|
newLines.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we ended while "fixing" and never saw a closing fence, abort changes for safety
|
||||||
|
if (fixing) {
|
||||||
|
return {
|
||||||
|
filePath,
|
||||||
|
fixes: [],
|
||||||
|
modified: false,
|
||||||
|
newContent: content
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue