Erweiterung die feste log regel fällt raus, dafür sind jetzt alle möglichen BMC TPL erweiterungen als auto complete drin
This commit is contained in:
parent
93f3179bcd
commit
a5cd6a74ca
|
|
@ -5,3 +5,6 @@ node_modules
|
|||
*.vsix
|
||||
src/*.js*
|
||||
src/test/*.js*
|
||||
out/*
|
||||
out/*/*
|
||||
out/*/*/*
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
export const tplBuiltIns: Record<string, Record<string, string>> = {
|
||||
"model": {
|
||||
"NodeKind": "Erstellt oder aktualisiert einen Knoten basierend auf der Taxonomie (z.B. model.SoftwareInstance(key:=\"...\", name:=\"...\")).",
|
||||
"destroy": "Zerstört den übergebenen Knoten dauerhaft und sofort aus der Datenbank.",
|
||||
"withdraw": "Zieht die Behauptung des aktuellen Patterns für diesen Knoten zurück (leitet das Standard-Aging ein).",
|
||||
"host": "Gibt den übergeordneten Host-Knoten zurück, der mit dem übergebenen Knoten (z.B. Prozess oder Software) verknüpft ist.",
|
||||
"addDisplayAttribute": "Fügt dem Knoten ein Attribut hinzu, das in der Web-Oberfläche ganz oben hervorgehoben wird.",
|
||||
"setRemovalGroup": "Ordnet den Knoten einer Gruppe zu. Knoten dieser Gruppe, die beim nächsten Scan nicht neu bestätigt werden, werden gelöscht.",
|
||||
"findEq": "Führt eine exakte Suche nach Knoten in der Datenbank durch (z.B. Suche nach einer bestimmten ID)."
|
||||
},
|
||||
"text": {
|
||||
"split": "Teilt einen String anhand eines Trennzeichens in eine Liste von Strings auf.",
|
||||
"join": "Verbindet eine Liste von Strings zu einem einzigen String unter Verwendung eines Trennzeichens.",
|
||||
"lower": "Konvertiert alle Zeichen eines Strings in Kleinbuchstaben.",
|
||||
"upper": "Konvertiert alle Zeichen eines Strings in Großbuchstaben.",
|
||||
"strip": "Entfernt führende und nachfolgende Leerzeichen (Whitespaces) aus einem String.",
|
||||
"replace": "Ersetzt alle Vorkommen eines Suchstrings durch einen neuen String.",
|
||||
"hasPrefix": "Prüft, ob ein String mit einem bestimmten Präfix beginnt (gibt true oder false zurück).",
|
||||
"hasSuffix": "Prüft, ob ein String mit einem bestimmten Suffix endet (gibt true oder false zurück).",
|
||||
"find": "Sucht einen Teilstring innerhalb eines Strings und gibt die numerische Position zurück (oder -1, wenn nicht gefunden).",
|
||||
"hash": "Generiert einen numerischen Hash-Wert aus dem übergebenen String (nützlich für die Key-Generierung)."
|
||||
},
|
||||
"regex": {
|
||||
"extract": "Extrahiert den ersten Treffer einer Capture-Group (in runden Klammern) aus einem String. Nimmt optional einen Fallback-Wert.",
|
||||
"match": "Prüft, ob ein regulärer Ausdruck auf einen String zutrifft (gibt true oder false zurück).",
|
||||
"findAll": "Findet alle Treffer eines regulären Ausdrucks in einem String und gibt sie als Liste zurück.",
|
||||
"replace": "Ersetzt Muster in einem String, die auf den regulären Ausdruck zutreffen, durch einen neuen String."
|
||||
},
|
||||
"discovery": {
|
||||
"runCommand": "Führt einen Kommandozeilenbefehl auf dem gescannten Zielsystem (Host) aus und gibt das Ergebnis zurück.",
|
||||
"fileGet": "Ruft den vollständigen Inhalt einer Datei (z.B. Config-File) vom Zielsystem ab.",
|
||||
"fileInfo": "Ruft nur die Metadaten (Größe, Rechte, Zeitstempel) einer Datei ab, ohne den Inhalt zu lesen.",
|
||||
"listDirectory": "Listet den Inhalt (Dateien und Unterordner) eines Verzeichnisses auf dem Zielsystem auf.",
|
||||
"registryKey": "Liest einen bestimmten Registry-Schlüssel auf einem Windows-Host aus.",
|
||||
"wmiQuery": "Führt eine WQL-Abfrage per WMI auf einem Windows-Host aus.",
|
||||
"snmpGet": "Führt einen SNMP GET-Request für eine spezifische OID aus (z.B. für Netzwerkgeräte).",
|
||||
"snmpGetNext": "Führt einen SNMP GETNEXT-Request aus.",
|
||||
"snmpGetTable": "Ruft eine komplette SNMP-Tabelle vom Zielgerät ab.",
|
||||
"databaseQuery": "Führt eine SQL-Abfrage über eine in Discovery konfigurierte Datenbank-Integration aus.",
|
||||
"restRequest": "Führt einen REST-API Request gegen ein konfiguriertes Ziel aus."
|
||||
},
|
||||
"log": {
|
||||
"debug": "Schreibt eine Debug-Nachricht in das Discovery-Log (nur bei Loglevel DEBUG sichtbar, für tiefe Fehleranalyse).",
|
||||
"info": "Schreibt eine Informationsnachricht in das Log (verfolgt den normalen Ablauf).",
|
||||
"warn": "Schreibt eine Warnung in das Log (für unkritische Fehler, z.B. wenn eine Datei leer ist).",
|
||||
"error": "Schreibt eine Fehlermeldung in das Log (für kritische Pattern-Fehler, die den Ablauf stören).",
|
||||
"critical": "Schreibt einen kritischen Systemfehler in das Log."
|
||||
},
|
||||
"time": {
|
||||
"current": "Gibt den aktuellen Zeitpunkt der Ausführung in TPL-Epochenzeit zurück.",
|
||||
"format": "Formatiert einen TPL-Zeitstempel in einen für Menschen lesbaren String (z.B. \"%Y-%m-%d %H:%M:%S\").",
|
||||
"parseUTC": "Parst einen Zeit-String als UTC-Zeit und konvertiert ihn in einen TPL-Zeitstempel.",
|
||||
"parseLocal": "Parst einen Zeit-String als lokale Zeit des Hosts und konvertiert ihn in einen TPL-Zeitstempel.",
|
||||
"delta": "Berechnet die Differenz zwischen zwei Zeitstempeln (gibt eine Dauer zurück)."
|
||||
},
|
||||
"json": {
|
||||
"decode": "Parst einen JSON-String und konvertiert ihn in durchsuchbare TPL-Datenstrukturen (Listen und Dictionaries).",
|
||||
"encode": "Konvertiert eine TPL-Datenstruktur (wie eine Liste oder ein Dictionary) zurück in einen JSON-formatierten String."
|
||||
},
|
||||
"xpath": {
|
||||
"evaluate": "Führt eine XPath-Abfrage direkt auf einem XML-String aus und gibt die gefundenen Elemente oder Texte zurück.",
|
||||
"open": "Öffnet und parst ein XML-Dokument für komplexere oder mehrfache Abfragen, was performanter als wiederholtes evaluate ist."
|
||||
},
|
||||
"hash": {
|
||||
"md5": "Generiert einen MD5-Hash aus dem übergebenen String.",
|
||||
"sha1": "Generiert einen SHA-1-Hash aus dem übergebenen String.",
|
||||
"sha256": "Generiert einen SHA-256-Hash aus dem übergebenen String."
|
||||
},
|
||||
"math": {
|
||||
"abs": "Gibt den absoluten (positiven) Wert einer Zahl zurück.",
|
||||
"ceil": "Rundet eine Zahl auf die nächste Ganzzahl auf.",
|
||||
"floor": "Rundet eine Zahl auf die nächste Ganzzahl ab.",
|
||||
"round": "Rundet eine Zahl nach kaufmännischen Regeln.",
|
||||
"min": "Gibt die kleinste Zahl aus einer übergebenen Liste von Zahlen zurück.",
|
||||
"max": "Gibt die größte Zahl aus einer übergebenen Liste von Zahlen zurück."
|
||||
},
|
||||
"net": {
|
||||
"ipFromString": "Konvertiert einen IP-String in ein internes TPL-IP-Objekt zur weiteren Verarbeitung.",
|
||||
"ipToString": "Konvertiert ein TPL-IP-Objekt zurück in einen lesbaren String.",
|
||||
"isIPv4": "Prüft, ob es sich bei dem String um eine gültige IPv4-Adresse handelt (true/false).",
|
||||
"isIPv6": "Prüft, ob es sich bei dem String um eine gültige IPv6-Adresse handelt (true/false).",
|
||||
"isMAC": "Prüft, ob der String das Format einer gültigen MAC-Adresse hat (true/false).",
|
||||
"inSubnet": "Prüft, ob eine bestimmte IP-Adresse innerhalb eines angegebenen Subnetzes liegt."
|
||||
},
|
||||
"built_in_globals": {
|
||||
"size": "Gibt die Länge oder Anzahl der Elemente einer Liste, eines Strings oder eines Dictionaries zurück.",
|
||||
"toNumber": "Konvertiert einen String (z.B. \"123\") in einen Integer (Ganzzahl).",
|
||||
"toString": "Konvertiert einen Wert (z.B. eine Zahl oder ein Objekt) in einen String.",
|
||||
"type": "Gibt den Datentyp der übergebenen Variable als String zurück (z.B. \"list\", \"string\").",
|
||||
"defined": "Prüft, ob eine Variable existiert oder ob ein Attribut an einem Knoten vorhanden ist, bevor darauf zugegriffen wird."
|
||||
}
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { tplBuiltIns } from './TplBuiltIns';
|
||||
|
||||
export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
||||
async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.CompletionItem[]> {
|
||||
|
|
@ -6,15 +7,6 @@ export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
|||
const items: vscode.CompletionItem[] = [];
|
||||
const text = document.getText();
|
||||
|
||||
// 1. Die feste "log." Regel
|
||||
if (linePrefix.endsWith('log.')) {
|
||||
['debug', 'info', 'warn', 'error'].forEach(method => {
|
||||
items.push(new vscode.CompletionItem(method, vscode.CompletionItemKind.Method));
|
||||
});
|
||||
return items; // Zwingt VS Code, nur diese 4 anzuzeigen!
|
||||
}
|
||||
|
||||
// 2. Importe und lokale Definitions-Blöcke (Namespaces) sammeln
|
||||
const importMap = new Map<string, string>();
|
||||
const importRegex = /from\s+([\w.]+)\s+import\s+(\w+)/g;
|
||||
let m;
|
||||
|
|
@ -28,11 +20,10 @@ export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
|||
localNamespaces.add(m[1]);
|
||||
}
|
||||
|
||||
// 3. Wenn wir auf freier Fläche tippen (kein Punkt davor)
|
||||
const matchDot = linePrefix.match(/([a-zA-Z0-9_]+)\.$/);
|
||||
|
||||
if (!matchDot) {
|
||||
// A) Namespaces (Importiert und Lokal) vorschlagen
|
||||
// A) Externe & Lokale Module
|
||||
importMap.forEach((_, key) => {
|
||||
const item = new vscode.CompletionItem(key, vscode.CompletionItemKind.Module);
|
||||
item.detail = "Importiertes Modul";
|
||||
|
|
@ -44,7 +35,23 @@ export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
|||
items.push(item);
|
||||
});
|
||||
|
||||
// B) Lokale Variablen vorschlagen (z.B. attribute_list :=)
|
||||
// B) Standard BMC Namespaces (text, regex, discovery etc.)
|
||||
for (const key of Object.keys(tplBuiltIns)) {
|
||||
if (key !== 'built_in_globals') {
|
||||
const item = new vscode.CompletionItem(key, vscode.CompletionItemKind.Module);
|
||||
item.detail = "BMC Built-In Namespace";
|
||||
items.push(item);
|
||||
} else {
|
||||
for (const glob of Object.keys(tplBuiltIns[key])) {
|
||||
const item = new vscode.CompletionItem(glob, vscode.CompletionItemKind.Function);
|
||||
item.detail = "BMC Globale Funktion";
|
||||
item.documentation = new vscode.MarkdownString(tplBuiltIns[key][glob]);
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// C) Lokale Variablen
|
||||
const varRegex = /\b([a-zA-Z0-9_]+)\s*:=/g;
|
||||
const variables = new Set<string>();
|
||||
while ((m = varRegex.exec(text)) !== null) {
|
||||
|
|
@ -54,39 +61,20 @@ export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
|||
items.push(new vscode.CompletionItem(v, vscode.CompletionItemKind.Variable));
|
||||
});
|
||||
|
||||
// C) Das große Skeleton Snippet
|
||||
// D) Snippets
|
||||
const skeletonItem = new vscode.CompletionItem('tpl_skeleton', vscode.CompletionItemKind.Snippet);
|
||||
skeletonItem.insertText = new vscode.SnippetString(
|
||||
'tpl 1.23 module ${1:module_name};\n\n' +
|
||||
'metadata\n' +
|
||||
' origin := \'${2:origin}\';\n' +
|
||||
' author := \'${3:author}\';\n' +
|
||||
' products := \'${4:products}\';\n' +
|
||||
' publishers := \'${5:publishers}\';\n' +
|
||||
' tree_path := \'${6:company}\', \'${7:category}\', \'${8:subcategory}\';\n' +
|
||||
'end metadata;\n\n' +
|
||||
'pattern ${9:pattern_name} 1.0\n' +
|
||||
' """\n' +
|
||||
' TODO: explain\n' +
|
||||
' """\n\n' +
|
||||
' triggers\n' +
|
||||
' on process := DiscoveredProcess where cmd matches regex_escape("${10:process}");\n' +
|
||||
' end triggers;\n\n' +
|
||||
' body\n' +
|
||||
' log.debug("Start pattern ${9:pattern_name}");\n' +
|
||||
' $0\n' +
|
||||
' end body;\n' +
|
||||
'end pattern;'
|
||||
'metadata\n origin := \'${2:origin}\';\n author := \'${3:author}\';\n products := \'${4:products}\';\n publishers := \'${5:publishers}\';\n tree_path := \'${6:company}\', \'${7:category}\', \'${8:subcategory}\';\nend metadata;\n\n' +
|
||||
'pattern ${9:pattern_name} 1.0\n """\n TODO: explain\n """\n\n triggers\n on process := DiscoveredProcess where cmd matches regex_escape("${10:process}");\n end triggers;\n\n body\n log.debug("Start pattern ${9:pattern_name}");\n $0\n end body;\nend pattern;'
|
||||
);
|
||||
skeletonItem.detail = "Generiert das komplette TPL Modul-Grundgerüst";
|
||||
items.push(skeletonItem);
|
||||
|
||||
// D) Block-Snippets
|
||||
const blocks = ['metadata', 'overview', 'constants', 'triggers', 'body', 'mapping', 'removal', 'definitions', 'enumeration', 'configuration'];
|
||||
for (const block of blocks) {
|
||||
const item = new vscode.CompletionItem(block, vscode.CompletionItemKind.Snippet);
|
||||
item.insertText = new vscode.SnippetString(`${block}\n $0\nend ${block};`);
|
||||
item.detail = `Fügt ${block} Block mit 'end' ein`;
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
|
|
@ -106,39 +94,36 @@ export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
|||
tableItem.insertText = new vscode.SnippetString('table ${1:name}${2:1.0}\n $0\n default -> "unknown";\nend table;');
|
||||
items.push(tableItem);
|
||||
|
||||
const ifItem = new vscode.CompletionItem('if', vscode.CompletionItemKind.Snippet);
|
||||
ifItem.insertText = new vscode.SnippetString('if ${1:condition} then\n $0\nend if;');
|
||||
items.push(ifItem);
|
||||
|
||||
const forItem = new vscode.CompletionItem('for', vscode.CompletionItemKind.Snippet);
|
||||
forItem.insertText = new vscode.SnippetString('for ${1:item} in${2:list} do\n $0\nend for;');
|
||||
items.push(forItem);
|
||||
|
||||
return items;
|
||||
} else {
|
||||
// 4. Wir haben nach einem Punkt getippt (z.B. helpers.)
|
||||
const ns = matchDot[1];
|
||||
const moduleMethods: vscode.CompletionItem[] = [];
|
||||
|
||||
// Regex sucht nach dem Wort direkt vor der Klammer: define [add] method_name(
|
||||
// NEU: Ist es ein BMC Built-In Namespace? (z.B. text. oder log.)
|
||||
if (tplBuiltIns[ns]) {
|
||||
const bmcMethods: vscode.CompletionItem[] = [];
|
||||
for (const [method, desc] of Object.entries(tplBuiltIns[ns])) {
|
||||
const item = new vscode.CompletionItem(method, vscode.CompletionItemKind.Method);
|
||||
item.documentation = new vscode.MarkdownString(desc);
|
||||
bmcMethods.push(item);
|
||||
}
|
||||
return bmcMethods;
|
||||
}
|
||||
|
||||
const moduleMethods: vscode.CompletionItem[] = [];
|
||||
const extractDefineRegex = /^\s*define\s+.*?\b([a-zA-Z0-9_]+)\s*\(/gm;
|
||||
|
||||
// Fall A: Lokaler Definitions-Block (gleiche Datei!)
|
||||
if (localNamespaces.has(ns)) {
|
||||
let defMatch;
|
||||
while ((defMatch = extractDefineRegex.exec(text)) !== null) {
|
||||
const methodName = defMatch[1];
|
||||
const item = new vscode.CompletionItem(methodName, vscode.CompletionItemKind.Method);
|
||||
const docStr = this.extractDocString(text, defMatch.index);
|
||||
if (docStr) {
|
||||
item.documentation = new vscode.MarkdownString(docStr);
|
||||
}
|
||||
if (docStr) item.documentation = new vscode.MarkdownString(docStr);
|
||||
moduleMethods.push(item);
|
||||
}
|
||||
return moduleMethods;
|
||||
}
|
||||
|
||||
// Fall B: Importiertes Modul (externe Datei)
|
||||
if (importMap.has(ns)) {
|
||||
const modPath = importMap.get(ns)!;
|
||||
const uris = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**', 50);
|
||||
|
|
@ -146,28 +131,21 @@ export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
|||
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)) {
|
||||
let defMatch;
|
||||
while ((defMatch = extractDefineRegex.exec(docText)) !== null) {
|
||||
const methodName = defMatch[1];
|
||||
const item = new vscode.CompletionItem(methodName, vscode.CompletionItemKind.Method);
|
||||
|
||||
const docStr = this.extractDocString(docText, defMatch.index);
|
||||
if (docStr) {
|
||||
item.documentation = new vscode.MarkdownString(docStr);
|
||||
}
|
||||
if (docStr) item.documentation = new vscode.MarkdownString(docStr);
|
||||
moduleMethods.push(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
if (moduleMethods.length > 0) {
|
||||
return moduleMethods;
|
||||
}
|
||||
if (moduleMethods.length > 0) return moduleMethods;
|
||||
}
|
||||
}
|
||||
return items;
|
||||
|
|
@ -178,32 +156,23 @@ export class TplCompletionProvider implements vscode.CompletionItemProvider {
|
|||
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;
|
||||
}
|
||||
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 { break; }
|
||||
} else {
|
||||
if (line.endsWith(quoteType)) {
|
||||
const content = line.substring(0, line.length - 3).trim();
|
||||
if (content) docString += content;
|
||||
break;
|
||||
} else {
|
||||
docString += line + "\n";
|
||||
}
|
||||
} else { docString += line + "\n"; }
|
||||
}
|
||||
}
|
||||
return docString.trim() || null;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
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> {
|
||||
|
|
@ -17,32 +18,34 @@ export class TplHoverProvider implements vscode.HoverProvider {
|
|||
methodOrPatternName = parts[1];
|
||||
}
|
||||
|
||||
// FEHLERBEHEBUNG: [ \t] statt \s verwenden!
|
||||
// \s frisst Zeilenumbrüche (\n), was dazu geführt hat, dass match.index
|
||||
// auf leere Zeilen oberhalb der Methode gezeigt hat -> Leeres Hover-Fenster.
|
||||
const searchRegex = new RegExp(`^[ \\t]*(?:(?:define[ \\t]+(?:[a-zA-Z0-9_]+[ \\t]+)*${methodOrPatternName}[ \\t]*\\()|(?:(?:pattern|syncmapping|table|enumeration|configuration)[ \\t]+${methodOrPatternName}\\b))`, 'm');
|
||||
|
||||
// 1. Suche im aktuellen Dokument (Ohne Namespace)
|
||||
if (!namespace) {
|
||||
const match = searchRegex.exec(currentText);
|
||||
if (match) {
|
||||
return this.buildHover(currentText, match.index);
|
||||
}
|
||||
return null; // Bricht sauber ab, ohne das System zu blockieren
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 2. Suche mit Namespace (z.B. helpers.method)
|
||||
// A) Ist der Namespace in derselben Datei? (lokaler definitions block)
|
||||
const localDefRegex = new RegExp(`^[ \\t]*definitions[ \\t]+${namespace}\\b`, 'm');
|
||||
if (localDefRegex.test(currentText)) {
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// B) Ist der Namespace importiert? (Workspace durchsuchen)
|
||||
const importRegex = new RegExp(`from\\s+([\\w.]+)\\s+import\\s+${namespace}\\b`);
|
||||
const importMatch = importRegex.exec(currentText);
|
||||
|
||||
|
|
@ -53,35 +56,25 @@ export class TplHoverProvider implements vscode.HoverProvider {
|
|||
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);
|
||||
}
|
||||
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();
|
||||
|
||||
// Schneidet exakt die Code-Zeile aus (ohne vorausgehende Leerzeilen)
|
||||
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)*`);
|
||||
}
|
||||
if (docStr) markdown.appendMarkdown(`---\n*${docStr}*`);
|
||||
else markdown.appendMarkdown(`---\n*(Keine Dokumentation gefunden)*`);
|
||||
return new vscode.Hover(markdown);
|
||||
}
|
||||
|
||||
|
|
@ -90,32 +83,23 @@ export class TplHoverProvider implements vscode.HoverProvider {
|
|||
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;
|
||||
}
|
||||
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 { break; }
|
||||
} else {
|
||||
if (line.endsWith(quoteType)) {
|
||||
const content = line.substring(0, line.length - 3).trim();
|
||||
if (content) docString += content;
|
||||
break;
|
||||
} else {
|
||||
docString += line + "\n";
|
||||
}
|
||||
} else { docString += line + "\n"; }
|
||||
}
|
||||
}
|
||||
return docString.trim() || null;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { TplCodeActionProvider } from './TplCodeActionProvider';
|
|||
import { TplDocumentFormatter } from './TplDocumentFormatter';
|
||||
import { TplCompletionProvider } from './TplCompletionProvider';
|
||||
import { TplHoverProvider } from './TplHoverProvider';
|
||||
import { tplBuiltIns } from './TplBuiltIns';
|
||||
|
||||
const SUPPORTED_TPL_VERSION = 23;
|
||||
const warnedDocuments = new Set<string>();
|
||||
|
|
@ -150,6 +151,21 @@ function validateTplDocument(document: vscode.TextDocument, collection: vscode.D
|
|||
}
|
||||
}
|
||||
|
||||
// Linter-Check für falsche BMC Built-In Methoden
|
||||
const callMatch = rawLine.match(/\b(text|regex|discovery|log|time|json|xpath|hash|math|net)\.([a-zA-Z0-9_]+)\b/g);
|
||||
if (callMatch && !inMultiLineComment && !isInternalStringLine) {
|
||||
for (const call of callMatch) {
|
||||
const parts = call.split('.');
|
||||
const ns = parts[0];
|
||||
const method = parts[1];
|
||||
|
||||
if (tplBuiltIns[ns] && !tplBuiltIns[ns][method]) {
|
||||
const idx = rawLine.indexOf(call);
|
||||
addDiag(i, idx, idx + call.length, `Unbekannte Methode '${method}' im Namespace '${ns}'.`, vscode.DiagnosticSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasModuleDef) {
|
||||
if (line.startsWith('tpl ')) { hasModuleDef = true; }
|
||||
else { addDiag(i, 0, rawLine.length, "Jede Datei muss mit 'tpl 1.x module <name>;' beginnen.", vscode.DiagnosticSeverity.Error); hasModuleDef = true; }
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"mocha"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"strict": true, /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
|
|
|
|||
Loading…
Reference in New Issue