Skip to content

Commit

Permalink
Adds ability to install a missing bsc version
Browse files Browse the repository at this point in the history
  • Loading branch information
TwitchBronBron committed Jul 30, 2024
1 parent 0bd91c0 commit 26ec6f7
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 11 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--timeout",
"0"
"987654"
],
"internalConsoleOptions": "openOnSessionStart"
}
Expand Down Expand Up @@ -104,4 +104,4 @@
]
}
]
}
}
16 changes: 12 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"@types/fs-extra": "^5.0.4",
"@types/glob": "^7.1.1",
"@types/mocha": "^7.0.2",
"@types/node": "^12.12.0",
"@types/node": "^20.14.10",
"@types/node-ssdp": "^3.3.0",
"@types/prompt": "^1.1.2",
"@types/resolve": "^1.20.6",
Expand Down
57 changes: 56 additions & 1 deletion src/LanguageServerManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
LanguageClient,
State
} from 'vscode-languageclient/node';
import * as childProcess from 'child_process';
const Module = require('module');
const sinon = createSandbox();

Expand Down Expand Up @@ -75,7 +76,7 @@ describe('LanguageServerManager', () => {
//disable starting so we can manually test
sinon.stub(languageServerManager, 'syncVersionAndTryRun').callsFake(() => Promise.resolve());

await languageServerManager.init(languageServerManager['context'], languageServerManager['definitionRepository']);
await languageServerManager.init(languageServerManager['context'], languageServerManager['definitionRepository'], languageServerManager['logger']);

languageServerManager['lspRunTracker'].debounceDelay = 100;

Expand Down Expand Up @@ -323,4 +324,58 @@ describe('LanguageServerManager', () => {
expect(bsdkPath).to.eql(null);
});
});

describe.only('ensureBscVersionInstalled', function() {
//these tests take a long time (due to running `npm install`)
this.timeout(20_000);

const storageDir = s`${tempDir}/brighterscript-storage`;
beforeEach(() => {
fsExtra.removeSync(storageDir);
(languageServerManager['context'] as any).globalStorageUri = URI.file(storageDir);
});

it('installs a bsc version when not present', async () => {
expect(
await languageServerManager['ensureBscVersionInstalled']('0.65.0')
).to.eql(s`${storageDir}/packages/brighterscript-0.65.0/node_modules/brighterscript`);
expect(
fsExtra.pathExistsSync(s`${storageDir}/packages/brighterscript-0.65.0/node_modules/brighterscript`)
).to.be.true;
});

it('reuses the same bsc version when already exists', async () => {
let stub = sinon.stub(childProcess, 'exec');
expect(
await languageServerManager['ensureBscVersionInstalled']('0.65.0')
).to.eql(s`${storageDir}/packages/brighterscript-0.65.0/node_modules/brighterscript`);
expect(
fsExtra.pathExistsSync(s`${storageDir}/packages/brighterscript-0.65.0/node_modules/brighterscript`)
).to.be.true;
expect(stub.called).to.be.false;
});

it('repairs a broken bsc version', async () => {
let stub = sinon.stub(fsExtra, 'remove');
fsExtra.ensureDirSync(
s`${storageDir}/packages/brighterscript-0.65.1/node_modules/brighterscript`
);
fsExtra.writeFileSync(
s`${storageDir}/packages/brighterscript-0.65.1/node_modules/brighterscript/package.json`,
'bad json'
);

expect(
await languageServerManager['ensureBscVersionInstalled']('0.65.1')
).to.eql(s`${storageDir}/packages/brighterscript-0.65.1/node_modules/brighterscript`);
expect(
fsExtra.pathExistsSync(s`${storageDir}/packages/brighterscript-0.65.1/node_modules/brighterscript`)
).to.be.true;

//make sure we deleted the bad folder
expect(
s`${stub.getCalls()[0].args[0]}`
).to.eql(s`${storageDir}/packages/brighterscript-0.65.1`);
});
});
});
59 changes: 56 additions & 3 deletions src/LanguageServerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
window,
workspace
} from 'vscode';
import { BusyStatus, NotificationName, Logger } from 'brighterscript';
import { BusyStatus, NotificationName, standardizePath as s } from 'brighterscript';
import { Logger } from '@rokucommunity/logger';
import { CustomCommands, Deferred } from 'brighterscript';
import type { CodeWithSourceMap } from 'source-map';
import BrightScriptDefinitionProvider from './BrightScriptDefinitionProvider';
Expand All @@ -29,6 +30,7 @@ import { util } from './util';
import { LanguageServerInfoCommand, languageServerInfoCommand } from './commands/LanguageServerInfoCommand';
import * as fsExtra from 'fs-extra';
import { EventEmitter } from 'eventemitter3';
import * as childProcess from 'child_process';

