Files
nx_post_support/client/src/common/handlers.ts
T

451 lines
14 KiB
TypeScript

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 || []
}