multi line column support, smarter import "from", test changes
This commit is contained in:
parent
f3506cc10b
commit
1c160c3e1b
|
|
@ -14,6 +14,10 @@ export class LinterContext {
|
||||||
public inMultiLineComment = false;
|
public inMultiLineComment = false;
|
||||||
public inConditionBlock = false;
|
public inConditionBlock = false;
|
||||||
|
|
||||||
|
// Multi-Line Import-Tracking
|
||||||
|
public inImportBlock = false;
|
||||||
|
public currentImportModule = "";
|
||||||
|
|
||||||
// String-Tracking (Basis-Lexer)
|
// String-Tracking (Basis-Lexer)
|
||||||
public inTripleDouble = false;
|
public inTripleDouble = false;
|
||||||
public inTripleSingle = false;
|
public inTripleSingle = false;
|
||||||
|
|
@ -48,4 +52,4 @@ export class LinterContext {
|
||||||
this.justOpenedString = false;
|
this.justOpenedString = false;
|
||||||
this.isClosingString = false;
|
this.isClosingString = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export class WorkspaceIndex {
|
||||||
}
|
}
|
||||||
this.fileModules.set(fsPath, currentModule);
|
this.fileModules.set(fsPath, currentModule);
|
||||||
|
|
||||||
// 2. Definitions-Blöcke und Funktionen parsen
|
// 2. Blöcke und Funktionen parsen
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
|
|
||||||
|
|
@ -50,6 +50,12 @@ export class WorkspaceIndex {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Patterns, Syncmappings und Tabellen für den Import freigeben
|
||||||
|
const structMatch = line.match(/^\s*(?:pattern|syncmapping|table)\s+([a-zA-Z0-9_]+)/);
|
||||||
|
if (structMatch) {
|
||||||
|
defBlocks.add(structMatch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
// Sucht nach 'define name(param1, param2)'
|
// Sucht nach 'define name(param1, param2)'
|
||||||
const defineMatch = line.match(/^\s*define\s+([a-zA-Z0-9_]+)\b(?:\s*\((.*?)\))?/);
|
const defineMatch = line.match(/^\s*define\s+([a-zA-Z0-9_]+)\b(?:\s*\((.*?)\))?/);
|
||||||
if (defineMatch) {
|
if (defineMatch) {
|
||||||
|
|
@ -116,4 +122,4 @@ export class WorkspaceIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const workspaceIndex = new WorkspaceIndex();
|
export const workspaceIndex = new WorkspaceIndex();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { LinterRule } from './LinterRule';
|
import { LinterRule } from './LinterRule';
|
||||||
import { LinterContext } from '../LinterContext';
|
import { LinterContext } from '../LinterContext';
|
||||||
|
import { workspaceIndex } from '../WorkspaceIndex';
|
||||||
|
|
||||||
export class SyntaxRule implements LinterRule {
|
export class SyntaxRule implements LinterRule {
|
||||||
|
|
||||||
|
|
@ -13,6 +14,43 @@ export class SyntaxRule implements LinterRule {
|
||||||
|
|
||||||
if (codePart.length === 0) {return;}
|
if (codePart.length === 0) {return;}
|
||||||
|
|
||||||
|
// --- MULTI-LINE IMPORT HANDLING & VALIDATION ---
|
||||||
|
if (codePart.startsWith('from ')) {
|
||||||
|
const match = codePart.match(/^from\s+([a-zA-Z0-9_.]+)\s+import/);
|
||||||
|
if (match) {
|
||||||
|
ctx.inImportBlock = true;
|
||||||
|
ctx.currentImportModule = match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.inImportBlock) {
|
||||||
|
// Remove 'from X import' and any trailing semicolons
|
||||||
|
const importString = codePart.replace(/^from\s+[a-zA-Z0-9_.]+\s+import/, '').replace(/;$/, '');
|
||||||
|
const imports = importString.split(',').filter(i => i.trim().length > 0);
|
||||||
|
|
||||||
|
// Check against RAM index
|
||||||
|
const availableItems = workspaceIndex.getDefinitionBlocksForModule(ctx.currentImportModule);
|
||||||
|
|
||||||
|
// Only warn if we actually have the module in RAM, but the specific item is missing
|
||||||
|
if (availableItems.length > 0) {
|
||||||
|
for (const imp of imports) {
|
||||||
|
const cleanImp = imp.trim().split(/\s+/)[0]; // Trims version number (e.g. "HostToPoolMember 2.0" -> "HostToPoolMember")
|
||||||
|
if (cleanImp.length > 0 && !availableItems.includes(cleanImp)) {
|
||||||
|
const idx = rawLine.indexOf(cleanImp);
|
||||||
|
ctx.addDiag(lineIdx, Math.max(0, idx), Math.max(0, idx) + cleanImp.length, `The item '${cleanImp}' does not exist in module '${ctx.currentImportModule}'.`, vscode.DiagnosticSeverity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the line ends with a semicolon, the import block is closed
|
||||||
|
if (codePart.endsWith(';')) {
|
||||||
|
ctx.inImportBlock = false;
|
||||||
|
ctx.currentImportModule = "";
|
||||||
|
}
|
||||||
|
return; // Skip all other syntax checks for lines inside an import block
|
||||||
|
}
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
let isStructuralKeyword = /^(tpl|pattern|syncmapping|table|definitions?|define|enumeration|configuration|metadata|overview|constants|triggers|mapping|removal|body|end|on|tags|origin|author|products|publishers|tree_path|import|from)\b/.test(codePart);
|
let isStructuralKeyword = /^(tpl|pattern|syncmapping|table|definitions?|define|enumeration|configuration|metadata|overview|constants|triggers|mapping|removal|body|end|on|tags|origin|author|products|publishers|tree_path|import|from)\b/.test(codePart);
|
||||||
|
|
||||||
if (codePart.startsWith('"') || codePart.startsWith("'")) {
|
if (codePart.startsWith('"') || codePart.startsWith("'")) {
|
||||||
|
|
@ -31,9 +69,9 @@ export class SyntaxRule implements LinterRule {
|
||||||
ctx.inBlock === 'definitions' ||
|
ctx.inBlock === 'definitions' ||
|
||||||
ctx.openDefines.length > 0;
|
ctx.openDefines.length > 0;
|
||||||
|
|
||||||
// 'then' und 'do' auf alleinstehenden Zeilen sind legitimer Code
|
// 'then' and 'do' on standalone lines are legitimate code
|
||||||
if (!isStructuralKeyword && !isValidExecutionArea && !/^(then|do)\b/.test(codePart)) {
|
if (!isStructuralKeyword && !isValidExecutionArea && !/^(then|do)\b/.test(codePart)) {
|
||||||
ctx.addDiag(lineIdx, 0, rawLine.length, "Ausführbarer Code ist hier nicht erlaubt. (Erlaubt in: 'body', 'triggers', 'constants', 'overview', 'mapping', 'metadata', 'definitions' oder 'define').", vscode.DiagnosticSeverity.Error);
|
ctx.addDiag(lineIdx, 0, rawLine.length, "Executable code is not allowed here. (Allowed in: 'body', 'triggers', 'constants', 'overview', 'mapping', 'metadata', 'definitions', or 'define').", vscode.DiagnosticSeverity.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkBrackets(codePart, rawLine, lineIdx, ctx);
|
this.checkBrackets(codePart, rawLine, lineIdx, ctx);
|
||||||
|
|
@ -42,14 +80,14 @@ export class SyntaxRule implements LinterRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
public checkEnd(lines: string[], ctx: LinterContext): void {
|
public checkEnd(lines: string[], ctx: LinterContext): void {
|
||||||
for (const unclosedLine of ctx.parenStack) { ctx.addDiag(unclosedLine, 0, lines[unclosedLine].length, "Öffnende Klammer '(' wird nicht geschlossen!", vscode.DiagnosticSeverity.Error); }
|
for (const unclosedLine of ctx.parenStack) { ctx.addDiag(unclosedLine, 0, lines[unclosedLine].length, "Opening parenthesis '(' is not closed!", vscode.DiagnosticSeverity.Error); }
|
||||||
for (const unclosedLine of ctx.bracketStack) { ctx.addDiag(unclosedLine, 0, lines[unclosedLine].length, "Öffnende eckige Klammer '[' wird nicht geschlossen!", vscode.DiagnosticSeverity.Error); }
|
for (const unclosedLine of ctx.bracketStack) { ctx.addDiag(unclosedLine, 0, lines[unclosedLine].length, "Opening bracket '[' is not closed!", vscode.DiagnosticSeverity.Error); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkTabs(rawLine: string, lineIdx: number, ctx: LinterContext) {
|
private checkTabs(rawLine: string, lineIdx: number, ctx: LinterContext) {
|
||||||
const tabIndex = rawLine.indexOf('\t');
|
const tabIndex = rawLine.indexOf('\t');
|
||||||
if (tabIndex !== -1) {
|
if (tabIndex !== -1) {
|
||||||
const diag = ctx.addDiag(lineIdx, tabIndex, tabIndex + 1, "Bitte Tabs durch Spaces ersetzen", vscode.DiagnosticSeverity.Warning);
|
const diag = ctx.addDiag(lineIdx, tabIndex, tabIndex + 1, "Please replace tabs with spaces", vscode.DiagnosticSeverity.Warning);
|
||||||
diag.code = 'invalid_tab';
|
diag.code = 'invalid_tab';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,12 +98,12 @@ export class SyntaxRule implements LinterRule {
|
||||||
if (char === '(') { ctx.parenStack.push(lineIdx); }
|
if (char === '(') { ctx.parenStack.push(lineIdx); }
|
||||||
else if (char === ')') {
|
else if (char === ')') {
|
||||||
if (ctx.parenStack.length > 0) { ctx.parenStack.pop(); }
|
if (ctx.parenStack.length > 0) { ctx.parenStack.pop(); }
|
||||||
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Unerwartete schließende Klammer ')'. Es fehlt eine öffnende Klammer.", vscode.DiagnosticSeverity.Error); }
|
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Unexpected closing parenthesis ')'. Missing an opening parenthesis.", vscode.DiagnosticSeverity.Error); }
|
||||||
}
|
}
|
||||||
else if (char === '[') { ctx.bracketStack.push(lineIdx); }
|
else if (char === '[') { ctx.bracketStack.push(lineIdx); }
|
||||||
else if (char === ']') {
|
else if (char === ']') {
|
||||||
if (ctx.bracketStack.length > 0) { ctx.bracketStack.pop(); }
|
if (ctx.bracketStack.length > 0) { ctx.bracketStack.pop(); }
|
||||||
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Unerwartete schließende eckige Klammer ']'. Es fehlt eine öffnende Klammer.", vscode.DiagnosticSeverity.Error); }
|
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Unexpected closing bracket ']'. Missing an opening bracket.", vscode.DiagnosticSeverity.Error); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +115,7 @@ export class SyntaxRule implements LinterRule {
|
||||||
if (!ctx.inConditionBlock && ctx.parenStack.length === 0 && ctx.bracketStack.length === 0 && !/\b(matches)\b/.test(codePart)) {
|
if (!ctx.inConditionBlock && ctx.parenStack.length === 0 && ctx.bracketStack.length === 0 && !/\b(matches)\b/.test(codePart)) {
|
||||||
const idx = rawLine.indexOf('=');
|
const idx = rawLine.indexOf('=');
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
ctx.addDiag(lineIdx, idx, idx + 1, "Falsche Zuweisung. In TPL wird ':=' für Zuweisungen genutzt.", vscode.DiagnosticSeverity.Error);
|
ctx.addDiag(lineIdx, idx, idx + 1, "Invalid assignment. In TPL, ':=' is used for assignments.", vscode.DiagnosticSeverity.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -88,20 +126,22 @@ export class SyntaxRule implements LinterRule {
|
||||||
private checkSemicolons(codePart: string, rawLine: string, lineIdx: number, ctx: LinterContext, originalTrimmedLine: string) {
|
private checkSemicolons(codePart: string, rawLine: string, lineIdx: number, ctx: LinterContext, originalTrimmedLine: string) {
|
||||||
if (ctx.isClosingString) {
|
if (ctx.isClosingString) {
|
||||||
if (ctx.stringNeedsSemicolon && !originalTrimmedLine.endsWith(';')) {
|
if (ctx.stringNeedsSemicolon && !originalTrimmedLine.endsWith(';')) {
|
||||||
ctx.addDiag(lineIdx, Math.max(0, rawLine.length - 1), rawLine.length, "Fehlendes Semikolon am Ende der Zuweisung.", vscode.DiagnosticSeverity.Error);
|
ctx.addDiag(lineIdx, Math.max(0, rawLine.length - 1), rawLine.length, "Missing semicolon at the end of the assignment.", vscode.DiagnosticSeverity.Error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// "then" and "do" can stand alone without a semicolon requirement
|
||||||
const noSemicolonNeeded = /^(pattern|syncmapping|traverse|table|enumeration|configuration|definitions?|define|metadata|overview|constants|triggers|mapping|removal|body|end\s+|if\s+|else|elif\s+|for\s+|while\s+|then\b|do\b|\/\/|\/\*|\*)/.test(codePart);
|
const noSemicolonNeeded = /^(pattern|syncmapping|traverse|table|enumeration|configuration|definitions?|define|metadata|overview|constants|triggers|mapping|removal|body|end\s+|if\s+|else|elif\s+|for\s+|while\s+|then\b|do\b|\/\/|\/\*|\*)/.test(codePart);
|
||||||
|
|
||||||
|
// "in" (for loops) is a valid line break
|
||||||
const continuesOnNextLine = /(,|and|or|not|\+|-|\*|\/|=|:=|matches|in)$/i.test(codePart);
|
const continuesOnNextLine = /(,|and|or|not|\+|-|\*|\/|=|:=|matches|in)$/i.test(codePart);
|
||||||
|
|
||||||
const endsWithBlockOpener = /\b(then|do)$/.test(codePart);
|
const endsWithBlockOpener = /\b(then|do)$/.test(codePart);
|
||||||
|
|
||||||
if (!noSemicolonNeeded && !ctx.justOpenedString && ctx.parenStack.length === 0 && ctx.bracketStack.length === 0 && ctx.inBlock !== 'enumeration' && !ctx.inConditionBlock && !continuesOnNextLine && !endsWithBlockOpener) {
|
if (!noSemicolonNeeded && !ctx.justOpenedString && ctx.parenStack.length === 0 && ctx.bracketStack.length === 0 && ctx.inBlock !== 'enumeration' && !ctx.inConditionBlock && !continuesOnNextLine && !endsWithBlockOpener) {
|
||||||
if (codePart.length > 0 && !codePart.endsWith(';') && !codePart.endsWith('{') && !codePart.endsWith(',')) {
|
if (codePart.length > 0 && !codePart.endsWith(';') && !codePart.endsWith('{') && !codePart.endsWith(',')) {
|
||||||
ctx.addDiag(lineIdx, Math.max(0, rawLine.length - 1), rawLine.length, "Fehlendes Semikolon am Ende der Anweisung.", vscode.DiagnosticSeverity.Error);
|
ctx.addDiag(lineIdx, Math.max(0, rawLine.length - 1), rawLine.length, "Missing semicolon at the end of the statement.", vscode.DiagnosticSeverity.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,26 +8,34 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
const text = document.getText();
|
const text = document.getText();
|
||||||
const items: vscode.CompletionItem[] = [];
|
const items: vscode.CompletionItem[] = [];
|
||||||
|
|
||||||
// 1. SMART IMPORT: "from "
|
const textBeforeCursor = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
|
||||||
if (linePrefix.match(/\bfrom\s+$/)) {
|
const lastFromIndex = textBeforeCursor.lastIndexOf('from ');
|
||||||
for (const mod of workspaceIndex.getAllModules()) {
|
const lastSemicolonIndex = textBeforeCursor.lastIndexOf(';');
|
||||||
const item = new vscode.CompletionItem(mod, vscode.CompletionItemKind.Module);
|
|
||||||
item.detail = "Workspace Module";
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
return items; // Nichts anderes vorschlagen!
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. SMART IMPORT: "from X import "
|
// 1 & 2. MULTI-LINE SMART IMPORT
|
||||||
const importContextMatch = linePrefix.match(/\bfrom\s+([a-zA-Z0-9_.]+)\s+import\s+$/);
|
// Wenn das letzte 'from' NACH dem letzten Semikolon kam, sind wir mitten in einem Import-Block!
|
||||||
if (importContextMatch) {
|
if (lastFromIndex > lastSemicolonIndex || (lastFromIndex !== -1 && lastSemicolonIndex === -1)) {
|
||||||
const modName = importContextMatch[1];
|
const importContext = textBeforeCursor.substring(lastFromIndex);
|
||||||
for (const defBlock of workspaceIndex.getDefinitionBlocksForModule(modName)) {
|
const match = importContext.match(/^from\s+([a-zA-Z0-9_.]+)\s+import/);
|
||||||
const item = new vscode.CompletionItem(defBlock, vscode.CompletionItemKind.Class);
|
|
||||||
item.detail = `Definitions from ${modName}`;
|
if (!match) {
|
||||||
items.push(item);
|
// Wir tippen noch den Modul-Namen
|
||||||
|
for (const mod of workspaceIndex.getAllModules()) {
|
||||||
|
const item = new vscode.CompletionItem(mod, vscode.CompletionItemKind.Module);
|
||||||
|
item.detail = "Workspace Module";
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
} else {
|
||||||
|
// Wir tippen die zu importierenden Klassen/Patterns (auch nach Zeilenumbrüchen!)
|
||||||
|
const modName = match[1];
|
||||||
|
for (const defBlock of workspaceIndex.getDefinitionBlocksForModule(modName)) {
|
||||||
|
const item = new vscode.CompletionItem(defBlock, vscode.CompletionItemKind.Class);
|
||||||
|
item.detail = `Available in ${modName}`;
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
return items; // Nichts anderes vorschlagen!
|
||||||
}
|
}
|
||||||
return items; // Nichts anderes vorschlagen!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. NAMESPACE CALLS: "prefix."
|
// 3. NAMESPACE CALLS: "prefix."
|
||||||
|
|
@ -44,7 +52,7 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
let funcs = [];
|
let funcs = [];
|
||||||
|
|
||||||
// A) RAM-Abfrage für importierte Module
|
// A) RAM-Abfrage für importierte Module
|
||||||
const importRegex = new RegExp(`from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+${prefix}\\b`);
|
const importRegex = new RegExp(`from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+[\\s\\S]*?\\b${prefix}\\b`);
|
||||||
const match = importRegex.exec(text);
|
const match = importRegex.exec(text);
|
||||||
if (match) {
|
if (match) {
|
||||||
const moduleName = match[1];
|
const moduleName = match[1];
|
||||||
|
|
@ -76,7 +84,7 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
const item = new vscode.CompletionItem(shortName, vscode.CompletionItemKind.Function);
|
const item = new vscode.CompletionItem(shortName, vscode.CompletionItemKind.Function);
|
||||||
item.detail = f.moduleName === 'local' ? `Local Method` : `Method from ${f.moduleName}`;
|
item.detail = f.moduleName === 'local' ? `Local Method` : `Method from ${f.moduleName}`;
|
||||||
|
|
||||||
// FIX: Dynamische Parameter-Generierung! Baut z.B. ${1:host}, ${2:ip}
|
// Dynamische Parameter-Generierung! Baut z.B. ${1:host}, ${2:ip}
|
||||||
let paramSnippet = "";
|
let paramSnippet = "";
|
||||||
if (f.params && f.params.length > 0) {
|
if (f.params && f.params.length > 0) {
|
||||||
paramSnippet = f.params.map((p, idx) => `\${${idx + 1}:${p}}`).join(', ');
|
paramSnippet = f.params.map((p, idx) => `\${${idx + 1}:${p}}`).join(', ');
|
||||||
|
|
@ -85,10 +93,10 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
item.insertText = new vscode.SnippetString(`${shortName}(${paramSnippet});$0`);
|
item.insertText = new vscode.SnippetString(`${shortName}(${paramSnippet});$0`);
|
||||||
items.push(item);
|
items.push(item);
|
||||||
}
|
}
|
||||||
return items; // Gibt exakt die Funktionen zurück
|
return items; // Gibt exakt die gefundenen Funktionen zurück
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIX: Wenn es ein unbekannter Namespace mit Punkt ist, geben wir ein leeres Array [] zurück!
|
// Wenn es ein unbekannter Namespace mit Punkt ist, geben wir ein leeres Array [] zurück!
|
||||||
// Das zwingt VS Code dazu, KEINE generischen Textwörter vorzuschlagen.
|
// Das zwingt VS Code dazu, KEINE generischen Textwörter vorzuschlagen.
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +118,7 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
const item = new vscode.CompletionItem(fullName, vscode.CompletionItemKind.Function);
|
const item = new vscode.CompletionItem(fullName, vscode.CompletionItemKind.Function);
|
||||||
item.detail = currentDefBlock ? `Local Function in '${currentDefBlock}'` : `Local Function`;
|
item.detail = currentDefBlock ? `Local Function in '${currentDefBlock}'` : `Local Function`;
|
||||||
|
|
||||||
// Dynamische Parameter-Generierung auch hier!
|
// Dynamische Parameter-Generierung
|
||||||
const params = (defineMatch[2] || "").split(',').map(p => p.trim()).filter(p => p.length > 0);
|
const params = (defineMatch[2] || "").split(',').map(p => p.trim()).filter(p => p.length > 0);
|
||||||
let paramSnippet = "";
|
let paramSnippet = "";
|
||||||
if (params.length > 0) {
|
if (params.length > 0) {
|
||||||
|
|
@ -122,13 +130,20 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import-Referenzen für den globalen Scope bereitstellen
|
// Import-Referenzen für den globalen Scope bereitstellen (angepasst für Multi-Line)
|
||||||
const fromImportRegex = /from\s+([a-zA-Z0-9_.]+)\s+import\s+([a-zA-Z0-9_]+)/g;
|
const fromImportRegex = /from\s+([a-zA-Z0-9_.]+)\s+import\s+([\s\S]*?);/g;
|
||||||
let matchVar;
|
let matchVar;
|
||||||
while ((matchVar = fromImportRegex.exec(text)) !== null) {
|
while ((matchVar = fromImportRegex.exec(text)) !== null) {
|
||||||
const item = new vscode.CompletionItem(matchVar[2], vscode.CompletionItemKind.Module);
|
const moduleName = matchVar[1];
|
||||||
item.detail = `Imported Definitions from ${matchVar[1]}`;
|
const imports = matchVar[2].split(',').map(i => i.trim().split(/\s+/)[0]);
|
||||||
items.push(item);
|
|
||||||
|
for (const imp of imports) {
|
||||||
|
if (imp.length > 0) {
|
||||||
|
const item = new vscode.CompletionItem(imp, vscode.CompletionItemKind.Module);
|
||||||
|
item.detail = `Imported from ${moduleName}`;
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lokale Variablen (:=) vorschlagen
|
// Lokale Variablen (:=) vorschlagen
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,10 @@ end definitions;
|
||||||
assert.ok(result !== undefined);
|
assert.ok(result !== undefined);
|
||||||
assert.strictEqual(result!.length, 0, "Must return an empty array to block VS Code text spam.");
|
assert.strictEqual(result!.length, 0, "Must return an empty array to block VS Code text spam.");
|
||||||
});
|
});
|
||||||
|
test('LocalContextCompletion: Imported namespace generates parameter snippet', async () => {
|
||||||
test('LocalContextCompletion: Imported namespace generates parameter snippet', async () => {
|
|
||||||
const strategy = new LocalContextCompletion();
|
const strategy = new LocalContextCompletion();
|
||||||
const docText = `from Company.tools import network_helpers\nnetwork_helpers.`;
|
|
||||||
|
const docText = `from Company.tools import network_helpers;\nnetwork_helpers.`;
|
||||||
const mockDoc = createMockDoc(docText);
|
const mockDoc = createMockDoc(docText);
|
||||||
const position = new vscode.Position(1, 16);
|
const position = new vscode.Position(1, 16);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue