diff --git a/linter_fixes.sh b/linter_fixes.sh new file mode 100755 index 0000000..e3b5d05 --- /dev/null +++ b/linter_fixes.sh @@ -0,0 +1 @@ +npm run lint -- --fix diff --git a/src/core/TplLinter.ts b/src/core/TplLinter.ts index 90ec625..d8e920e 100644 --- a/src/core/TplLinter.ts +++ b/src/core/TplLinter.ts @@ -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'); diff --git a/src/core/rules/IndentationRule.ts b/src/core/rules/IndentationRule.ts index 41b3a1f..c36d8ab 100644 --- a/src/core/rules/IndentationRule.ts +++ b/src/core/rules/IndentationRule.ts @@ -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; diff --git a/src/core/rules/MetadataRule.ts b/src/core/rules/MetadataRule.ts index ae12df7..f5ad52f 100644 --- a/src/core/rules/MetadataRule.ts +++ b/src/core/rules/MetadataRule.ts @@ -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('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'); + 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)); diff --git a/src/core/rules/SyntaxRule.ts b/src/core/rules/SyntaxRule.ts index dcc4ffd..88cfcf1 100644 --- a/src/core/rules/SyntaxRule.ts +++ b/src/core/rules/SyntaxRule.ts @@ -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); diff --git a/src/providers/TplHoverProvider.ts b/src/providers/TplHoverProvider.ts index fa4a06b..b541cb4 100644 --- a/src/providers/TplHoverProvider.ts +++ b/src/providers/TplHoverProvider.ts @@ -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 { 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(); diff --git a/src/providers/actions/MetadataFix.ts b/src/providers/actions/MetadataFix.ts index 3a50a02..f7b8a4a 100644 --- a/src/providers/actions/MetadataFix.ts +++ b/src/providers/actions/MetadataFix.ts @@ -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]; diff --git a/src/providers/completions/SnippetCompletion.ts b/src/providers/completions/SnippetCompletion.ts index 4c3e3c0..820a4ed 100644 --- a/src/providers/completions/SnippetCompletion.ts +++ b/src/providers/completions/SnippetCompletion.ts @@ -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 { - // 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('metadata.author', 'Kuro'); const origin = config.get('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('metadata.require.origin', true)) metadataBlock += ` origin := '${origin}';\n`; - if (config.get('metadata.require.treePath', true)) metadataBlock += ` tree_path := ${treePathStr};\n`; - if (config.get('metadata.require.products', true)) metadataBlock += ` products := '\${${snippetTabIdx++}:Product}';\n`; - if (config.get('metadata.require.publishers', true)) metadataBlock += ` publishers := '${publisher}';\n`; - if (config.get('metadata.require.author', true)) metadataBlock += ` author := '${author}';\n`; + if (config.get('metadata.require.origin', true)) {metadataBlock += ` origin := '${origin}';\n`;} + if (config.get('metadata.require.treePath', true)) {metadataBlock += ` tree_path := ${treePathStr};\n`;} + if (config.get('metadata.require.products', true)) {metadataBlock += ` products := '\${${snippetTabIdx++}:Product}';\n`;} + if (config.get('metadata.require.publishers', true)) {metadataBlock += ` publishers := '${publisher}';\n`;} + if (config.get('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); diff --git a/src/test/suite/completions.test.ts b/src/test/suite/completions.test.ts index 42f9b94..5237d95 100644 --- a/src/test/suite/completions.test.ts +++ b/src/test/suite/completions.test.ts @@ -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