import * as vscode from 'vscode'; import { tplBuiltIns } from '../data/TplBuiltIns'; import { generatedTplBuiltIns } from '../data/GeneratedTplBuiltIns'; // Pfad ggf. anpassen // Beide Datenquellen zusammenführen const allTplFunctions: Record = { ...tplBuiltIns, ...generatedTplBuiltIns }; 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;} const word = document.getText(range); const text = document.getText(); const lines = text.split('\n'); const parts = word.split('.'); const importBase = parts[0]; const funcName = parts.length > 1 ? parts[1] : word; // 1. BMC BUILT-INS & TKU DEFINES DYNAMISCH AUSLESEN const bmcNamespaces = Object.keys(allTplFunctions); if (bmcNamespaces.includes(importBase)) { // Wenn die Methode in der Datei existiert, zeige die echte Doku! if (funcName !== importBase && allTplFunctions[importBase][funcName]) { const bmcDoc = allTplFunctions[importBase][funcName]; const md = new vscode.MarkdownString(); // Wir zeigen den echten Methodennamen als Code-Block md.appendCodeblock(`${importBase}.${funcName}`, 'tpl'); md.appendMarkdown(`\n\n---\n\n${bmcDoc.description}`); return new vscode.Hover(md); } // Fallback, wenn man nur über dem Namespace (z.B. "discovery") hovert return new vscode.Hover(new vscode.MarkdownString(`**${word}**\n\n*(BMC Built-in / TKU Namespace)*`)); } // 2. Lokale Defines if (parts.length === 1 || parts.length === 2) { for (let i = 0; i < lines.length; i++) { const line = lines[i]; const defMatch = line.match(new RegExp(`^\\s*define\\s+${funcName}\\b(?:\\s*\\((.*?)\\))?`)); if (defMatch) { const params = defMatch[1] ? defMatch[1].trim() : ''; const md = new vscode.MarkdownString(); md.appendCodeblock(`define ${funcName}(${params})`, 'tpl'); const textAfterDefine = text.substring(text.indexOf(line) + line.length); const docMatch = textAfterDefine.match(/^\s*(?:"""|''')([\s\S]*?)(?:"""|''')/); if (docMatch) { md.appendMarkdown(`\n\n---\n\n${docMatch[1].trim()}`); } return new vscode.Hover(md); } } } // 3. Cross-File Hover für from ... import const fromImportRegex = new RegExp(`from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+${importBase}\\b`); const fromMatch = text.match(fromImportRegex); if (fromMatch) { const moduleName = fromMatch[1]; if (funcName !== importBase) { const files = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**'); for (const file of files) { const doc = await vscode.workspace.openTextDocument(file); const docText = doc.getText(); if (new RegExp(`tpl\\s+1\\.\\d+\\s+module\\s+${moduleName.replace(/\./g, '\\.')}\\s*;`).test(docText)) { const docLines = docText.split('\n'); for (let i = 0; i < docLines.length; i++) { const line = docLines[i]; const defMatch = line.match(new RegExp(`^\\s*define\\s+${funcName}\\b(?:\\s*\\((.*?)\\))?`)); if (defMatch) { const params = defMatch[1] ? defMatch[1].trim() : ''; const md = new vscode.MarkdownString(); md.appendCodeblock(`define ${funcName}(${params})`, 'tpl'); const textAfterDefine = docText.substring(docText.indexOf(line) + line.length); const docMatch = textAfterDefine.match(/^\s*(?:"""|''')([\s\S]*?)(?:"""|''')/); if (docMatch) { md.appendMarkdown(`\n\n---\n\n${docMatch[1].trim()}`); } md.appendMarkdown(`\n\n*(Importiert aus \`${moduleName}\`)*`); return new vscode.Hover(md); } } } } } return new vscode.Hover(new vscode.MarkdownString(`Importierte Definitions **${importBase}** aus Modul \`${moduleName}\`.`)); } return null; } }