Compare commits
No commits in common. "939d9073e6491a2d7a8795cffaaca28815585434" and "21e6833cb1a17f6fc474c9351880d3a5957e23a5" have entirely different histories.
939d9073e6
...
21e6833cb1
|
|
@ -1,46 +0,0 @@
|
||||||
name: Nightly Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# Läuft jeden Tag um 02:00 Uhr nachts
|
|
||||||
- cron: '0 2 * * *'
|
|
||||||
# Erlaubt es dir, den Build im Gitea-Webinterface auch manuell per Button zu starten
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-nightly:
|
|
||||||
runs-on: ubuntu-latest # Pass das an das Label deines Gitea-Runners an
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '24' # Passend zu deiner package.json Umgebung
|
|
||||||
|
|
||||||
- name: Install System Dependencies for Headless Tests
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y xvfb libx11-xcb1 libxcomposite1 libxcursor1 libxdamage1 libxi6 libxtst6 libnss3 libcups2 libxss1 libxrandr2 libasound2 libatk1.0-0 libatk-bridge2.0-0 libgtk-3-0
|
|
||||||
|
|
||||||
- name: Install NPM Packages
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Inject Nightly Version
|
|
||||||
run: |
|
|
||||||
NIGHTLY_DATE=$(date +%Y%m%d)
|
|
||||||
sed -i "s/\"version\": \"\(.*\)\"/\"version\": \"\1-nightly-$NIGHTLY_DATE\"/" package.json
|
|
||||||
echo "Building Nightly Version for $NIGHTLY_DATE..."
|
|
||||||
|
|
||||||
- name: Package & Test (with xvfb)
|
|
||||||
# xvfb-run fängt die GUI-Aufrufe der VS Code Test-Engine ab
|
|
||||||
run: xvfb-run -a npm run package
|
|
||||||
|
|
||||||
- name: Upload VSIX Artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: bmc-tpl-code-all-around-nightly
|
|
||||||
path: '*.vsix'
|
|
||||||
retention-days: 14
|
|
||||||
|
|
@ -133,14 +133,13 @@
|
||||||
"watch": "npm-run-all -p watch:*",
|
"watch": "npm-run-all -p watch:*",
|
||||||
"watch:esbuild": "node esbuild.js --watch",
|
"watch:esbuild": "node esbuild.js --watch",
|
||||||
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
|
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
|
||||||
"package": "npm run check-types && npm run lint && npm run test && node esbuild.js --production",
|
"package": "npm run check-types && npm run lint && node esbuild.js --production",
|
||||||
"compile-tests": "tsc -p . --outDir out",
|
"compile-tests": "tsc -p . --outDir out",
|
||||||
"watch-tests": "tsc -p . -w --outDir out",
|
"watch-tests": "tsc -p . -w --outDir out",
|
||||||
"pretest": "npm run compile-tests && npm run compile && npm run lint",
|
"pretest": "npm run compile-tests && npm run compile && npm run lint",
|
||||||
"check-types": "tsc --noEmit",
|
"check-types": "tsc --noEmit",
|
||||||
"lint": "eslint src",
|
"lint": "eslint src",
|
||||||
"test": "vscode-test",
|
"test": "vscode-test"
|
||||||
"generate-builtins": "python scripts/generate_builtins.py ."
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/vscode": "^1.140.0",
|
"@types/vscode": "^1.140.0",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export class TplLookup {
|
||||||
if (coreKey) {
|
if (coreKey) {
|
||||||
const methods = (tplBuiltIns as any)[coreKey];
|
const methods = (tplBuiltIns as any)[coreKey];
|
||||||
const mKey = Object.keys(methods).find(k => k.toLowerCase() === fnLower);
|
const mKey = Object.keys(methods).find(k => k.toLowerCase() === fnLower);
|
||||||
if (mKey) {return methods[mKey];}
|
if (mKey) return methods[mKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Suche in Generierten TKU Built-Ins (rdbms_functions, etc.)
|
// 2. Suche in Generierten TKU Built-Ins (rdbms_functions, etc.)
|
||||||
|
|
@ -20,7 +20,7 @@ export class TplLookup {
|
||||||
if (tkuKey) {
|
if (tkuKey) {
|
||||||
const methods = (generatedTplBuiltIns as any)[tkuKey];
|
const methods = (generatedTplBuiltIns as any)[tkuKey];
|
||||||
const mKey = Object.keys(methods).find(k => k.toLowerCase() === fnLower);
|
const mKey = Object.keys(methods).find(k => k.toLowerCase() === fnLower);
|
||||||
if (mKey) {return methods[mKey];}
|
if (mKey) return methods[mKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -31,10 +31,10 @@ export class TplLookup {
|
||||||
const nsLower = namespace.toLowerCase();
|
const nsLower = namespace.toLowerCase();
|
||||||
|
|
||||||
const coreKey = Object.keys(tplBuiltIns).find(k => k.toLowerCase() === nsLower);
|
const coreKey = Object.keys(tplBuiltIns).find(k => k.toLowerCase() === nsLower);
|
||||||
if (coreKey) {return (tplBuiltIns as any)[coreKey];}
|
if (coreKey) return (tplBuiltIns as any)[coreKey];
|
||||||
|
|
||||||
const tkuKey = Object.keys(generatedTplBuiltIns).find(k => k.toLowerCase() === nsLower);
|
const tkuKey = Object.keys(generatedTplBuiltIns).find(k => k.toLowerCase() === nsLower);
|
||||||
if (tkuKey) {return (generatedTplBuiltIns as any)[tkuKey];}
|
if (tkuKey) return (generatedTplBuiltIns as any)[tkuKey];
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export class WorkspaceIndex {
|
||||||
private moduleDefinitions = new Map<string, Set<string>>();
|
private moduleDefinitions = new Map<string, Set<string>>();
|
||||||
|
|
||||||
public updateFile(uri: vscode.Uri, text: string) {
|
public updateFile(uri: vscode.Uri, text: string) {
|
||||||
if (!uri.fsPath.endsWith('.tpl')) {return;}
|
if (!uri.fsPath.endsWith('.tpl')) return;
|
||||||
|
|
||||||
const fsPath = uri.fsPath;
|
const fsPath = uri.fsPath;
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
|
|
@ -64,7 +64,7 @@ export class WorkspaceIndex {
|
||||||
|
|
||||||
let hasReturnValue = false;
|
let hasReturnValue = false;
|
||||||
for (let j = i + 1; j < lines.length; j++) {
|
for (let j = i + 1; j < lines.length; j++) {
|
||||||
if (lines[j].match(/^\s*end\s+define/)) {break;}
|
if (lines[j].match(/^\s*end\s+define/)) break;
|
||||||
if (lines[j].match(/^\s*return\b/)) {
|
if (lines[j].match(/^\s*return\b/)) {
|
||||||
hasReturnValue = true;
|
hasReturnValue = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,10 @@ export class ParameterRule implements LinterRule {
|
||||||
cleanLine += char;
|
cleanLine += char;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cleanLine.trim().length === 0) {return;}
|
if (cleanLine.trim().length === 0) return;
|
||||||
|
|
||||||
// Die Definition der Funktion selbst ist kein Aufruf und wird ignoriert.
|
// Die Definition der Funktion selbst ist kein Aufruf und wird ignoriert.
|
||||||
if (/^\s*define\s+/.test(cleanLine)) {return;}
|
if (/^\s*define\s+/.test(cleanLine)) return;
|
||||||
|
|
||||||
const callRegex = /\b([a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)?)\s*\(/g;
|
const callRegex = /\b([a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)?)\s*\(/g;
|
||||||
let match;
|
let match;
|
||||||
|
|
@ -78,12 +78,12 @@ export class ParameterRule implements LinterRule {
|
||||||
const callStart = match.index;
|
const callStart = match.index;
|
||||||
const parenStart = callStart + funcName.length;
|
const parenStart = callStart + funcName.length;
|
||||||
|
|
||||||
if (/^(if|elif|where|for|while|in|matches|has|subword|defined)\b/.test(funcName)) {continue;}
|
if (/^(if|elif|where|for|while|in|matches|has|subword|defined)\b/.test(funcName)) continue;
|
||||||
|
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
let parenEnd = -1;
|
let parenEnd = -1;
|
||||||
for (let i = parenStart; i < cleanLine.length; i++) {
|
for (let i = parenStart; i < cleanLine.length; i++) {
|
||||||
if (cleanLine[i] === '(') {depth++;}
|
if (cleanLine[i] === '(') depth++;
|
||||||
else if (cleanLine[i] === ')') {
|
else if (cleanLine[i] === ')') {
|
||||||
depth--;
|
depth--;
|
||||||
if (depth === 0) {
|
if (depth === 0) {
|
||||||
|
|
@ -101,9 +101,9 @@ export class ParameterRule implements LinterRule {
|
||||||
let paramDepth = 0;
|
let paramDepth = 0;
|
||||||
let commas = 0;
|
let commas = 0;
|
||||||
for (let i = 0; i < paramsStr.length; i++) {
|
for (let i = 0; i < paramsStr.length; i++) {
|
||||||
if (paramsStr[i] === '(' || paramsStr[i] === '[') {paramDepth++;}
|
if (paramsStr[i] === '(' || paramsStr[i] === '[') paramDepth++;
|
||||||
else if (paramsStr[i] === ')' || paramsStr[i] === ']') {paramDepth--;}
|
else if (paramsStr[i] === ')' || paramsStr[i] === ']') paramDepth--;
|
||||||
else if (paramsStr[i] === ',' && paramDepth === 0) {commas++;}
|
else if (paramsStr[i] === ',' && paramDepth === 0) commas++;
|
||||||
}
|
}
|
||||||
paramCount = commas + 1;
|
paramCount = commas + 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { workspaceIndex } from '../core/WorkspaceIndex';
|
||||||
export class TplDefinitionProvider implements vscode.DefinitionProvider {
|
export class TplDefinitionProvider implements vscode.DefinitionProvider {
|
||||||
public async provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Definition | null> {
|
public async provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Definition | null> {
|
||||||
const range = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_.]+/);
|
const range = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_.]+/);
|
||||||
if (!range) {return null;}
|
if (!range) return null;
|
||||||
|
|
||||||
const word = document.getText(range);
|
const word = document.getText(range);
|
||||||
const parts = word.split('.');
|
const parts = word.split('.');
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { workspaceIndex } from '../core/WorkspaceIndex';
|
||||||
export class TplHoverProvider implements vscode.HoverProvider {
|
export class TplHoverProvider implements vscode.HoverProvider {
|
||||||
public provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult<vscode.Hover> {
|
public provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult<vscode.Hover> {
|
||||||
const range = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_.]+/);
|
const range = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_.]+/);
|
||||||
if (!range) {return null;}
|
if (!range) return null;
|
||||||
|
|
||||||
const word = document.getText(range);
|
const word = document.getText(range);
|
||||||
const parts = word.split('.');
|
const parts = word.split('.');
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ import { FormatState } from './FormatState';
|
||||||
|
|
||||||
export class IndentationRules {
|
export class IndentationRules {
|
||||||
public apply(line: string, state: FormatState): string {
|
public apply(line: string, state: FormatState): string {
|
||||||
if (line.trim().length === 0) {return "";}
|
// 1. Leere Zeilen komplett rasieren
|
||||||
|
if (line.trim().length === 0) return "";
|
||||||
|
|
||||||
|
// 2. Strings und Kommentare sicher entfernen (für die Keyword-Erkennung)
|
||||||
let cleanLine = "";
|
let cleanLine = "";
|
||||||
let inString = false;
|
let inString = false;
|
||||||
let stringChar = '';
|
let stringChar = '';
|
||||||
|
|
@ -11,7 +13,7 @@ export class IndentationRules {
|
||||||
|
|
||||||
for (let i = 0; i < line.length; i++) {
|
for (let i = 0; i < line.length; i++) {
|
||||||
const char = line[i];
|
const char = line[i];
|
||||||
if (!inString && !inTripleQuote && char === '/' && line[i+1] === '/') {break;}
|
if (!inString && !inTripleQuote && char === '/' && line[i+1] === '/') break;
|
||||||
|
|
||||||
if (!inString && !inTripleQuote && (char === '"' || char === "'") && line[i+1] === char && line[i+2] === char) {
|
if (!inString && !inTripleQuote && (char === '"' || char === "'") && line[i+1] === char && line[i+2] === char) {
|
||||||
inTripleQuote = true; stringChar = char; cleanLine += char+char+char; i += 2; continue;
|
inTripleQuote = true; stringChar = char; cleanLine += char+char+char; i += 2; continue;
|
||||||
|
|
@ -23,7 +25,7 @@ export class IndentationRules {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (inString) {
|
if (inString) {
|
||||||
if (char === '\\') {i++;} else if (char === stringChar) {inString = false;}
|
if (char === '\\') i++; else if (char === stringChar) inString = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!inString && !inTripleQuote && (char === '"' || char === "'")) {
|
if (!inString && !inTripleQuote && (char === '"' || char === "'")) {
|
||||||
|
|
@ -33,40 +35,47 @@ export class IndentationRules {
|
||||||
}
|
}
|
||||||
cleanLine = cleanLine.trim();
|
cleanLine = cleanLine.trim();
|
||||||
|
|
||||||
|
// Alle vorhandenen falschen Leerzeichen am Anfang (2, 3, 6, etc.) brutal abschneiden
|
||||||
const trimmedLine = line.trimStart();
|
const trimmedLine = line.trimStart();
|
||||||
|
|
||||||
// 3. DEDENT
|
// 3. DEDENT: Aktuelle Zeile rückt nach links (end, elif, else)
|
||||||
if (/^end\b/.test(cleanLine)) {
|
if (/^end\b/.test(cleanLine)) {
|
||||||
state.indentLevel = Math.max(0, state.indentLevel - 1);
|
state.indentLevel = Math.max(0, state.indentLevel - 1);
|
||||||
} else if (/^(elif|else)\b/.test(cleanLine)) {
|
} else if (/^(elif|else)\b/.test(cleanLine)) {
|
||||||
state.indentLevel = Math.max(0, state.indentLevel - 1);
|
state.indentLevel = Math.max(0, state.indentLevel - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. SET SPACES
|
// 4. DIE EINZIGE WAHRHEIT: Harte Spaces setzen
|
||||||
let result = trimmedLine;
|
let result = trimmedLine;
|
||||||
|
|
||||||
if (state.parenAlignments && state.parenAlignments.length > 0) {
|
if (state.parenAlignments && state.parenAlignments.length > 0) {
|
||||||
// Visual Alignment greift später
|
// Visual Alignment (Runde Klammern) - Das fasst diese Regel nicht an,
|
||||||
|
// das übernimmt die nachfolgende AlignmentRule.
|
||||||
} else if (state.inImportBlock) {
|
} else if (state.inImportBlock) {
|
||||||
|
// Im Multi-Line Import rücken wir zur besseren Lesbarkeit eine Ebene tiefer ein
|
||||||
if (!cleanLine.startsWith('from ')) {
|
if (!cleanLine.startsWith('from ')) {
|
||||||
result = " ".repeat((state.indentLevel + 2) * state.tabSize) + trimmedLine;
|
result = " ".repeat((state.indentLevel + 2) * state.tabSize) + trimmedLine;
|
||||||
} else {
|
} else {
|
||||||
result = state.currentIndentString + trimmedLine;
|
result = state.currentIndentString + trimmedLine;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Standard-Einrückung unter Nutzung deines Getters
|
||||||
result = state.currentIndentString + trimmedLine;
|
result = state.currentIndentString + trimmedLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. INDENT (Gefixt: Mutually Exclusive / else if)
|
// 5. INDENT: Nächste Zeile rückt nach rechts
|
||||||
if (/^(pattern|syncmapping|table|definitions?|define|enumeration|configuration|overview|metadata|constants|triggers|body|mapping|removal)\b/.test(cleanLine)) {
|
if (/^(pattern|syncmapping|table|definitions?|define|enumeration|configuration|overview|metadata|constants|triggers|body|mapping|removal)\b/.test(cleanLine)) {
|
||||||
state.indentLevel++;
|
state.indentLevel++;
|
||||||
} else if (/\b(then|do)\b$/.test(cleanLine)) {
|
}
|
||||||
|
if (/\b(then|do)\b$/.test(cleanLine)) {
|
||||||
state.indentLevel++;
|
state.indentLevel++;
|
||||||
} else if (/^else\b/.test(cleanLine)) {
|
}
|
||||||
|
// elif/else eröffnen sofort wieder einen neuen Block für die Folgezeile
|
||||||
|
if (/^(elif|else)\b/.test(cleanLine)) {
|
||||||
state.indentLevel++;
|
state.indentLevel++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Import Block Tracking
|
// 6. Multi-Line Import Tracking
|
||||||
if (cleanLine.startsWith('from ')) {
|
if (cleanLine.startsWith('from ')) {
|
||||||
state.inImportBlock = true;
|
state.inImportBlock = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
tpl 1.21 module TestFormat;
|
|
||||||
|
|
||||||
define format_me()
|
|
||||||
// 6 Leerzeichen hier!
|
|
||||||
log.debug("2 Leerzeichen hier");
|
|
||||||
if true then
|
|
||||||
log.debug("3 Leerzeichen!");
|
|
||||||
end if;
|
|
||||||
end define;
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
tpl 1.21 module TestEdgeCases;
|
|
||||||
|
|
||||||
define my_edge_func()
|
|
||||||
"""
|
|
||||||
Ein böser DocString mit ( [ = und if und end
|
|
||||||
"""
|
|
||||||
// Ein Kommentar mit DiscoveryFunctions.escapePath(1,2,3,4,5,6)
|
|
||||||
|
|
||||||
clean_path := DiscoveryFunctions.escapePath(host, "böse(Klammern)", true);
|
|
||||||
|
|
||||||
if test = "ein_string_mit_komma," then
|
|
||||||
log.debug("alles gut");
|
|
||||||
end if;
|
|
||||||
end define;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
tpl 1.21 module TestNegative;
|
|
||||||
|
|
||||||
define my_bad_func()
|
|
||||||
// Unbekannte Funktion (Info)
|
|
||||||
rdbms_functions.does_not_exist(host);
|
|
||||||
|
|
||||||
// Falsche Parameter-Anzahl (Warning)
|
|
||||||
DiscoveryFunctions.escapePath(host, "path", true, "zu_viel");
|
|
||||||
|
|
||||||
// Zuweisung fehlt für Return-Value (Warning)
|
|
||||||
DiscoveryFunctions.escapePath(host, "path", true);
|
|
||||||
end define;
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
import * as assert from 'assert';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { ParameterRule } from '../../core/rules/ParameterRule';
|
|
||||||
import { IndentationRules } from '../../providers/formatters/IndentationRules';
|
|
||||||
import { FormatState } from '../../providers/formatters/FormatState';
|
|
||||||
import { LinterContext } from '../../core/LinterContext';
|
|
||||||
import { TplLookup } from '../../core/TplLookup';
|
|
||||||
import { workspaceIndex } from '../../core/WorkspaceIndex';
|
|
||||||
|
|
||||||
class MockLinterContext extends LinterContext {
|
|
||||||
public diagnostics: vscode.Diagnostic[] = [];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public addDiag(lineIdx: number, start: number, end: number, msg: string, severity: vscode.DiagnosticSeverity): vscode.Diagnostic {
|
|
||||||
const range = new vscode.Range(lineIdx, start, lineIdx, end);
|
|
||||||
const diag = new vscode.Diagnostic(range, msg, severity);
|
|
||||||
this.diagnostics.push(diag);
|
|
||||||
return diag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suite('BMC TPL Core Engine Tests', () => {
|
|
||||||
|
|
||||||
const fixturesPath = path.resolve(__dirname, '../../../src/test/fixtures');
|
|
||||||
|
|
||||||
suite('TplLookup & Autocomplete', () => {
|
|
||||||
test('Should resolve Core and TKU built-ins case-insensitively', () => {
|
|
||||||
const runCmd = TplLookup.getBuiltInFunction('discovery', 'runcommand');
|
|
||||||
assert.ok(runCmd, 'discovery.runCommand should be found ignoring case');
|
|
||||||
|
|
||||||
const tkuCmd = TplLookup.getBuiltInFunction('COMMON_FUNCTIONS', 'identify_host');
|
|
||||||
assert.ok(tkuCmd, 'Common_Functions.identify_host should be found ignoring case');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('ParameterRule (Lexer & Linter)', () => {
|
|
||||||
test('Should catch negative cases (unknown func, param count, unused return)', () => {
|
|
||||||
const rule = new ParameterRule();
|
|
||||||
const ctx = new MockLinterContext();
|
|
||||||
|
|
||||||
const originalGetSignatures = workspaceIndex.getFunctionSignatures.bind(workspaceIndex);
|
|
||||||
workspaceIndex.getFunctionSignatures = (name: string) => {
|
|
||||||
if (name === 'my_local_func') {
|
|
||||||
return [{
|
|
||||||
name: 'my_local_func',
|
|
||||||
params: ['a', 'b'],
|
|
||||||
hasReturnValue: true,
|
|
||||||
docString: '',
|
|
||||||
moduleName: 'TestNegative',
|
|
||||||
lineIdx: 1,
|
|
||||||
fsPath: 'test.tpl'
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
return originalGetSignatures(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
// GEFIXT: "dummy :=" hinzugefügt, um den doppelten Return-Value-Fehler zu vermeiden
|
|
||||||
const content = `tpl 1.21 module TestNegative;
|
|
||||||
define test_func()
|
|
||||||
unknown_module.does_not_exist(host);
|
|
||||||
dummy := my_local_func(1, 2, 3);
|
|
||||||
my_local_func(1, 2);
|
|
||||||
end define;`;
|
|
||||||
|
|
||||||
const lines = content.split('\n');
|
|
||||||
lines.forEach((line, idx) => rule.checkLine(line, line, idx, ctx, lines));
|
|
||||||
|
|
||||||
workspaceIndex.getFunctionSignatures = originalGetSignatures;
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.diagnostics.length, 3, 'Should find exactly 3 errors in negative fixture');
|
|
||||||
|
|
||||||
assert.ok(ctx.diagnostics.some(d => d.message.includes("Unknown function 'unknown_module.does_not_exist'")));
|
|
||||||
assert.ok(ctx.diagnostics.some(d => d.message.includes("expects 2 parameter(s), but 3 were provided")));
|
|
||||||
assert.ok(ctx.diagnostics.some(d => d.message.includes("is not used. Did you forget an assignment (:=)?")));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should ignore strings, docstrings and comments (Lexer Edge Cases)', () => {
|
|
||||||
const rule = new ParameterRule();
|
|
||||||
const ctx = new MockLinterContext();
|
|
||||||
|
|
||||||
const content = fs.readFileSync(path.join(fixturesPath, 'lexer_edge_cases.tpl'), 'utf-8');
|
|
||||||
const lines = content.split('\n');
|
|
||||||
|
|
||||||
lines.forEach((line, idx) => rule.checkLine(line, line, idx, ctx, lines));
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.diagnostics.length, 0, 'Should find NO errors in lexer edge cases');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('IndentationRules (Auto-Formatter)', () => {
|
|
||||||
test('Should forcefully fix bad indentation', () => {
|
|
||||||
const rule = new IndentationRules();
|
|
||||||
const state = new FormatState();
|
|
||||||
|
|
||||||
const content = fs.readFileSync(path.join(fixturesPath, 'format_bad.tpl'), 'utf-8');
|
|
||||||
const lines = content.split('\n');
|
|
||||||
|
|
||||||
const formattedLines = lines.map(line => rule.apply(line, state));
|
|
||||||
|
|
||||||
assert.strictEqual(formattedLines[3], ' // 6 Leerzeichen hier!');
|
|
||||||
assert.strictEqual(formattedLines[4], ' log.debug("2 Leerzeichen hier");');
|
|
||||||
assert.strictEqual(formattedLines[5], ' if true then');
|
|
||||||
assert.strictEqual(formattedLines[6], ' log.debug("3 Leerzeichen!");');
|
|
||||||
assert.strictEqual(formattedLines[7], ' end if;');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Loading…
Reference in New Issue