A Visual Studio Code extension for Pterodactyl
at main 148 lines 4.1 kB view raw
1import * as path from 'path'; 2import * as fs from 'fs'; 3import * as vscode from 'vscode'; 4import { workspace, ExtensionContext } from 'vscode'; 5import { 6 LanguageClient, 7 LanguageClientOptions, 8 ServerOptions, 9 TransportKind, 10 Trace 11} from 'vscode-languageclient/node'; 12 13import { AssistantPanelController } from './AssistantPanelController'; 14 15let client: LanguageClient | undefined; 16 17function executableExists(command: string): boolean { 18 if (path.isAbsolute(command)) { 19 return fs.existsSync(command); 20 } 21 const dirs = (process.env.PATH || '').split(path.delimiter); 22 return dirs.some(dir => fs.existsSync(path.join(dir, command))); 23} 24 25async function promptForServerPath(): Promise<void> { 26 const openSettings = 'Open Settings'; 27 const action = await vscode.window.showWarningMessage( 28 'Pterodactyl language server not found. Please configure the path to the server executable.', 29 openSettings 30 ); 31 if (action === openSettings) { 32 await vscode.commands.executeCommand('workbench.action.openSettings', 'pterodactyl.serverPath'); 33 } 34} 35 36async function startServer(assistantPanelController: AssistantPanelController): Promise<void> { 37 if (client) { 38 await client.stop(); 39 client = undefined; 40 } 41 42 const serverCommand = workspace.getConfiguration('pterodactyl').get<string>('serverPath')!; 43 44 if (!executableExists(serverCommand)) { 45 promptForServerPath(); 46 return; 47 } 48 49 const serverOptions: ServerOptions = { 50 command: serverCommand, 51 args: [], 52 transport: TransportKind.stdio 53 }; 54 55 const clientOptions: LanguageClientOptions = { 56 documentSelector: [{ scheme: 'file', language: 'pterodactyl' }], 57 synchronize: { 58 fileEvents: workspace.createFileSystemWatcher('**/*.ptero') 59 }, 60 outputChannel: vscode.window.createOutputChannel("Pterodactyl Server"), 61 traceOutputChannel: vscode.window.createOutputChannel('LSP Trace'), 62 }; 63 64 client = new LanguageClient( 65 'pterodactylLanguageServer', 66 'Pterodactyl Language Server', 67 serverOptions, 68 clientOptions 69 ); 70 71 client.setTrace(Trace.Verbose); 72 client.start(); 73 74 client.onTelemetry((data: any) => { 75 if (data?.type === 'assistantPanel' && typeof data.uri === 'string' && typeof data.html === 'string') { 76 assistantPanelController.updateHtml(data.uri, data.html); 77 } 78 }); 79} 80 81export function activate(context: ExtensionContext) { 82 let assistantPanelController = new AssistantPanelController(context); 83 84 // Update panel whenever the active editor changes 85 context.subscriptions.push( 86 vscode.window.onDidChangeActiveTextEditor(editor => assistantPanelController.update(editor)) 87 ); 88 89 // Populate panel for the editor open at activation 90 assistantPanelController.update(vscode.window.activeTextEditor); 91 92 // Serialize startServer calls so that a rapid config change can't orphan a client. 93 let startServerQueue: Promise<void> = Promise.resolve(); 94 const enqueueStartServer = () => { 95 startServerQueue = startServerQueue.then(() => startServer(assistantPanelController)); 96 }; 97 98 enqueueStartServer(); 99 100 context.subscriptions.push( 101 workspace.onDidChangeConfiguration(e => { 102 if (e.affectsConfiguration('pterodactyl.serverPath')) { 103 enqueueStartServer(); 104 } 105 }) 106 ); 107 108 context.subscriptions.push( 109 vscode.commands.registerCommand('pterodactyl.refreshDocument', async () => { 110 if (!client) { 111 return; 112 } 113 const editor = vscode.window.activeTextEditor; 114 if (!editor) { 115 return; 116 } 117 const uri = editor.document.uri.toString(); 118 await client.sendRequest('workspace/executeCommand', { 119 command: 'refreshDocument', 120 arguments: [uri] 121 }); 122 }) 123 ); 124 125 context.subscriptions.push( 126 vscode.commands.registerCommand('pterodactyl.refreshWorkspace', async () => { 127 if (!client) { 128 return; 129 } 130 const editor = vscode.window.activeTextEditor; 131 if (!editor) { 132 return; 133 } 134 const uri = editor.document.uri.toString(); 135 await client.sendRequest('workspace/executeCommand', { 136 command: 'refreshWorkspace', 137 arguments: [uri] 138 }); 139 }) 140 ); 141} 142 143export function deactivate(): Thenable<void> | undefined { 144 if (!client) { 145 return undefined; 146 } 147 return client.stop(); 148}