2026-10-11 04:11:11 +00:00
|
|
|
import * as vscode from 'vscode';
|
|
|
|
|
import { CompletionStrategy } from './CompletionStrategy';
|
|
|
|
|
|
|
|
|
|
export class LocalContextCompletion implements CompletionStrategy {
|
2026-10-12 19:43:46 +00:00
|
|
|
public async provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): Promise<vscode.CompletionItem[] | undefined> {
|
|
|
|
|
const text = document.getText();
|
|
|
|
|
const items: vscode.CompletionItem[] = [];
|
|
|
|
|
|
|
|
|
|
// 1. WENN DER USER NACH EINEM PUNKT TIPPT (Cross-File Lookup)
|
|
|
|
|
const dotMatch = linePrefix.match(/\b([a-zA-Z0-9_]+)\.$/);
|
|
|
|
|
if (dotMatch) {
|
|
|
|
|
const prefix = dotMatch[1]; // z.B. "modelling"
|
|
|
|
|
if (prefix === 'log' || prefix === 'discovery') return undefined; // Built-ins ignorieren
|
|
|
|
|
|
|
|
|
|
// Prüfen, ob der Präfix importiert wurde
|
|
|
|
|
const importRegex = new RegExp(`from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+${prefix}\\b`);
|
|
|
|
|
const match = importRegex.exec(text);
|
|
|
|
|
if (match) {
|
|
|
|
|
const moduleName = match[1]; // z.B. "Company.software_modelling"
|
|
|
|
|
const externalItems = await this.resolveImportedMethods(moduleName, prefix);
|
|
|
|
|
return externalItems; // Nur externe Methoden zurückgeben
|
|
|
|
|
}
|
|
|
|
|
return undefined; // Unbekannter Namespace
|
|
|
|
|
}
|
2026-10-11 04:11:11 +00:00
|
|
|
|
2026-10-12 19:43:46 +00:00
|
|
|
// 2. LOKALE DEFINITIONS & IMPORTS PARSEN
|
|
|
|
|
const lines = text.split('\n');
|
|
|
|
|
let currentDefBlock = "";
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
|
|
const line = lines[i];
|
|
|
|
|
const defBlockMatch = line.match(/^\s*definitions?\s+([a-zA-Z0-9_]+)/);
|
|
|
|
|
if (defBlockMatch) { currentDefBlock = defBlockMatch[1]; continue; }
|
|
|
|
|
if (line.match(/^\s*end\s+definitions?/)) { currentDefBlock = ""; continue; }
|
|
|
|
|
|
|
|
|
|
const defineMatch = line.match(/^\s*define\s+([a-zA-Z0-9_]+)\b/);
|
|
|
|
|
if (defineMatch) {
|
|
|
|
|
const funcName = defineMatch[1];
|
|
|
|
|
const fullName = currentDefBlock ? `${currentDefBlock}.${funcName}` : funcName;
|
|
|
|
|
const item = new vscode.CompletionItem(fullName, vscode.CompletionItemKind.Function);
|
|
|
|
|
item.detail = currentDefBlock ? `Local Function in '${currentDefBlock}'` : `Local Function`;
|
|
|
|
|
item.insertText = new vscode.SnippetString(`${fullName}(\${1:params});$0`);
|
|
|
|
|
items.push(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Imports für die globale Worterkennung bereitstellen
|
|
|
|
|
const fromImportRegex = /from\s+([a-zA-Z0-9_.]+)\s+import\s+([a-zA-Z0-9_]+)/g;
|
|
|
|
|
let matchVar;
|
|
|
|
|
while ((matchVar = fromImportRegex.exec(text)) !== null) {
|
|
|
|
|
const item = new vscode.CompletionItem(matchVar[2], vscode.CompletionItemKind.Module);
|
|
|
|
|
item.detail = `Imported Definitions from ${matchVar[1]}`;
|
|
|
|
|
items.push(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const varRegex = /([a-zA-Z0-9_]+)\s*:=/g;
|
|
|
|
|
const foundVars = new Set<string>();
|
|
|
|
|
while ((matchVar = varRegex.exec(text)) !== null) {
|
|
|
|
|
if (matchVar[1] !== 'host') { foundVars.add(matchVar[1]); }
|
|
|
|
|
}
|
|
|
|
|
for (const v of foundVars) {
|
|
|
|
|
items.push(new vscode.CompletionItem(v, vscode.CompletionItemKind.Variable));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return items;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hilfsmethode: Sucht im Workspace nach der anderen Datei!
|
|
|
|
|
private async resolveImportedMethods(moduleName: string, defBlockName: string): Promise<vscode.CompletionItem[]> {
|
|
|
|
|
const items: vscode.CompletionItem[] = [];
|
|
|
|
|
// Sucht alle .tpl Dateien im Projekt
|
|
|
|
|
const files = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**');
|
|
|
|
|
|
|
|
|
|
for (const file of files) {
|
|
|
|
|
const doc = await vscode.workspace.openTextDocument(file);
|
|
|
|
|
const text = doc.getText();
|
|
|
|
|
|
|
|
|
|
// Ist das das gesuchte Modul? (Punkte escapen)
|
|
|
|
|
const moduleRegex = new RegExp(`tpl\\s+1\\.\\d+\\s+module\\s+${moduleName.replace(/\./g, '\\.')}\\s*;`);
|
|
|
|
|
if (moduleRegex.test(text)) {
|
|
|
|
|
// Modul gefunden! Jetzt die Defines im entsprechenden Block auslesen
|
|
|
|
|
const lines = text.split('\n');
|
|
|
|
|
let inTargetBlock = false;
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
if (line.match(new RegExp(`^\\s*definitions?\\s+${defBlockName}\\b`))) { inTargetBlock = true; continue; }
|
|
|
|
|
if (inTargetBlock && line.match(/^\s*end\s+definitions?/)) { inTargetBlock = false; break; }
|
|
|
|
|
|
|
|
|
|
if (inTargetBlock) {
|
|
|
|
|
const defineMatch = line.match(/^\s*define\s+([a-zA-Z0-9_]+)\b/);
|
|
|
|
|
if (defineMatch) {
|
|
|
|
|
const funcName = defineMatch[1];
|
|
|
|
|
const item = new vscode.CompletionItem(funcName, vscode.CompletionItemKind.Function);
|
|
|
|
|
item.detail = `Method from ${moduleName}`;
|
|
|
|
|
item.insertText = new vscode.SnippetString(`${funcName}(\${1:params});$0`);
|
|
|
|
|
items.push(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break; // Datei gefunden und geparst, wir können abbrechen
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return items;
|
2026-10-11 04:11:11 +00:00
|
|
|
}
|
|
|
|
|
}
|