bmc-tpl-code-all-around/src/core/rules/MetadataRule.ts

77 lines
3.6 KiB
TypeScript
Raw Normal View History

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');
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');}
if (line.match(/^tree_path\b/)) {
ctx.foundModuleMetadata.add('tree_path');
if (config.get<boolean>('metadata.require.treePath', true)) {
this.checkTreePath(line, rawLine, lineIdx, ctx, config);
}
}
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<number>('metadata.treePath.minLevels', 3);
const staticNodes = config.get<string[]>('metadata.treePath.staticNodes', ['Grafing']); // Fallback
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, `The tree_path must contain at least ${minLevels} levels.`, vscode.DiagnosticSeverity.Error);
}
for (let i = 0; i < staticNodes.length; i++) {
if (paths[i] !== staticNodes[i]) {
ctx.addDiag(lineIdx, 0, rawLine.length, `Tree path level ${i + 1} must strictly be '${staticNodes[i]}'.`, vscode.DiagnosticSeverity.Error);
}
}
}
}
private checkRequiredFields(ctx: LinterContext, config: vscode.WorkspaceConfiguration) {
const requiredFields: string[] = [];
if (config.get<boolean>('metadata.require.origin', true)) {requiredFields.push('origin');}
if (config.get<boolean>('metadata.require.author', true)) {requiredFields.push('author');}
if (config.get<boolean>('metadata.require.products', true)) {requiredFields.push('products');}
if (config.get<boolean>('metadata.require.publishers', true)) {requiredFields.push('publishers');}
if (config.get<boolean>('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, `Missing mandatory module metadata fields: ${missing.join(', ')}`, vscode.DiagnosticSeverity.Error);
diag.code = 'missing_module_metadata';
}
}
public checkEnd(lines: string[], ctx: LinterContext): void {
// empty
}
}