bmc-tpl-code-all-around/src/providers/TplHoverProvider.ts

96 lines
4.9 KiB
TypeScript

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<string, any> = { ...tplBuiltIns, ...generatedTplBuiltIns };
export class TplHoverProvider implements vscode.HoverProvider {
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | null> {
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;
}
}