Refactoring fixes, translations, extensions for settings
This commit is contained in:
parent
1910b0def2
commit
b9035feb99
46
package.json
46
package.json
|
|
@ -35,27 +35,63 @@
|
||||||
"main": "./dist/extension.js",
|
"main": "./dist/extension.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"title": "BMC TPL Einstellungen",
|
"title": "BMC TPL Settings",
|
||||||
"properties": {
|
"properties": {
|
||||||
"tpl.metadata.author": {
|
"tpl.metadata.author": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "Autor",
|
"default": "Autor",
|
||||||
"description": "Standard-Autor für neu generierte TPL-Module."
|
"description": "Standard-Author for new generated TPL-Modules."
|
||||||
},
|
},
|
||||||
"tpl.metadata.origin": {
|
"tpl.metadata.origin": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "Custom",
|
"default": "Custom",
|
||||||
"description": "Standard-Origin für neu generierte TPL-Module."
|
"description": "Standard-Origin for new generated TPL-Modules."
|
||||||
},
|
},
|
||||||
"tpl.metadata.publisher": {
|
"tpl.metadata.publisher": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "IT-Operations",
|
"default": "IT-Operations",
|
||||||
"description": "Standard-Publisher für neu generierte TPL-Module."
|
"description": "Standard-Publisher ffor new generated TPL-Modules."
|
||||||
},
|
},
|
||||||
"tpl.metadata.treePathBase": {
|
"tpl.metadata.treePathBase": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "Company",
|
"default": "Company",
|
||||||
"description": "Erster Teil (Basis) des tree_paths in den Metadaten."
|
"description": "First Part (Basis) of the tree_paths in the Metadata."
|
||||||
|
},
|
||||||
|
"tpl.metadata.require.origin": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Marks 'origin' as Required on module level."
|
||||||
|
},
|
||||||
|
"tpl.metadata.require.author": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Marks 'author' as Required on module level."
|
||||||
|
},
|
||||||
|
"tpl.metadata.require.products": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Marks 'products' aas Required on module level."
|
||||||
|
},
|
||||||
|
"tpl.metadata.require.publishers": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Marks 'publishers' as Required on module level."
|
||||||
|
},
|
||||||
|
"tpl.metadata.require.treePath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Marks 'tree_path' as Required on module level."
|
||||||
|
},
|
||||||
|
"tpl.metadata.treePath.minLevels": {
|
||||||
|
"type": "number",
|
||||||
|
"default": 3,
|
||||||
|
"description": "How many levels should the tree_path in Metadata at least have? (e.g. 3 for 'Base', 'Category', 'Subcategory')."
|
||||||
|
},
|
||||||
|
"tpl.metadata.treePath.staticNodes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"default": ["default"],
|
||||||
|
"description": "Fixed definitions for the tree_path left to right (e.g. ['Company', 'Software']). The rest of the levels are free to choose."
|
||||||
},
|
},
|
||||||
"tpl.version": {
|
"tpl.version": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export class LinterContext {
|
||||||
public inModuleMetadata = false;
|
public inModuleMetadata = false;
|
||||||
public hasModuleMetadata = false;
|
public hasModuleMetadata = false;
|
||||||
public moduleMetadataBlockLine = -1;
|
public moduleMetadataBlockLine = -1;
|
||||||
public foundModuleMetadata = { origin: false, author: false, products: false, publishers: false, tree_path: false };
|
public foundModuleMetadata = new Set<string>();
|
||||||
|
|
||||||
// Klammer-Tracking
|
// Klammer-Tracking
|
||||||
public parenStack: number[] = [];
|
public parenStack: number[] = [];
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,27 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { LinterContext } from './LinterContext';
|
import { LinterContext } from './LinterContext';
|
||||||
import { LinterRule } from './rules/LinterRule';
|
|
||||||
import { SyntaxRule } from './rules/SyntaxRule';
|
import { SyntaxRule } from './rules/SyntaxRule';
|
||||||
import { BlockRule } from './rules/BlockRule';
|
import { BlockRule } from './rules/BlockRule';
|
||||||
|
import { MetadataRule } from './rules/MetadataRule';
|
||||||
|
import { IndentationRule } from './rules/IndentationRule'; // 1. Hier importieren
|
||||||
|
|
||||||
|
// ... (deine eventuellen anderen Hilfs-Imports wie trackStrings) ...
|
||||||
|
|
||||||
export function validateTplDocument(document: vscode.TextDocument, collection: vscode.DiagnosticCollection) {
|
export function validateTplDocument(document: vscode.TextDocument, collection: vscode.DiagnosticCollection) {
|
||||||
if (document.languageId !== 'tpl') { return; }
|
if (document.languageId !== 'tpl') return;
|
||||||
|
|
||||||
const ctx = new LinterContext();
|
|
||||||
const config = vscode.workspace.getConfiguration('tpl');
|
|
||||||
ctx.configuredTplVersion = config.get<number>('version', 23);
|
|
||||||
|
|
||||||
const text = document.getText();
|
const text = document.getText();
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
|
const ctx = new LinterContext();
|
||||||
|
|
||||||
// 1. Version prüfen
|
// 2. Hier nur die 3 existierenden Regeln reinlegen (MetadataRule entfernt!)
|
||||||
const versionMatch = text.match(/tpl\s+1\.(\d+)\s+module/);
|
const rules = [
|
||||||
if (versionMatch) {
|
|
||||||
ctx.tplVersion = parseInt(versionMatch[1], 10);
|
|
||||||
if (ctx.tplVersion < ctx.configuredTplVersion) {
|
|
||||||
const linesToSearch = text.substring(0, versionMatch.index! + 30).split('\n');
|
|
||||||
ctx.addDiag(linesToSearch.length - 1, 0, versionMatch[0].length, `Das Modul nutzt eine alte TPL Version (1.${ctx.tplVersion}). Aktuell in den Settings definiert ist 1.${ctx.configuredTplVersion}.`, vscode.DiagnosticSeverity.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Regeln laden
|
|
||||||
const rules: LinterRule[] = [
|
|
||||||
new SyntaxRule(),
|
new SyntaxRule(),
|
||||||
new BlockRule()
|
new MetadataRule(),
|
||||||
|
new BlockRule(),
|
||||||
|
new IndentationRule()
|
||||||
];
|
];
|
||||||
|
|
||||||
// 3. Zeilenweise iterieren
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const rawLine = lines[i];
|
const rawLine = lines[i];
|
||||||
const line = rawLine.trim();
|
const line = rawLine.trim();
|
||||||
|
|
@ -54,18 +45,14 @@ export function validateTplDocument(document: vscode.TextDocument, collection: v
|
||||||
|
|
||||||
// Regeln anwenden
|
// Regeln anwenden
|
||||||
for (const rule of rules) {
|
for (const rule of rules) {
|
||||||
rule.checkLine(line, rawLine, i, ctx, lines); // Hier 'lines' ergänzen!
|
rule.checkLine(line, rawLine, i, ctx, lines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Abschluss-Prüfungen durchführen
|
|
||||||
for (const rule of rules) {
|
for (const rule of rules) {
|
||||||
if (rule.checkEnd) {
|
|
||||||
rule.checkEnd(lines, ctx);
|
rule.checkEnd(lines, ctx);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Diagnostics an VS Code übergeben
|
|
||||||
collection.set(document.uri, ctx.diagnostics);
|
collection.set(document.uri, ctx.diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,8 @@ export class BlockRule implements LinterRule {
|
||||||
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Jede Datei muss mit 'tpl 1.x module <name>;' beginnen.", vscode.DiagnosticSeverity.Error); ctx.hasModuleDef = true; }
|
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Jede Datei muss mit 'tpl 1.x module <name>;' beginnen.", vscode.DiagnosticSeverity.Error); ctx.hasModuleDef = true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.startsWith('metadata') && ctx.inBlock === 'none') {
|
// NEU: Wenn wir im Metadata-Block sind, ignorieren wir den Rest. Die MetadataRule übernimmt!
|
||||||
ctx.inModuleMetadata = true;
|
if (ctx.inModuleMetadata || (line.startsWith('metadata') && ctx.inBlock === 'none')) {
|
||||||
ctx.hasModuleMetadata = true;
|
|
||||||
ctx.moduleMetadataBlockLine = lineIdx;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.inModuleMetadata) {
|
|
||||||
this.trackModuleMetadata(line, lineIdx, ctx);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,8 +22,6 @@ export class BlockRule implements LinterRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleBlockOpenings(line, rawLine, lineIdx, ctx);
|
this.handleBlockOpenings(line, rawLine, lineIdx, ctx);
|
||||||
|
|
||||||
// NEU: if, for und while tracken
|
|
||||||
this.handleControlBlocks(line, rawLine, lineIdx, ctx);
|
this.handleControlBlocks(line, rawLine, lineIdx, ctx);
|
||||||
|
|
||||||
if (ctx.inBlock === 'pattern') {
|
if (ctx.inBlock === 'pattern') {
|
||||||
|
|
@ -40,16 +31,6 @@ export class BlockRule implements LinterRule {
|
||||||
this.checkEndBlocks(line, rawLine, lineIdx, ctx);
|
this.checkEndBlocks(line, rawLine, lineIdx, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public checkEnd(lines: string[], ctx: LinterContext): void {
|
|
||||||
for (const unclosedLine of ctx.openDefines) {
|
|
||||||
ctx.addDiag(unclosedLine, 0, lines[unclosedLine].length, "Block 'define' wurde nicht mit 'end define;' geschlossen!", vscode.DiagnosticSeverity.Error);
|
|
||||||
}
|
|
||||||
// NEU: Offene if/for/while Blöcke am Dateiende ankreiden
|
|
||||||
for (const block of ctx.openControlBlocks) {
|
|
||||||
ctx.addDiag(block.lineIdx, 0, lines[block.lineIdx].length, `Block '${block.type}' wurde nicht mit 'end ${block.type};' geschlossen!`, vscode.DiagnosticSeverity.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleControlBlocks(line: string, rawLine: string, lineIdx: number, ctx: LinterContext) {
|
private handleControlBlocks(line: string, rawLine: string, lineIdx: number, ctx: LinterContext) {
|
||||||
// Block wird geöffnet
|
// Block wird geöffnet
|
||||||
if (/^if\b/.test(line)) { ctx.openControlBlocks.push({ type: 'if', lineIdx }); }
|
if (/^if\b/.test(line)) { ctx.openControlBlocks.push({ type: 'if', lineIdx }); }
|
||||||
|
|
@ -193,4 +174,8 @@ export class BlockRule implements LinterRule {
|
||||||
else { ctx.addDiag(lineIdx, 0, line.length, "'end define' gefunden, ohne offenes 'define'.", vscode.DiagnosticSeverity.Error); }
|
else { ctx.addDiag(lineIdx, 0, line.length, "'end define' gefunden, ohne offenes 'define'.", vscode.DiagnosticSeverity.Error); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public checkEnd(lines: string[], ctx: LinterContext): void {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { LinterRule } from './LinterRule';
|
||||||
|
import { LinterContext } from '../LinterContext';
|
||||||
|
|
||||||
|
export class IndentationRule implements LinterRule {
|
||||||
|
|
||||||
|
public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, lines: string[]): void {
|
||||||
|
// Leere Zeilen oder Zeilen, die nur aus Leerzeichen/Tabs bestehen, ignorieren
|
||||||
|
if (rawLine.trim().length === 0) return;
|
||||||
|
|
||||||
|
// Wenn Tabs drin sind, meckert die SyntaxRule bereits. Das ignorieren wir hier.
|
||||||
|
if (rawLine.includes('\t')) return;
|
||||||
|
|
||||||
|
const leadingSpacesMatch = rawLine.match(/^ +/);
|
||||||
|
const spaceCount = leadingSpacesMatch ? leadingSpacesMatch[0].length : 0;
|
||||||
|
|
||||||
|
// Ist die Anzahl der Leerzeichen kein Vielfaches von 4?
|
||||||
|
if (spaceCount > 0 && spaceCount % 4 !== 0) {
|
||||||
|
ctx.addDiag(
|
||||||
|
lineIdx,
|
||||||
|
0,
|
||||||
|
spaceCount, // Markiert genau den Bereich der Leerzeichen
|
||||||
|
`Unsaubere Einrückung (${spaceCount} Leerzeichen). Nutze ein Vielfaches von 4. (Tipp: Shift+Alt+F zum Auto-Formatieren)`,
|
||||||
|
vscode.DiagnosticSeverity.Warning // Warning reicht hier völlig, Error wäre beim Tippen zu aggressiv
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public checkEnd(lines: string[], ctx: LinterContext): void {
|
||||||
|
// Hier muss am Ende der Datei nichts geprüft werden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { LinterRule } from './LinterRule';
|
||||||
|
import { LinterContext } from '../LinterContext';
|
||||||
|
|
||||||
|
export class MetadataRule implements LinterRule {
|
||||||
|
public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, lines: string[]): void {
|
||||||
|
if (line.startsWith('metadata') && ctx.inBlock === 'none') {
|
||||||
|
ctx.inModuleMetadata = true;
|
||||||
|
ctx.hasModuleMetadata = true;
|
||||||
|
ctx.moduleMetadataBlockLine = lineIdx;
|
||||||
|
ctx.foundModuleMetadata.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.inModuleMetadata) {
|
||||||
|
const config = vscode.workspace.getConfiguration('tpl');
|
||||||
|
|
||||||
|
// Felder erfassen
|
||||||
|
if (line.match(/^origin\b/)) ctx.foundModuleMetadata.add('origin');
|
||||||
|
if (line.match(/^author\b/)) ctx.foundModuleMetadata.add('author');
|
||||||
|
if (line.match(/^products\b/)) ctx.foundModuleMetadata.add('products');
|
||||||
|
if (line.match(/^publishers\b/)) ctx.foundModuleMetadata.add('publishers');
|
||||||
|
|
||||||
|
// Tree-Path Sonderprüfung
|
||||||
|
if (line.match(/^tree_path\b/)) {
|
||||||
|
ctx.foundModuleMetadata.add('tree_path');
|
||||||
|
if (config.get<boolean>('metadata.require.treePath', true)) {
|
||||||
|
this.checkTreePath(line, rawLine, lineIdx, ctx, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block-Ende: Pflichtfelder abgleichen
|
||||||
|
if (line.startsWith('end metadata')) {
|
||||||
|
ctx.inModuleMetadata = false;
|
||||||
|
this.checkRequiredFields(ctx, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkTreePath(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, config: vscode.WorkspaceConfiguration) {
|
||||||
|
const minLevels = config.get<number>('metadata.treePath.minLevels', 3);
|
||||||
|
const staticNodes = config.get<string[]>('metadata.treePath.staticNodes', ['Grafing']);
|
||||||
|
|
||||||
|
// Extrahiert die Strings aus tree_path := 'A', 'B';
|
||||||
|
const match = line.match(/tree_path\s*:=\s*(.*);/);
|
||||||
|
if (match) {
|
||||||
|
const paths = match[1].split(',').map(s => s.trim().replace(/^['"]|['"]$/g, ''));
|
||||||
|
|
||||||
|
if (paths.length < minLevels) {
|
||||||
|
ctx.addDiag(lineIdx, 0, rawLine.length, `Der tree_path muss mindestens ${minLevels} Ebenen haben!`, vscode.DiagnosticSeverity.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < staticNodes.length; i++) {
|
||||||
|
if (paths[i] !== staticNodes[i]) {
|
||||||
|
ctx.addDiag(lineIdx, 0, rawLine.length, `Die tree_path Ebene ${i + 1} muss zwingend '${staticNodes[i]}' lauten!`, vscode.DiagnosticSeverity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkRequiredFields(ctx: LinterContext, config: vscode.WorkspaceConfiguration) {
|
||||||
|
const requiredFields: string[] = [];
|
||||||
|
if (config.get<boolean>('metadata.require.origin', true)) requiredFields.push('origin');
|
||||||
|
if (config.get<boolean>('metadata.require.author', true)) requiredFields.push('author');
|
||||||
|
if (config.get<boolean>('metadata.require.products', true)) requiredFields.push('products');
|
||||||
|
if (config.get<boolean>('metadata.require.publishers', true)) requiredFields.push('publishers');
|
||||||
|
if (config.get<boolean>('metadata.require.treePath', true)) requiredFields.push('tree_path');
|
||||||
|
|
||||||
|
const missing = requiredFields.filter(f => !ctx.foundModuleMetadata.has(f));
|
||||||
|
|
||||||
|
if (missing.length > 0 && ctx.moduleMetadataBlockLine !== -1) {
|
||||||
|
const diag = ctx.addDiag(ctx.moduleMetadataBlockLine, 0, 8, `Fehlende Pflicht-Metadaten (Modul-Ebene): ${missing.join(', ')}`, vscode.DiagnosticSeverity.Error);
|
||||||
|
diag.code = 'missing_module_metadata';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public checkEnd(lines: string[], ctx: LinterContext): void {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ export class SyntaxRule implements LinterRule {
|
||||||
public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, lines: string[]): void {
|
public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext, lines: string[]): void {
|
||||||
this.checkTabs(rawLine, lineIdx, ctx);
|
this.checkTabs(rawLine, lineIdx, ctx);
|
||||||
|
|
||||||
// TODOs finden (blauer Info-Hinweis)
|
|
||||||
const todoMatch = rawLine.match(/\bTODO\s*:(.*)/i);
|
const todoMatch = rawLine.match(/\bTODO\s*:(.*)/i);
|
||||||
if (todoMatch) {
|
if (todoMatch) {
|
||||||
ctx.addDiag(lineIdx, todoMatch.index!, rawLine.length, `TODO: ${todoMatch[1].trim()}`, vscode.DiagnosticSeverity.Information);
|
ctx.addDiag(lineIdx, todoMatch.index!, rawLine.length, `TODO: ${todoMatch[1].trim()}`, vscode.DiagnosticSeverity.Information);
|
||||||
|
|
@ -103,8 +102,7 @@ export class SyntaxRule implements LinterRule {
|
||||||
// "in" (für for-Schleifen) als gültigen Zeilenumbruch hinzugefügt
|
// "in" (für for-Schleifen) als gültigen Zeilenumbruch hinzugefügt
|
||||||
const continuesOnNextLine = /(,|and|or|not|\+|-|\*|\/|=|:=|matches|in)$/i.test(codePart);
|
const continuesOnNextLine = /(,|and|or|not|\+|-|\*|\/|=|:=|matches|in)$/i.test(codePart);
|
||||||
|
|
||||||
// HIER GEFIXT: Wenn die Zeile auf 'then' oder 'do' endet, verlangt sie kein Semikolon!
|
´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(',')) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import { tplBuiltIns } from '../data/TplBuiltIns';
|
||||||
|
|
||||||
export class TplHoverProvider implements vscode.HoverProvider {
|
export class TplHoverProvider implements vscode.HoverProvider {
|
||||||
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | null> {
|
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | 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); // z.B. "modelling.testDefine"
|
const word = document.getText(range);
|
||||||
const text = document.getText();
|
const text = document.getText();
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
|
|
||||||
|
|
@ -13,7 +14,26 @@ export class TplHoverProvider implements vscode.HoverProvider {
|
||||||
const importBase = parts[0];
|
const importBase = parts[0];
|
||||||
const funcName = parts.length > 1 ? parts[1] : word;
|
const funcName = parts.length > 1 ? parts[1] : word;
|
||||||
|
|
||||||
// 1. Lokale Defines
|
// 1. BMC BUILT-INS DYNAMISCH AUSLESEN
|
||||||
|
const bmcNamespaces = Object.keys(tplBuiltIns);
|
||||||
|
|
||||||
|
if (bmcNamespaces.includes(importBase)) {
|
||||||
|
// Wenn die Methode in der Datei existiert, zeige die echte Doku!
|
||||||
|
if (funcName !== importBase && tplBuiltIns[importBase][funcName]) {
|
||||||
|
const bmcDoc = tplBuiltIns[importBase][funcName];
|
||||||
|
const md = new vscode.MarkdownString();
|
||||||
|
|
||||||
|
// Wir zeigen den echten Methodennamen als Code-Block
|
||||||
|
md.appendCodeblock(`${importBase}.${funcName}`, 'tpl');
|
||||||
|
md.appendMarkdown(`\n\n---\n\n${bmcDoc.description}`);
|
||||||
|
|
||||||
|
return new vscode.Hover(md);
|
||||||
|
}
|
||||||
|
// Fallback, wenn man nur über dem Namespace (z.B. "discovery") hovert
|
||||||
|
return new vscode.Hover(new vscode.MarkdownString(`**${word}**\n\n*(BMC Built-in Namespace)*`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Lokale Defines
|
||||||
if (parts.length === 1 || parts.length === 2) {
|
if (parts.length === 1 || parts.length === 2) {
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
|
|
@ -32,13 +52,12 @@ export class TplHoverProvider implements vscode.HoverProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Cross-File Hover für from ... import
|
// 3. Cross-File Hover für from ... import
|
||||||
const fromImportRegex = new RegExp(`from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+${importBase}\\b`);
|
const fromImportRegex = new RegExp(`from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+${importBase}\\b`);
|
||||||
const fromMatch = text.match(fromImportRegex);
|
const fromMatch = text.match(fromImportRegex);
|
||||||
if (fromMatch) {
|
if (fromMatch) {
|
||||||
const moduleName = fromMatch[1];
|
const moduleName = fromMatch[1];
|
||||||
|
|
||||||
// Wenn der User über die Methode fährt (modelling.XYZ)
|
|
||||||
if (funcName !== importBase) {
|
if (funcName !== importBase) {
|
||||||
const files = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**');
|
const files = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**');
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
|
|
@ -64,14 +83,9 @@ export class TplHoverProvider implements vscode.HoverProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wenn er nur über "modelling" fährt
|
|
||||||
return new vscode.Hover(new vscode.MarkdownString(`Importierte Definitions **${importBase}** aus Modul \`${moduleName}\`.`));
|
return new vscode.Hover(new vscode.MarkdownString(`Importierte Definitions **${importBase}** aus Modul \`${moduleName}\`.`));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Hover für Built-Ins
|
|
||||||
if (word.startsWith('log.')) { return new vscode.Hover(new vscode.MarkdownString(`TPL Logging-Modul`)); }
|
|
||||||
if (word.startsWith('discovery.')) { return new vscode.Hover(new vscode.MarkdownString(`TPL Discovery-Modul`)); }
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,10 +9,10 @@ export class MetadataFix implements TplQuickFix {
|
||||||
fix.edit = new vscode.WorkspaceEdit();
|
fix.edit = new vscode.WorkspaceEdit();
|
||||||
|
|
||||||
const config = vscode.workspace.getConfiguration('tpl');
|
const config = vscode.workspace.getConfiguration('tpl');
|
||||||
const author = config.get<string>('metadata.author', 'Kuro');
|
const author = config.get<string>('metadata.author', 'default');
|
||||||
const origin = config.get<string>('metadata.origin', 'Custom');
|
const origin = config.get<string>('metadata.origin', 'Custom');
|
||||||
const publisher = config.get<string>('metadata.publisher', 'IT-Operations');
|
const publisher = config.get<string>('metadata.publisher', 'IT-Operations');
|
||||||
const treePathBase = config.get<string>('metadata.treePathBase', 'Grafing');
|
const treePathBase = config.get<string>('metadata.treePathBase', 'default');
|
||||||
|
|
||||||
const missingFieldsStr = diagnostic.message.replace('Fehlende Pflicht-Metadaten (Modul-Ebene): ', '');
|
const missingFieldsStr = diagnostic.message.replace('Fehlende Pflicht-Metadaten (Modul-Ebene): ', '');
|
||||||
const missingFields = missingFieldsStr.split(', ');
|
const missingFields = missingFieldsStr.split(', ');
|
||||||
|
|
@ -24,7 +24,6 @@ export class MetadataFix implements TplQuickFix {
|
||||||
if (missingFields.includes('publishers')) insertText += ` publishers := '${publisher}';\n`;
|
if (missingFields.includes('publishers')) insertText += ` publishers := '${publisher}';\n`;
|
||||||
if (missingFields.includes('tree_path')) insertText += ` tree_path := '${treePathBase}', 'TODO_Category', 'TODO_Subcategory';\n`;
|
if (missingFields.includes('tree_path')) insertText += ` tree_path := '${treePathBase}', 'TODO_Category', 'TODO_Subcategory';\n`;
|
||||||
|
|
||||||
// HIER GEFIXT: Einfügen auf Zeile + 1, damit es IN den Block rutscht
|
|
||||||
fix.edit.insert(document.uri, new vscode.Position(diagnostic.range.start.line + 1, 0), insertText);
|
fix.edit.insert(document.uri, new vscode.Position(diagnostic.range.start.line + 1, 0), insertText);
|
||||||
fix.diagnostics = [diagnostic];
|
fix.diagnostics = [diagnostic];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ export class BuiltInCompletion implements CompletionStrategy {
|
||||||
const items: vscode.CompletionItem[] = [];
|
const items: vscode.CompletionItem[] = [];
|
||||||
|
|
||||||
// Prüft, ob wir im Namespace "log." oder "discovery." sind (auch wenn man danach weiter tippt!)
|
// Prüft, ob wir im Namespace "log." oder "discovery." sind (auch wenn man danach weiter tippt!)
|
||||||
const match = linePrefix.match(/\b(log|discovery)\.([a-zA-Z0-9_]*)$/);
|
const match = linePrefix.match(/\b(discovery|log|model|text|regex|time|xpath|json|hash|inference)\.([a-zA-Z0-9_]*)$/);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
const namespace = match[1];
|
const namespace = match[1];
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,32 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { CompletionStrategy } from './CompletionStrategy';
|
import { CompletionStrategy } from './CompletionStrategy';
|
||||||
|
import { tplBuiltIns } from '../../data/TplBuiltIns';
|
||||||
|
|
||||||
export class LocalContextCompletion implements CompletionStrategy {
|
export class LocalContextCompletion implements CompletionStrategy {
|
||||||
public async provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): Promise<vscode.CompletionItem[] | undefined> {
|
public async provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): Promise<vscode.CompletionItem[] | undefined> {
|
||||||
const text = document.getText();
|
const text = document.getText();
|
||||||
const items: vscode.CompletionItem[] = [];
|
const items: vscode.CompletionItem[] = [];
|
||||||
|
|
||||||
// 1. WENN DER USER NACH EINEM PUNKT TIPPT (Cross-File Lookup)
|
const dotMatch = linePrefix.match(/\b([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]*)$/);
|
||||||
const dotMatch = linePrefix.match(/\b([a-zA-Z0-9_]+)\.$/);
|
|
||||||
if (dotMatch) {
|
if (dotMatch) {
|
||||||
const prefix = dotMatch[1]; // z.B. "modelling"
|
const prefix = dotMatch[1];
|
||||||
if (prefix === 'log' || prefix === 'discovery') return undefined; // Built-ins ignorieren
|
|
||||||
|
const bmcNamespaces = Object.keys(tplBuiltIns);
|
||||||
|
if (bmcNamespaces.includes(prefix)) {
|
||||||
|
return undefined; // Überlässt das Feld komplett der BuiltInCompletion!
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfen, ob der Präfix importiert wurde
|
|
||||||
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+${prefix}\\b`);
|
||||||
const match = importRegex.exec(text);
|
const match = importRegex.exec(text);
|
||||||
if (match) {
|
if (match) {
|
||||||
const moduleName = match[1]; // z.B. "Company.software_modelling"
|
const moduleName = match[1];
|
||||||
const externalItems = await this.resolveImportedMethods(moduleName, prefix);
|
const externalItems = await this.resolveImportedMethods(moduleName, prefix);
|
||||||
return externalItems; // Nur externe Methoden zurückgeben
|
return externalItems;
|
||||||
}
|
}
|
||||||
return undefined; // Unbekannter Namespace
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. LOKALE DEFINITIONS & IMPORTS PARSEN
|
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
let currentDefBlock = "";
|
let currentDefBlock = "";
|
||||||
|
|
||||||
|
|
@ -44,7 +47,6 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Imports für die globale Worterkennung bereitstellen
|
|
||||||
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+([a-zA-Z0-9_]+)/g;
|
||||||
let matchVar;
|
let matchVar;
|
||||||
while ((matchVar = fromImportRegex.exec(text)) !== null) {
|
while ((matchVar = fromImportRegex.exec(text)) !== null) {
|
||||||
|
|
@ -65,20 +67,16 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hilfsmethode: Sucht im Workspace nach der anderen Datei!
|
|
||||||
private async resolveImportedMethods(moduleName: string, defBlockName: string): Promise<vscode.CompletionItem[]> {
|
private async resolveImportedMethods(moduleName: string, defBlockName: string): Promise<vscode.CompletionItem[]> {
|
||||||
const items: vscode.CompletionItem[] = [];
|
const items: vscode.CompletionItem[] = [];
|
||||||
// Sucht alle .tpl Dateien im Projekt
|
|
||||||
const files = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**');
|
const files = await vscode.workspace.findFiles('**/*.tpl', '**/node_modules/**');
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const doc = await vscode.workspace.openTextDocument(file);
|
const doc = await vscode.workspace.openTextDocument(file);
|
||||||
const text = doc.getText();
|
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*;`);
|
const moduleRegex = new RegExp(`tpl\\s+1\\.\\d+\\s+module\\s+${moduleName.replace(/\./g, '\\.')}\\s*;`);
|
||||||
if (moduleRegex.test(text)) {
|
if (moduleRegex.test(text)) {
|
||||||
// Modul gefunden! Jetzt die Defines im entsprechenden Block auslesen
|
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
let inTargetBlock = false;
|
let inTargetBlock = false;
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
|
|
@ -96,7 +94,7 @@ export class LocalContextCompletion implements CompletionStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break; // Datei gefunden und geparst, wir können abbrechen
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
|
|
|
||||||
|
|
@ -2,92 +2,144 @@ import * as vscode from 'vscode';
|
||||||
import { CompletionStrategy } from './CompletionStrategy';
|
import { CompletionStrategy } from './CompletionStrategy';
|
||||||
|
|
||||||
export class SnippetCompletion implements CompletionStrategy {
|
export class SnippetCompletion implements CompletionStrategy {
|
||||||
|
public async provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): Promise<vscode.CompletionItem[] | undefined> {
|
||||||
public provideCompletions(document: vscode.TextDocument, position: vscode.Position, linePrefix: string): vscode.CompletionItem[] | undefined {
|
// Bei Modul-Aufrufen (Punkt) keine Snippets feuern
|
||||||
if (linePrefix.match(/\.[a-zA-Z0-9_]*$/)) { return undefined; }
|
if (linePrefix.match(/\.[a-zA-Z0-9_]*$/)) { return undefined; }
|
||||||
|
|
||||||
const items: vscode.CompletionItem[] = [];
|
const items: vscode.CompletionItem[] = [];
|
||||||
|
|
||||||
|
// 1. Kontext herausfinden (Sind wir in einem Pattern oder Syncmapping?)
|
||||||
|
const textBeforeCursor = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
|
||||||
|
const inPattern = textBeforeCursor.lastIndexOf('pattern ') > textBeforeCursor.lastIndexOf('end pattern;');
|
||||||
|
const inSyncmapping = textBeforeCursor.lastIndexOf('syncmapping ') > textBeforeCursor.lastIndexOf('end syncmapping;');
|
||||||
|
const inBlock = inPattern || inSyncmapping;
|
||||||
|
|
||||||
|
// 2. Settings laden
|
||||||
const config = vscode.workspace.getConfiguration('tpl');
|
const config = vscode.workspace.getConfiguration('tpl');
|
||||||
|
const author = config.get<string>('metadata.author', 'Kuro');
|
||||||
|
const origin = config.get<string>('metadata.origin', 'Custom');
|
||||||
|
const publisher = config.get<string>('metadata.publisher', 'IT-Operations');
|
||||||
|
const version = config.get<string>('version', '23');
|
||||||
|
|
||||||
// Kontext-Scanner: Wo befindet sich der Cursor gerade?
|
// Dynamischer Tree-Path
|
||||||
let inBlock: 'none' | 'pattern' | 'definitions' = 'none';
|
const minLevels = config.get<number>('metadata.treePath.minLevels', 3);
|
||||||
let inPhase = '';
|
const staticNodes = config.get<string[]>('metadata.treePath.staticNodes', ['Grafing']);
|
||||||
|
|
||||||
for (let i = 0; i <= position.line; i++) {
|
let treePathStr = '';
|
||||||
const text = document.lineAt(i).text.trim();
|
let snippetTabIdx = 1;
|
||||||
if (text.match(/^(pattern|syncmapping)\b/)) { inBlock = 'pattern'; inPhase = ''; }
|
|
||||||
else if (text.match(/^definitions?\b/)) { inBlock = 'definitions'; inPhase = ''; }
|
|
||||||
else if (text.match(/^end (pattern|syncmapping|definitions?)\b/)) { inBlock = 'none'; inPhase = ''; }
|
|
||||||
|
|
||||||
if (inBlock === 'pattern') {
|
for (let i = 0; i < minLevels; i++) {
|
||||||
['metadata', 'overview', 'constants', 'triggers', 'mapping', 'removal', 'body'].forEach(p => {
|
if (i < staticNodes.length) {
|
||||||
if (text.startsWith(p)) { inPhase = p; }
|
treePathStr += `'${staticNodes[i]}'`;
|
||||||
else if (text.startsWith(`end ${p}`)) { inPhase = ''; }
|
} else {
|
||||||
});
|
treePathStr += `'\${${snippetTabIdx}:Level_${i+1}}'`;
|
||||||
|
snippetTabIdx++;
|
||||||
}
|
}
|
||||||
if (inBlock === 'definitions') {
|
if (i < minLevels - 1) treePathStr += ', ';
|
||||||
if (text.startsWith('define ')) { inPhase = 'define'; }
|
|
||||||
else if (text.startsWith('end define')) { inPhase = ''; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dynamischer Metadata-String
|
||||||
|
let metadataBlock = 'metadata\n';
|
||||||
|
if (config.get<boolean>('metadata.require.origin', true)) metadataBlock += ` origin := '${origin}';\n`;
|
||||||
|
if (config.get<boolean>('metadata.require.treePath', true)) metadataBlock += ` tree_path := ${treePathStr};\n`;
|
||||||
|
if (config.get<boolean>('metadata.require.products', true)) metadataBlock += ` products := '\${${snippetTabIdx++}:Product}';\n`;
|
||||||
|
if (config.get<boolean>('metadata.require.publishers', true)) metadataBlock += ` publishers := '${publisher}';\n`;
|
||||||
|
if (config.get<boolean>('metadata.require.author', true)) metadataBlock += ` author := '${author}';\n`;
|
||||||
|
metadataBlock += 'end metadata;';
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// SNIPPETS FÜR DIE MODUL-EBENE (Außerhalb)
|
||||||
|
// ==========================================
|
||||||
|
if (!inBlock) {
|
||||||
|
const skeleton = new vscode.CompletionItem('tpl_skeleton', vscode.CompletionItemKind.Snippet);
|
||||||
|
skeleton.detail = "Generiert ein frisches TPL Modul";
|
||||||
|
skeleton.insertText = new vscode.SnippetString(
|
||||||
|
`tpl 1.\${1:${version}} module \${2:MyModule};\n\n` +
|
||||||
|
`metadata\n` +
|
||||||
|
` origin := '${origin}';\n` +
|
||||||
|
` tree_path := '${treePathBase}', '\${3:Category}', '\${4:Subcategory}';\n` +
|
||||||
|
` products := '\${5:Product}';\n` +
|
||||||
|
` publishers := '${publisher}';\n` +
|
||||||
|
` author := '${author}';\n` +
|
||||||
|
`end metadata;\n\n` +
|
||||||
|
`pattern \${6:MyPattern} 1.0\n` +
|
||||||
|
` """\n \${7:Beschreibung des Patterns}\n """\n` +
|
||||||
|
` metadata\n` +
|
||||||
|
` publishers := '${publisher}';\n` +
|
||||||
|
` tags := '\${8:Tags}';\n` +
|
||||||
|
` end metadata;\n\n` +
|
||||||
|
` overview\n` +
|
||||||
|
` tags \${9:Tags};\n` +
|
||||||
|
` end overview;\n\n` +
|
||||||
|
` constants\n` +
|
||||||
|
` \${10:const_name} := '\${11:value}';\n` +
|
||||||
|
` end constants;\n\n` +
|
||||||
|
` triggers\n` +
|
||||||
|
` on process := SoftwareInstance created, confirmed where type = '\${12:Type}';\n` +
|
||||||
|
` end triggers;\n\n` +
|
||||||
|
` body\n` +
|
||||||
|
` log.debug("Start pattern %pattern_name%");\n` +
|
||||||
|
` $0\n` +
|
||||||
|
` end body;\n` +
|
||||||
|
`end pattern;`
|
||||||
|
);
|
||||||
|
items.push(skeleton);
|
||||||
|
|
||||||
|
const modMeta = new vscode.CompletionItem('metadata', vscode.CompletionItemKind.Snippet);
|
||||||
|
modMeta.detail = "Modul-Metadaten Block (Pflichtfelder)";
|
||||||
|
modMeta.insertText = new vscode.SnippetString(
|
||||||
|
`metadata\n` +
|
||||||
|
` origin := '${origin}';\n` +
|
||||||
|
` tree_path := '${treePathBase}', '\${1:Category}', '\${2:Subcategory}';\n` +
|
||||||
|
` products := '\${3:Product}';\n` +
|
||||||
|
` publishers := '${publisher}';\n` +
|
||||||
|
` author := '${author}';\n` +
|
||||||
|
`end metadata;\n$0`
|
||||||
|
);
|
||||||
|
items.push(modMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 1. GLOBAL (Top-Level: Vor dem Pattern / Import-Bereich)
|
// SNIPPETS FÜR DIE PATTERN-EBENE (Innerhalb)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
if (inBlock === 'none') {
|
if (inBlock) {
|
||||||
const skel = new vscode.CompletionItem('tpl_skeleton', vscode.CompletionItemKind.Snippet);
|
const patMeta = new vscode.CompletionItem('metadata', vscode.CompletionItemKind.Snippet);
|
||||||
skel.insertText = new vscode.SnippetString(`tpl 1.23 module \${1:Name};\n\nmetadata\n origin := '\${2:Custom}';\n tree_path := 'Grafing', '\${3:Category}', '\${4:SubCategory}';\nend metadata;\n\npattern \${5:PatternName} 1.0\n """\n \${6:Beschreibung}\n """\n overview\n tags \${7:tag1};\n end overview;\n\n triggers\n on host := Host created, confirmed;\n end triggers;\n\n body\n log.debug("Start pattern \${5:PatternName}");\n $0\n end body;\nend pattern;`);
|
patMeta.detail = "Pattern-Metadaten Block";
|
||||||
items.push(skel);
|
patMeta.insertText = new vscode.SnippetString(
|
||||||
|
`metadata\n` +
|
||||||
['pattern', 'syncmapping', 'definitions'].forEach(block => {
|
` publishers := '${publisher}';\n` +
|
||||||
const item = new vscode.CompletionItem(block, vscode.CompletionItemKind.Snippet);
|
` tags := '\${1:Tags}';\n` +
|
||||||
item.insertText = new vscode.SnippetString(`${block} \${1:Name} \${2:1.0}\n """\n \${3:DocString}\n """\n $0\nend ${block};`);
|
`end metadata;\n$0`
|
||||||
items.push(item);
|
);
|
||||||
});
|
items.push(patMeta);
|
||||||
|
|
||||||
const imp = new vscode.CompletionItem('import', vscode.CompletionItemKind.Snippet);
|
|
||||||
imp.insertText = new vscode.SnippetString('import ${1:module_name};');
|
|
||||||
items.push(imp);
|
|
||||||
|
|
||||||
const fromImp = new vscode.CompletionItem('from import', vscode.CompletionItemKind.Snippet);
|
|
||||||
fromImp.insertText = new vscode.SnippetString('from ${1:module_name} import${2:function_name};');
|
|
||||||
items.push(fromImp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 2. INNERHALB PATTERN (aber VOR dem Body)
|
// ALLGEMEINE SNIPPETS (Überall gültig)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
if (inBlock === 'pattern' && inPhase === '') {
|
|
||||||
['metadata', 'overview', 'constants', 'triggers', 'mapping', 'removal', 'body'].forEach(sec => {
|
|
||||||
const item = new vscode.CompletionItem(sec, vscode.CompletionItemKind.Snippet);
|
|
||||||
item.insertText = new vscode.SnippetString(`${sec}\n $0\nend ${sec};`);
|
|
||||||
items.push(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inBlock === 'definitions' && inPhase === '') {
|
|
||||||
const item = new vscode.CompletionItem('define', vscode.CompletionItemKind.Snippet);
|
|
||||||
item.insertText = new vscode.SnippetString(`define \${1:methodName}(\${2:params})\n """\n \${3:DocString}\n """\n $0\nend define;`);
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 3. INNERHALB VON BODY ODER DEFINE (Ausführbarer Code)
|
|
||||||
// ==========================================
|
|
||||||
if (inPhase === 'body' || inPhase === 'define') {
|
|
||||||
['discovery', 'log', 'model', 'search', 'text', 'regex', 'time', 'hash', 'number', 'inference'].forEach(ns => {
|
|
||||||
const item = new vscode.CompletionItem(ns, vscode.CompletionItemKind.Module);
|
|
||||||
items.push(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
const ifSnippet = new vscode.CompletionItem('if', vscode.CompletionItemKind.Snippet);
|
const ifSnippet = new vscode.CompletionItem('if', vscode.CompletionItemKind.Snippet);
|
||||||
ifSnippet.insertText = new vscode.SnippetString('if ${1:condition} then\n $0\nend if;');
|
ifSnippet.insertText = new vscode.SnippetString(`if \${1:condition} then\n $0\nend if;`);
|
||||||
items.push(ifSnippet);
|
items.push(ifSnippet);
|
||||||
|
|
||||||
|
const elifSnippet = new vscode.CompletionItem('elif', vscode.CompletionItemKind.Snippet);
|
||||||
|
elifSnippet.insertText = new vscode.SnippetString(`elif \${1:condition} then\n $0`);
|
||||||
|
items.push(elifSnippet);
|
||||||
|
|
||||||
|
const elseSnippet = new vscode.CompletionItem('else', vscode.CompletionItemKind.Snippet);
|
||||||
|
elseSnippet.insertText = new vscode.SnippetString(`else\n $0`);
|
||||||
|
items.push(elseSnippet);
|
||||||
|
|
||||||
const forSnippet = new vscode.CompletionItem('for', vscode.CompletionItemKind.Snippet);
|
const forSnippet = new vscode.CompletionItem('for', vscode.CompletionItemKind.Snippet);
|
||||||
forSnippet.insertText = new vscode.SnippetString('for ${1:item} in${2:list} do\n $0\nend for;');
|
forSnippet.insertText = new vscode.SnippetString(`for \${1:item} in \${2:list} do\n $0\nend for;`);
|
||||||
items.push(forSnippet);
|
items.push(forSnippet);
|
||||||
}
|
|
||||||
|
const whileSnippet = new vscode.CompletionItem('while', vscode.CompletionItemKind.Snippet);
|
||||||
|
whileSnippet.insertText = new vscode.SnippetString(`while \${1:condition} do\n $0\nend while;`);
|
||||||
|
items.push(whileSnippet);
|
||||||
|
|
||||||
|
const defineSnippet = new vscode.CompletionItem('define', vscode.CompletionItemKind.Snippet);
|
||||||
|
defineSnippet.insertText = new vscode.SnippetString(`define \${1:name}(\${2:params})\n """\n \${3:DocString}\n """\n $0\nend define;`);
|
||||||
|
items.push(defineSnippet);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue