import * as vscode from 'vscode'; import { LinterRule } from './LinterRule'; import { LinterContext } from '../LinterContext'; export class MetadataRule implements LinterRule { public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, lines: string[]): void { if (line.startsWith('metadata') && ctx.inBlock === 'none') { ctx.inModuleMetadata = true; ctx.hasModuleMetadata = true; ctx.moduleMetadataBlockLine = lineIdx; ctx.foundModuleMetadata.clear(); return; } if (ctx.inModuleMetadata) { const config = vscode.workspace.getConfiguration('tpl'); // Felder erfassen if (line.match(/^origin\b/)) {ctx.foundModuleMetadata.add('origin');} if (line.match(/^author\b/)) {ctx.foundModuleMetadata.add('author');} if (line.match(/^products\b/)) {ctx.foundModuleMetadata.add('products');} if (line.match(/^publishers\b/)) {ctx.foundModuleMetadata.add('publishers');} // Tree-Path Sonderprüfung if (line.match(/^tree_path\b/)) { ctx.foundModuleMetadata.add('tree_path'); if (config.get('metadata.require.treePath', true)) { this.checkTreePath(line, rawLine, lineIdx, ctx, config); } } // Block-Ende: Pflichtfelder abgleichen if (line.startsWith('end metadata')) { ctx.inModuleMetadata = false; this.checkRequiredFields(ctx, config); } } } private checkTreePath(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, config: vscode.WorkspaceConfiguration) { const minLevels = config.get('metadata.treePath.minLevels', 3); const staticNodes = config.get('metadata.treePath.staticNodes', ['Grafing']); // Extrahiert die Strings aus tree_path := 'A', 'B'; const match = line.match(/tree_path\s*:=\s*(.*);/); if (match) { const paths = match[1].split(',').map(s => s.trim().replace(/^['"]|['"]$/g, '')); if (paths.length < minLevels) { ctx.addDiag(lineIdx, 0, rawLine.length, `Der tree_path muss mindestens ${minLevels} Ebenen haben!`, vscode.DiagnosticSeverity.Error); } for (let i = 0; i < staticNodes.length; i++) { if (paths[i] !== staticNodes[i]) { ctx.addDiag(lineIdx, 0, rawLine.length, `Die tree_path Ebene ${i + 1} muss zwingend '${staticNodes[i]}' lauten!`, vscode.DiagnosticSeverity.Error); } } } } private checkRequiredFields(ctx: LinterContext, config: vscode.WorkspaceConfiguration) { const requiredFields: string[] = []; if (config.get('metadata.require.origin', true)) {requiredFields.push('origin');} if (config.get('metadata.require.author', true)) {requiredFields.push('author');} if (config.get('metadata.require.products', true)) {requiredFields.push('products');} if (config.get('metadata.require.publishers', true)) {requiredFields.push('publishers');} if (config.get('metadata.require.treePath', true)) {requiredFields.push('tree_path');} const missing = requiredFields.filter(f => !ctx.foundModuleMetadata.has(f)); if (missing.length > 0 && ctx.moduleMetadataBlockLine !== -1) { const diag = ctx.addDiag(ctx.moduleMetadataBlockLine, 0, 8, `Fehlende Pflicht-Metadaten (Modul-Ebene): ${missing.join(', ')}`, vscode.DiagnosticSeverity.Error); diag.code = 'missing_module_metadata'; } } public checkEnd(lines: string[], ctx: LinterContext): void { // empty } }