import * as vscode from "vscode" export function formatCdlFile(content: string): string { let indentLevel = 0 const formattedLines = [] for (const line of content.split("\n")) { const strippedLine = line.trim() if (strippedLine.endsWith("}")) { indentLevel = Math.max(indentLevel - 1, 0) } formattedLines.push(" ".repeat(indentLevel) + strippedLine) if (strippedLine.endsWith("{")) { indentLevel += 1 } if (strippedLine.startsWith("#")) { formattedLines[formattedLines.length - 1] = " ".repeat(indentLevel) + strippedLine.replace(/^#\s*/, "# ") } } return formattedLines.join("\n") } export function formatDefFile(content: string): string { let indentLevel = 0 const formattedLines = [] for (const line of content.split("\n")) { const strippedLine = line.trim() if (strippedLine.endsWith("}")) { indentLevel = Math.max(indentLevel - 1, 0) } formattedLines.push(" ".repeat(indentLevel) + strippedLine) if (strippedLine.endsWith("{")) { indentLevel += 1 } } return formattedLines.join("\n") } export function isFirstLineMachine(content: string): boolean { const lines = content.split("\n").map((line) => line.trim()) for (const line of lines) { console.log(line) if (line === "" || line.startsWith("#")) { console.log("skipping line") continue } const machineRegex = /^MACHINE\s+\S+/ return machineRegex.test(line) } return false } export function diagnosticHandler(document: vscode.TextDocument) { const diagnostics: vscode.Diagnostic[] = [] if (document.languageId === "cdl" || document.languageId === "def") { if (!isFirstLineMachine(document.getText())) { const range = new vscode.Range( document.positionAt(0), document.positionAt(document.getText().length) ) const diagnostic = new vscode.Diagnostic( range, "The first line should contain 'MACHINE'.", vscode.DiagnosticSeverity.Error ) diagnostics.push(diagnostic) } } return diagnostics } export function completionHandlerCdl(document: vscode.TextDocument, position: vscode.Position) { const linePrefix = document.lineAt(position).text.substring(0, position.character) const categories = ["MILL", "LATHE", "DRILL"] if (linePrefix.endsWith("TYPE ")) { return [ new vscode.CompletionItem("o", vscode.CompletionItemKind.TypeParameter), new vscode.CompletionItem("b", vscode.CompletionItemKind.TypeParameter), new vscode.CompletionItem("i", vscode.CompletionItemKind.TypeParameter), new vscode.CompletionItem("d", vscode.CompletionItemKind.TypeParameter), new vscode.CompletionItem("g", vscode.CompletionItemKind.TypeParameter), new vscode.CompletionItem("s", vscode.CompletionItemKind.TypeParameter) ] } if (linePrefix.endsWith("TOGGLE ")) { return [ new vscode.CompletionItem("off", vscode.CompletionItemKind.TypeParameter), new vscode.CompletionItem("on", vscode.CompletionItemKind.TypeParameter) ] } if (linePrefix.endsWith("CATEGORY ")) { return categories.map( (cat) => new vscode.CompletionItem(cat, vscode.CompletionItemKind.TypeParameter) ) } const categoryPrefix = linePrefix.match(/CATEGORY (.*) /) if (categoryPrefix) { const usedCategories = categoryPrefix[1].split(" ") const remainingCategories = categories.filter((cat) => !usedCategories.includes(cat)) return remainingCategories.map( (cat) => new vscode.CompletionItem(cat, vscode.CompletionItemKind.TypeParameter) ) } return undefined } export function hoverCdlHandler(document: vscode.TextDocument, position: vscode.Position) { const wordRange = document.getWordRangeAtPosition(position) const word = document.getText(wordRange) const text = document.getText() const lines = text.split("\n") let hoverText: string | undefined for (const line of lines) { const words = line.split(/\s+/) const wordIndex = words.indexOf(word) if (wordIndex > 0) { if (words[wordIndex - 1] === "EVENT") { hoverText = `MOM_${word}` } else if (words[wordIndex - 1] === "PARAM") { hoverText = `mom_${word}` } break } } if (hoverText) { return new vscode.Hover(hoverText) } return undefined } export function tclDocumentSymbolProvider(document: vscode.TextDocument): vscode.DocumentSymbol[] { const symbols: vscode.DocumentSymbol[] = [] const lines = document.getText().split("\n") interface Scope { name: string symbol: vscode.DocumentSymbol startLine: number braceCount: number children: Scope[] } const rootScope: Scope = { name: "", symbol: new vscode.DocumentSymbol( "root", "", vscode.SymbolKind.Namespace, new vscode.Range(0, 0, lines.length, 0), new vscode.Range(0, 0, 0, 0) ), startLine: 0, braceCount: 0, children: [] } let scopeStack: Scope[] = [rootScope] const namespaceRegex = /^\s*namespace\s+eval\s+([^\s\{]+)/ const procRegex = /^\s*proc\s+([^\s\{]+)\s+\{.*\}\s+\{/ const eventStartRegex = /^\s*LIB_GE_command_buffer_edit_(prepend|append|insert)\b/ let pendingEvent: { editType: string; startLine: number } | null = null for (let i = 0; i < lines.length; i++) { const line = lines[i] const nsMatch = line.match(namespaceRegex) const procMatch = line.match(procRegex) const eventStartMatch = line.match(eventStartRegex) if (pendingEvent) { // Wir suchen nach schließender Klammer mit Eventnamen danach const closeMatch = line.match(/^\s*\}\s*(\w+)\s*$/) if (closeMatch) { const eventName = closeMatch[1] const eventSymbol = new vscode.DocumentSymbol( eventName, `Event (${pendingEvent.editType})`, vscode.SymbolKind.Event, new vscode.Range(pendingEvent.startLine, 0, i, line.length), new vscode.Range(i, 0, i, line.length) ) scopeStack[scopeStack.length - 1].children.push({ name: eventName, symbol: eventSymbol, startLine: pendingEvent.startLine, braceCount: 0, children: [] }) pendingEvent = null continue // nichts anderes prüfen, wenn wir Event abgeschlossen haben } } if (eventStartMatch) { // Beginn eines Event-Blocks erkannt pendingEvent = { editType: eventStartMatch[1], startLine: i } continue } if (nsMatch) { const nsName = nsMatch[1] const nsSymbol = new vscode.DocumentSymbol( nsName, "Namespace", vscode.SymbolKind.Namespace, new vscode.Range(i, 0, i, line.length), new vscode.Range(i, 0, i, line.length) ) const nsScope: Scope = { name: nsName, symbol: nsSymbol, startLine: i, braceCount: 0, children: [] } scopeStack[scopeStack.length - 1].children.push(nsScope) scopeStack.push(nsScope) } else if (procMatch) { const procName = procMatch[1] const procSymbol = new vscode.DocumentSymbol( procName, "Procedure", vscode.SymbolKind.Function, new vscode.Range(i, 0, i, line.length), new vscode.Range(i, 0, i, line.length) ) scopeStack[scopeStack.length - 1].children.push({ name: procName, symbol: procSymbol, startLine: i, braceCount: 0, children: [] }) } // Brace-Balancing für Namespaces und Procs const openCount = (line.match(/\{/g) || []).length const closeCount = (line.match(/\}/g) || []).length scopeStack[scopeStack.length - 1].braceCount += openCount - closeCount while (scopeStack.length > 1 && scopeStack[scopeStack.length - 1].braceCount <= 0) { const finishedScope = scopeStack.pop() if (finishedScope) { finishedScope.symbol.range = new vscode.Range( finishedScope.startLine, 0, i, line.length ) } } } function collectSymbols(scope: Scope): vscode.DocumentSymbol[] { for (const child of scope.children) { child.symbol.children = collectSymbols(child) } return scope.children.map((child) => child.symbol) } return collectSymbols(rootScope) } export function cdlDocumentSymbolProvider(document: vscode.TextDocument): vscode.DocumentSymbol[] { const lines = document.getText().split("\n") interface Scope { symbol: vscode.DocumentSymbol startLine: number } const root = new vscode.DocumentSymbol( "root", "", vscode.SymbolKind.Namespace, new vscode.Range(0, 0, lines.length, 0), new vscode.Range(0, 0, 0, 0) ) root.children = [] const stack: Scope[] = [{ symbol: root, startLine: 0 }] let pending: { name: string; kind: vscode.SymbolKind; startLine: number } | null = null for (let i = 0; i < lines.length; i++) { const line = lines[i] const trimmed = line.trim() const createSymbol = () => { if (!pending) { return } const startRange = new vscode.Range( pending.startLine, 0, pending.startLine, lines[pending.startLine].length ) const symbol = new vscode.DocumentSymbol( pending.name, "", pending.kind, startRange, startRange ) symbol.children = [] stack[stack.length - 1].symbol.children?.push(symbol) stack.push({ symbol, startLine: pending.startLine }) pending = null } if (pending && trimmed.includes("{")) { createSymbol() } if (trimmed.startsWith("}")) { const scope = stack.pop() if (scope) { scope.symbol.range = new vscode.Range(scope.startLine, 0, i, line.length) } continue } let match if ((match = /^\s*EVENT\s+(\w+)/.exec(line))) { pending = { name: match[1], kind: vscode.SymbolKind.Event, startLine: i } if (line.includes("{")) { createSymbol() } } else if ((match = /^\s*PARAM\s+(\w+)/.exec(line))) { pending = { name: match[1], kind: vscode.SymbolKind.Field, startLine: i } if (line.includes("{")) { createSymbol() } } } return root.children || [] } export function defDocumentSymbolProvider(document: vscode.TextDocument): vscode.DocumentSymbol[] { const lines = document.getText().split("\n") interface Scope { symbol: vscode.DocumentSymbol startLine: number } const root = new vscode.DocumentSymbol( "root", "", vscode.SymbolKind.Namespace, new vscode.Range(0, 0, lines.length, 0), new vscode.Range(0, 0, 0, 0) ) root.children = [] const stack: Scope[] = [{ symbol: root, startLine: 0 }] let pending: { name: string; kind: vscode.SymbolKind; startLine: number } | null = null for (let i = 0; i < lines.length; i++) { const line = lines[i] const trimmed = line.trim() const createSymbol = () => { if (!pending) { return } const startRange = new vscode.Range( pending.startLine, 0, pending.startLine, lines[pending.startLine].length ) const symbol = new vscode.DocumentSymbol( pending.name, "", pending.kind, startRange, startRange ) symbol.children = [] stack[stack.length - 1].symbol.children?.push(symbol) stack.push({ symbol, startLine: pending.startLine }) pending = null } if (pending && trimmed.includes("{")) { createSymbol() } if (trimmed.startsWith("}")) { const scope = stack.pop() if (scope) { scope.symbol.range = new vscode.Range(scope.startLine, 0, i, line.length) } continue } let match if ((match = /^\s*FORMATTING\b/.exec(line))) { pending = { name: "FORMATTING", kind: vscode.SymbolKind.Module, startLine: i } if (line.includes("{")) { createSymbol() } } else if ((match = /^\s*ADDRESS\s+(\w+)/.exec(line))) { pending = { name: match[1], kind: vscode.SymbolKind.Field, startLine: i } if (line.includes("{")) { createSymbol() } } else if ((match = /^\s*BLOCK_TEMPLATE\s+(\w+)/.exec(line))) { pending = { name: match[1], kind: vscode.SymbolKind.Function, startLine: i } if (line.includes("{")) { createSymbol() } } } return root.children || [] }