|
| 1 | +import * as vscode from 'vscode'; |
| 2 | +import * as cp from 'child_process'; |
| 3 | + |
| 4 | +export function activate(context: vscode.ExtensionContext) { |
| 5 | + const diagnosticCollection = vscode.languages.createDiagnosticCollection('proxpl'); |
| 6 | + context.subscriptions.push(diagnosticCollection); |
| 7 | + |
| 8 | + // 1. Code Runner Command |
| 9 | + let runCommand = vscode.commands.registerCommand('proxpl.run', () => { |
| 10 | + const editor = vscode.window.activeTextEditor; |
| 11 | + if (!editor) { |
| 12 | + vscode.window.showErrorMessage('No active editor found.'); |
| 13 | + return; |
| 14 | + } |
| 15 | + |
| 16 | + const fileName = editor.document.fileName; |
| 17 | + if (!fileName.endsWith('.prox')) { |
| 18 | + vscode.window.showErrorMessage('Not a ProXPL (.prox) file.'); |
| 19 | + return; |
| 20 | + } |
| 21 | + |
| 22 | + // Save file before running |
| 23 | + editor.document.save().then(() => { |
| 24 | + const terminal = vscode.window.activeTerminal || vscode.window.createTerminal('ProXPL'); |
| 25 | + terminal.show(); |
| 26 | + terminal.sendText(`proxpl run "${fileName}"`); |
| 27 | + |
| 28 | + // Optional: Background execution for diagnostics |
| 29 | + // We run another process to capture output specifically for the "linter" |
| 30 | + cp.exec(`proxpl check "${fileName}"`, (error, stdout, stderr) => { |
| 31 | + diagnosticCollection.clear(); |
| 32 | + const diagnostics: vscode.Diagnostic[] = []; |
| 33 | + |
| 34 | + // Simple pattern matching for "Error at line X: message" |
| 35 | + const errorLog = stderr || stdout; |
| 36 | + const errorLines = errorLog.split('\n'); |
| 37 | + |
| 38 | + errorLines.forEach(line => { |
| 39 | + const match = line.match(/Error at line (\d+): (.*)/); |
| 40 | + if (match) { |
| 41 | + const lineNum = parseInt(match[1]) - 1; |
| 42 | + const message = match[2]; |
| 43 | + const range = new vscode.Range(lineNum, 0, lineNum, 100); |
| 44 | + diagnostics.push(new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error)); |
| 45 | + } |
| 46 | + }); |
| 47 | + |
| 48 | + diagnosticCollection.set(editor.document.uri, diagnostics); |
| 49 | + }); |
| 50 | + }); |
| 51 | + }); |
| 52 | + context.subscriptions.push(runCommand); |
| 53 | + |
| 54 | + // 2. Formatter Provider |
| 55 | + const formattingProvider = vscode.languages.registerDocumentFormattingEditProvider('proxpl', { |
| 56 | + provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] { |
| 57 | + const edits: vscode.TextEdit[] = []; |
| 58 | + |
| 59 | + for (let i = 0; i < document.lineCount; i++) { |
| 60 | + const line = document.lineAt(i); |
| 61 | + const text = line.text; |
| 62 | + |
| 63 | + // Remove trailing whitespace |
| 64 | + if (text.endsWith(' ') || text.endsWith('\t')) { |
| 65 | + edits.push(vscode.TextEdit.delete(new vscode.Range(i, text.trimEnd().length, i, text.length))); |
| 66 | + } |
| 67 | + |
| 68 | + // Basic Indentation (simplified example - fixes existing whitespace to 4 spaces) |
| 69 | + // This is a naive implementation; complex ones use AST |
| 70 | + const indentMatch = text.match(/^(\s+)/); |
| 71 | + if (indentMatch) { |
| 72 | + const oldIndent = indentMatch[1]; |
| 73 | + // Example: convert tabs to 4 spaces or ensure 4-space blocks |
| 74 | + const newIndent = oldIndent.replace(/\t/g, ' '); |
| 75 | + if (oldIndent !== newIndent) { |
| 76 | + edits.push(vscode.TextEdit.replace(new vscode.Range(i, 0, i, oldIndent.length), newIndent)); |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + return edits; |
| 81 | + } |
| 82 | + }); |
| 83 | + context.subscriptions.push(formattingProvider); |
| 84 | + |
| 85 | + // 3. Hover Support |
| 86 | + const hoverProvider = vscode.languages.registerHoverProvider('proxpl', { |
| 87 | + provideHover(document, position) { |
| 88 | + const range = document.getWordRangeAtPosition(position); |
| 89 | + const word = document.getText(range); |
| 90 | + |
| 91 | + const descriptions: { [key: string]: string } = { |
| 92 | + 'func': 'Defines a new function in ProXPL. Syntax: `func name(params) { ... }`', |
| 93 | + 'var': 'Declares a new variable. ProXPL is dynamically typed but variables must be declared.', |
| 94 | + 'if': 'Conditional statement. Executes a block if the condition is true.', |
| 95 | + 'else': 'Defines an alternative block for an `if` statement.', |
| 96 | + 'while': 'Loop that continues as long as a condition is true.', |
| 97 | + 'return': 'Exits a function and optionally returns a value.', |
| 98 | + 'print': 'Built-in function to output values to the terminal.', |
| 99 | + 'import': 'Incorporates external modules into the current script.' |
| 100 | + }; |
| 101 | + |
| 102 | + if (descriptions[word]) { |
| 103 | + return new vscode.Hover(new vscode.MarkdownString(descriptions[word])); |
| 104 | + } |
| 105 | + return null; |
| 106 | + } |
| 107 | + }); |
| 108 | + context.subscriptions.push(hoverProvider); |
| 109 | +} |
| 110 | + |
| 111 | +export function deactivate() { } |
0 commit comments