diff --git a/package-lock.json b/package-lock.json index e4c6a878..febfe254 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@vscode/extension-telemetry": "^0.4.7", "array-sort": "^1.0.0", "backoff": "^2.5.0", - "brighterscript": "^0.65.1", + "brighterscript": "^0.65.4", "brighterscript-formatter": "^1.6.30", "debounce": "^1.2.0", "dotenv": "^6.2.0", @@ -2461,9 +2461,9 @@ } }, "node_modules/brighterscript": { - "version": "0.65.1", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.1.tgz", - "integrity": "sha512-OmNQd5Sckrx2K63y6cLbK8EIPqVi4yoc6bmz4k2J47NdnGib//JcMngJWG9JxJnCQCOuz1EheyPHW3jXryI7yg==", + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.4.tgz", + "integrity": "sha512-bp2aVhLOM1xRuZiyKx1WQp4JS8Fm6wfD7kAI4rE3YYMYeMhZ/SxTCvBtmTsaW/Z40oB8bBJXuKSuXgz2uF+ywQ==", "dependencies": { "@rokucommunity/bslib": "^0.1.1", "@xml-tools/parser": "^1.0.7", @@ -2488,7 +2488,7 @@ "parse-ms": "^2.1.0", "readline": "^1.3.0", "require-relative": "^0.8.7", - "roku-deploy": "^3.10.2", + "roku-deploy": "^3.10.3", "serialize-error": "^7.0.1", "source-map": "^0.7.4", "vscode-languageserver": "7.0.0", @@ -9089,9 +9089,9 @@ } }, "node_modules/roku-deploy": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.10.2.tgz", - "integrity": "sha512-oKMw8+CpbvFrNjf3g7PmhSRInTXitaFmtFhxb0ANiE+sC2p+/8wn/1/KqnYsTWPdEKUOLh0xIz2FHK9Nd5o9UA==", + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.10.3.tgz", + "integrity": "sha512-COJSQ638QklcM+8AN1nujFuzT04rTZLFuLSww35edm8w/y0l60oF/Iu7TQ46m75DwoGFzGFfomLEmA1ltQk9mA==", "dependencies": { "chalk": "^2.4.2", "dateformat": "^3.0.3", @@ -13506,9 +13506,9 @@ } }, "brighterscript": { - "version": "0.65.1", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.1.tgz", - "integrity": "sha512-OmNQd5Sckrx2K63y6cLbK8EIPqVi4yoc6bmz4k2J47NdnGib//JcMngJWG9JxJnCQCOuz1EheyPHW3jXryI7yg==", + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.4.tgz", + "integrity": "sha512-bp2aVhLOM1xRuZiyKx1WQp4JS8Fm6wfD7kAI4rE3YYMYeMhZ/SxTCvBtmTsaW/Z40oB8bBJXuKSuXgz2uF+ywQ==", "requires": { "@rokucommunity/bslib": "^0.1.1", "@xml-tools/parser": "^1.0.7", @@ -13533,7 +13533,7 @@ "parse-ms": "^2.1.0", "readline": "^1.3.0", "require-relative": "^0.8.7", - "roku-deploy": "^3.10.2", + "roku-deploy": "^3.10.3", "serialize-error": "^7.0.1", "source-map": "^0.7.4", "vscode-languageserver": "7.0.0", @@ -18547,9 +18547,9 @@ } }, "roku-deploy": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.10.2.tgz", - "integrity": "sha512-oKMw8+CpbvFrNjf3g7PmhSRInTXitaFmtFhxb0ANiE+sC2p+/8wn/1/KqnYsTWPdEKUOLh0xIz2FHK9Nd5o9UA==", + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.10.3.tgz", + "integrity": "sha512-COJSQ638QklcM+8AN1nujFuzT04rTZLFuLSww35edm8w/y0l60oF/Iu7TQ46m75DwoGFzGFfomLEmA1ltQk9mA==", "requires": { "chalk": "^2.4.2", "dateformat": "^3.0.3", diff --git a/package.json b/package.json index 29fddfd6..57388324 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@vscode/extension-telemetry": "^0.4.7", "array-sort": "^1.0.0", "backoff": "^2.5.0", - "brighterscript": "^0.65.1", + "brighterscript": "^0.65.4", "brighterscript-formatter": "^1.6.30", "debounce": "^1.2.0", "dotenv": "^6.2.0", diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index a7a1eb9a..6e1113e1 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -14,6 +14,7 @@ import { window, workspace } from 'vscode'; +import { BusyStatus, NotificationName, Logger } from 'brighterscript'; import { CustomCommands, Deferred } from 'brighterscript'; import type { CodeWithSourceMap } from 'source-map'; import BrightScriptDefinitionProvider from './BrightScriptDefinitionProvider'; @@ -26,6 +27,8 @@ import { util } from './util'; import { LanguageServerInfoCommand, languageServerInfoCommand } from './commands/LanguageServerInfoCommand'; import * as fsExtra from 'fs-extra'; +export const LANGUAGE_SERVER_NAME = 'BrighterScript Language Server'; + export class LanguageServerManager { constructor() { this.deferred = new Deferred(); @@ -101,7 +104,10 @@ export class LanguageServerManager { //create the statusbar this.languageServerStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); this.languageServerStatusBar.command = LanguageServerInfoCommand.commandName; - this.updateStatusbar('running'); + + //enable the statusbar loading anmation. the language server will disable once it finishes loading + this.updateStatusbar(false); + this.languageServerStatusBar.show(); //disable the simple providers (the language server will handle all of these) @@ -151,7 +157,7 @@ export class LanguageServerManager { // Create the language client and start the client. this.client = new LanguageClient( 'brighterScriptLanguageServer', - 'BrighterScript Language Server', + LANGUAGE_SERVER_NAME, serverOptions, clientOptions ); @@ -162,19 +168,7 @@ export class LanguageServerManager { this.client.onNotification('critical-failure', (message) => { void window.showErrorMessage(message); }); - - //update the statusbar with build statuses - this.client.onNotification('build-status', (message) => { - if (message === 'building') { - this.updateStatusbar('running'); - - } else if (message === 'success') { - this.updateStatusbar('running'); - - } else if (message === 'critical-error') { - this.updateStatusbar('encountered a critical runtime error', '#FF0000'); - } - }); + this.registerBusyStatusHandler(); this.deferred.resolve(true); } catch (e) { console.error(e); @@ -188,10 +182,43 @@ export class LanguageServerManager { return this.ready(); } - private updateStatusbar(tooltip: string, color?: string) { - this.languageServerStatusBar.text = `$(flame)bsc-${this.selectedBscInfo.version}`; - this.languageServerStatusBar.tooltip = 'BrightScript Language Server: ' + tooltip; - this.languageServerStatusBar.color = color; + private registerBusyStatusHandler() { + let timeoutHandle: NodeJS.Timeout; + + const logger = new Logger(); + this.client.onNotification(NotificationName.busyStatus, (event: any) => { + this.setBusyStatus(event.status); + + //if the busy status takes too long, write a lsp log entry with details of what's still pending + if (event.status === BusyStatus.busy) { + timeoutHandle = setTimeout(() => { + const delay = Date.now() - event.timestamp; + this.client.outputChannel.appendLine(`${logger.getTimestamp()} language server has been 'busy' for ${delay}ms. most recent busyStatus event: ${JSON.stringify(event, undefined, 4)}`); + }, 60_000); + + //clear any existing timeout + } else if (timeoutHandle) { + clearTimeout(timeoutHandle); + } + }); + + } + + private setBusyStatus(status: BusyStatus) { + if (status === BusyStatus.busy) { + this.updateStatusbar(true); + } else { + this.updateStatusbar(false); + } + } + + /** + * Enable/disable the loading spinner on the statusbar item + */ + private updateStatusbar(isLoading: boolean) { + const icon = isLoading ? '$(sync~spin)' : '$(flame)'; + this.languageServerStatusBar.text = `${icon} bsc-${this.selectedBscInfo.version}`; + this.languageServerStatusBar.tooltip = `BrightScript Language Server: running`; } /** diff --git a/src/commands/LanguageServerInfoCommand.ts b/src/commands/LanguageServerInfoCommand.ts index 1cef7961..a183f120 100644 --- a/src/commands/LanguageServerInfoCommand.ts +++ b/src/commands/LanguageServerInfoCommand.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { languageServerManager } from '../LanguageServerManager'; +import { LANGUAGE_SERVER_NAME, languageServerManager } from '../LanguageServerManager'; import * as path from 'path'; export class LanguageServerInfoCommand { @@ -18,12 +18,25 @@ export class LanguageServerInfoCommand { label: `Restart BrighterScript Language Server`, description: ``, command: this.restartLanguageServer.bind(this) + }, { + label: `View language server logs`, + description: ``, + command: this.focusLanguageServerOutputChannel.bind(this) }]; + let selection = await vscode.window.showQuickPick(commands, { placeHolder: `BrighterScript Project Info` }); await selection?.command(); })); } + private async focusLanguageServerOutputChannel() { + const commands = await vscode.commands.getCommands(); + const command = commands.find(x => x.endsWith(LANGUAGE_SERVER_NAME)); + if (command) { + void vscode.commands.executeCommand(command); + } + } + private async restartLanguageServer() { await vscode.commands.executeCommand('extension.brightscript.languageServer.restart'); }