brighterscript 0.51.4 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/DiagnosticCollection.d.ts +3 -3
- package/dist/DiagnosticCollection.js +6 -6
- package/dist/DiagnosticCollection.js.map +1 -1
- package/dist/LanguageServer.d.ts +47 -22
- package/dist/LanguageServer.js +273 -202
- package/dist/LanguageServer.js.map +1 -1
- package/dist/ProgramBuilder.js +3 -3
- package/dist/ProgramBuilder.js.map +1 -1
- package/dist/util.js +5 -1
- package/dist/util.js.map +1 -1
- package/package.json +10 -11
package/dist/LanguageServer.js
CHANGED
|
@@ -8,7 +8,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.CustomCommands = exports.LanguageServer = void 0;
|
|
10
10
|
require("array-flat-polyfill");
|
|
11
|
-
const
|
|
11
|
+
const fastGlob = require("fast-glob");
|
|
12
12
|
const path = require("path");
|
|
13
13
|
const roku_deploy_1 = require("roku-deploy");
|
|
14
14
|
const node_1 = require("vscode-languageserver/node");
|
|
@@ -27,18 +27,18 @@ const SemanticTokenUtils_1 = require("./SemanticTokenUtils");
|
|
|
27
27
|
class LanguageServer {
|
|
28
28
|
constructor() {
|
|
29
29
|
this.connection = undefined;
|
|
30
|
-
this.
|
|
30
|
+
this.projects = [];
|
|
31
31
|
/**
|
|
32
32
|
* The number of milliseconds that should be used for language server typing debouncing
|
|
33
33
|
*/
|
|
34
34
|
this.debounceTimeout = 150;
|
|
35
35
|
/**
|
|
36
|
-
* These
|
|
37
|
-
* in any of the workspace projects.
|
|
38
|
-
* Basically these are single-file
|
|
36
|
+
* These projects are created on the fly whenever a file is opened that is not included
|
|
37
|
+
* in any of the workspace-based projects.
|
|
38
|
+
* Basically these are single-file projects to at least get parsing for standalone files.
|
|
39
39
|
* Also, they should only be created when the file is opened, and destroyed when the file is closed.
|
|
40
40
|
*/
|
|
41
|
-
this.
|
|
41
|
+
this.standaloneFileProjects = {};
|
|
42
42
|
this.hasConfigurationCapability = false;
|
|
43
43
|
/**
|
|
44
44
|
* Indicates whether the client supports workspace folders
|
|
@@ -77,17 +77,11 @@ class LanguageServer {
|
|
|
77
77
|
// when the text document is first opened, when its content has changed,
|
|
78
78
|
// or when document is closed without saving (original contents are sent as a change)
|
|
79
79
|
//
|
|
80
|
-
this.documents.onDidChangeContent(
|
|
81
|
-
await this.validateTextDocument(change.document);
|
|
82
|
-
});
|
|
80
|
+
this.documents.onDidChangeContent(this.validateTextDocument.bind(this));
|
|
83
81
|
//whenever a document gets closed
|
|
84
|
-
this.documents.onDidClose(
|
|
85
|
-
await this.onDocumentClose(change.document);
|
|
86
|
-
});
|
|
82
|
+
this.documents.onDidClose(this.onDocumentClose.bind(this));
|
|
87
83
|
// This handler provides the initial list of the completion items.
|
|
88
|
-
this.connection.onCompletion(
|
|
89
|
-
return this.onCompletion(params.textDocument.uri, params.position);
|
|
90
|
-
});
|
|
84
|
+
this.connection.onCompletion(this.onCompletion.bind(this));
|
|
91
85
|
// This handler resolves additional information for the item selected in
|
|
92
86
|
// the completion list.
|
|
93
87
|
this.connection.onCompletionResolve(this.onCompletionResolve.bind(this));
|
|
@@ -169,42 +163,118 @@ class LanguageServer {
|
|
|
169
163
|
}
|
|
170
164
|
};
|
|
171
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Ask the client for the list of `files.exclude` patterns. Useful when determining if we should process a file
|
|
168
|
+
*/
|
|
169
|
+
async getWorkspaceExcludeGlobs(workspaceFolder) {
|
|
170
|
+
var _a;
|
|
171
|
+
//get any `files.exclude` globs to use to filter
|
|
172
|
+
let config = await this.connection.workspace.getConfiguration({
|
|
173
|
+
scopeUri: workspaceFolder,
|
|
174
|
+
section: 'files'
|
|
175
|
+
});
|
|
176
|
+
return Object
|
|
177
|
+
.keys((_a = config === null || config === void 0 ? void 0 : config.exclude) !== null && _a !== void 0 ? _a : {})
|
|
178
|
+
.filter(x => { var _a; return (_a = config === null || config === void 0 ? void 0 : config.exclude) === null || _a === void 0 ? void 0 : _a[x]; })
|
|
179
|
+
//vscode files.exclude patterns support ignoring folders without needing to add `**/*`. So for our purposes, we need to
|
|
180
|
+
//append **/* to everything without a file extension or magic at the end
|
|
181
|
+
.map(pattern => [
|
|
182
|
+
//send the pattern as-is (this handles weird cases and exact file matches)
|
|
183
|
+
pattern,
|
|
184
|
+
//treat the pattern as a directory (no harm in doing this because if it's a file, the pattern will just never match anything)
|
|
185
|
+
`${pattern}/**/*`
|
|
186
|
+
])
|
|
187
|
+
.flat(1)
|
|
188
|
+
.concat([
|
|
189
|
+
//always ignore projects from node_modules
|
|
190
|
+
'**/node_modules/**/*'
|
|
191
|
+
]);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Scan the workspace for all `bsconfig.json` files. If at least one is found, then only folders who have bsconfig.json are returned.
|
|
195
|
+
* If none are found, then the workspaceFolder itself is treated as a project
|
|
196
|
+
*/
|
|
197
|
+
async getProjectPaths(workspaceFolder) {
|
|
198
|
+
const excludes = (await this.getWorkspaceExcludeGlobs(workspaceFolder)).map(x => (0, util_1.standardizePath) `!${x}`);
|
|
199
|
+
const files = await roku_deploy_1.rokuDeploy.getFilePaths([
|
|
200
|
+
'**/bsconfig.json',
|
|
201
|
+
//exclude all files found in `files.exclude`
|
|
202
|
+
...excludes
|
|
203
|
+
], workspaceFolder);
|
|
204
|
+
//if we found at least one bsconfig.json, then ALL projects must have a bsconfig.json.
|
|
205
|
+
if (files.length > 0) {
|
|
206
|
+
return files.map(file => (0, util_1.standardizePath) `${path.dirname(file.src)}`);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
//treat the workspace folder as a brightscript project itself
|
|
210
|
+
return [workspaceFolder];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Find all folders with bsconfig.json files in them, and treat each as a project.
|
|
215
|
+
* Treat workspaces that don't have a bsconfig.json as a project.
|
|
216
|
+
* Handle situations where bsconfig.json files were added or removed (to elevate/lower workspaceFolder projects accordingly)
|
|
217
|
+
* Leave existing projects alone if they are not affected by these changes
|
|
218
|
+
*/
|
|
219
|
+
async syncProjects() {
|
|
220
|
+
const workspacePaths = await this.getWorkspacePaths();
|
|
221
|
+
let projectPaths = (await Promise.all(workspacePaths.map(async (workspacePath) => {
|
|
222
|
+
const projectPaths = await this.getProjectPaths(workspacePath);
|
|
223
|
+
return projectPaths.map(projectPath => ({
|
|
224
|
+
projectPath: projectPath,
|
|
225
|
+
workspacePath: workspacePath
|
|
226
|
+
}));
|
|
227
|
+
}))).flat(1);
|
|
228
|
+
//delete projects not represented in the list
|
|
229
|
+
for (const project of this.getProjects()) {
|
|
230
|
+
if (!projectPaths.find(x => x.projectPath === project.projectPath)) {
|
|
231
|
+
this.removeProject(project);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//exclude paths to projects we already have
|
|
235
|
+
projectPaths = projectPaths.filter(x => {
|
|
236
|
+
//only keep this project path if there's not a project with that path
|
|
237
|
+
return !this.projects.find(project => project.projectPath === x.projectPath);
|
|
238
|
+
});
|
|
239
|
+
//dedupe by project path
|
|
240
|
+
projectPaths = [
|
|
241
|
+
...projectPaths.reduce((acc, x) => acc.set(x.projectPath, x), new Map()).values()
|
|
242
|
+
];
|
|
243
|
+
//create missing projects
|
|
244
|
+
await Promise.all(projectPaths.map(x => this.createProject(x.projectPath, x.workspacePath)));
|
|
245
|
+
//flush diagnostics
|
|
246
|
+
await this.sendDiagnostics();
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get all workspace paths from the client
|
|
250
|
+
*/
|
|
251
|
+
async getWorkspacePaths() {
|
|
252
|
+
var _a;
|
|
253
|
+
let workspaceFolders = (_a = await this.connection.workspace.getWorkspaceFolders()) !== null && _a !== void 0 ? _a : [];
|
|
254
|
+
return workspaceFolders.map((x) => {
|
|
255
|
+
return util_1.util.uriToPath(x.uri);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
172
258
|
/**
|
|
173
259
|
* Called when the client has finished initializing
|
|
174
260
|
* @param params
|
|
175
261
|
*/
|
|
176
262
|
async onInitialized() {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.initialWorkspacesCreated = workspaceCreatedDeferred.promise;
|
|
263
|
+
let projectCreatedDeferred = new deferred_1.Deferred();
|
|
264
|
+
this.initialProjectsCreated = projectCreatedDeferred.promise;
|
|
180
265
|
try {
|
|
181
266
|
if (this.hasConfigurationCapability) {
|
|
182
267
|
// Register for all configuration changes.
|
|
183
268
|
await this.connection.client.register(node_1.DidChangeConfigurationNotification.type, undefined);
|
|
184
269
|
}
|
|
185
|
-
|
|
186
|
-
let workspaceFolders = (_a = await this.connection.workspace.getWorkspaceFolders()) !== null && _a !== void 0 ? _a : [];
|
|
187
|
-
let workspacePaths = workspaceFolders.map((x) => {
|
|
188
|
-
return util_1.util.uriToPath(x.uri);
|
|
189
|
-
});
|
|
190
|
-
await this.createWorkspaces(workspacePaths);
|
|
270
|
+
await this.syncProjects();
|
|
191
271
|
if (this.clientHasWorkspaceFolderCapability) {
|
|
192
272
|
this.connection.workspace.onDidChangeWorkspaceFolders(async (evt) => {
|
|
193
|
-
|
|
194
|
-
for (let removed of evt.removed) {
|
|
195
|
-
let workspacePath = util_1.util.uriToPath(removed.uri);
|
|
196
|
-
let workspace = this.workspaces.find((x) => x.workspacePath === workspacePath);
|
|
197
|
-
if (workspace) {
|
|
198
|
-
workspace.builder.dispose();
|
|
199
|
-
this.workspaces.splice(this.workspaces.indexOf(workspace), 1);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
//create programs for new workspace folders
|
|
203
|
-
await this.createWorkspaces(evt.added.map((x) => util_1.util.uriToPath(x.uri)));
|
|
273
|
+
await this.syncProjects();
|
|
204
274
|
});
|
|
205
275
|
}
|
|
206
|
-
await this.
|
|
207
|
-
|
|
276
|
+
await this.waitAllProjectFirstRuns(false);
|
|
277
|
+
projectCreatedDeferred.resolve();
|
|
208
278
|
await this.sendDiagnostics();
|
|
209
279
|
}
|
|
210
280
|
catch (e) {
|
|
@@ -224,34 +294,25 @@ class LanguageServer {
|
|
|
224
294
|
/**
|
|
225
295
|
* Wait for all programs' first run to complete
|
|
226
296
|
*/
|
|
227
|
-
async
|
|
297
|
+
async waitAllProjectFirstRuns(waitForFirstWorkSpace = true) {
|
|
228
298
|
if (waitForFirstWorkSpace) {
|
|
229
|
-
await this.
|
|
299
|
+
await this.initialProjectsCreated;
|
|
230
300
|
}
|
|
231
301
|
let status;
|
|
232
|
-
let
|
|
233
|
-
for (let workspace of workspaces) {
|
|
302
|
+
for (let project of this.getProjects()) {
|
|
234
303
|
try {
|
|
235
|
-
await
|
|
304
|
+
await project.firstRunPromise;
|
|
236
305
|
}
|
|
237
306
|
catch (e) {
|
|
238
307
|
status = 'critical-error';
|
|
239
308
|
//the first run failed...that won't change unless we reload the workspace, so replace with resolved promise
|
|
240
309
|
//so we don't show this error again
|
|
241
|
-
|
|
310
|
+
project.firstRunPromise = Promise.resolve();
|
|
242
311
|
this.sendCriticalFailure(`BrighterScript language server failed to start: \n${e.message}`);
|
|
243
312
|
}
|
|
244
313
|
}
|
|
245
314
|
this.connection.sendNotification('build-status', status ? status : 'success');
|
|
246
315
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Create project for each new workspace. If the workspace is already known,
|
|
249
|
-
* it is skipped.
|
|
250
|
-
* @param workspaceFolders
|
|
251
|
-
*/
|
|
252
|
-
async createWorkspaces(workspacePaths) {
|
|
253
|
-
return Promise.all(workspacePaths.map(async (workspacePath) => this.createWorkspace(workspacePath)));
|
|
254
|
-
}
|
|
255
316
|
/**
|
|
256
317
|
* Event handler for when the program wants to load file contents.
|
|
257
318
|
* anytime the program wants to load a file, check with our in-memory document cache first
|
|
@@ -284,7 +345,7 @@ class LanguageServer {
|
|
|
284
345
|
return configFilePath;
|
|
285
346
|
}
|
|
286
347
|
else {
|
|
287
|
-
this.sendCriticalFailure(`Cannot find config file specified in user/workspace settings at '${configFilePath}'`);
|
|
348
|
+
this.sendCriticalFailure(`Cannot find config file specified in user / workspace settings at '${configFilePath}'`);
|
|
288
349
|
}
|
|
289
350
|
}
|
|
290
351
|
//default to config file path found in the root of the workspace
|
|
@@ -300,10 +361,11 @@ class LanguageServer {
|
|
|
300
361
|
//no config file could be found
|
|
301
362
|
return undefined;
|
|
302
363
|
}
|
|
303
|
-
async
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if
|
|
364
|
+
async createProject(projectPath, workspacePath = projectPath) {
|
|
365
|
+
workspacePath !== null && workspacePath !== void 0 ? workspacePath : (workspacePath = projectPath);
|
|
366
|
+
let project = this.projects.find((x) => x.projectPath === projectPath);
|
|
367
|
+
//skip this project if we already have it
|
|
368
|
+
if (project) {
|
|
307
369
|
return;
|
|
308
370
|
}
|
|
309
371
|
let builder = new ProgramBuilder_1.ProgramBuilder();
|
|
@@ -311,8 +373,8 @@ class LanguageServer {
|
|
|
311
373
|
builder.allowConsoleClearing = false;
|
|
312
374
|
//look for files in our in-memory cache before going to the file system
|
|
313
375
|
builder.addFileResolver(this.documentFileResolver.bind(this));
|
|
314
|
-
let configFilePath = await this.getConfigFilePath(
|
|
315
|
-
let cwd =
|
|
376
|
+
let configFilePath = await this.getConfigFilePath(projectPath);
|
|
377
|
+
let cwd = projectPath;
|
|
316
378
|
//if the config file exists, use it and its folder as cwd
|
|
317
379
|
if (configFilePath && await util_1.util.pathExists(configFilePath)) {
|
|
318
380
|
cwd = path.dirname(configFilePath);
|
|
@@ -333,22 +395,23 @@ class LanguageServer {
|
|
|
333
395
|
firstRunPromise.catch((err) => {
|
|
334
396
|
console.error(err);
|
|
335
397
|
});
|
|
336
|
-
let
|
|
398
|
+
let newProject = {
|
|
337
399
|
builder: builder,
|
|
338
400
|
firstRunPromise: firstRunPromise,
|
|
401
|
+
projectPath: projectPath,
|
|
339
402
|
workspacePath: workspacePath,
|
|
340
403
|
isFirstRunComplete: false,
|
|
341
404
|
isFirstRunSuccessful: false,
|
|
342
405
|
configFilePath: configFilePath,
|
|
343
|
-
|
|
406
|
+
isStandaloneFileProject: false
|
|
344
407
|
};
|
|
345
|
-
this.
|
|
408
|
+
this.projects.push(newProject);
|
|
346
409
|
await firstRunPromise.then(() => {
|
|
347
|
-
|
|
348
|
-
|
|
410
|
+
newProject.isFirstRunComplete = true;
|
|
411
|
+
newProject.isFirstRunSuccessful = true;
|
|
349
412
|
}).catch(() => {
|
|
350
|
-
|
|
351
|
-
|
|
413
|
+
newProject.isFirstRunComplete = true;
|
|
414
|
+
newProject.isFirstRunSuccessful = false;
|
|
352
415
|
}).then(() => {
|
|
353
416
|
//if we found a deprecated brsconfig.json, add a diagnostic warning the user
|
|
354
417
|
if (configFilePath && path.basename(configFilePath) === 'brsconfig.json') {
|
|
@@ -357,10 +420,10 @@ class LanguageServer {
|
|
|
357
420
|
}
|
|
358
421
|
});
|
|
359
422
|
}
|
|
360
|
-
async
|
|
423
|
+
async createStandaloneFileProject(srcPath) {
|
|
361
424
|
//skip this workspace if we already have it
|
|
362
|
-
if (this.
|
|
363
|
-
return this.
|
|
425
|
+
if (this.standaloneFileProjects[srcPath]) {
|
|
426
|
+
return this.standaloneFileProjects[srcPath];
|
|
364
427
|
}
|
|
365
428
|
let builder = new ProgramBuilder_1.ProgramBuilder();
|
|
366
429
|
//prevent clearing the console on run...this isn't the CLI so we want to keep a full log of everything
|
|
@@ -387,45 +450,46 @@ class LanguageServer {
|
|
|
387
450
|
] })).catch((err) => {
|
|
388
451
|
console.error(err);
|
|
389
452
|
});
|
|
390
|
-
let
|
|
453
|
+
let newProject = {
|
|
391
454
|
builder: builder,
|
|
392
455
|
firstRunPromise: firstRunPromise,
|
|
456
|
+
projectPath: srcPath,
|
|
393
457
|
workspacePath: srcPath,
|
|
394
458
|
isFirstRunComplete: false,
|
|
395
459
|
isFirstRunSuccessful: false,
|
|
396
460
|
configFilePath: configFilePath,
|
|
397
|
-
|
|
461
|
+
isStandaloneFileProject: true
|
|
398
462
|
};
|
|
399
|
-
this.
|
|
463
|
+
this.standaloneFileProjects[srcPath] = newProject;
|
|
400
464
|
await firstRunPromise.then(() => {
|
|
401
|
-
|
|
402
|
-
|
|
465
|
+
newProject.isFirstRunComplete = true;
|
|
466
|
+
newProject.isFirstRunSuccessful = true;
|
|
403
467
|
}).catch(() => {
|
|
404
|
-
|
|
405
|
-
|
|
468
|
+
newProject.isFirstRunComplete = true;
|
|
469
|
+
newProject.isFirstRunSuccessful = false;
|
|
406
470
|
});
|
|
407
|
-
return
|
|
471
|
+
return newProject;
|
|
408
472
|
}
|
|
409
|
-
|
|
410
|
-
let
|
|
411
|
-
for (let key in this.
|
|
412
|
-
|
|
473
|
+
getProjects() {
|
|
474
|
+
let projects = this.projects.slice();
|
|
475
|
+
for (let key in this.standaloneFileProjects) {
|
|
476
|
+
projects.push(this.standaloneFileProjects[key]);
|
|
413
477
|
}
|
|
414
|
-
return
|
|
478
|
+
return projects;
|
|
415
479
|
}
|
|
416
480
|
/**
|
|
417
481
|
* Provide a list of completion items based on the current cursor position
|
|
418
482
|
* @param textDocumentPosition
|
|
419
483
|
*/
|
|
420
|
-
async onCompletion(
|
|
484
|
+
async onCompletion(params) {
|
|
421
485
|
//ensure programs are initialized
|
|
422
|
-
await this.
|
|
423
|
-
let filePath = util_1.util.uriToPath(uri);
|
|
486
|
+
await this.waitAllProjectFirstRuns();
|
|
487
|
+
let filePath = util_1.util.uriToPath(params.textDocument.uri);
|
|
424
488
|
//wait until the file has settled
|
|
425
489
|
await this.keyedThrottler.onIdleOnce(filePath, true);
|
|
426
490
|
let completions = this
|
|
427
|
-
.
|
|
428
|
-
.flatMap(workspace => workspace.builder.program.getCompletions(filePath, position));
|
|
491
|
+
.getProjects()
|
|
492
|
+
.flatMap(workspace => workspace.builder.program.getCompletions(filePath, params.position));
|
|
429
493
|
for (let completion of completions) {
|
|
430
494
|
completion.commitCharacters = ['.'];
|
|
431
495
|
}
|
|
@@ -448,12 +512,12 @@ class LanguageServer {
|
|
|
448
512
|
}
|
|
449
513
|
async onCodeAction(params) {
|
|
450
514
|
//ensure programs are initialized
|
|
451
|
-
await this.
|
|
515
|
+
await this.waitAllProjectFirstRuns();
|
|
452
516
|
let srcPath = util_1.util.uriToPath(params.textDocument.uri);
|
|
453
517
|
//wait until the file has settled
|
|
454
518
|
await this.keyedThrottler.onIdleOnce(srcPath, true);
|
|
455
519
|
const codeActions = this
|
|
456
|
-
.
|
|
520
|
+
.getProjects()
|
|
457
521
|
//skip programs that don't have this file
|
|
458
522
|
.filter(x => { var _a, _b; return (_b = (_a = x.builder) === null || _a === void 0 ? void 0 : _a.program) === null || _b === void 0 ? void 0 : _b.hasFile(srcPath); })
|
|
459
523
|
.flatMap(workspace => workspace.builder.program.getCodeActions(srcPath, params.range));
|
|
@@ -466,38 +530,42 @@ class LanguageServer {
|
|
|
466
530
|
return codeActions;
|
|
467
531
|
}
|
|
468
532
|
/**
|
|
469
|
-
*
|
|
533
|
+
* Remove a project from the language server
|
|
534
|
+
*/
|
|
535
|
+
removeProject(project) {
|
|
536
|
+
var _a;
|
|
537
|
+
const idx = this.projects.indexOf(project);
|
|
538
|
+
if (idx > -1) {
|
|
539
|
+
this.projects.splice(idx, 1);
|
|
540
|
+
}
|
|
541
|
+
(_a = project === null || project === void 0 ? void 0 : project.builder) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Reload each of the specified workspaces
|
|
470
545
|
*/
|
|
471
|
-
async
|
|
472
|
-
|
|
473
|
-
await Promise.all(workspaces.map(async (workspace) => {
|
|
546
|
+
async reloadProjects(projects) {
|
|
547
|
+
await Promise.all(projects.map(async (project) => {
|
|
474
548
|
//ensure the workspace has finished starting up
|
|
475
549
|
try {
|
|
476
|
-
await
|
|
550
|
+
await project.firstRunPromise;
|
|
477
551
|
}
|
|
478
552
|
catch (e) { }
|
|
479
553
|
//handle standard workspace
|
|
480
|
-
if (
|
|
481
|
-
|
|
482
|
-
if (idx > -1) {
|
|
483
|
-
//remove this workspace
|
|
484
|
-
this.workspaces.splice(idx, 1);
|
|
485
|
-
//dispose this workspace's resources
|
|
486
|
-
workspace.builder.dispose();
|
|
487
|
-
}
|
|
554
|
+
if (project.isStandaloneFileProject === false) {
|
|
555
|
+
this.removeProject(project);
|
|
488
556
|
//create a new workspace/brs program
|
|
489
|
-
await this.
|
|
557
|
+
await this.createProject(project.projectPath, project.workspacePath);
|
|
490
558
|
//handle temp workspace
|
|
491
559
|
}
|
|
492
560
|
else {
|
|
493
|
-
|
|
494
|
-
delete this.
|
|
495
|
-
await this.
|
|
561
|
+
project.builder.dispose();
|
|
562
|
+
delete this.standaloneFileProjects[project.projectPath];
|
|
563
|
+
await this.createStandaloneFileProject(project.projectPath);
|
|
496
564
|
}
|
|
497
565
|
}));
|
|
498
|
-
if (
|
|
566
|
+
if (projects.length > 0) {
|
|
499
567
|
//wait for all of the programs to finish starting up
|
|
500
|
-
await this.
|
|
568
|
+
await this.waitAllProjectFirstRuns();
|
|
501
569
|
// valdiate all workspaces
|
|
502
570
|
this.validateAllThrottled(); //eslint-disable-line
|
|
503
571
|
}
|
|
@@ -514,42 +582,42 @@ class LanguageServer {
|
|
|
514
582
|
*
|
|
515
583
|
* Sometimes files that used to be included are now excluded, so those open files need to be re-processed as standalone
|
|
516
584
|
*/
|
|
517
|
-
async
|
|
585
|
+
async synchronizeStandaloneProjects() {
|
|
518
586
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
519
587
|
//remove standalone workspaces that are now included in projects
|
|
520
|
-
for (let standaloneFilePath in this.
|
|
521
|
-
let
|
|
522
|
-
for (let
|
|
523
|
-
await
|
|
524
|
-
let dest = roku_deploy_1.rokuDeploy.getDestPath(standaloneFilePath, (_d = (_c = (_b = (_a =
|
|
588
|
+
for (let standaloneFilePath in this.standaloneFileProjects) {
|
|
589
|
+
let standaloneProject = this.standaloneFileProjects[standaloneFilePath];
|
|
590
|
+
for (let project of this.projects) {
|
|
591
|
+
await standaloneProject.firstRunPromise;
|
|
592
|
+
let dest = roku_deploy_1.rokuDeploy.getDestPath(standaloneFilePath, (_d = (_c = (_b = (_a = project === null || project === void 0 ? void 0 : project.builder) === null || _a === void 0 ? void 0 : _a.program) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.files) !== null && _d !== void 0 ? _d : [], this.getRootDir(project));
|
|
525
593
|
//destroy this standalone workspace because the file has now been included in an actual workspace,
|
|
526
594
|
//or if the workspace wants the file
|
|
527
|
-
if (((_f = (_e =
|
|
528
|
-
|
|
529
|
-
delete this.
|
|
595
|
+
if (((_f = (_e = project === null || project === void 0 ? void 0 : project.builder) === null || _e === void 0 ? void 0 : _e.program) === null || _f === void 0 ? void 0 : _f.hasFile(standaloneFilePath)) || dest) {
|
|
596
|
+
standaloneProject.builder.dispose();
|
|
597
|
+
delete this.standaloneFileProjects[standaloneFilePath];
|
|
530
598
|
}
|
|
531
599
|
}
|
|
532
600
|
}
|
|
533
|
-
//create standalone
|
|
601
|
+
//create standalone projects for open files that no longer have a project
|
|
534
602
|
let textDocuments = this.documents.all();
|
|
535
603
|
outer: for (let textDocument of textDocuments) {
|
|
536
604
|
let filePath = vscode_uri_1.URI.parse(textDocument.uri).fsPath;
|
|
537
|
-
let
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (((_m = (_l = workspace === null || workspace === void 0 ? void 0 : workspace.builder) === null || _l === void 0 ? void 0 : _l.program) === null || _m === void 0 ? void 0 : _m.hasFile(filePath)) || dest) {
|
|
605
|
+
for (let project of this.getProjects()) {
|
|
606
|
+
let dest = roku_deploy_1.rokuDeploy.getDestPath(filePath, (_k = (_j = (_h = (_g = project === null || project === void 0 ? void 0 : project.builder) === null || _g === void 0 ? void 0 : _g.program) === null || _h === void 0 ? void 0 : _h.options) === null || _j === void 0 ? void 0 : _j.files) !== null && _k !== void 0 ? _k : [], this.getRootDir(project));
|
|
607
|
+
//if this project has the file, or it wants the file, do NOT make a standaloneProject for this file
|
|
608
|
+
if (((_m = (_l = project === null || project === void 0 ? void 0 : project.builder) === null || _l === void 0 ? void 0 : _l.program) === null || _m === void 0 ? void 0 : _m.hasFile(filePath)) || dest) {
|
|
542
609
|
continue outer;
|
|
543
610
|
}
|
|
544
611
|
}
|
|
545
612
|
//if we got here, no workspace has this file, so make a standalone file workspace
|
|
546
|
-
let
|
|
547
|
-
await
|
|
613
|
+
let project = await this.createStandaloneFileProject(filePath);
|
|
614
|
+
await project.firstRunPromise;
|
|
548
615
|
}
|
|
549
616
|
}
|
|
550
617
|
async onDidChangeConfiguration() {
|
|
551
618
|
if (this.hasConfigurationCapability) {
|
|
552
|
-
|
|
619
|
+
//if the user changes any config value, just mass-reload all projects
|
|
620
|
+
await this.reloadProjects(this.getProjects());
|
|
553
621
|
// Reset all cached document settings
|
|
554
622
|
}
|
|
555
623
|
else {
|
|
@@ -567,9 +635,9 @@ class LanguageServer {
|
|
|
567
635
|
*/
|
|
568
636
|
async onDidChangeWatchedFiles(params) {
|
|
569
637
|
//ensure programs are initialized
|
|
570
|
-
await this.
|
|
638
|
+
await this.waitAllProjectFirstRuns();
|
|
571
639
|
this.connection.sendNotification('build-status', 'building');
|
|
572
|
-
let
|
|
640
|
+
let projects = this.getProjects();
|
|
573
641
|
//convert all file paths to absolute paths
|
|
574
642
|
let changes = params.changes.map(x => {
|
|
575
643
|
return {
|
|
@@ -582,23 +650,27 @@ class LanguageServer {
|
|
|
582
650
|
changes = changes.filter(x => keys.includes(x.srcPath));
|
|
583
651
|
//if we have changes to work with
|
|
584
652
|
if (changes.length > 0) {
|
|
653
|
+
//if any bsconfig files were added or deleted, re-sync all projects instead of the more specific approach below
|
|
654
|
+
if (changes.find(x => (x.type === node_1.FileChangeType.Created || x.type === node_1.FileChangeType.Deleted) && path.basename(x.srcPath).toLowerCase() === 'bsconfig.json')) {
|
|
655
|
+
return this.syncProjects();
|
|
656
|
+
}
|
|
585
657
|
//reload any workspace whose bsconfig.json file has changed
|
|
586
658
|
{
|
|
587
|
-
let
|
|
659
|
+
let projectsToReload = [];
|
|
588
660
|
//get the file paths as a string array
|
|
589
661
|
let filePaths = changes.map((x) => x.srcPath);
|
|
590
|
-
for (let
|
|
591
|
-
if (
|
|
592
|
-
|
|
662
|
+
for (let project of projects) {
|
|
663
|
+
if (project.configFilePath && filePaths.includes(project.configFilePath)) {
|
|
664
|
+
projectsToReload.push(project);
|
|
593
665
|
}
|
|
594
666
|
}
|
|
595
|
-
if (
|
|
596
|
-
//vsc can generate a ton of these changes, for vsc system files, so we need to bail if there's no work to do on any of our actual
|
|
597
|
-
//reload any
|
|
598
|
-
await this.
|
|
667
|
+
if (projectsToReload.length > 0) {
|
|
668
|
+
//vsc can generate a ton of these changes, for vsc system files, so we need to bail if there's no work to do on any of our actual project files
|
|
669
|
+
//reload any projects that need to be reloaded
|
|
670
|
+
await this.reloadProjects(projectsToReload);
|
|
599
671
|
}
|
|
600
|
-
//
|
|
601
|
-
|
|
672
|
+
//reassign `projects` to the non-reloaded projects
|
|
673
|
+
projects = projects.filter(x => !projectsToReload.includes(x));
|
|
602
674
|
}
|
|
603
675
|
//convert created folders into a list of files of their contents
|
|
604
676
|
const directoryChanges = changes
|
|
@@ -616,10 +688,10 @@ class LanguageServer {
|
|
|
616
688
|
.filter(dirPath => !dirPath.includes('.roku-deploy-staging'))
|
|
617
689
|
//get the files for each folder recursively
|
|
618
690
|
.flatMap(dirPath => {
|
|
619
|
-
//
|
|
620
|
-
let
|
|
621
|
-
|
|
622
|
-
|
|
691
|
+
//look up all files
|
|
692
|
+
let files = fastGlob.sync('**/*', {
|
|
693
|
+
absolute: true,
|
|
694
|
+
cwd: roku_deploy_1.util.toForwardSlashes(dirPath)
|
|
623
695
|
});
|
|
624
696
|
return files.map(x => {
|
|
625
697
|
return {
|
|
@@ -631,7 +703,7 @@ class LanguageServer {
|
|
|
631
703
|
//add the new file changes to the changes array.
|
|
632
704
|
changes.push(...newFileChanges);
|
|
633
705
|
//give every workspace the chance to handle file changes
|
|
634
|
-
await Promise.all(
|
|
706
|
+
await Promise.all(projects.map((project) => this.handleFileChanges(project, changes)));
|
|
635
707
|
}
|
|
636
708
|
this.connection.sendNotification('build-status', 'success');
|
|
637
709
|
}
|
|
@@ -640,13 +712,13 @@ class LanguageServer {
|
|
|
640
712
|
* any file changes you receive with no unexpected side-effects
|
|
641
713
|
* @param changes
|
|
642
714
|
*/
|
|
643
|
-
async handleFileChanges(
|
|
715
|
+
async handleFileChanges(project, changes) {
|
|
644
716
|
//this loop assumes paths are both file paths and folder paths, which eliminates the need to detect.
|
|
645
717
|
//All functions below can handle being given a file path AND a folder path, and will only operate on the one they are looking for
|
|
646
718
|
let consumeCount = 0;
|
|
647
719
|
await Promise.all(changes.map(async (change) => {
|
|
648
720
|
await this.keyedThrottler.run(change.srcPath, async () => {
|
|
649
|
-
consumeCount += await this.handleFileChange(
|
|
721
|
+
consumeCount += await this.handleFileChange(project, change) ? 1 : 0;
|
|
650
722
|
});
|
|
651
723
|
}));
|
|
652
724
|
if (consumeCount > 0) {
|
|
@@ -658,14 +730,12 @@ class LanguageServer {
|
|
|
658
730
|
* any file changes you receive with no unexpected side-effects
|
|
659
731
|
* @param changes
|
|
660
732
|
*/
|
|
661
|
-
async handleFileChange(
|
|
662
|
-
const program =
|
|
663
|
-
const options = workspace.builder.options;
|
|
664
|
-
const rootDir = workspace.builder.rootDir;
|
|
733
|
+
async handleFileChange(project, change) {
|
|
734
|
+
const { program, options, rootDir } = project.builder;
|
|
665
735
|
//deleted
|
|
666
736
|
if (change.type === node_1.FileChangeType.Deleted) {
|
|
667
737
|
//try to act on this path as a directory
|
|
668
|
-
|
|
738
|
+
project.builder.removeFilesInFolder(change.srcPath);
|
|
669
739
|
//if this is a file loaded in the program, remove it
|
|
670
740
|
if (program.hasFile(change.srcPath)) {
|
|
671
741
|
program.removeFile(change.srcPath);
|
|
@@ -685,7 +755,7 @@ class LanguageServer {
|
|
|
685
755
|
program.setFile({
|
|
686
756
|
src: change.srcPath,
|
|
687
757
|
dest: roku_deploy_1.rokuDeploy.getDestPath(change.srcPath, options.files, rootDir)
|
|
688
|
-
}, await
|
|
758
|
+
}, await project.builder.getFileContents(change.srcPath));
|
|
689
759
|
return true;
|
|
690
760
|
}
|
|
691
761
|
else {
|
|
@@ -701,7 +771,7 @@ class LanguageServer {
|
|
|
701
771
|
program.setFile({
|
|
702
772
|
src: change.srcPath,
|
|
703
773
|
dest: roku_deploy_1.rokuDeploy.getDestPath(change.srcPath, options.files, rootDir)
|
|
704
|
-
}, await
|
|
774
|
+
}, await project.builder.getFileContents(change.srcPath));
|
|
705
775
|
}
|
|
706
776
|
else {
|
|
707
777
|
program.removeFile(change.srcPath);
|
|
@@ -711,75 +781,77 @@ class LanguageServer {
|
|
|
711
781
|
}
|
|
712
782
|
async onHover(params) {
|
|
713
783
|
//ensure programs are initialized
|
|
714
|
-
await this.
|
|
784
|
+
await this.waitAllProjectFirstRuns();
|
|
715
785
|
const srcPath = util_1.util.uriToPath(params.textDocument.uri);
|
|
716
|
-
let
|
|
717
|
-
let hovers = await Promise.all(Array.prototype.concat.call([],
|
|
786
|
+
let projects = this.getProjects();
|
|
787
|
+
let hovers = await Promise.all(Array.prototype.concat.call([], projects.map(async (x) => x.builder.program.getHover(srcPath, params.position))));
|
|
718
788
|
//return the first non-falsey hover. TODO is there a way to handle multiple hover results?
|
|
719
789
|
let hover = hovers.filter((x) => !!x)[0];
|
|
720
790
|
return hover;
|
|
721
791
|
}
|
|
722
|
-
async onDocumentClose(
|
|
723
|
-
|
|
724
|
-
let
|
|
792
|
+
async onDocumentClose(event) {
|
|
793
|
+
const { document } = event;
|
|
794
|
+
let filePath = vscode_uri_1.URI.parse(document.uri).fsPath;
|
|
795
|
+
let standaloneFileProject = this.standaloneFileProjects[filePath];
|
|
725
796
|
//if this was a temp file, close it
|
|
726
|
-
if (
|
|
727
|
-
await
|
|
728
|
-
|
|
729
|
-
delete this.
|
|
797
|
+
if (standaloneFileProject) {
|
|
798
|
+
await standaloneFileProject.firstRunPromise;
|
|
799
|
+
standaloneFileProject.builder.dispose();
|
|
800
|
+
delete this.standaloneFileProjects[filePath];
|
|
730
801
|
await this.sendDiagnostics();
|
|
731
802
|
}
|
|
732
803
|
}
|
|
733
|
-
async validateTextDocument(
|
|
804
|
+
async validateTextDocument(event) {
|
|
805
|
+
const { document } = event;
|
|
734
806
|
//ensure programs are initialized
|
|
735
|
-
await this.
|
|
736
|
-
let filePath = vscode_uri_1.URI.parse(
|
|
807
|
+
await this.waitAllProjectFirstRuns();
|
|
808
|
+
let filePath = vscode_uri_1.URI.parse(document.uri).fsPath;
|
|
737
809
|
try {
|
|
738
810
|
//throttle file processing. first call is run immediately, and then the last call is processed.
|
|
739
811
|
await this.keyedThrottler.run(filePath, () => {
|
|
740
812
|
var _a;
|
|
741
813
|
this.connection.sendNotification('build-status', 'building');
|
|
742
|
-
let documentText =
|
|
743
|
-
for (const
|
|
814
|
+
let documentText = document.getText();
|
|
815
|
+
for (const project of this.getProjects()) {
|
|
744
816
|
//only add or replace existing files. All of the files in the project should
|
|
745
817
|
//have already been loaded by other means
|
|
746
|
-
if (
|
|
747
|
-
let rootDir = (_a =
|
|
748
|
-
let dest = roku_deploy_1.rokuDeploy.getDestPath(filePath,
|
|
749
|
-
|
|
818
|
+
if (project.builder.program.hasFile(filePath)) {
|
|
819
|
+
let rootDir = (_a = project.builder.program.options.rootDir) !== null && _a !== void 0 ? _a : project.builder.program.options.cwd;
|
|
820
|
+
let dest = roku_deploy_1.rokuDeploy.getDestPath(filePath, project.builder.program.options.files, rootDir);
|
|
821
|
+
project.builder.program.setFile({
|
|
750
822
|
src: filePath,
|
|
751
823
|
dest: dest
|
|
752
824
|
}, documentText);
|
|
753
825
|
}
|
|
754
826
|
}
|
|
755
827
|
});
|
|
756
|
-
// validate all
|
|
828
|
+
// validate all projects
|
|
757
829
|
await this.validateAllThrottled();
|
|
758
830
|
}
|
|
759
831
|
catch (e) {
|
|
760
|
-
this.sendCriticalFailure(`Critical error parsing/ validating ${filePath}: ${e.message}`);
|
|
832
|
+
this.sendCriticalFailure(`Critical error parsing / validating ${filePath}: ${e.message}`);
|
|
761
833
|
}
|
|
762
834
|
}
|
|
763
835
|
async validateAll() {
|
|
764
836
|
var _a;
|
|
765
837
|
try {
|
|
766
838
|
//synchronize parsing for open files that were included/excluded from projects
|
|
767
|
-
await this.
|
|
768
|
-
let
|
|
839
|
+
await this.synchronizeStandaloneProjects();
|
|
840
|
+
let projects = this.getProjects();
|
|
769
841
|
//validate all programs
|
|
770
|
-
await Promise.all(
|
|
842
|
+
await Promise.all(projects.map((x) => x.builder.program.validate()));
|
|
771
843
|
await this.sendDiagnostics();
|
|
772
844
|
}
|
|
773
845
|
catch (e) {
|
|
774
846
|
this.connection.console.error(e);
|
|
775
|
-
this.sendCriticalFailure(`Critical error validating
|
|
847
|
+
this.sendCriticalFailure(`Critical error validating project: ${e.message}${(_a = e.stack) !== null && _a !== void 0 ? _a : ''}`);
|
|
776
848
|
}
|
|
777
849
|
this.connection.sendNotification('build-status', 'success');
|
|
778
850
|
}
|
|
779
851
|
async onWorkspaceSymbol(params) {
|
|
780
|
-
await this.
|
|
781
|
-
const results = util_1.util.flatMap(await Promise.all(this.
|
|
782
|
-
return
|
|
852
|
+
await this.waitAllProjectFirstRuns();
|
|
853
|
+
const results = util_1.util.flatMap(await Promise.all(this.getProjects().map(project => {
|
|
854
|
+
return project.builder.program.getWorkspaceSymbols();
|
|
783
855
|
})), c => c);
|
|
784
856
|
// Remove duplicates
|
|
785
857
|
const allSymbols = Object.values(results.reduce((map, symbol) => {
|
|
@@ -790,31 +862,31 @@ class LanguageServer {
|
|
|
790
862
|
return allSymbols;
|
|
791
863
|
}
|
|
792
864
|
async onDocumentSymbol(params) {
|
|
793
|
-
await this.
|
|
865
|
+
await this.waitAllProjectFirstRuns();
|
|
794
866
|
await this.keyedThrottler.onIdleOnce(util_1.util.uriToPath(params.textDocument.uri), true);
|
|
795
867
|
const srcPath = util_1.util.uriToPath(params.textDocument.uri);
|
|
796
|
-
for (const
|
|
797
|
-
const file =
|
|
868
|
+
for (const project of this.getProjects()) {
|
|
869
|
+
const file = project.builder.program.getFile(srcPath);
|
|
798
870
|
if ((0, reflection_1.isBrsFile)(file)) {
|
|
799
871
|
return file.getDocumentSymbols();
|
|
800
872
|
}
|
|
801
873
|
}
|
|
802
874
|
}
|
|
803
875
|
async onDefinition(params) {
|
|
804
|
-
await this.
|
|
876
|
+
await this.waitAllProjectFirstRuns();
|
|
805
877
|
const srcPath = util_1.util.uriToPath(params.textDocument.uri);
|
|
806
|
-
const results = util_1.util.flatMap(await Promise.all(this.
|
|
807
|
-
return
|
|
878
|
+
const results = util_1.util.flatMap(await Promise.all(this.getProjects().map(project => {
|
|
879
|
+
return project.builder.program.getDefinition(srcPath, params.position);
|
|
808
880
|
})), c => c);
|
|
809
881
|
return results;
|
|
810
882
|
}
|
|
811
883
|
async onSignatureHelp(params) {
|
|
812
884
|
var _a, _b, _c;
|
|
813
|
-
await this.
|
|
885
|
+
await this.waitAllProjectFirstRuns();
|
|
814
886
|
const filepath = util_1.util.uriToPath(params.textDocument.uri);
|
|
815
887
|
await this.keyedThrottler.onIdleOnce(filepath, true);
|
|
816
888
|
try {
|
|
817
|
-
const signatures = util_1.util.flatMap(await Promise.all(this.
|
|
889
|
+
const signatures = util_1.util.flatMap(await Promise.all(this.getProjects().map(project => project.builder.program.getSignatureHelp(filepath, params.position))), c => c);
|
|
818
890
|
const activeSignature = signatures.length > 0 ? 0 : null;
|
|
819
891
|
const activeParameter = activeSignature >= 0 ? (_a = signatures[activeSignature]) === null || _a === void 0 ? void 0 : _a.index : null;
|
|
820
892
|
let results = {
|
|
@@ -834,22 +906,22 @@ class LanguageServer {
|
|
|
834
906
|
}
|
|
835
907
|
}
|
|
836
908
|
async onReferences(params) {
|
|
837
|
-
await this.
|
|
909
|
+
await this.waitAllProjectFirstRuns();
|
|
838
910
|
const position = params.position;
|
|
839
911
|
const srcPath = util_1.util.uriToPath(params.textDocument.uri);
|
|
840
|
-
const results = util_1.util.flatMap(await Promise.all(this.
|
|
841
|
-
return
|
|
912
|
+
const results = util_1.util.flatMap(await Promise.all(this.getProjects().map(project => {
|
|
913
|
+
return project.builder.program.getReferences(srcPath, position);
|
|
842
914
|
})), c => c);
|
|
843
915
|
return results.filter((r) => r);
|
|
844
916
|
}
|
|
845
917
|
async onFullSemanticTokens(params) {
|
|
846
|
-
await this.
|
|
918
|
+
await this.waitAllProjectFirstRuns();
|
|
847
919
|
await this.keyedThrottler.onIdleOnce(util_1.util.uriToPath(params.textDocument.uri), true);
|
|
848
920
|
const srcPath = util_1.util.uriToPath(params.textDocument.uri);
|
|
849
|
-
for (const
|
|
921
|
+
for (const project of this.projects) {
|
|
850
922
|
//find the first program that has this file, since it would be incredibly inefficient to generate semantic tokens for the same file multiple times.
|
|
851
|
-
if (
|
|
852
|
-
let semanticTokens =
|
|
923
|
+
if (project.builder.program.hasFile(srcPath)) {
|
|
924
|
+
let semanticTokens = project.builder.program.getSemanticTokens(srcPath);
|
|
853
925
|
return {
|
|
854
926
|
data: (0, SemanticTokenUtils_1.encodeSemanticTokens)(semanticTokens)
|
|
855
927
|
};
|
|
@@ -858,7 +930,7 @@ class LanguageServer {
|
|
|
858
930
|
}
|
|
859
931
|
async sendDiagnostics() {
|
|
860
932
|
//Get only the changes to diagnostics since the last time we sent them to the client
|
|
861
|
-
const patch = await this.diagnosticCollection.getPatch(this.
|
|
933
|
+
const patch = await this.diagnosticCollection.getPatch(this.projects);
|
|
862
934
|
for (let filePath in patch) {
|
|
863
935
|
const diagnostics = patch[filePath].map(d => util_1.util.toDiagnostic(d));
|
|
864
936
|
this.connection.sendDiagnostics({
|
|
@@ -868,7 +940,7 @@ class LanguageServer {
|
|
|
868
940
|
}
|
|
869
941
|
}
|
|
870
942
|
async onExecuteCommand(params) {
|
|
871
|
-
await this.
|
|
943
|
+
await this.waitAllProjectFirstRuns();
|
|
872
944
|
if (params.command === CustomCommands.TranspileFile) {
|
|
873
945
|
const result = await this.transpileFile(params.arguments[0]);
|
|
874
946
|
//back-compat: include `pathAbsolute` property so older vscode versions still work
|
|
@@ -877,12 +949,11 @@ class LanguageServer {
|
|
|
877
949
|
}
|
|
878
950
|
async transpileFile(srcPath) {
|
|
879
951
|
//wait all program first runs
|
|
880
|
-
await this.
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
return workspace.builder.program.getTranspiledFileContents(srcPath);
|
|
952
|
+
await this.waitAllProjectFirstRuns();
|
|
953
|
+
//find the first project that has this file
|
|
954
|
+
for (let project of this.getProjects()) {
|
|
955
|
+
if (project.builder.program.hasFile(srcPath)) {
|
|
956
|
+
return project.builder.program.getTranspiledFileContents(srcPath);
|
|
886
957
|
}
|
|
887
958
|
}
|
|
888
959
|
}
|