linter fixes, fixes after refactoring and error fixing after i made MetadataRule seperate from BlockRule

This commit is contained in:
kuro 2026-10-18 03:27:52 +02:00
parent 9dcbf58f3a
commit 387f3793df
9 changed files with 43 additions and 68 deletions

1
linter_fixes.sh Executable file
View File

@ -0,0 +1 @@
npm run lint -- --fix

View File

@ -8,7 +8,7 @@ import { IndentationRule } from './rules/IndentationRule'; // 1. Hier importiere
// ... (deine eventuellen anderen Hilfs-Imports wie trackStrings) ... // ... (deine eventuellen anderen Hilfs-Imports wie trackStrings) ...
export function validateTplDocument(document: vscode.TextDocument, collection: vscode.DiagnosticCollection) { export function validateTplDocument(document: vscode.TextDocument, collection: vscode.DiagnosticCollection) {
if (document.languageId !== 'tpl') return; if (document.languageId !== 'tpl') {return;}
const text = document.getText(); const text = document.getText();
const lines = text.split('\n'); const lines = text.split('\n');

View File

@ -6,10 +6,10 @@ export class IndentationRule implements LinterRule {
public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, lines: string[]): void { public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, lines: string[]): void {
// Leere Zeilen oder Zeilen, die nur aus Leerzeichen/Tabs bestehen, ignorieren // Leere Zeilen oder Zeilen, die nur aus Leerzeichen/Tabs bestehen, ignorieren
if (rawLine.trim().length === 0) return; if (rawLine.trim().length === 0) {return;}
// Wenn Tabs drin sind, meckert die SyntaxRule bereits. Das ignorieren wir hier. // Wenn Tabs drin sind, meckert die SyntaxRule bereits. Das ignorieren wir hier.
if (rawLine.includes('\t')) return; if (rawLine.includes('\t')) {return;}
const leadingSpacesMatch = rawLine.match(/^ +/); const leadingSpacesMatch = rawLine.match(/^ +/);
const spaceCount = leadingSpacesMatch ? leadingSpacesMatch[0].length : 0; const spaceCount = leadingSpacesMatch ? leadingSpacesMatch[0].length : 0;

View File

@ -16,10 +16,10 @@ export class MetadataRule implements LinterRule {
const config = vscode.workspace.getConfiguration('tpl'); const config = vscode.workspace.getConfiguration('tpl');
// Felder erfassen // Felder erfassen
if (line.match(/^origin\b/)) ctx.foundModuleMetadata.add('origin'); if (line.match(/^origin\b/)) {ctx.foundModuleMetadata.add('origin');}
if (line.match(/^author\b/)) ctx.foundModuleMetadata.add('author'); if (line.match(/^author\b/)) {ctx.foundModuleMetadata.add('author');}
if (line.match(/^products\b/)) ctx.foundModuleMetadata.add('products'); if (line.match(/^products\b/)) {ctx.foundModuleMetadata.add('products');}
if (line.match(/^publishers\b/)) ctx.foundModuleMetadata.add('publishers'); if (line.match(/^publishers\b/)) {ctx.foundModuleMetadata.add('publishers');}
// Tree-Path Sonderprüfung // Tree-Path Sonderprüfung
if (line.match(/^tree_path\b/)) { if (line.match(/^tree_path\b/)) {
@ -60,11 +60,11 @@ export class MetadataRule implements LinterRule {
private checkRequiredFields(ctx: LinterContext, config: vscode.WorkspaceConfiguration) { private checkRequiredFields(ctx: LinterContext, config: vscode.WorkspaceConfiguration) {
const requiredFields: string[] = []; const requiredFields: string[] = [];
if (config.get<boolean>('metadata.require.origin', true)) requiredFields.push('origin'); 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.author', true)) {requiredFields.push('author');}
if (config.get<boolean>('metadata.require.products', true)) requiredFields.push('products'); 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.publishers', true)) {requiredFields.push('publishers');}
if (config.get<boolean>('metadata.require.treePath', true)) requiredFields.push('tree_path'); if (config.get<boolean>('metadata.require.treePath', true)) {requiredFields.push('tree_path');}
const missing = requiredFields.filter(f => !ctx.foundModuleMetadata.has(f)); const missing = requiredFields.filter(f => !ctx.foundModuleMetadata.has(f));

View File

@ -16,7 +16,7 @@ export class SyntaxRule implements LinterRule {
const commentIdx = cleanLine.indexOf('//'); const commentIdx = cleanLine.indexOf('//');
let codePart = commentIdx !== -1 ? cleanLine.substring(0, commentIdx).trim() : cleanLine.trim(); let codePart = commentIdx !== -1 ? cleanLine.substring(0, commentIdx).trim() : cleanLine.trim();
if (codePart.length === 0) return; if (codePart.length === 0) {return;}
let isStructuralKeyword = /^(tpl|pattern|syncmapping|table|definitions?|define|enumeration|configuration|metadata|overview|constants|triggers|mapping|removal|body|end|on|tags|origin|author|products|publishers|tree_path|import|from)\b/.test(codePart); let isStructuralKeyword = /^(tpl|pattern|syncmapping|table|definitions?|define|enumeration|configuration|metadata|overview|constants|triggers|mapping|removal|body|end|on|tags|origin|author|products|publishers|tree_path|import|from)\b/.test(codePart);

View File

@ -4,7 +4,7 @@ import { tplBuiltIns } from '../data/TplBuiltIns';
export class TplHoverProvider implements vscode.HoverProvider { export class TplHoverProvider implements vscode.HoverProvider {
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | null> { public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | null> {
const range = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_.]+/); const range = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_.]+/);
if (!range) return null; if (!range) {return null;}
const word = document.getText(range); const word = document.getText(range);
const text = document.getText(); const text = document.getText();

View File

@ -18,11 +18,11 @@ export class MetadataFix implements TplQuickFix {
const missingFields = missingFieldsStr.split(', '); const missingFields = missingFieldsStr.split(', ');
let insertText = ''; let insertText = '';
if (missingFields.includes('origin')) insertText += ` origin := '${origin}';\n`; if (missingFields.includes('origin')) {insertText += ` origin := '${origin}';\n`;}
if (missingFields.includes('author')) insertText += ` author := '${author}';\n`; if (missingFields.includes('author')) {insertText += ` author := '${author}';\n`;}
if (missingFields.includes('products')) insertText += ` products := 'TODO_Product';\n`; if (missingFields.includes('products')) {insertText += ` products := 'TODO_Product';\n`;}
if (missingFields.includes('publishers')) insertText += ` publishers := '${publisher}';\n`; if (missingFields.includes('publishers')) {insertText += ` publishers := '${publisher}';\n`;}
if (missingFields.includes('tree_path')) insertText += ` tree_path := '${treePathBase}', 'TODO_Category', 'TODO_Subcategory';\n`; if (missingFields.includes('tree_path')) {insertText += ` tree_path := '${treePathBase}', 'TODO_Category', 'TODO_Subcategory';\n`;}
fix.edit.insert(document.uri, new vscode.Position(diagnostic.range.start.line + 1, 0), insertText); fix.edit.insert(document.uri, new vscode.Position(diagnostic.range.start.line + 1, 0), insertText);
fix.diagnostics = [diagnostic]; fix.diagnostics = [diagnostic];

View File

@ -3,18 +3,15 @@ import { CompletionStrategy } from './CompletionStrategy';
export class SnippetCompletion implements CompletionStrategy { export class SnippetCompletion implements CompletionStrategy {
public async provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): Promise<vscode.CompletionItem[] | undefined> { public async provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): Promise<vscode.CompletionItem[] | undefined> {
// Bei Modul-Aufrufen (Punkt) keine Snippets feuern
if (linePrefix.match(/\.[a-zA-Z0-9_]*$/)) { return undefined; } if (linePrefix.match(/\.[a-zA-Z0-9_]*$/)) { return undefined; }
const items: vscode.CompletionItem[] = []; const items: vscode.CompletionItem[] = [];
// 1. Kontext herausfinden (Sind wir in einem Pattern oder Syncmapping?)
const textBeforeCursor = document.getText(new vscode.Range(new vscode.Position(0, 0), position)); const textBeforeCursor = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
const inPattern = textBeforeCursor.lastIndexOf('pattern ') > textBeforeCursor.lastIndexOf('end pattern;'); const inPattern = textBeforeCursor.lastIndexOf('pattern ') > textBeforeCursor.lastIndexOf('end pattern;');
const inSyncmapping = textBeforeCursor.lastIndexOf('syncmapping ') > textBeforeCursor.lastIndexOf('end syncmapping;'); const inSyncmapping = textBeforeCursor.lastIndexOf('syncmapping ') > textBeforeCursor.lastIndexOf('end syncmapping;');
const inBlock = inPattern || inSyncmapping; const inBlock = inPattern || inSyncmapping;
// 2. Settings laden
const config = vscode.workspace.getConfiguration('tpl'); const config = vscode.workspace.getConfiguration('tpl');
const author = config.get<string>('metadata.author', 'Kuro'); const author = config.get<string>('metadata.author', 'Kuro');
const origin = config.get<string>('metadata.origin', 'Custom'); const origin = config.get<string>('metadata.origin', 'Custom');
@ -35,47 +32,38 @@ export class SnippetCompletion implements CompletionStrategy {
treePathStr += `'\${${snippetTabIdx}:Level_${i+1}}'`; treePathStr += `'\${${snippetTabIdx}:Level_${i+1}}'`;
snippetTabIdx++; snippetTabIdx++;
} }
if (i < minLevels - 1) treePathStr += ', '; if (i < minLevels - 1) {treePathStr += ', ';}
} }
// Dynamischer Metadata-String // Dynamischer Modul-Metadata-Block
let metadataBlock = 'metadata\n'; let metadataBlock = 'metadata\n';
if (config.get<boolean>('metadata.require.origin', true)) metadataBlock += ` origin := '${origin}';\n`; if (config.get<boolean>('metadata.require.origin', true)) {metadataBlock += ` origin := '${origin}';\n`;}
if (config.get<boolean>('metadata.require.treePath', true)) metadataBlock += ` tree_path := ${treePathStr};\n`; if (config.get<boolean>('metadata.require.treePath', true)) {metadataBlock += ` tree_path := ${treePathStr};\n`;}
if (config.get<boolean>('metadata.require.products', true)) metadataBlock += ` products := '\${${snippetTabIdx++}:Product}';\n`; if (config.get<boolean>('metadata.require.products', true)) {metadataBlock += ` products := '\${${snippetTabIdx++}:Product}';\n`;}
if (config.get<boolean>('metadata.require.publishers', true)) metadataBlock += ` publishers := '${publisher}';\n`; if (config.get<boolean>('metadata.require.publishers', true)) {metadataBlock += ` publishers := '${publisher}';\n`;}
if (config.get<boolean>('metadata.require.author', true)) metadataBlock += ` author := '${author}';\n`; if (config.get<boolean>('metadata.require.author', true)) {metadataBlock += ` author := '${author}';\n`;}
metadataBlock += 'end metadata;'; metadataBlock += 'end metadata;';
// ==========================================
// SNIPPETS FÜR DIE MODUL-EBENE (Außerhalb)
// ==========================================
if (!inBlock) { if (!inBlock) {
const skeleton = new vscode.CompletionItem('tpl_skeleton', vscode.CompletionItemKind.Snippet); const skeleton = new vscode.CompletionItem('tpl_skeleton', vscode.CompletionItemKind.Snippet);
skeleton.detail = "Generiert ein frisches TPL Modul"; skeleton.detail = "Generiert ein frisches TPL Modul";
skeleton.insertText = new vscode.SnippetString( skeleton.insertText = new vscode.SnippetString(
`tpl 1.\${1:${version}} module \${2:MyModule};\n\n` + `tpl 1.\${${snippetTabIdx++}:${version}} module \${${snippetTabIdx++}:MyModule};\n\n` +
`metadata\n` + `${metadataBlock}\n\n` +
` origin := '${origin}';\n` + `pattern \${${snippetTabIdx++}:MyPattern} 1.0\n` +
` tree_path := '${treePathBase}', '\${3:Category}', '\${4:Subcategory}';\n` + ` """\n \${${snippetTabIdx++}:Beschreibung des Patterns}\n """\n` +
` products := '\${5:Product}';\n` +
` publishers := '${publisher}';\n` +
` author := '${author}';\n` +
`end metadata;\n\n` +
`pattern \${6:MyPattern} 1.0\n` +
` """\n \${7:Beschreibung des Patterns}\n """\n` +
` metadata\n` + ` metadata\n` +
` publishers := '${publisher}';\n` + ` publishers := '${publisher}';\n` +
` tags := '\${8:Tags}';\n` + ` tags := '\${${snippetTabIdx++}:Tags}';\n` +
` end metadata;\n\n` + ` end metadata;\n\n` +
` overview\n` + ` overview\n` +
` tags \${9:Tags};\n` + ` tags \${${snippetTabIdx++}:Tags};\n` +
` end overview;\n\n` + ` end overview;\n\n` +
` constants\n` + ` constants\n` +
` \${10:const_name} := '\${11:value}';\n` + ` \${${snippetTabIdx++}:const_name} := '\${${snippetTabIdx++}:value}';\n` +
` end constants;\n\n` + ` end constants;\n\n` +
` triggers\n` + ` triggers\n` +
` on process := SoftwareInstance created, confirmed where type = '\${12:Type}';\n` + ` on process := SoftwareInstance created, confirmed where type = '\${${snippetTabIdx++}:Type}';\n` +
` end triggers;\n\n` + ` end triggers;\n\n` +
` body\n` + ` body\n` +
` log.debug("Start pattern %pattern_name%");\n` + ` log.debug("Start pattern %pattern_name%");\n` +
@ -87,21 +75,10 @@ export class SnippetCompletion implements CompletionStrategy {
const modMeta = new vscode.CompletionItem('metadata', vscode.CompletionItemKind.Snippet); const modMeta = new vscode.CompletionItem('metadata', vscode.CompletionItemKind.Snippet);
modMeta.detail = "Modul-Metadaten Block (Pflichtfelder)"; modMeta.detail = "Modul-Metadaten Block (Pflichtfelder)";
modMeta.insertText = new vscode.SnippetString( modMeta.insertText = new vscode.SnippetString(`${metadataBlock}\n$0`);
`metadata\n` +
` origin := '${origin}';\n` +
` tree_path := '${treePathBase}', '\${1:Category}', '\${2:Subcategory}';\n` +
` products := '\${3:Product}';\n` +
` publishers := '${publisher}';\n` +
` author := '${author}';\n` +
`end metadata;\n$0`
);
items.push(modMeta); items.push(modMeta);
} }
// ==========================================
// SNIPPETS FÜR DIE PATTERN-EBENE (Innerhalb)
// ==========================================
if (inBlock) { if (inBlock) {
const patMeta = new vscode.CompletionItem('metadata', vscode.CompletionItemKind.Snippet); const patMeta = new vscode.CompletionItem('metadata', vscode.CompletionItemKind.Snippet);
patMeta.detail = "Pattern-Metadaten Block"; patMeta.detail = "Pattern-Metadaten Block";
@ -114,9 +91,6 @@ export class SnippetCompletion implements CompletionStrategy {
items.push(patMeta); items.push(patMeta);
} }
// ==========================================
// ALLGEMEINE SNIPPETS (Überall gültig)
// ==========================================
const ifSnippet = new vscode.CompletionItem('if', vscode.CompletionItemKind.Snippet); const ifSnippet = new vscode.CompletionItem('if', vscode.CompletionItemKind.Snippet);
ifSnippet.insertText = new vscode.SnippetString(`if \${1:condition} then\n $0\nend if;`); ifSnippet.insertText = new vscode.SnippetString(`if \${1:condition} then\n $0\nend if;`);
items.push(ifSnippet); items.push(ifSnippet);

View File

@ -14,7 +14,7 @@ function createMockDoc(text: string): any {
} }
suite('Completion Provider Test Suite', () => { suite('Completion Provider Test Suite', () => {
test('SnippetCompletion: Kontext-Check (Kein if außerhalb des Bodys)', () => { test('SnippetCompletion: Kontext-Check (Kein if außerhalb des Bodys)', async () => {
const strategy = new SnippetCompletion(); const strategy = new SnippetCompletion();
// Simuliert, dass wir im "metadata" Block tippen // Simuliert, dass wir im "metadata" Block tippen
@ -22,14 +22,14 @@ suite('Completion Provider Test Suite', () => {
const mockDoc = createMockDoc(docText); const mockDoc = createMockDoc(docText);
const position = new vscode.Position(2, 4); // Zeile 2, Cursor am Ende const position = new vscode.Position(2, 4); // Zeile 2, Cursor am Ende
const result = strategy.provideCompletions(mockDoc, position, ' '); const result = await strategy.provideCompletions(mockDoc, position, ' ');
assert.ok(result !== undefined); assert.ok(result !== undefined);
const hasIf = result.some(item => item.label === 'if'); const hasIf = result!.some(item => item.label === 'if');
assert.strictEqual(hasIf, false, "Darf kein 'if' im metadata Block vorschlagen"); assert.strictEqual(hasIf, false, "Darf kein 'if' im metadata Block vorschlagen");
}); });
test('SnippetCompletion: Vorschläge im Body korrekt', () => { test('SnippetCompletion: Vorschläge im Body korrekt', async () => {
const strategy = new SnippetCompletion(); const strategy = new SnippetCompletion();
// Simuliert, dass wir im "body" tippen // Simuliert, dass wir im "body" tippen
@ -37,10 +37,10 @@ suite('Completion Provider Test Suite', () => {
const mockDoc = createMockDoc(docText); const mockDoc = createMockDoc(docText);
const position = new vscode.Position(2, 4); const position = new vscode.Position(2, 4);
const result = strategy.provideCompletions(mockDoc, position, ' '); const result = await strategy.provideCompletions(mockDoc, position, ' ');
assert.ok(result !== undefined); assert.ok(result !== undefined);
const hasIf = result.some(item => item.label === 'if'); const hasIf = result!.some(item => item.label === 'if');
assert.strictEqual(hasIf, true, "Muss 'if' im Body vorschlagen"); assert.strictEqual(hasIf, true, "Muss 'if' im Body vorschlagen");
}); });
@ -54,7 +54,7 @@ suite('Completion Provider Test Suite', () => {
const result = await strategy.provideCompletions(mockDoc, position, 'do'); const result = await strategy.provideCompletions(mockDoc, position, 'do');
assert.ok(result !== undefined); assert.ok(result !== undefined);
const funcItem = result.find(item => item.label === 'my_tools.doWork'); const funcItem = result!.find(item => item.label === 'my_tools.doWork');
assert.ok(funcItem !== undefined, "Muss die Methode inkl. Namespace extrahieren"); assert.ok(funcItem !== undefined, "Muss die Methode inkl. Namespace extrahieren");
// Prüft, ob das generierte Snippet das Auto-Semikolon und den $0 Cursor hat // Prüft, ob das generierte Snippet das Auto-Semikolon und den $0 Cursor hat