import * as vscode from 'vscode'; import { CompletionStrategy } from './CompletionStrategy'; import { tplBuiltIns } from '../../data/TplBuiltIns'; import { workspaceIndex } from '../../core/WorkspaceIndex'; 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[] = []; // 1. SMART IMPORT: "from " if (linePrefix.match(/\bfrom\s+$/)) { for (const mod of workspaceIndex.getAllModules()) { const item = new vscode.CompletionItem(mod, vscode.CompletionItemKind.Module); item.detail = "Workspace Module"; items.push(item); } return items; // Nichts anderes vorschlagen! } // 2. SMART IMPORT: "from X import " const importContextMatch = linePrefix.match(/\bfrom\s+([a-zA-Z0-9_.]+)\s+import\s+$/); if (importContextMatch) { const modName = importContextMatch[1]; for (const defBlock of workspaceIndex.getDefinitionBlocksForModule(modName)) { const item = new vscode.CompletionItem(defBlock, vscode.CompletionItemKind.Class); item.detail = `Definitions from ${modName}`; items.push(item); } return items; // Nichts anderes vorschlagen! } // 3. NAMESPACE CALLS: "prefix." const dotMatch = linePrefix.match(/\b([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]*)$/); if (dotMatch) { const prefix = dotMatch[1]; // BMC Built-ins ignorieren (übernimmt BuiltInCompletion) const bmcNamespaces = Object.keys(tplBuiltIns); if (bmcNamespaces.includes(prefix)) { return undefined; // Hier darf undefined sein, weil BuiltInCompletion das fängt } let funcs = []; // A) RAM-Abfrage für importierte Module 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]; // Filtere Funktionen aus dem importierten Modul, die zu diesem Prefix gehören funcs = workspaceIndex.getFunctionsForModule(moduleName).filter(f => f.name.startsWith(`${prefix}.`) || !f.name.includes('.')); } else { // B) Lokale Definitions in der aktuellen Datei prüfen! const localDefRegex = new RegExp(`^\\s*definitions?\\s+${prefix}\\b`, 'm'); if (localDefRegex.test(text)) { const lines = text.split('\n'); let inTargetBlock = false; for (const line of lines) { if (line.match(new RegExp(`^\\s*definitions?\\s+${prefix}\\b`))) { inTargetBlock = true; continue; } if (line.match(/^\s*end\s+definitions?/)) { inTargetBlock = false; continue; } if (inTargetBlock) { const defineMatch = line.match(/^\s*define\s+([a-zA-Z0-9_]+)\b(?:\s*\((.*?)\))?/); if (defineMatch) { const params = (defineMatch[2] || "").split(',').map(p => p.trim()).filter(p => p.length > 0); funcs.push({ name: defineMatch[1], moduleName: 'local', params: params }); } } } } } if (funcs.length > 0) { for (const f of funcs) { const shortName = f.name.split('.').pop() || f.name; const item = new vscode.CompletionItem(shortName, vscode.CompletionItemKind.Function); item.detail = f.moduleName === 'local' ? `Local Method` : `Method from ${f.moduleName}`; // FIX: Dynamische Parameter-Generierung! Baut z.B. ${1:host}, ${2:ip} let paramSnippet = ""; if (f.params && f.params.length > 0) { paramSnippet = f.params.map((p, idx) => `\${${idx + 1}:${p}}`).join(', '); } item.insertText = new vscode.SnippetString(`${shortName}(${paramSnippet});$0`); items.push(item); } return items; // Gibt exakt die Funktionen zurück } // FIX: Wenn es ein unbekannter Namespace mit Punkt ist, geben wir ein leeres Array [] zurück! // Das zwingt VS Code dazu, KEINE generischen Textwörter vorzuschlagen. return []; } // 4. LOKALE DEFINITIONS PARSEN (Für den globalen Scope, noch ohne Punkt aufgerufen) 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(?:\s*\((.*?)\))?/); 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`; // Dynamische Parameter-Generierung auch hier! const params = (defineMatch[2] || "").split(',').map(p => p.trim()).filter(p => p.length > 0); let paramSnippet = ""; if (params.length > 0) { paramSnippet = params.map((p, idx) => `\${${idx + 1}:${p}}`).join(', '); } item.insertText = new vscode.SnippetString(`${fullName}(${paramSnippet});$0`); items.push(item); } } // Import-Referenzen für den globalen Scope bereitstellen 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); } // Lokale Variablen (:=) vorschlagen 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; } }