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) ...
export function validateTplDocument(document: vscode.TextDocument, collection: vscode.DiagnosticCollection) {
if (document.languageId !== 'tpl') return;
if (document.languageId !== 'tpl') {return;}
const text = document.getText();
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 {
// 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.
if (rawLine.includes('\t')) return;
if (rawLine.includes('\t')) {return;}
const leadingSpacesMatch = rawLine.match(/^ +/);
const spaceCount = leadingSpacesMatch ? leadingSpacesMatch[0].length : 0;

View File

@ -16,10 +16,10 @@ export class MetadataRule implements LinterRule {
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');
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/)) {
@ -60,11 +60,11 @@ export class MetadataRule implements LinterRule {
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');
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));

View File

@ -16,7 +16,7 @@ export class SyntaxRule implements LinterRule {
const commentIdx = cleanLine.indexOf('//');
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);

View File

@ -4,7 +4,7 @@ import { tplBuiltIns } from '../data/TplBuiltIns';
export class TplHoverProvider implements vscode.HoverProvider {
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_.]+/);
if (!range) return null;
if (!range) {return null;}
const word = document.getText(range);
const text = document.getText();

View File

@ -18,11 +18,11 @@ export class MetadataFix implements TplQuickFix {
const missingFields = missingFieldsStr.split(', ');
let insertText = '';
if (missingFields.includes('origin')) insertText += ` origin := '${origin}';\n`;
if (missingFields.includes('author')) insertText += ` author := '${author}';\n`;
if (missingFields.includes('products')) insertText += ` products := 'TODO_Product';\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('origin')) {insertText += ` origin := '${origin}';\n`;}
if (missingFields.includes('author')) {insertText += ` author := '${author}';\n`;}
if (missingFields.includes('products')) {insertText += ` products := 'TODO_Product';\n`;}
if (missingFields.includes('publishers')) {insertText += ` publishers := '${publisher}';\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.diagnostics = [diagnostic];

View File

@ -3,18 +3,15 @@ import { CompletionStrategy } from './CompletionStrategy';
export class SnippetCompletion implements CompletionStrategy {
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; }
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 inPattern = textBeforeCursor.lastIndexOf('pattern ') > textBeforeCursor.lastIndexOf('end pattern;');
const inSyncmapping = textBeforeCursor.lastIndexOf('syncmapping ') > textBeforeCursor.lastIndexOf('end syncmapping;');
const inBlock = inPattern || inSyncmapping;
// 2. Settings laden
const config = vscode.workspace.getConfiguration('tpl');
const author = config.get<string>('metadata.author', 'Kuro');
const origin = config.get<string>('metadata.origin', 'Custom');
@ -35,47 +32,38 @@ export class SnippetCompletion implements CompletionStrategy {
treePathStr += `'\${${snippetTabIdx}:Level_${i+1}}'`;
snippetTabIdx++;
}
if (i < minLevels - 1) treePathStr += ', ';
if (i < minLevels - 1) {treePathStr += ', ';}
}
// Dynamischer Metadata-String
// Dynamischer Modul-Metadata-Block
let metadataBlock = 'metadata\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.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.author', true)) metadataBlock += ` author := '${author}';\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.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.author', true)) {metadataBlock += ` author := '${author}';\n`;}
metadataBlock += 'end metadata;';
// ==========================================
// SNIPPETS FÜR DIE MODUL-EBENE (Außerhalb)
// ==========================================
if (!inBlock) {
const skeleton = new vscode.CompletionItem('tpl_skeleton', vscode.CompletionItemKind.Snippet);
skeleton.detail = "Generiert ein frisches TPL Modul";
skeleton.insertText = new vscode.SnippetString(
`tpl 1.\${1:${version}} module \${2:MyModule};\n\n` +
`metadata\n` +
` origin := '${origin}';\n` +
` tree_path := '${treePathBase}', '\${3:Category}', '\${4:Subcategory}';\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` +
`tpl 1.\${${snippetTabIdx++}:${version}} module \${${snippetTabIdx++}:MyModule};\n\n` +
`${metadataBlock}\n\n` +
`pattern \${${snippetTabIdx++}:MyPattern} 1.0\n` +
` """\n \${${snippetTabIdx++}:Beschreibung des Patterns}\n """\n` +
` metadata\n` +
` publishers := '${publisher}';\n` +
` tags := '\${8:Tags}';\n` +
` tags := '\${${snippetTabIdx++}:Tags}';\n` +
` end metadata;\n\n` +
` overview\n` +
` tags \${9:Tags};\n` +
` tags \${${snippetTabIdx++}:Tags};\n` +
` end overview;\n\n` +
` constants\n` +
` \${10:const_name} := '\${11:value}';\n` +
` \${${snippetTabIdx++}:const_name} := '\${${snippetTabIdx++}:value}';\n` +
` end constants;\n\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` +
` body\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);
modMeta.detail = "Modul-Metadaten Block (Pflichtfelder)";
modMeta.insertText = new vscode.SnippetString(
`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`
);
modMeta.insertText = new vscode.SnippetString(`${metadataBlock}\n$0`);
items.push(modMeta);
}
// ==========================================
// SNIPPETS FÜR DIE PATTERN-EBENE (Innerhalb)
// ==========================================
if (inBlock) {
const patMeta = new vscode.CompletionItem('metadata', vscode.CompletionItemKind.Snippet);
patMeta.detail = "Pattern-Metadaten Block";
@ -114,9 +91,6 @@ export class SnippetCompletion implements CompletionStrategy {
items.push(patMeta);
}
// ==========================================
// ALLGEMEINE SNIPPETS (Überall gültig)
// ==========================================
const ifSnippet = new vscode.CompletionItem('if', vscode.CompletionItemKind.Snippet);
ifSnippet.insertText = new vscode.SnippetString(`if \${1:condition} then\n $0\nend if;`);
items.push(ifSnippet);

View File

@ -14,7 +14,7 @@ function createMockDoc(text: string): any {
}
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();
// Simuliert, dass wir im "metadata" Block tippen
@ -22,14 +22,14 @@ suite('Completion Provider Test Suite', () => {
const mockDoc = createMockDoc(docText);
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);
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");
});
test('SnippetCompletion: Vorschläge im Body korrekt', () => {
test('SnippetCompletion: Vorschläge im Body korrekt', async () => {
const strategy = new SnippetCompletion();
// Simuliert, dass wir im "body" tippen
@ -37,10 +37,10 @@ suite('Completion Provider Test Suite', () => {
const mockDoc = createMockDoc(docText);
const position = new vscode.Position(2, 4);
const result = strategy.provideCompletions(mockDoc, position, ' ');
const result = await strategy.provideCompletions(mockDoc, position, ' ');
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");
});
@ -54,7 +54,7 @@ suite('Completion Provider Test Suite', () => {
const result = await strategy.provideCompletions(mockDoc, position, 'do');
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");
// Prüft, ob das generierte Snippet das Auto-Semikolon und den $0 Cursor hat