/**
* Tracks the running/stopped state of the language server. When the lsp crashes, vscode will restart it. After the 5th crash, they'll leave it permanently crashed.
Expand Down Expand Up @@ -88,10 +90,13 @@ export class LanguageServerManager {
return this.definitionRepository.provider;
}

private logger: Logger;

Check failure on line 93 in src/LanguageServerManager.ts

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest)

'logger' is declared but its value is never read.
public async init(
context: vscode.ExtensionContext,
definitionRepository: DefinitionRepository
definitionRepository: DefinitionRepository,
logger: Logger
) {
this.logger = logger;
this.context = context;
this.definitionRepository = definitionRepository;

Expand Down Expand Up @@ -266,7 +271,7 @@ export class LanguageServerManager {
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)}`);
this.client.outputChannel.appendLine(`${logger.formatTimestamp(new Date())} language server has been 'busy' for ${delay}ms. most recent busyStatus event: ${JSON.stringify(event, undefined, 4)}`);
}, 60_000);

//clear any existing timeout
Expand Down Expand Up @@ -386,6 +391,7 @@ export class LanguageServerManager {
* and if different, re-launch the specific version of the language server'
*/
public async syncVersionAndTryRun() {
await this.ensureBscVersionInstalled('0.67.3');
const bsdkPath = await this.getBsdkPath();

//if the path to bsc is different, spin down the old server and start a new one
Expand Down Expand Up @@ -460,6 +466,53 @@ export class LanguageServerManager {
).toString()
);
}

/**
* Ensure that the specified bsc version is installed in the global storage directory.
* @param version
* @param retryCount the number of times we should retry before giving up
* @returns full path to the root of where the brighterscript module is installed
*/
private async ensureBscVersionInstalled(version: string, retryCount = 1) {
console.log('Ensuring bsc version is installed', version);
const bscNpmDir = s`${this.context.globalStorageUri.fsPath}/packages/brighterscript-${version}`;
if (await fsExtra.pathExists(bscNpmDir) === false) {
//write a simple package.json file referencing the version of brighterscript we want
await fsExtra.outputJson(`${bscNpmDir}/package.json`, {
name: 'vscode-brighterscript-host',
private: true,
version: '1.0.0',
dependencies: {
'brighterscript': version
}
});
await new Promise<void>((resolve, reject) => {
const process = childProcess.exec(`npm install`, {
cwd: bscNpmDir
});
process.on('error', (err) => {
console.error(err);
reject(err);
});
process.on('close', (code) => {
if (code === 0) {
resolve();
}
});
});
}
const bscPath = s`${bscNpmDir}/node_modules/brighterscript`;

//if the module is invalid, try again
if (await fsExtra.pathExists(`${bscPath}/dist/index.js`) === false && retryCount > 0) {
console.log(`Failed to load brighterscript module at ${bscNpmDir}. Deleting directory and trying again`);
//remove the dir and try again
await fsExtra.remove(bscNpmDir);
return this.ensureBscVersionInstalled(version, retryCount - 1);
}

return bscPath;
}
}

export const languageServerManager = new LanguageServerManager();
Expand Down

0 comments on commit 26ec6f7

Please sign in to comment.