YAML: Stack-Fix, best-effort Output auch bei nicht-parsebarem YAML
This commit is contained in:
@@ -120,31 +120,40 @@ function collectPaths(data, path='root', paths=[]) {
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── YAML Auto-Fix: normalisiert Einrückung bevor js-yaml parst ───────────────
|
// ── YAML Auto-Fix: Stack-basierter Strukturerhalter ──────────────────────────
|
||||||
function fixYamlIndentation(text) {
|
function fixYamlIndentation(text) {
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n').map(l => l.replace(/\t/g, ' '));
|
||||||
|
const stack = [{ indent: -1, depth: -1 }];
|
||||||
|
const result = [];
|
||||||
|
|
||||||
// 1. Tabs → Spaces (4 oder 2)
|
for (const line of lines) {
|
||||||
const detabbed = lines.map(l => l.replace(/\t/g, ' '));
|
if (!line.trim()) { result.push(''); continue; }
|
||||||
|
|
||||||
// 2. Finde die kleinste vorkommende Einrückung > 0 (das ist die Einheit)
|
const indent = line.search(/\S/);
|
||||||
const indents = detabbed
|
const content = line.trim();
|
||||||
.filter(l => l.trim() && !l.trim().startsWith('#'))
|
|
||||||
.map(l => l.search(/\S/))
|
|
||||||
.filter(n => n > 0);
|
|
||||||
const unit = indents.length ? Math.min(...indents) : 2;
|
|
||||||
|
|
||||||
// 3. Remap: jede Einrückung → Niveau * 2 Spaces
|
if (content.startsWith('#')) {
|
||||||
const remapped = detabbed.map(line => {
|
result.push(' '.repeat(Math.max(0, stack[stack.length-1].depth + 1)) + content);
|
||||||
if (!line.trim()) return '';
|
continue;
|
||||||
if (line.trim().startsWith('#')) return line.trimEnd();
|
}
|
||||||
const spaces = line.search(/\S/);
|
|
||||||
if (spaces <= 0) return line.trimEnd();
|
|
||||||
const level = Math.round(spaces / unit);
|
|
||||||
return ' '.repeat(level) + line.trimStart().trimEnd();
|
|
||||||
});
|
|
||||||
|
|
||||||
return remapped.join('\n');
|
// Pop stack-Einträge die >= aktuelle Einrückung haben
|
||||||
|
while (stack.length > 1 && stack[stack.length-1].indent >= indent) {
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const depth = stack[stack.length-1].depth + 1;
|
||||||
|
stack.push({ indent, depth });
|
||||||
|
result.push(' '.repeat(depth) + content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Versucht einen lesbaren Fehler aus dem js-yaml Fehler zu bauen
|
||||||
|
function shortError(e) {
|
||||||
|
const first = e.message.split('\n')[0];
|
||||||
|
return first.length > 120 ? first.slice(0,120)+'…' : first;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── YAML Tool ─────────────────────────────────────────────────────────────────
|
// ── YAML Tool ─────────────────────────────────────────────────────────────────
|
||||||
@@ -167,17 +176,18 @@ function YamlTool({ mobile }) {
|
|||||||
try { obj = jsyaml.load(text); }
|
try { obj = jsyaml.load(text); }
|
||||||
catch(e) { parseError = e; }
|
catch(e) { parseError = e; }
|
||||||
|
|
||||||
// Versuch 2: wenn Fehler → Auto-Fix, dann nochmal
|
// Versuch 2: Auto-Fix, dann nochmal
|
||||||
if (parseError) {
|
if (parseError) {
|
||||||
|
const fixed = fixYamlIndentation(text);
|
||||||
try {
|
try {
|
||||||
const fixed = fixYamlIndentation(text);
|
|
||||||
obj = jsyaml.load(fixed);
|
obj = jsyaml.load(fixed);
|
||||||
setWarning(`⚠ Einrückung automatisch korrigiert: ${parseError.message.split('\n')[0]}`);
|
setWarning(`⚠ Einrückung automatisch korrigiert – bitte Ergebnis prüfen`);
|
||||||
} catch(e2) {
|
} catch(e2) {
|
||||||
// Fix hat auch nicht geholfen – originalen Fehler zeigen
|
// Auch nach Fix nicht parsebar → best-effort Text trotzdem anzeigen
|
||||||
setError(parseError.message);
|
setError(shortError(parseError));
|
||||||
setWarning('');
|
setWarning('Struktur zu stark beschädigt – nur Einrückung wurde normalisiert. Bitte manuell korrigieren.');
|
||||||
setParsed(null); setFormatted('');
|
setParsed(null);
|
||||||
|
setFormatted(fixed); // zeige korrigierten Text zum manuellen Nachbessern
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -260,17 +270,26 @@ function YamlTool({ mobile }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error ? (
|
{error && !formatted ? (
|
||||||
<div style={{background:'rgba(255,107,157,0.08)',border:'1px solid rgba(255,107,157,0.2)',
|
<div style={{background:'rgba(255,107,157,0.08)',border:'1px solid rgba(255,107,157,0.2)',
|
||||||
borderRadius:8,padding:14,color:'#ff6b9d',fontFamily:'monospace',fontSize:11,lineHeight:1.7}}>
|
borderRadius:8,padding:14,color:'#ff6b9d',fontFamily:'monospace',fontSize:11,lineHeight:1.7}}>
|
||||||
⚠ {error}
|
⚠ {error}
|
||||||
</div>
|
</div>
|
||||||
) : (<>
|
) : (<>
|
||||||
{warning && (
|
{warning && (
|
||||||
<div style={{background:'rgba(255,230,109,0.07)',border:'1px solid rgba(255,230,109,0.2)',
|
<div style={{background: error ? 'rgba(255,230,109,0.07)' : 'rgba(78,205,196,0.07)',
|
||||||
borderRadius:7,padding:'7px 12px',color:'#ffe66d',fontFamily:'monospace',
|
border:`1px solid ${error ? 'rgba(255,230,109,0.25)' : 'rgba(78,205,196,0.2)'}`,
|
||||||
fontSize:10,lineHeight:1.6,marginBottom:8}}>
|
borderRadius:7,padding:'7px 12px',
|
||||||
{warning}
|
color: error ? '#ffe66d' : '#4ecdc4',
|
||||||
|
fontFamily:'monospace',fontSize:10,lineHeight:1.6,marginBottom:8}}>
|
||||||
|
{error ? '⚠ ' : '✓ '}{warning}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{error && formatted && (
|
||||||
|
<div style={{background:'rgba(255,107,157,0.06)',border:'1px solid rgba(255,107,157,0.15)',
|
||||||
|
borderRadius:7,padding:'6px 12px',color:'rgba(255,107,157,0.7)',
|
||||||
|
fontFamily:'monospace',fontSize:9,lineHeight:1.6,marginBottom:8}}>
|
||||||
|
⚠ {error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{view==='raw' ? (
|
{view==='raw' ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user