import * as vscode from 'vscode'; 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); // z.B. "modelling.testDefine" 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. 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); } } } // 2. 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]; // Wenn der User über die Methode fährt (modelling.XYZ) 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); } } } } } // Wenn er nur über "modelling" fährt return new vscode.Hover(new vscode.MarkdownString(`Importierte Definitions **${importBase}** aus Modul \`${moduleName}\`.`)); } // 3. Hover für Built-Ins if (word.startsWith('log.')) { return new vscode.Hover(new vscode.MarkdownString(`TPL Logging-Modul`)); } if (word.startsWith('discovery.')) { return new vscode.Hover(new vscode.MarkdownString(`TPL Discovery-Modul`)); } return null; } }