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

107 lines
5.0 KiB
TypeScript
Raw Normal View History

import * as vscode from 'vscode';
import { tplBuiltIns } from './TplBuiltIns';
export class TplHoverProvider implements vscode.HoverProvider {
async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | null> {
const wordRange = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_.]+/);
if (!wordRange) return null;
const fullWord = document.getText(wordRange);
const currentText = document.getText();
let namespace = "";
let methodOrPatternName = fullWord;
if (fullWord.includes('.')) {
const parts = fullWord.split('.');
namespace = parts[0];
methodOrPatternName = parts[1];
}
// NEU: BMC Built-Ins abfangen und direkt ausgeben!
if (namespace && tplBuiltIns[namespace] && tplBuiltIns[namespace][methodOrPatternName]) {
const markdown = new vscode.MarkdownString();
markdown.appendCodeblock(`${namespace}.${methodOrPatternName}`, 'tpl');
markdown.appendMarkdown(`---\n*${tplBuiltIns[namespace][methodOrPatternName]}*`);
return new vscode.Hover(markdown);
} else if (!namespace && tplBuiltIns['built_in_globals'][methodOrPatternName]) {
const markdown = new vscode.MarkdownString();
markdown.appendCodeblock(`${methodOrPatternName}`, 'tpl');
markdown.appendMarkdown(`---\n*${tplBuiltIns['built_in_globals'][methodOrPatternName]}*`);
return new vscode.Hover(markdown);
}
const searchRegex = new RegExp(`^[ \\t]*(?:(?:define[ \\t]+(?:[a-zA-Z0-9_]+[ \\t]+)*${methodOrPatternName}[ \\t]*\\()|(?:(?:pattern|syncmapping|table|enumeration|configuration)[ \\t]+${methodOrPatternName}\\b))`, 'm');
if (!namespace) {
const match = searchRegex.exec(currentText);
if (match) return this.buildHover(currentText, match.index);
return null;
}
const localDefRegex = new RegExp(`^[ \\t]*definitions[ \\t]+${namespace}\\b`, 'm');
if (localDefRegex.test(currentText)) {
const match = searchRegex.exec(currentText);
if (match) return this.buildHover(currentText, match.index);
return null;
}
const importRegex = new RegExp(`from\\s+([\\w.]+)\\s+import\\s+${namespace}\\b`);
const importMatch = importRegex.exec(currentText);
if (importMatch) {
const modPath = importMatch[1];
const uris = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**', 50);
for (const uri of uris) {
try {
const docBytes = await vscode.workspace.fs.readFile(uri);
const docText = Buffer.from(docBytes).toString('utf8');
const modRegex = new RegExp(`tpl\\s+1\\.\\d+\\s+module\\s+${modPath.replace(/\./g, '\\\\.')}\\s*;`);
if (modRegex.test(docText)) {
const match = searchRegex.exec(docText);
if (match) return this.buildHover(docText, match.index);
break;
}
} catch(e) {}
}
}
return null;
}
private buildHover(text: string, matchIndex: number): vscode.Hover {
const docStr = this.extractDocString(text, matchIndex);
const markdown = new vscode.MarkdownString();
const declLine = text.substring(matchIndex).split('\n')[0].trim();
markdown.appendCodeblock(declLine, 'tpl');
if (docStr) markdown.appendMarkdown(`---\n*${docStr}*`);
else markdown.appendMarkdown(`---\n*(Keine Dokumentation gefunden)*`);
return new vscode.Hover(markdown);
}
private extractDocString(text: string, startIndex: number): string | null {
const lines = text.substring(startIndex).split('\n');
let docString = "";
let inDocString = false;
let quoteType = "";
for (let j = 1; j < Math.min(15, lines.length); j++) {
const line = lines[j].trim();
if (line.length === 0) continue;
if (!inDocString) {
if (line.startsWith('"""') || line.startsWith("'''")) {
inDocString = true;
quoteType = line.substring(0, 3);
if (line.length > 3 && line.endsWith(quoteType)) { docString = line.substring(3, line.length - 3).trim(); break; }
const content = line.substring(3).trim();
if (content) docString += content + "\n";
} else { break; }
} else {
if (line.endsWith(quoteType)) {
const content = line.substring(0, line.length - 3).trim();
if (content) docString += content;
break;
} else { docString += line + "\n"; }
}
}
return docString.trim() || null;
}
}