feat(cdl): add CDL event handler parsing and snippet support
build_and_puplish.yml / build_and_publish (release) Successful in 36s
build_and_puplish.yml / build_and_publish (release) Successful in 36s
This adds a small DSL-aware helper to extract CDL event declarations and generate a ready-to-use snippet. It also extends hover support to show the snippet and parameter hints for CDL events, improving developer productivity. - Generate a MOM event handler scaffold and local vars - Hover shows the generated snippet and mom_ parameter hints
This commit is contained in:
@@ -0,0 +1,116 @@
|
|||||||
|
export interface CdlEventHandler {
|
||||||
|
eventName: string
|
||||||
|
parameterNames: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function structuralCode(line: string): string {
|
||||||
|
let result = ""
|
||||||
|
let inString = false
|
||||||
|
let escaped = false
|
||||||
|
|
||||||
|
for (const character of line) {
|
||||||
|
if (escaped) {
|
||||||
|
escaped = false
|
||||||
|
result += inString ? " " : character
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (character === "\\") {
|
||||||
|
escaped = true
|
||||||
|
result += inString ? " " : character
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (character === '"') {
|
||||||
|
inString = !inString
|
||||||
|
result += " "
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (character === "#" && !inString) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result += inString ? " " : character
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function braceDelta(line: string): number {
|
||||||
|
let delta = 0
|
||||||
|
for (const character of structuralCode(line)) {
|
||||||
|
if (character === "{") {
|
||||||
|
delta += 1
|
||||||
|
} else if (character === "}") {
|
||||||
|
delta -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return delta
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cdlEventHandlerAtLine(
|
||||||
|
source: string,
|
||||||
|
declarationLine: number
|
||||||
|
): CdlEventHandler | undefined {
|
||||||
|
const lines = source.split(/\r?\n/)
|
||||||
|
const declaration = lines[declarationLine]
|
||||||
|
if (declaration === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventMatch = /^\s*EVENT\s+([^\s{]+)/.exec(structuralCode(declaration))
|
||||||
|
if (!eventMatch) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameterNames: string[] = []
|
||||||
|
let eventOpened = false
|
||||||
|
let depth = 0
|
||||||
|
|
||||||
|
for (let lineNumber = declarationLine; lineNumber < lines.length; lineNumber++) {
|
||||||
|
const line = lines[lineNumber]
|
||||||
|
const code = structuralCode(line)
|
||||||
|
|
||||||
|
if (eventOpened && depth === 1) {
|
||||||
|
const parameterMatch = /^\s*PARAM\s+([^\s{]+)/.exec(code)
|
||||||
|
if (parameterMatch) {
|
||||||
|
parameterNames.push(parameterMatch[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const delta = braceDelta(line)
|
||||||
|
if (!eventOpened && delta > 0) {
|
||||||
|
eventOpened = true
|
||||||
|
}
|
||||||
|
if (eventOpened) {
|
||||||
|
depth += delta
|
||||||
|
if (depth <= 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventName: eventMatch[1],
|
||||||
|
parameterNames
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function momEventName(eventName: string): string {
|
||||||
|
return `MOM_${eventName.replace(/^MOM_/i, "")}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function momVariableName(parameterName: string): string {
|
||||||
|
return `mom_${parameterName.replace(/^mom_/i, "")}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCdlEventHandlerSnippet(handler: CdlEventHandler): string {
|
||||||
|
const globals = [
|
||||||
|
...new Set(handler.parameterNames.map((parameter) => momVariableName(parameter)))
|
||||||
|
]
|
||||||
|
const lines = [`proc ${momEventName(handler.eventName)} { } {`]
|
||||||
|
|
||||||
|
if (globals.length > 0) {
|
||||||
|
lines.push(...globals.map((variable) => ` global ${variable}`), "")
|
||||||
|
}
|
||||||
|
lines.push(" #Put your UDE Handler Tcl here", "", "}")
|
||||||
|
|
||||||
|
return lines.join("\n")
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
import * as vscode from "vscode"
|
import * as vscode from "vscode"
|
||||||
|
import {
|
||||||
|
cdlEventHandlerAtLine,
|
||||||
|
createCdlEventHandlerSnippet
|
||||||
|
} from "./cdlEventHandler"
|
||||||
|
|
||||||
export function formatCdlFile(content: string): string {
|
export function formatCdlFile(content: string): string {
|
||||||
let indentLevel = 0
|
let indentLevel = 0
|
||||||
@@ -110,30 +114,33 @@ export function completionHandlerCdl(document: vscode.TextDocument, position: vs
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hoverCdlHandler(document: vscode.TextDocument, position: vscode.Position) {
|
export function hoverCdlHandler(document: vscode.TextDocument, position: vscode.Position) {
|
||||||
const wordRange = document.getWordRangeAtPosition(position)
|
const line = document.lineAt(position.line).text
|
||||||
const word = document.getText(wordRange)
|
const eventMatch = /^\s*EVENT\s+([^\s{]+)/.exec(line)
|
||||||
const text = document.getText()
|
if (eventMatch) {
|
||||||
const lines = text.split("\n")
|
const eventStart = line.indexOf(eventMatch[1], eventMatch.index)
|
||||||
|
const declarationEnd = eventStart + eventMatch[1].length
|
||||||
let hoverText: string | undefined
|
if (position.character <= declarationEnd) {
|
||||||
|
const handler = cdlEventHandlerAtLine(document.getText(), position.line)
|
||||||
for (const line of lines) {
|
if (handler) {
|
||||||
const words = line.split(/\s+/)
|
const markdown = new vscode.MarkdownString()
|
||||||
const wordIndex = words.indexOf(word)
|
markdown.appendCodeblock(createCdlEventHandlerSnippet(handler), "tcl")
|
||||||
|
return new vscode.Hover(
|
||||||
if (wordIndex > 0) {
|
markdown,
|
||||||
if (words[wordIndex - 1] === "EVENT") {
|
new vscode.Range(position.line, eventMatch.index, position.line, declarationEnd)
|
||||||
hoverText = `MOM_${word}`
|
)
|
||||||
} else if (words[wordIndex - 1] === "PARAM") {
|
|
||||||
hoverText = `mom_${word}`
|
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hoverText) {
|
const parameterMatch = /^\s*PARAM\s+([^\s{]+)/.exec(line)
|
||||||
return new vscode.Hover(hoverText)
|
if (parameterMatch) {
|
||||||
|
const parameterStart = line.indexOf(parameterMatch[1], parameterMatch.index)
|
||||||
|
const parameterEnd = parameterStart + parameterMatch[1].length
|
||||||
|
if (position.character >= parameterStart && position.character <= parameterEnd) {
|
||||||
|
return new vscode.Hover(`mom_${parameterMatch[1].replace(/^mom_/i, "")}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user