import * as vscode from 'vscode'; import { CompletionStrategy } from './CompletionStrategy'; import { tplBuiltIns } from '../../data/TplBuiltIns'; import { generatedTplBuiltIns } from '../../data/GeneratedTplBuiltIns'; // Pfad ggf. anpassen const allTplFunctions: Record = { ...tplBuiltIns, ...generatedTplBuiltIns }; export class LocalContextCompletion implements CompletionStrategy { public async provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): Promise { const text = document.getText(); const items: vscode.CompletionItem[] = []; const dotMatch = linePrefix.match(/\b([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]*)$/); if (dotMatch) { const prefix = dotMatch[1]; // Auf erweitertes Dictionary prüfen const bmcNamespaces = Object.keys(allTplFunctions); if (bmcNamespaces.includes(prefix)) { return undefined; // Überlässt das Feld komplett der BuiltInCompletion! } const importRegex = new RegExp(`from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+${prefix}\\b`); const match = importRegex.exec(text); if (match) { const moduleName = match[1]; const externalItems = await this.resolveImportedMethods(moduleName, prefix); return externalItems; } return undefined; } const lines = text.split('\n'); let currentDefBlock = ""; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const defBlockMatch = line.match(/^\s*definitions?\s+([a-zA-Z0-9_]+)/); if (defBlockMatch) { currentDefBlock = defBlockMatch[1]; continue; } if (line.match(/^\s*end\s+definitions?/)) { currentDefBlock = ""; continue; } const defineMatch = line.match(/^\s*define\s+([a-zA-Z0-9_]+)\b/); if (defineMatch) { const funcName = defineMatch[1]; const fullName = currentDefBlock ? `${currentDefBlock}.${funcName}` : funcName; const item = new vscode.CompletionItem(fullName, vscode.CompletionItemKind.Function); item.detail = currentDefBlock ? `Local Function in '${currentDefBlock}'` : `Local Function`; item.insertText = new vscode.SnippetString(`${fullName}(\${1:params});$0`); items.push(item); } } const fromImportRegex = /from\s+([a-zA-Z0-9_.]+)\s+import\s+([a-zA-Z0-9_]+)/g; let matchVar; while ((matchVar = fromImportRegex.exec(text)) !== null) { const item = new vscode.CompletionItem(matchVar[2], vscode.CompletionItemKind.Module); item.detail = `Imported Definitions from ${matchVar[1]}`; items.push(item); } const varRegex = /([a-zA-Z0-9_]+)\s*:=/g; const foundVars = new Set(); while ((matchVar = varRegex.exec(text)) !== null) { if (matchVar[1] !== 'host') { foundVars.add(matchVar[1]); } } for (const v of foundVars) { items.push(new vscode.CompletionItem(v, vscode.CompletionItemKind.Variable)); } return items; } private async resolveImportedMethods(moduleName: string, defBlockName: string): Promise { const items: vscode.CompletionItem[] = []; const files = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**'); for (const file of files) { const doc = await vscode.workspace.openTextDocument(file); const text = doc.getText(); const moduleRegex = new RegExp(`tpl\\s+1\\.\\d+\\s+module\\s+${moduleName.replace(/\./g, '\\.')}\\s*;`); if (moduleRegex.test(text)) { const lines = text.split('\n'); let inTargetBlock = false; for (const line of lines) { if (line.match(new RegExp(`^\\s*definitions?\\s+${defBlockName}\\b`))) { inTargetBlock = true; continue; } if (inTargetBlock && line.match(/^\s*end\s+definitions?/)) { inTargetBlock = false; break; } if (inTargetBlock) { const defineMatch = line.match(/^\s*define\s+([a-zA-Z0-9_]+)\b/); if (defineMatch) { const funcName = defineMatch[1]; const item = new vscode.CompletionItem(funcName, vscode.CompletionItemKind.Function); item.detail = `Method from ${moduleName}`; item.insertText = new vscode.SnippetString(`${funcName}(\${1:params});$0`); items.push(item); } } } break; } } return items; } }