diff options
Diffstat (limited to 'js/baba-yaga/dev/vscode/extension.js')
-rw-r--r-- | js/baba-yaga/dev/vscode/extension.js | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/js/baba-yaga/dev/vscode/extension.js b/js/baba-yaga/dev/vscode/extension.js new file mode 100644 index 0000000..f0be4f5 --- /dev/null +++ b/js/baba-yaga/dev/vscode/extension.js @@ -0,0 +1,896 @@ +const vscode = require('vscode'); + +// Type information for Baba Yaga +const builtinFunctions = new Map([ + ['io.out', { name: 'io.out', kind: 'function', signature: 'io.out(value: any) -> void', description: 'Print value to console' }], + ['io.in', { name: 'io.in', kind: 'function', signature: 'io.in() -> String', description: 'Read input from console' }], + ['map', { name: 'map', kind: 'function', signature: 'map(func: (T) -> U, list: [T]) -> [U]', description: 'Apply function to each element' }], + ['filter', { name: 'filter', kind: 'function', signature: 'filter(pred: (T) -> Bool, list: [T]) -> [T]', description: 'Filter list by predicate' }], + ['reduce', { name: 'reduce', kind: 'function', signature: 'reduce(func: (acc: T, item: T) -> T, init: T, list: [T]) -> T', description: 'Fold list to single value' }], + ['append', { name: 'append', kind: 'function', signature: 'append(list: [T], item: T) -> [T]', description: 'Add item to end of list' }], + ['set', { name: 'set', kind: 'function', signature: 'set(table: Table, key: String, value: any) -> Table', description: 'Set table property' }], + ['merge', { name: 'merge', kind: 'function', signature: 'merge(table1: Table, table2: Table) -> Table', description: 'Merge two tables' }], + ['shape', { name: 'shape', kind: 'function', signature: 'shape(value: any) -> Table', description: 'Get value metadata' }] +]); + +// String functions +const stringFunctions = ['concat', 'split', 'join', 'length', 'substring', 'replace', 'trim', 'upper', 'lower']; +stringFunctions.forEach(func => { + builtinFunctions.set(`str.${func}`, { + name: `str.${func}`, + kind: 'function', + signature: `str.${func}(...args) -> String`, + description: `String ${func} operation` + }); +}); + +// Math functions +const mathFunctions = ['abs', 'sign', 'floor', 'ceil', 'round', 'trunc', 'min', 'max', 'clamp', 'pow', 'sqrt', 'exp', 'log', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'atan2', 'deg', 'rad', 'random', 'randomInt']; +mathFunctions.forEach(func => { + builtinFunctions.set(`math.${func}`, { + name: `math.${func}`, + kind: 'function', + signature: `math.${func}(...args) -> Number`, + description: `Math ${func} operation` + }); +}); + +// Keywords +const keywords = new Map([ + ['when', { name: 'when', kind: 'keyword', description: 'Pattern matching expression' }], + ['then', { name: 'then', kind: 'keyword', description: 'Pattern match result' }], + ['is', { name: 'is', kind: 'keyword', description: 'Pattern match operator' }], + ['Ok', { name: 'Ok', kind: 'keyword', description: 'Success result constructor' }], + ['Err', { name: 'Err', kind: 'keyword', description: 'Error result constructor' }], + ['true', { name: 'true', kind: 'keyword', description: 'Boolean true value' }], + ['false', { name: 'false', kind: 'keyword', description: 'Boolean false value' }], + ['PI', { name: 'PI', kind: 'keyword', description: 'Mathematical constant π' }], + ['INFINITY', { name: 'INFINITY', kind: 'keyword', description: 'Positive infinity' }], + ['and', { name: 'and', kind: 'keyword', description: 'Logical AND operator' }], + ['or', { name: 'or', kind: 'keyword', description: 'Logical OR operator' }], + ['xor', { name: 'xor', kind: 'keyword', description: 'Logical XOR operator' }] +]); + +// Types +const types = new Map([ + ['Bool', { name: 'Bool', kind: 'type', description: 'Boolean type (true/false)' }], + ['Int', { name: 'Int', kind: 'type', description: 'Integer type' }], + ['Float', { name: 'Float', kind: 'type', description: 'Floating-point type' }], + ['String', { name: 'String', kind: 'type', description: 'String type' }], + ['List', { name: 'List', kind: 'type', description: 'List type [T]' }], + ['Table', { name: 'Table', kind: 'type', description: 'Table type {key: value}' }], + ['Result', { name: 'Result', kind: 'type', description: 'Result type (Ok T | Err String)' }], + ['Number', { name: 'Number', kind: 'type', description: 'Numeric supertype (Int | Float)' }] +]); + +// Helper functions +function findFunctionDefinition(document, functionName) { + const text = document.getText(); + const lines = text.split('\n'); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const match = line.match(new RegExp(`\\b${functionName}\\s*:\\s*(.+?)\\s*->\\s*(.+?)\\s*;`)); + if (match) { + const signature = `${functionName} : ${match[1]} -> ${match[2]}`; + const startPos = document.positionAt(text.indexOf(line)); + const endPos = document.positionAt(text.indexOf(line) + line.length); + return { + signature, + range: new vscode.Range(startPos, endPos), + description: `Function defined at line ${i + 1}` + }; + } + } + + return null; +} + +// Helper function to check if position is in a Baba Yaga code block +function isInBabaYagaCodeBlock(document, position) { + const text = document.getText(); + const line = document.lineAt(position.line).text; + + // Check if we're in a markdown file + if (document.languageId === 'markdown') { + // Look for ```baba code blocks + const lines = text.split('\n'); + let inBabaBlock = false; + + for (let i = 0; i <= position.line; i++) { + const currentLine = lines[i]; + if (currentLine.trim().startsWith('```baba') || currentLine.trim().startsWith('```baba-yaga') || currentLine.trim().startsWith('```by')) { + inBabaBlock = true; + } else if (currentLine.trim() === '```' && inBabaBlock) { + inBabaBlock = false; + } + } + + return inBabaBlock; + } + + return document.languageId === 'baba-yaga'; +} + +// Hover Provider +class BabaYagaHoverProvider { + provideHover(document, position, token) { + // Only provide hover for Baba Yaga content + if (!isInBabaYagaCodeBlock(document, position)) { + return null; + } + + const wordRange = document.getWordRangeAtPosition(position); + if (!wordRange) return null; + + const word = document.getText(wordRange); + + // Check built-in functions + const builtin = builtinFunctions.get(word); + if (builtin) { + return new vscode.Hover([ + `**${builtin.name}**`, + `\`${builtin.signature}\``, + builtin.description || '' + ]); + } + + // Check keywords + const keyword = keywords.get(word); + if (keyword) { + return new vscode.Hover([ + `**${keyword.name}** (keyword)`, + keyword.description || '' + ]); + } + + // Check types + const type = types.get(word); + if (type) { + return new vscode.Hover([ + `**${type.name}** (type)`, + type.description || '' + ]); + } + + // Check for function definitions in the document + const functionDef = findFunctionDefinition(document, word); + if (functionDef) { + return new vscode.Hover([ + `**${word}** (function)`, + `\`${functionDef.signature}\``, + functionDef.description || '' + ]); + } + + return null; + } +} + +// Completion Provider +class BabaYagaCompletionProvider { + provideCompletionItems(document, position, token, context) { + // Only provide completion for Baba Yaga content + if (!isInBabaYagaCodeBlock(document, position)) { + return []; + } + + const items = []; + + // Add built-in functions + builtinFunctions.forEach((func, name) => { + const item = new vscode.CompletionItem(name, vscode.CompletionItemKind.Function); + item.detail = func.signature; + item.documentation = func.description; + items.push(item); + }); + + // Add keywords + keywords.forEach((keyword, name) => { + const item = new vscode.CompletionItem(name, vscode.CompletionItemKind.Keyword); + item.documentation = keyword.description; + items.push(item); + }); + + // Add types + types.forEach((type, name) => { + const item = new vscode.CompletionItem(name, vscode.CompletionItemKind.TypeParameter); + item.documentation = type.description; + items.push(item); + }); + + // Add operators + const operators = ['+', '-', '*', '/', '%', '=', '!=', '>', '<', '>=', '<=', '->', '..', ':', 'and', 'or', 'xor']; + operators.forEach(op => { + const item = new vscode.CompletionItem(op, vscode.CompletionItemKind.Operator); + items.push(item); + }); + + return items; + } +} + +// Definition Provider +class BabaYagaDefinitionProvider { + provideDefinition(document, position, token) { + // Only provide definition for Baba Yaga content + if (!isInBabaYagaCodeBlock(document, position)) { + return null; + } + + const wordRange = document.getWordRangeAtPosition(position); + if (!wordRange) return null; + + const word = document.getText(wordRange); + + // Find function definition in the document + const functionDef = findFunctionDefinition(document, word); + if (functionDef) { + return new vscode.Location(document.uri, functionDef.range); + } + + return null; + } +} + +// Reference Provider +class BabaYagaReferenceProvider { + provideReferences(document, position, context, token) { + // Only provide references for Baba Yaga content + if (!isInBabaYagaCodeBlock(document, position)) { + return []; + } + + const wordRange = document.getWordRangeAtPosition(position); + if (!wordRange) return null; + + const word = document.getText(wordRange); + const references = []; + + // Find all references in the document + const text = document.getText(); + const regex = new RegExp(`\\b${word}\\b`, 'g'); + let match; + + while ((match = regex.exec(text)) !== null) { + const startPos = document.positionAt(match.index); + const endPos = document.positionAt(match.index + match[0].length); + references.push(new vscode.Location(document.uri, new vscode.Range(startPos, endPos))); + } + + return references; + } +} + +// Diagnostic Provider +class BabaYagaDiagnosticProvider { + constructor() { + this.diagnosticCollection = vscode.languages.createDiagnosticCollection('baba-yaga'); + vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument.bind(this)); + } + + onDidChangeTextDocument(event) { + if (event.document.languageId === 'baba-yaga' || event.document.languageId === 'markdown' || event.document.languageId === 'baba' || event.document.languageId === 'by') { + this.updateDiagnostics(event.document); + } + } + + updateDiagnostics(document) { + // Disable automatic diagnostics to prevent false semicolon warnings + // Baba Yaga has different syntax rules than JavaScript/TypeScript + this.diagnosticCollection.set(document.uri, []); + } + + needsSemicolon(line) { + const trimmed = line.trim(); + + // Skip empty lines + if (!trimmed) return false; + + // Skip comment-only lines + if (trimmed.startsWith('//')) return false; + + // Extract code part before any inline comment + const commentIndex = trimmed.indexOf('//'); + const codePart = commentIndex >= 0 ? trimmed.substring(0, commentIndex).trim() : trimmed; + + // Skip if no actual code content + if (!codePart) return false; + + // Skip single identifiers or incomplete expressions (likely continuations) + if (codePart.match(/^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*$/)) { + return false; + } + + // Lines that don't need semicolons + if (codePart.endsWith(';') || + codePart.endsWith('{') || + codePart.endsWith('}') || + codePart.endsWith('then') || // Pattern match continuations + codePart.endsWith('is')) { // Pattern match starts + return false; + } + + // Lines that are likely continuations (start with pattern keywords) + if (codePart.match(/^\s*(when|then|is|\d+|"[^"]*"|true|false|Ok|Err|_)\s/)) { + return false; + } + + // Skip arrow function definitions (they don't need semicolons) + if (codePart.includes('->')) return false; + + // Skip pattern matching expressions + if (codePart.includes('when') || codePart.includes('is') || codePart.includes('then')) return false; + + // Skip table/record definitions + if (codePart.match(/\{[^}]*\}/)) return false; + + // Skip list definitions + if (codePart.match(/\[[^\]]*\]/)) return false; + + // Skip function calls that might be part of larger expressions + if (codePart.match(/[a-zA-Z_][a-zA-Z0-9_]*\s*\(/)) return false; + + // This line needs a semicolon + return true; + } +} + +// Type Hints Provider - Shows type information in different modes +class BabaYagaTypeHintsProvider { + constructor(mode = 'above') { + this.mode = mode; + + // Inline decorations (after expressions) + this.inlineDecorations = vscode.window.createTextEditorDecorationType({ + after: { + margin: '0 0 0 1em', + color: new vscode.ThemeColor('editorCodeLens.foreground'), + fontStyle: 'italic', + fontSize: '0.9em' + } + }); + + // Above-line decorations (on line above expressions) - DISABLED + // this.aboveDecorations = vscode.window.createTextEditorDecorationType({ + // after: { + // contentText: '', + // color: new vscode.ThemeColor('editorCodeLens.foreground'), + // fontStyle: 'italic', + // fontSize: '1em', + // textDecoration: 'none; display: block; text-align: right; margin-right: 1em; line-height: 1.5;' + // } + // }); + } + + updateTypeHints(editor) { + if (!editor || !isInBabaYagaCodeBlock(editor.document, editor.selection.active)) { + return; + } + + // Clear previous decorations + editor.setDecorations(this.inlineDecorations, []); + // editor.setDecorations(this.aboveDecorations, []); + + if (this.mode === 'none') { + return; // No active type hints, only hover + } + + const document = editor.document; + const text = document.getText(); + const inlineDecorations = []; + // const aboveDecorations = []; // Not used when above mode is disabled + + // Parse function definitions to build type map + const functionTypes = new Map(); + const lines = text.split('\n'); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + const funcMatch = line.match(/^(\w+)\s*:\s*(.+?)\s*->\s*(.+?)\s*;$/); + if (funcMatch) { + const [, funcName, params, returnType] = funcMatch; + functionTypes.set(funcName, { params, returnType }); + } + } + + // Find expressions to add type hints + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Skip comments + if (line.trim().startsWith('//')) { + continue; + } + + // Function calls + const funcCallMatch = line.match(/\b(\w+)\s*\(/g); + if (funcCallMatch) { + funcCallMatch.forEach(match => { + const funcName = match.replace(/\s*\($/, ''); + const typeInfo = this.getTypeInfo(funcName, functionTypes); + if (typeInfo) { + const matchIndex = line.indexOf(match); + const lineStartIndex = text.split('\n').slice(0, i).join('\n').length + (i > 0 ? 1 : 0); + const startPos = document.positionAt(lineStartIndex + matchIndex); + const endPos = document.positionAt(lineStartIndex + matchIndex + match.length); + + if (this.mode === 'inline') { + inlineDecorations.push({ + range: new vscode.Range(startPos, endPos), + renderOptions: { + after: { + contentText: ` : ${typeInfo}`, + color: new vscode.ThemeColor('editorCodeLens.foreground'), + fontStyle: 'italic', + fontSize: '0.9em' + } + } + }); + } + // Above mode disabled - commented out + // } else if (this.mode === 'above') { + // const lineAbove = new vscode.Position(Math.max(0, startPos.line - 1), 0); + // aboveDecorations.push({ + // range: new vscode.Range(lineAbove, lineAbove), + // renderOptions: { + // after: { + // contentText: `\n${' '.repeat(Math.max(0, startPos.character - typeInfo.length - 3))} : ${typeInfo}`, + // color: new vscode.ThemeColor('editorCodeLens.foreground'), + // fontStyle: 'italic', + // fontSize: '0.8em' + // } + // } + // }); + // } + } + }); + } + + // Member access (str.func, math.func) + const memberMatch = line.match(/\b(\w+)\.(\w+)\s*\(/g); + if (memberMatch) { + memberMatch.forEach(match => { + const [, namespace, funcName] = match.match(/\b(\w+)\.(\w+)\s*\(/); + const typeInfo = this.getBuiltinTypeInfo(namespace, funcName); + if (typeInfo) { + const matchIndex = line.indexOf(match); + const lineStartIndex = text.split('\n').slice(0, i).join('\n').length + (i > 0 ? 1 : 0); + const startPos = document.positionAt(lineStartIndex + matchIndex); + const endPos = document.positionAt(lineStartIndex + matchIndex + match.length); + + if (this.mode === 'inline') { + inlineDecorations.push({ + range: new vscode.Range(startPos, endPos), + renderOptions: { + after: { + contentText: ` : ${typeInfo}`, + color: new vscode.ThemeColor('editorCodeLens.foreground'), + fontStyle: 'italic', + fontSize: '0.9em' + } + } + }); + } + // Above mode disabled - commented out + // else if (this.mode === 'above') { + // const lineAbove = new vscode.Position(Math.max(0, startPos.line - 1), 0); + // aboveDecorations.push({ + // range: new vscode.Range(lineAbove, lineAbove), + // renderOptions: { + // after: { + // contentText: `\n${' '.repeat(Math.max(0, startPos.character - typeInfo.length - 3))} : ${typeInfo}`, + // color: new vscode.ThemeColor('editorCodeLens.foreground'), + // fontStyle: 'italic', + // fontSize: '0.8em' + // } + // } + // }); + // } + } + }); + } + + // When expressions + if (line.includes('when') && line.includes('is')) { + const matchIndex = line.indexOf('when'); + const lineStartIndex = text.split('\n').slice(0, i).join('\n').length + (i > 0 ? 1 : 0); + const startPos = document.positionAt(lineStartIndex + matchIndex); + const endPos = document.positionAt(lineStartIndex + line.length); + + if (this.mode === 'inline') { + inlineDecorations.push({ + range: new vscode.Range(startPos, endPos), + renderOptions: { + after: { + contentText: ' : Pattern Matching', + color: new vscode.ThemeColor('editorCodeLens.foreground'), + fontStyle: 'italic', + fontSize: '0.9em' + } + } + }); + } + // Above mode disabled - commented out + // else if (this.mode === 'above') { + // const lineAbove = new vscode.Position(Math.max(0, startPos.line - 1), 0); + // aboveDecorations.push({ + // range: new vscode.Range(lineAbove, lineAbove), + // renderOptions: { + // after: { + // contentText: `\n${' '.repeat(Math.max(0, startPos.character - 18))} : Pattern Matching`, + // color: new vscode.ThemeColor('editorCodeLens.foreground'), + // fontStyle: 'italic', + // fontSize: '0.8em' + // } + // } + // }); + // } + } + + // Result constructors + if (line.includes('Ok') || line.includes('Err')) { + const okIndex = line.indexOf('Ok'); + const errIndex = line.indexOf('Err'); + const matchIndex = okIndex !== -1 ? okIndex : errIndex; + + if (matchIndex !== -1) { + const lineStartIndex = text.split('\n').slice(0, i).join('\n').length + (i > 0 ? 1 : 0); + const startPos = document.positionAt(lineStartIndex + matchIndex); + const endPos = document.positionAt(lineStartIndex + line.length); + + if (this.mode === 'inline') { + inlineDecorations.push({ + range: new vscode.Range(startPos, endPos), + renderOptions: { + after: { + contentText: ' : Result<T>', + color: new vscode.ThemeColor('editorCodeLens.foreground'), + fontStyle: 'italic', + fontSize: '0.9em' + } + } + }); + } + // Above mode disabled - commented out + // else if (this.mode === 'above') { + // const lineAbove = new vscode.Position(Math.max(0, startPos.line - 1), 0); + // aboveDecorations.push({ + // range: new vscode.Range(lineAbove, lineAbove), + // renderOptions: { + // after: { + // contentText: `\n${' '.repeat(Math.max(0, startPos.character - 12))} : Result<T>`, + // fontSize: '0.8em' + // } + // } + // }); + // } + } + } + } + + // Apply decorations based on mode + if (this.mode === 'inline') { + editor.setDecorations(this.inlineDecorations, inlineDecorations); + } + // if (this.mode === 'above') { + // editor.setDecorations(this.aboveDecorations, aboveDecorations); + // } + } + + getTypeInfo(funcName, functionTypes) { + // Check built-in functions first + const builtin = builtinFunctions.get(funcName); + if (builtin) { + return builtin.signature.split(' -> ')[1] || 'any'; + } + + // Check user-defined functions + const userFunc = functionTypes.get(funcName); + if (userFunc) { + return userFunc.returnType; + } + + return null; + } + + getBuiltinTypeInfo(namespace, funcName) { + const fullName = `${namespace}.${funcName}`; + const builtin = builtinFunctions.get(fullName); + if (builtin) { + return builtin.signature.split(' -> ')[1] || 'any'; + } + return null; + } + + dispose() { + this.inlineDecorations.dispose(); + // this.aboveDecorations.dispose(); + } +} + +// Command implementations +async function showTypeInfo() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + + const position = editor.selection.active; + + // Only work in Baba Yaga content + if (!isInBabaYagaCodeBlock(editor.document, position)) { + vscode.window.showInformationMessage('This command only works in Baba Yaga code blocks'); + return; + } + + const wordRange = editor.document.getWordRangeAtPosition(position); + if (!wordRange) return; + + const word = editor.document.getText(wordRange); + const type = builtinFunctions.get(word) || keywords.get(word) || types.get(word); + + if (type) { + vscode.window.showInformationMessage(`${word}: ${type.description || type.signature || type.name}`); + } +} + +async function goToDefinition() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + + const position = editor.selection.active; + + // Only work in Baba Yaga content + if (!isInBabaYagaCodeBlock(editor.document, position)) { + vscode.window.showInformationMessage('This command only works in Baba Yaga code blocks'); + return; + } + + const wordRange = editor.document.getWordRangeAtPosition(position); + if (!wordRange) return; + + const word = editor.document.getText(wordRange); + const functionDef = findFunctionDefinition(editor.document, word); + + if (functionDef) { + editor.selection = new vscode.Selection(functionDef.range.start, functionDef.range.end); + editor.revealRange(functionDef.range); + } +} + +async function findReferences() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + + const position = editor.selection.active; + + // Only work in Baba Yaga content + if (!isInBabaYagaCodeBlock(editor.document, position)) { + vscode.window.showInformationMessage('This command only works in Baba Yaga code blocks'); + return; + } + + const wordRange = editor.document.getWordRangeAtPosition(position); + if (!wordRange) return; + + const word = editor.document.getText(wordRange); + await vscode.commands.executeCommand('editor.action.referenceSearch.trigger'); +} + +async function showFunctionSignature() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + + const position = editor.selection.active; + + // Only work in Baba Yaga content + if (!isInBabaYagaCodeBlock(editor.document, position)) { + vscode.window.showInformationMessage('This command only works in Baba Yaga code blocks'); + return; + } + + const wordRange = editor.document.getWordRangeAtPosition(position); + if (!wordRange) return; + + const word = editor.document.getText(wordRange); + const builtin = builtinFunctions.get(word); + + if (builtin) { + vscode.window.showInformationMessage(`${word}: ${builtin.signature}`); + } +} + +// Baba Yaga Syntax Auto-Fix Rules +const syntaxFixRules = [ + // Rule 1: Function calls in comparisons + { + name: "Function Calls in Comparisons", + pattern: /(\w+(?:\.\w+)?(?:\s+[^><=!]+)*)\s*([><=!]=?)\s*([^;,\s]+)/g, + replacement: "($1) $2 $3", + description: "Wrap function calls in parentheses when used in comparisons" + }, + // Rule 2: Logical operators + { + name: "Logical Operators", + pattern: /([^;\s]+)\s+(and|or)\s+([^;\s]+)/g, + replacement: "($1) $2 ($3)", + description: "Wrap logical expressions in parentheses" + }, + // Rule 3: Complex arithmetic in comparisons + { + name: "Complex Arithmetic in Comparisons", + pattern: /([^;\s]*[\+\-\*\/][^;\s]*)\s*([><=!]=?)\s*([^;,\s]+)/g, + replacement: "($1) $2 $3", + description: "Wrap arithmetic expressions in parentheses when used in comparisons" + }, + // Rule 4: Nested function calls in comparisons + { + name: "Nested Function Calls in Comparisons", + pattern: /(\w+(?:\.\w+)?\s*\([^)]+\))\s*([><=!]=?)\s*([^;,\s]+)/g, + replacement: "($1) $2 $3", + description: "Wrap nested function calls in parentheses when used in comparisons" + } +]; + +// Function to apply syntax fixes +function applySyntaxFixes(editor) { + const document = editor.document; + const text = document.getText(); + let modifiedText = text; + let hasChanges = false; + + // Apply each rule in order + for (const rule of syntaxFixRules) { + const matches = [...modifiedText.matchAll(rule.pattern)]; + if (matches.length > 0) { + modifiedText = modifiedText.replace(rule.pattern, rule.replacement); + hasChanges = true; + } + } + + if (hasChanges) { + // Create edit + const fullRange = new vscode.Range( + document.positionAt(0), + document.positionAt(text.length) + ); + + const edit = new vscode.WorkspaceEdit(); + edit.replace(document.uri, fullRange, modifiedText); + + // Apply the edit + return vscode.workspace.applyEdit(edit); + } + + return Promise.resolve(false); +} + +// Function to show syntax fix suggestions +function showSyntaxFixSuggestions(editor) { + const document = editor.document; + const text = document.getText(); + const suggestions = []; + + for (const rule of syntaxFixRules) { + const matches = [...text.matchAll(rule.pattern)]; + if (matches.length > 0) { + suggestions.push({ + rule: rule.name, + count: matches.length, + description: rule.description, + examples: matches.slice(0, 3).map(match => match[0]) + }); + } + } + + if (suggestions.length > 0) { + const message = suggestions.map(s => + `${s.rule}: ${s.count} issue(s) found\n ${s.description}\n Examples: ${s.examples.join(', ')}` + ).join('\n\n'); + + vscode.window.showInformationMessage( + `Found ${suggestions.length} syntax issues that can be auto-fixed:\n\n${message}`, + { modal: true } + ); + } else { + vscode.window.showInformationMessage('No syntax issues found that can be auto-fixed.'); + } +} + +// Extension activation +function activate(context) { + console.log('Baba Yaga extension is now active!'); + + // Register commands + context.subscriptions.push( + vscode.commands.registerCommand('baba-yaga.showTypeInfo', showTypeInfo), + vscode.commands.registerCommand('baba-yaga.goToDefinition', goToDefinition), + vscode.commands.registerCommand('baba-yaga.findReferences', findReferences), + vscode.commands.registerCommand('baba-yaga.showFunctionSignature', showFunctionSignature), + vscode.commands.registerCommand('baba-yaga.autoFixSyntax', () => { + const editor = vscode.window.activeTextEditor; + if (editor && (editor.document.languageId === 'baba-yaga' || isInBabaYagaCodeBlock(editor.document, editor.selection.active))) { + applySyntaxFixes(editor).then(wasFixed => { + if (wasFixed) { + vscode.window.showInformationMessage('Syntax fixes applied successfully!'); + } else { + vscode.window.showInformationMessage('No syntax issues found to fix.'); + } + }); + } else { + vscode.window.showInformationMessage('This command only works in Baba Yaga code.'); + } + }), + vscode.commands.registerCommand('baba-yaga.showSyntaxIssues', () => { + const editor = vscode.window.activeTextEditor; + if (editor && (editor.document.languageId === 'baba-yaga' || isInBabaYagaCodeBlock(editor.document, editor.selection.active))) { + showSyntaxFixSuggestions(editor); + } else { + vscode.window.showInformationMessage('This command only works in Baba Yaga code.'); + } + }) + ); + + // Register language features + const config = vscode.workspace.getConfiguration('baba-yaga'); + + if (config.get('enableTypeHints')) { + context.subscriptions.push( + vscode.languages.registerHoverProvider('baba-yaga', new BabaYagaHoverProvider()), + vscode.languages.registerHoverProvider('markdown', new BabaYagaHoverProvider()), + vscode.languages.registerCompletionItemProvider('baba-yaga', new BabaYagaCompletionProvider(), '.', ':', '>'), + vscode.languages.registerCompletionItemProvider('markdown', new BabaYagaCompletionProvider(), '.', ':', '>') + ); + } + + if (config.get('enableFunctionReferences')) { + context.subscriptions.push( + vscode.languages.registerDefinitionProvider('baba-yaga', new BabaYagaDefinitionProvider()), + vscode.languages.registerDefinitionProvider('markdown', new BabaYagaDefinitionProvider()), + vscode.languages.registerReferenceProvider('baba-yaga', new BabaYagaReferenceProvider()), + vscode.languages.registerReferenceProvider('markdown', new BabaYagaReferenceProvider()) + ); + } + + if (config.get('enableErrorChecking')) { + context.subscriptions.push( + new BabaYagaDiagnosticProvider() + ); + } + + // Register type hints provider based on mode + const typeHintMode = config.get('typeHintMode'); + if (typeHintMode !== 'none') { + const typeHintsProvider = new BabaYagaTypeHintsProvider(typeHintMode); + context.subscriptions.push(typeHintsProvider); + + // Update type hints when text changes + vscode.workspace.onDidChangeTextDocument(event => { + if (event.document.languageId === 'baba-yaga' || event.document.languageId === 'markdown') { + const editor = vscode.window.activeTextEditor; + if (editor && editor.document === event.document) { + typeHintsProvider.updateTypeHints(editor); + } + } + }); + + // Update type hints when switching editors + vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor && (editor.document.languageId === 'baba-yaga' || editor.document.languageId === 'markdown')) { + typeHintsProvider.updateTypeHints(editor); + } + }); + + // Initial update for current editor + const currentEditor = vscode.window.activeTextEditor; + if (currentEditor && (currentEditor.document.languageId === 'baba-yaga' || currentEditor.document.languageId === 'markdown')) { + typeHintsProvider.updateTypeHints(currentEditor); + } + } +} + +function deactivate() {} + +module.exports = { + activate, + deactivate +}; \ No newline at end of file |