brighterscript 0.41.2 → 1.0.0-alpha.12
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 +112 -0
- package/README.md +2 -2
- package/dist/DiagnosticCollection.js +2 -2
- package/dist/DiagnosticCollection.js.map +1 -1
- package/dist/DiagnosticFilterer.js +2 -2
- package/dist/DiagnosticFilterer.js.map +1 -1
- package/dist/DiagnosticMessages.d.ts +6 -1
- package/dist/DiagnosticMessages.js +5 -0
- package/dist/DiagnosticMessages.js.map +1 -1
- package/dist/LanguageServer.d.ts +8 -2
- package/dist/LanguageServer.js +50 -44
- package/dist/LanguageServer.js.map +1 -1
- package/dist/Program.d.ts +61 -45
- package/dist/Program.js +305 -188
- package/dist/Program.js.map +1 -1
- package/dist/ProgramBuilder.d.ts +7 -7
- package/dist/ProgramBuilder.js +60 -51
- package/dist/ProgramBuilder.js.map +1 -1
- package/dist/Scope.d.ts +42 -19
- package/dist/Scope.js +261 -129
- package/dist/Scope.js.map +1 -1
- package/dist/SymbolTable.d.ts +73 -0
- package/dist/SymbolTable.js +157 -0
- package/dist/SymbolTable.js.map +1 -0
- package/dist/XmlScope.d.ts +5 -0
- package/dist/XmlScope.js +66 -28
- package/dist/XmlScope.js.map +1 -1
- package/dist/astUtils/creators.d.ts +15 -1
- package/dist/astUtils/creators.js +39 -9
- package/dist/astUtils/creators.js.map +1 -1
- package/dist/astUtils/reflection.d.ts +28 -16
- package/dist/astUtils/reflection.js +52 -30
- package/dist/astUtils/reflection.js.map +1 -1
- package/dist/astUtils/reflection.spec.js +3 -3
- package/dist/astUtils/reflection.spec.js.map +1 -1
- package/dist/astUtils/visitors.spec.js +12 -13
- package/dist/astUtils/visitors.spec.js.map +1 -1
- package/dist/astUtils/xml.d.ts +3 -3
- package/dist/astUtils/xml.js +2 -2
- package/dist/astUtils/xml.js.map +1 -1
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +5 -6
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +24 -22
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
- package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js +2 -2
- package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +1 -1
- package/dist/examples/plugins/removePrint.js +1 -1
- package/dist/examples/plugins/removePrint.js.map +1 -1
- package/dist/files/BrsFile.Class.spec.js +356 -41
- package/dist/files/BrsFile.Class.spec.js.map +1 -1
- package/dist/files/BrsFile.d.ts +55 -37
- package/dist/files/BrsFile.js +430 -399
- package/dist/files/BrsFile.js.map +1 -1
- package/dist/files/BrsFile.spec.js +199 -158
- package/dist/files/BrsFile.spec.js.map +1 -1
- package/dist/files/XmlFile.d.ts +20 -9
- package/dist/files/XmlFile.js +36 -31
- package/dist/files/XmlFile.js.map +1 -1
- package/dist/files/XmlFile.spec.js +113 -113
- package/dist/files/XmlFile.spec.js.map +1 -1
- package/dist/files/tests/imports.spec.js +32 -32
- package/dist/files/tests/imports.spec.js.map +1 -1
- package/dist/globalCallables.js +17 -6
- package/dist/globalCallables.js.map +1 -1
- package/dist/interfaces.d.ts +155 -39
- package/dist/parser/BrsTranspileState.d.ts +7 -0
- package/dist/parser/BrsTranspileState.js +10 -1
- package/dist/parser/BrsTranspileState.js.map +1 -1
- package/dist/parser/Expression.d.ts +23 -12
- package/dist/parser/Expression.js +45 -30
- package/dist/parser/Expression.js.map +1 -1
- package/dist/parser/Parser.Class.spec.js +100 -1
- package/dist/parser/Parser.Class.spec.js.map +1 -1
- package/dist/parser/Parser.d.ts +118 -5
- package/dist/parser/Parser.js +398 -37
- package/dist/parser/Parser.js.map +1 -1
- package/dist/parser/Parser.spec.js +404 -7
- package/dist/parser/Parser.spec.js.map +1 -1
- package/dist/parser/SGParser.d.ts +41 -4
- package/dist/parser/SGParser.js +185 -174
- package/dist/parser/SGParser.js.map +1 -1
- package/dist/parser/SGParser.spec.js +17 -4
- package/dist/parser/SGParser.spec.js.map +1 -1
- package/dist/parser/SGTypes.d.ts +203 -38
- package/dist/parser/SGTypes.js +464 -160
- package/dist/parser/SGTypes.js.map +1 -1
- package/dist/parser/SGTypes.spec.d.ts +1 -0
- package/dist/parser/SGTypes.spec.js +351 -0
- package/dist/parser/SGTypes.spec.js.map +1 -0
- package/dist/parser/Statement.d.ts +37 -26
- package/dist/parser/Statement.js +81 -20
- package/dist/parser/Statement.js.map +1 -1
- package/dist/parser/Statement.spec.js +5 -5
- package/dist/parser/Statement.spec.js.map +1 -1
- package/dist/parser/TranspileState.d.ts +1 -1
- package/dist/parser/TranspileState.js +15 -7
- package/dist/parser/TranspileState.js.map +1 -1
- package/dist/parser/tests/controlFlow/ForEach.spec.js +5 -4
- package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
- package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +1 -1
- package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +1 -1
- package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/TemplateStringExpression.spec.js +1 -1
- package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/TernaryExpression.spec.js +1 -1
- package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
- package/dist/types/ArrayType.d.ts +1 -0
- package/dist/types/ArrayType.js +23 -19
- package/dist/types/ArrayType.js.map +1 -1
- package/dist/types/BooleanType.d.ts +3 -2
- package/dist/types/BooleanType.js +6 -3
- package/dist/types/BooleanType.js.map +1 -1
- package/dist/types/BscType.d.ts +19 -4
- package/dist/types/BscType.js +9 -0
- package/dist/types/BscType.js.map +1 -1
- package/dist/types/CustomType.d.ts +8 -5
- package/dist/types/CustomType.js +12 -12
- package/dist/types/CustomType.js.map +1 -1
- package/dist/types/DoubleType.d.ts +1 -0
- package/dist/types/DoubleType.js +11 -11
- package/dist/types/DoubleType.js.map +1 -1
- package/dist/types/DynamicType.d.ts +1 -0
- package/dist/types/DynamicType.js +4 -0
- package/dist/types/DynamicType.js.map +1 -1
- package/dist/types/FloatType.d.ts +2 -1
- package/dist/types/FloatType.js +11 -11
- package/dist/types/FloatType.js.map +1 -1
- package/dist/types/FunctionType.d.ts +13 -10
- package/dist/types/FunctionType.js +34 -18
- package/dist/types/FunctionType.js.map +1 -1
- package/dist/types/FunctionType.spec.js +8 -2
- package/dist/types/FunctionType.spec.js.map +1 -1
- package/dist/types/IntegerType.d.ts +2 -1
- package/dist/types/IntegerType.js +11 -11
- package/dist/types/IntegerType.js.map +1 -1
- package/dist/types/InvalidType.d.ts +3 -2
- package/dist/types/InvalidType.js +7 -4
- package/dist/types/InvalidType.js.map +1 -1
- package/dist/types/LazyType.d.ts +17 -0
- package/dist/types/LazyType.js +44 -0
- package/dist/types/LazyType.js.map +1 -0
- package/dist/types/LongIntegerType.d.ts +2 -1
- package/dist/types/LongIntegerType.js +11 -11
- package/dist/types/LongIntegerType.js.map +1 -1
- package/dist/types/ObjectType.d.ts +6 -2
- package/dist/types/ObjectType.js +9 -3
- package/dist/types/ObjectType.js.map +1 -1
- package/dist/types/StringType.d.ts +3 -2
- package/dist/types/StringType.js +6 -3
- package/dist/types/StringType.js.map +1 -1
- package/dist/types/UninitializedType.d.ts +4 -2
- package/dist/types/UninitializedType.js +8 -3
- package/dist/types/UninitializedType.js.map +1 -1
- package/dist/types/VoidType.d.ts +3 -2
- package/dist/types/VoidType.js +6 -3
- package/dist/types/VoidType.js.map +1 -1
- package/dist/types/helpers.d.ts +42 -0
- package/dist/types/helpers.js +113 -0
- package/dist/types/helpers.js.map +1 -0
- package/dist/util.d.ts +68 -15
- package/dist/util.js +193 -45
- package/dist/util.js.map +1 -1
- package/dist/validators/ClassValidator.d.ts +5 -1
- package/dist/validators/ClassValidator.js +31 -17
- package/dist/validators/ClassValidator.js.map +1 -1
- package/package.json +1 -1
- package/dist/FunctionScope.d.ts +0 -27
- package/dist/FunctionScope.js +0 -49
- package/dist/FunctionScope.js.map +0 -1
package/dist/Program.js
CHANGED
|
@@ -23,9 +23,9 @@ const reflection_1 = require("./astUtils/reflection");
|
|
|
23
23
|
const parser_1 = require("./parser");
|
|
24
24
|
const lexer_1 = require("./lexer");
|
|
25
25
|
const BscPlugin_1 = require("./bscPlugin/BscPlugin");
|
|
26
|
-
const
|
|
27
|
-
const bslibNonAliasedRokuModulesPkgPath =
|
|
28
|
-
const bslibAliasedRokuModulesPkgPath =
|
|
26
|
+
const roku_deploy_1 = require("roku-deploy");
|
|
27
|
+
const bslibNonAliasedRokuModulesPkgPath = `pkg:/source/roku_modules/rokucommunity_bslib/bslib.brs`;
|
|
28
|
+
const bslibAliasedRokuModulesPkgPath = `pkg:/source/roku_modules/bslib/bslib.brs`;
|
|
29
29
|
class Program {
|
|
30
30
|
constructor(
|
|
31
31
|
/**
|
|
@@ -47,10 +47,13 @@ class Program {
|
|
|
47
47
|
*/
|
|
48
48
|
this.diagnostics = [];
|
|
49
49
|
/**
|
|
50
|
-
* A map of every file loaded
|
|
50
|
+
* A map of every file loaded ino this program, indexed by its lower-case pkgPath
|
|
51
51
|
*/
|
|
52
|
-
this.files = {};
|
|
53
52
|
this.pkgMap = {};
|
|
53
|
+
/**
|
|
54
|
+
* A map of every file loaded into this program, indexed by its lower-case srcPath
|
|
55
|
+
*/
|
|
56
|
+
this.files = {};
|
|
54
57
|
this.scopes = {};
|
|
55
58
|
/**
|
|
56
59
|
* A map of every component currently loaded into the program, indexed by the component name.
|
|
@@ -86,16 +89,16 @@ class Program {
|
|
|
86
89
|
*/
|
|
87
90
|
get bslibPkgPath() {
|
|
88
91
|
//if there's an aliased (preferred) version of bslib from roku_modules loaded into the program, use that
|
|
89
|
-
if (this.
|
|
92
|
+
if (this.getFile(bslibAliasedRokuModulesPkgPath)) {
|
|
90
93
|
return bslibAliasedRokuModulesPkgPath;
|
|
91
94
|
//if there's a non-aliased version of bslib from roku_modules, use that
|
|
92
95
|
}
|
|
93
|
-
else if (this.
|
|
96
|
+
else if (this.getFile(bslibNonAliasedRokuModulesPkgPath)) {
|
|
94
97
|
return bslibNonAliasedRokuModulesPkgPath;
|
|
95
98
|
//default to the embedded version
|
|
96
99
|
}
|
|
97
100
|
else {
|
|
98
|
-
return `source
|
|
101
|
+
return `pkg:/source/bslib.brs`;
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
get bslibPrefix() {
|
|
@@ -106,9 +109,18 @@ class Program {
|
|
|
106
109
|
return 'bslib';
|
|
107
110
|
}
|
|
108
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Get a copy of the list of files currently loaded in the program
|
|
114
|
+
*/
|
|
115
|
+
getFiles() {
|
|
116
|
+
return Object.values(this.files);
|
|
117
|
+
}
|
|
109
118
|
addScope(scope) {
|
|
110
119
|
this.scopes[scope.name] = scope;
|
|
111
|
-
this.plugins.emit('afterScopeCreate',
|
|
120
|
+
this.plugins.emit('afterScopeCreate', {
|
|
121
|
+
program: this,
|
|
122
|
+
scope: scope
|
|
123
|
+
});
|
|
112
124
|
}
|
|
113
125
|
/**
|
|
114
126
|
* Get the component with the specified name
|
|
@@ -178,8 +190,8 @@ class Program {
|
|
|
178
190
|
*/
|
|
179
191
|
getUnreferencedFiles() {
|
|
180
192
|
let result = [];
|
|
181
|
-
for (let
|
|
182
|
-
|
|
193
|
+
for (let key in this.files) {
|
|
194
|
+
const file = this.files[key];
|
|
183
195
|
if (!this.fileIsIncludedInAnyScope(file)) {
|
|
184
196
|
//no scopes reference this file. add it to the list
|
|
185
197
|
result.push(file);
|
|
@@ -219,13 +231,10 @@ class Program {
|
|
|
219
231
|
/**
|
|
220
232
|
* Determine if the specified file is loaded in this program right now.
|
|
221
233
|
* @param filePath
|
|
234
|
+
* @param normalizePath should the provided path be normalized before use
|
|
222
235
|
*/
|
|
223
|
-
hasFile(filePath) {
|
|
224
|
-
|
|
225
|
-
return this.files[filePath] !== undefined;
|
|
226
|
-
}
|
|
227
|
-
getPkgPath(...args) {
|
|
228
|
-
throw new Error('Not implemented');
|
|
236
|
+
hasFile(filePath, normalizePath = true) {
|
|
237
|
+
return !!this.getFile(filePath, normalizePath);
|
|
229
238
|
}
|
|
230
239
|
/**
|
|
231
240
|
* roku filesystem is case INsensitive, so find the scope by key case insensitive
|
|
@@ -235,9 +244,6 @@ class Program {
|
|
|
235
244
|
if (!scopeName) {
|
|
236
245
|
return undefined;
|
|
237
246
|
}
|
|
238
|
-
//most scopes are xml file pkg paths. however, the ones that are not are single names like "global" and "scope",
|
|
239
|
-
//so it's safe to run the standardizePkgPath method
|
|
240
|
-
scopeName = (0, util_1.standardizePath) `${scopeName}`;
|
|
241
247
|
let key = Object.keys(this.scopes).find(x => x.toLowerCase() === scopeName.toLowerCase());
|
|
242
248
|
return this.scopes[key];
|
|
243
249
|
}
|
|
@@ -254,65 +260,94 @@ class Program {
|
|
|
254
260
|
var _a;
|
|
255
261
|
return (_a = this.getComponent(componentName)) === null || _a === void 0 ? void 0 : _a.scope;
|
|
256
262
|
}
|
|
257
|
-
|
|
258
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Update internal maps with this file reference
|
|
265
|
+
*/
|
|
266
|
+
assignFile(file) {
|
|
267
|
+
this.files[file.srcPath.toLowerCase()] = file;
|
|
268
|
+
this.pkgMap[file.pkgPath.toLowerCase()] = file;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Remove this file from internal maps
|
|
272
|
+
*/
|
|
273
|
+
unassignFile(file) {
|
|
274
|
+
delete this.files[file.srcPath.toLowerCase()];
|
|
275
|
+
delete this.pkgMap[file.pkgPath.toLowerCase()];
|
|
276
|
+
}
|
|
277
|
+
setFile(fileParam, fileContents) {
|
|
278
|
+
assert.ok(fileParam, 'fileParam is required');
|
|
259
279
|
let srcPath;
|
|
260
280
|
let pkgPath;
|
|
261
281
|
if (typeof fileParam === 'string') {
|
|
262
|
-
|
|
263
|
-
|
|
282
|
+
//is a pkg path
|
|
283
|
+
if (fileParam.startsWith('pkg:/')) {
|
|
284
|
+
//srcPath is the pkgPath relative to the rootDir
|
|
285
|
+
srcPath = (0, util_1.standardizePath) `${this.options.rootDir}/${fileParam.substring(5)}`;
|
|
286
|
+
pkgPath = fileParam;
|
|
287
|
+
//is a srcPath (absolute path to src file location)
|
|
288
|
+
}
|
|
289
|
+
else if (path.isAbsolute(fileParam)) {
|
|
290
|
+
srcPath = util_1.util.standardizePath(fileParam);
|
|
291
|
+
//assume the file path is a sub path of rootDir
|
|
292
|
+
pkgPath = util_1.util.sanitizePkgPath(roku_deploy_1.util.stringReplaceInsensitive(srcPath, this.options.rootDir, ''));
|
|
293
|
+
//is destPath (path relative to rootDir and `pkg:/`)
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
srcPath = (0, util_1.standardizePath) `${this.options.rootDir}/${fileParam}`;
|
|
297
|
+
pkgPath = util_1.util.sanitizePkgPath(fileParam);
|
|
298
|
+
}
|
|
299
|
+
//is a FileObj
|
|
264
300
|
}
|
|
265
301
|
else {
|
|
266
302
|
srcPath = (0, util_1.standardizePath) `${fileParam.src}`;
|
|
267
|
-
pkgPath =
|
|
303
|
+
pkgPath = util_1.util.sanitizePkgPath(fileParam.dest);
|
|
268
304
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
assert.ok(
|
|
305
|
+
const lowerPkgPath = pkgPath.toLowerCase();
|
|
306
|
+
return this.logger.time(Logger_1.LogLevel.debug, ['program.setFile()', chalk_1.default.green(srcPath)], () => {
|
|
307
|
+
assert.ok(srcPath, 'srcPath is required');
|
|
308
|
+
assert.ok(pkgPath, 'pkgPath is required');
|
|
272
309
|
//if the file is already loaded, remove it
|
|
273
310
|
if (this.hasFile(srcPath)) {
|
|
274
311
|
this.removeFile(srcPath);
|
|
275
312
|
}
|
|
276
313
|
let fileExtension = path.extname(srcPath).toLowerCase();
|
|
277
314
|
let file;
|
|
315
|
+
const beforeFileParseEvent = {
|
|
316
|
+
program: this,
|
|
317
|
+
srcPath: srcPath,
|
|
318
|
+
source: fileContents
|
|
319
|
+
};
|
|
278
320
|
if (fileExtension === '.brs' || fileExtension === '.bs') {
|
|
279
321
|
let brsFile = new BrsFile_1.BrsFile(srcPath, pkgPath, this);
|
|
280
322
|
//add file to the `source` dependency list
|
|
281
|
-
if (brsFile.pkgPath.startsWith(
|
|
323
|
+
if (brsFile.pkgPath.startsWith('pkg:/source/')) {
|
|
282
324
|
this.createSourceScope();
|
|
283
325
|
this.dependencyGraph.addDependency('scope:source', brsFile.dependencyGraphKey);
|
|
284
326
|
}
|
|
285
327
|
//add the file to the program
|
|
286
|
-
this.
|
|
287
|
-
this.
|
|
288
|
-
let sourceObj = {
|
|
289
|
-
pathAbsolute: srcPath,
|
|
290
|
-
source: fileContents
|
|
291
|
-
};
|
|
292
|
-
this.plugins.emit('beforeFileParse', sourceObj);
|
|
328
|
+
this.assignFile(brsFile);
|
|
329
|
+
this.plugins.emit('beforeFileParse', beforeFileParseEvent);
|
|
293
330
|
this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
|
|
294
|
-
brsFile.parse(
|
|
331
|
+
brsFile.parse(beforeFileParseEvent.source);
|
|
295
332
|
});
|
|
296
333
|
file = brsFile;
|
|
297
334
|
brsFile.attachDependencyGraph(this.dependencyGraph);
|
|
298
|
-
this.plugins.emit('
|
|
335
|
+
this.plugins.emit('afterFileParse', {
|
|
336
|
+
program: this,
|
|
337
|
+
file: brsFile
|
|
338
|
+
});
|
|
299
339
|
}
|
|
300
340
|
else if (
|
|
301
341
|
//is xml file
|
|
302
342
|
fileExtension === '.xml' &&
|
|
303
343
|
//resides in the components folder (Roku will only parse xml files in the components folder)
|
|
304
|
-
|
|
344
|
+
lowerPkgPath.startsWith('pkg:/components/')) {
|
|
305
345
|
let xmlFile = new XmlFile_1.XmlFile(srcPath, pkgPath, this);
|
|
346
|
+
this.assignFile(xmlFile);
|
|
306
347
|
//add the file to the program
|
|
307
|
-
this.
|
|
308
|
-
this.pkgMap[xmlFile.pkgPath.toLowerCase()] = xmlFile;
|
|
309
|
-
let sourceObj = {
|
|
310
|
-
pathAbsolute: srcPath,
|
|
311
|
-
source: fileContents
|
|
312
|
-
};
|
|
313
|
-
this.plugins.emit('beforeFileParse', sourceObj);
|
|
348
|
+
this.plugins.emit('beforeFileParse', beforeFileParseEvent);
|
|
314
349
|
this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
|
|
315
|
-
xmlFile.parse(
|
|
350
|
+
xmlFile.parse(beforeFileParseEvent.source);
|
|
316
351
|
});
|
|
317
352
|
file = xmlFile;
|
|
318
353
|
//create a new scope for this xml file
|
|
@@ -320,12 +355,15 @@ class Program {
|
|
|
320
355
|
this.addScope(scope);
|
|
321
356
|
//register this compoent now that we have parsed it and know its component name
|
|
322
357
|
this.registerComponent(xmlFile, scope);
|
|
323
|
-
this.plugins.emit('
|
|
358
|
+
this.plugins.emit('afterFileParse', {
|
|
359
|
+
program: this,
|
|
360
|
+
file: xmlFile
|
|
361
|
+
});
|
|
324
362
|
}
|
|
325
363
|
else {
|
|
326
364
|
//TODO do we actually need to implement this? Figure out how to handle img paths
|
|
327
|
-
// let genericFile = this.files[
|
|
328
|
-
//
|
|
365
|
+
// let genericFile = this.files[srcPath] = <any>{
|
|
366
|
+
// srcPath: srcPath,
|
|
329
367
|
// pkgPath: pkgPath,
|
|
330
368
|
// wasProcessed: true
|
|
331
369
|
// } as File;
|
|
@@ -333,7 +371,6 @@ class Program {
|
|
|
333
371
|
}
|
|
334
372
|
return file;
|
|
335
373
|
});
|
|
336
|
-
return file;
|
|
337
374
|
}
|
|
338
375
|
/**
|
|
339
376
|
* Ensure source scope is created.
|
|
@@ -346,80 +383,79 @@ class Program {
|
|
|
346
383
|
this.addScope(sourceScope);
|
|
347
384
|
}
|
|
348
385
|
}
|
|
349
|
-
/**
|
|
350
|
-
* Find the file by its absolute path. This is case INSENSITIVE, since
|
|
351
|
-
* Roku is a case insensitive file system. It is an error to have multiple files
|
|
352
|
-
* with the same path with only case being different.
|
|
353
|
-
* @param pathAbsolute
|
|
354
|
-
*/
|
|
355
|
-
getFileByPathAbsolute(pathAbsolute) {
|
|
356
|
-
pathAbsolute = (0, util_1.standardizePath) `${pathAbsolute}`;
|
|
357
|
-
for (let filePath in this.files) {
|
|
358
|
-
if (filePath.toLowerCase() === pathAbsolute.toLowerCase()) {
|
|
359
|
-
return this.files[filePath];
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Get a list of files for the given (platform-normalized) pkgPath array.
|
|
365
|
-
* Missing files are just ignored.
|
|
366
|
-
*/
|
|
367
|
-
getFilesByPkgPaths(pkgPaths) {
|
|
368
|
-
return pkgPaths
|
|
369
|
-
.map(pkgPath => this.getFileByPkgPath(pkgPath))
|
|
370
|
-
.filter(file => file !== undefined);
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Get a file with the specified (platform-normalized) pkg path.
|
|
374
|
-
* If not found, return undefined
|
|
375
|
-
*/
|
|
376
|
-
getFileByPkgPath(pkgPath) {
|
|
377
|
-
return this.pkgMap[pkgPath.toLowerCase()];
|
|
378
|
-
}
|
|
379
386
|
/**
|
|
380
387
|
* Remove a set of files from the program
|
|
381
|
-
* @param
|
|
388
|
+
* @param srcPaths
|
|
382
389
|
*/
|
|
383
|
-
removeFiles(
|
|
384
|
-
for (let
|
|
385
|
-
this.removeFile(
|
|
390
|
+
removeFiles(srcPaths) {
|
|
391
|
+
for (let srcPath of srcPaths) {
|
|
392
|
+
this.removeFile(srcPath);
|
|
386
393
|
}
|
|
387
394
|
}
|
|
388
395
|
/**
|
|
389
396
|
* Remove a file from the program
|
|
390
|
-
* @param
|
|
397
|
+
* @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
|
|
398
|
+
* @param normalizePath should this function repair and standardize the path? Passing false should have a performance boost if you can guarantee your path is already sanitized
|
|
399
|
+
|
|
391
400
|
*/
|
|
392
|
-
removeFile(
|
|
393
|
-
this.logger.debug('Program.removeFile()',
|
|
394
|
-
|
|
395
|
-
throw new Error(`Path must be absolute: "${pathAbsolute}"`);
|
|
396
|
-
}
|
|
397
|
-
let file = this.getFile(pathAbsolute);
|
|
401
|
+
removeFile(filePath, normalizePath = true) {
|
|
402
|
+
this.logger.debug('Program.removeFile()', filePath);
|
|
403
|
+
let file = this.getFile(filePath, normalizePath);
|
|
398
404
|
if (file) {
|
|
399
|
-
this.plugins.emit('beforeFileDispose',
|
|
405
|
+
this.plugins.emit('beforeFileDispose', {
|
|
406
|
+
program: this,
|
|
407
|
+
file: file
|
|
408
|
+
});
|
|
400
409
|
//if there is a scope named the same as this file's path, remove it (i.e. xml scopes)
|
|
401
410
|
let scope = this.scopes[file.pkgPath];
|
|
402
411
|
if (scope) {
|
|
403
|
-
this.plugins.emit('beforeScopeDispose',
|
|
412
|
+
this.plugins.emit('beforeScopeDispose', {
|
|
413
|
+
program: this,
|
|
414
|
+
scope: scope
|
|
415
|
+
});
|
|
404
416
|
scope.dispose();
|
|
405
417
|
//notify dependencies of this scope that it has been removed
|
|
406
418
|
this.dependencyGraph.remove(scope.dependencyGraphKey);
|
|
407
419
|
delete this.scopes[file.pkgPath];
|
|
408
|
-
this.plugins.emit('afterScopeDispose',
|
|
420
|
+
this.plugins.emit('afterScopeDispose', {
|
|
421
|
+
program: this,
|
|
422
|
+
scope: scope
|
|
423
|
+
});
|
|
409
424
|
}
|
|
410
425
|
//remove the file from the program
|
|
411
|
-
|
|
412
|
-
delete this.pkgMap[file.pkgPath.toLowerCase()];
|
|
426
|
+
this.unassignFile(file);
|
|
413
427
|
this.dependencyGraph.remove(file.dependencyGraphKey);
|
|
414
428
|
//if this is a pkg:/source file, notify the `source` scope that it has changed
|
|
415
|
-
if (file.pkgPath.startsWith(
|
|
429
|
+
if (file.pkgPath.startsWith('pkg:/source/')) {
|
|
416
430
|
this.dependencyGraph.removeDependency('scope:source', file.dependencyGraphKey);
|
|
417
431
|
}
|
|
418
432
|
//if this is a component, remove it from our components map
|
|
419
433
|
if ((0, reflection_1.isXmlFile)(file)) {
|
|
420
434
|
this.unregisterComponent(file);
|
|
421
435
|
}
|
|
422
|
-
this.plugins.emit('afterFileDispose',
|
|
436
|
+
this.plugins.emit('afterFileDispose', {
|
|
437
|
+
program: this,
|
|
438
|
+
file: file
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Remove all files from the program that are in the specified folder path (recursive)
|
|
444
|
+
* @param folderSrcPath The absolute path to the folder on disk
|
|
445
|
+
* @param normalizePath should the provided path be normalized before use?
|
|
446
|
+
*/
|
|
447
|
+
removeFilesInFolder(folderSrcPath, normalizePath = true) {
|
|
448
|
+
if (normalizePath) {
|
|
449
|
+
folderSrcPath = util_1.util.standardizePath(folderSrcPath);
|
|
450
|
+
}
|
|
451
|
+
const lowerFolderSrcPath = folderSrcPath.toLowerCase();
|
|
452
|
+
for (const key in this.files) {
|
|
453
|
+
const file = this.files[key];
|
|
454
|
+
const lowerSrcPath = file.srcPath.toLowerCase();
|
|
455
|
+
//if the file path starts with the parent path and the file path does not exactly match the folder path
|
|
456
|
+
if (lowerSrcPath.toLowerCase().startsWith(lowerFolderSrcPath) && lowerSrcPath !== lowerFolderSrcPath) {
|
|
457
|
+
this.removeFile(file.srcPath, false);
|
|
458
|
+
}
|
|
423
459
|
}
|
|
424
460
|
}
|
|
425
461
|
/**
|
|
@@ -428,53 +464,86 @@ class Program {
|
|
|
428
464
|
*/
|
|
429
465
|
validate() {
|
|
430
466
|
this.logger.time(Logger_1.LogLevel.log, ['Validating project'], () => {
|
|
467
|
+
var _a;
|
|
431
468
|
this.diagnostics = [];
|
|
432
|
-
this.plugins.emit('beforeProgramValidate',
|
|
433
|
-
|
|
434
|
-
for (let scopeName in this.scopes) {
|
|
435
|
-
let scope = this.scopes[scopeName];
|
|
436
|
-
scope.validate();
|
|
437
|
-
}
|
|
469
|
+
this.plugins.emit('beforeProgramValidate', {
|
|
470
|
+
program: this
|
|
438
471
|
});
|
|
439
|
-
//
|
|
440
|
-
for (
|
|
441
|
-
|
|
472
|
+
//validate every file
|
|
473
|
+
for (const file of Object.values(this.files)) {
|
|
474
|
+
//find any files NOT loaded into a scope
|
|
442
475
|
if (!this.fileIsIncludedInAnyScope(file)) {
|
|
443
476
|
this.logger.debug('Program.validate(): fileNotReferenced by any scope', () => chalk_1.default.green(file === null || file === void 0 ? void 0 : file.pkgPath));
|
|
444
477
|
//the file is not loaded in any scope
|
|
445
478
|
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.fileNotReferencedByAnyOtherFile()), { file: file, range: util_1.util.createRange(0, 0, 0, Number.MAX_VALUE) }));
|
|
446
479
|
}
|
|
480
|
+
//for every unvalidated file, validate it
|
|
481
|
+
if (!file.isValidated) {
|
|
482
|
+
this.plugins.emit('beforeFileValidate', {
|
|
483
|
+
program: this,
|
|
484
|
+
file: file
|
|
485
|
+
});
|
|
486
|
+
//call file.validate() IF the file has that function defined
|
|
487
|
+
(_a = file.validate) === null || _a === void 0 ? void 0 : _a.call(file);
|
|
488
|
+
file.isValidated = true;
|
|
489
|
+
this.plugins.emit('afterFileValidate', {
|
|
490
|
+
program: this,
|
|
491
|
+
file: file
|
|
492
|
+
});
|
|
493
|
+
}
|
|
447
494
|
}
|
|
495
|
+
this.logger.time(Logger_1.LogLevel.info, ['Validate all scopes'], () => {
|
|
496
|
+
for (let scope of Object.values(this.scopes)) {
|
|
497
|
+
//only validate unvalidated scopes
|
|
498
|
+
if (!scope.isValidated) {
|
|
499
|
+
this.plugins.emit('beforeScopeValidate', {
|
|
500
|
+
program: this,
|
|
501
|
+
scope: scope
|
|
502
|
+
});
|
|
503
|
+
scope.validate();
|
|
504
|
+
scope.isValidated = true;
|
|
505
|
+
this.plugins.emit('afterScopeValidate', {
|
|
506
|
+
program: this,
|
|
507
|
+
scope: scope
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
});
|
|
448
512
|
this.detectDuplicateComponentNames();
|
|
449
|
-
this.plugins.emit('afterProgramValidate',
|
|
513
|
+
this.plugins.emit('afterProgramValidate', {
|
|
514
|
+
program: this
|
|
515
|
+
});
|
|
450
516
|
});
|
|
451
517
|
}
|
|
452
518
|
/**
|
|
453
519
|
* Flag all duplicate component names
|
|
454
520
|
*/
|
|
455
521
|
detectDuplicateComponentNames() {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
522
|
+
var _a;
|
|
523
|
+
const componentsByName = new Map();
|
|
524
|
+
for (const key in this.files) {
|
|
525
|
+
const file = this.files[key];
|
|
459
526
|
//if this is an XmlFile, and it has a valid `componentName` property
|
|
460
|
-
if ((0, reflection_1.isXmlFile)(file)
|
|
461
|
-
|
|
462
|
-
if (
|
|
463
|
-
|
|
527
|
+
if ((0, reflection_1.isXmlFile)(file)) {
|
|
528
|
+
const componentNameLower = (_a = file.componentName) === null || _a === void 0 ? void 0 : _a.text.toLowerCase();
|
|
529
|
+
if (componentNameLower) {
|
|
530
|
+
if (!componentsByName.has(componentNameLower)) {
|
|
531
|
+
componentsByName.set(componentNameLower, [file]);
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
componentsByName.get(componentNameLower).push(file);
|
|
535
|
+
}
|
|
464
536
|
}
|
|
465
|
-
map[lowerName].push(file);
|
|
466
537
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
for (let name in componentsByName) {
|
|
470
|
-
const xmlFiles = componentsByName[name];
|
|
538
|
+
}
|
|
539
|
+
for (const xmlFiles of componentsByName.values()) {
|
|
471
540
|
//add diagnostics for every duplicate component with this name
|
|
472
541
|
if (xmlFiles.length > 1) {
|
|
473
542
|
for (let xmlFile of xmlFiles) {
|
|
474
543
|
const { componentName } = xmlFile;
|
|
475
544
|
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.duplicateComponentName(componentName.text)), { range: xmlFile.componentName.range, file: xmlFile, relatedInformation: xmlFiles.filter(x => x !== xmlFile).map(x => {
|
|
476
545
|
return {
|
|
477
|
-
location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(xmlFile.
|
|
546
|
+
location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(xmlFile.srcPath).toString(), x.componentName.range),
|
|
478
547
|
message: 'Also defined here'
|
|
479
548
|
};
|
|
480
549
|
}) }));
|
|
@@ -483,11 +552,11 @@ class Program {
|
|
|
483
552
|
}
|
|
484
553
|
}
|
|
485
554
|
/**
|
|
486
|
-
* Determine
|
|
555
|
+
* Determine at least one scope has the file
|
|
487
556
|
*/
|
|
488
557
|
fileIsIncludedInAnyScope(file) {
|
|
489
|
-
for (let
|
|
490
|
-
if (
|
|
558
|
+
for (let scope of Object.values(this.scopes)) {
|
|
559
|
+
if (scope.hasFile(file)) {
|
|
491
560
|
return true;
|
|
492
561
|
}
|
|
493
562
|
}
|
|
@@ -495,11 +564,19 @@ class Program {
|
|
|
495
564
|
}
|
|
496
565
|
/**
|
|
497
566
|
* Get the file at the given path
|
|
498
|
-
* @param
|
|
567
|
+
* @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
|
|
568
|
+
* @param normalizePath should this function repair and standardize the path? Passing false should have a performance boost if you can guarantee your path is already sanitized
|
|
499
569
|
*/
|
|
500
|
-
getFile(
|
|
501
|
-
|
|
502
|
-
|
|
570
|
+
getFile(filePath, normalizePath = true) {
|
|
571
|
+
if (typeof filePath !== 'string') {
|
|
572
|
+
return undefined;
|
|
573
|
+
}
|
|
574
|
+
else if (path.isAbsolute(filePath)) {
|
|
575
|
+
return this.files[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
return this.pkgMap[(normalizePath ? util_1.util.sanitizePkgPath(filePath) : filePath).toLowerCase()];
|
|
579
|
+
}
|
|
503
580
|
}
|
|
504
581
|
/**
|
|
505
582
|
* Get a list of all scopes the file is loaded into
|
|
@@ -548,9 +625,12 @@ class Program {
|
|
|
548
625
|
let funcNames = new Set();
|
|
549
626
|
let currentScope = scope;
|
|
550
627
|
while ((0, reflection_1.isXmlScope)(currentScope)) {
|
|
551
|
-
for (let
|
|
552
|
-
if (
|
|
553
|
-
|
|
628
|
+
for (let member of (_b = (_a = currentScope.xmlFile.ast.component) === null || _a === void 0 ? void 0 : _a.interfaceMembers) !== null && _b !== void 0 ? _b : []) {
|
|
629
|
+
if ((0, reflection_1.isSGInterfaceFunction)(member)) {
|
|
630
|
+
const name = member.name;
|
|
631
|
+
if (!filterName || name === filterName) {
|
|
632
|
+
funcNames.add(name);
|
|
633
|
+
}
|
|
554
634
|
}
|
|
555
635
|
}
|
|
556
636
|
currentScope = currentScope.getParentScope();
|
|
@@ -573,17 +653,17 @@ class Program {
|
|
|
573
653
|
}
|
|
574
654
|
/**
|
|
575
655
|
* Find all available completion items at the given position
|
|
576
|
-
* @param
|
|
656
|
+
* @param srcPath The absolute path to the source file on disk
|
|
577
657
|
* @param lineIndex
|
|
578
658
|
* @param columnIndex
|
|
579
659
|
*/
|
|
580
|
-
getCompletions(
|
|
581
|
-
let file = this.getFile(
|
|
660
|
+
getCompletions(srcPath, position) {
|
|
661
|
+
let file = this.getFile(srcPath);
|
|
582
662
|
if (!file) {
|
|
583
663
|
return [];
|
|
584
664
|
}
|
|
585
665
|
let result = [];
|
|
586
|
-
if ((0, reflection_1.isBrsFile)(file) && file.isPositionNextToTokenKind(position, lexer_1.TokenKind.Callfunc)) {
|
|
666
|
+
if ((0, reflection_1.isBrsFile)(file) && file.parser.isPositionNextToTokenKind(position, lexer_1.TokenKind.Callfunc)) {
|
|
587
667
|
// is next to a @. callfunc invocation - must be an interface method
|
|
588
668
|
for (const scope of this.getScopes().filter((s) => (0, reflection_1.isXmlScope)(s))) {
|
|
589
669
|
let fileLinks = this.getStatementsForXmlFile(scope);
|
|
@@ -599,7 +679,12 @@ class Program {
|
|
|
599
679
|
//if there are no scopes, include the global scope so we at least get the built-in functions
|
|
600
680
|
scopes = scopes.length > 0 ? scopes : [this.globalScope];
|
|
601
681
|
//get the completions from all scopes for this file
|
|
602
|
-
let allCompletions = util_1.util.flatMap(scopes.map(ctx =>
|
|
682
|
+
let allCompletions = util_1.util.flatMap(scopes.map(ctx => {
|
|
683
|
+
ctx.linkSymbolTable();
|
|
684
|
+
const completions = file.getCompletions(position, ctx);
|
|
685
|
+
ctx.unlinkSymbolTable();
|
|
686
|
+
return completions;
|
|
687
|
+
}), c => c);
|
|
603
688
|
//only keep completions common to every scope for this file
|
|
604
689
|
let keyCounts = {};
|
|
605
690
|
for (let completion of allCompletions) {
|
|
@@ -615,21 +700,22 @@ class Program {
|
|
|
615
700
|
* Goes through each file and builds a list of workspace symbols for the program. Used by LanguageServer's onWorkspaceSymbol functionality
|
|
616
701
|
*/
|
|
617
702
|
getWorkspaceSymbols() {
|
|
618
|
-
const
|
|
703
|
+
const result = [];
|
|
704
|
+
for (const key in this.files) {
|
|
619
705
|
const file = this.files[key];
|
|
620
706
|
if ((0, reflection_1.isBrsFile)(file)) {
|
|
621
|
-
|
|
707
|
+
result.push(...file.getWorkspaceSymbols());
|
|
622
708
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return util_1.util.flatMap(results, c => c);
|
|
709
|
+
}
|
|
710
|
+
return result;
|
|
626
711
|
}
|
|
627
712
|
/**
|
|
628
713
|
* Given a position in a file, if the position is sitting on some type of identifier,
|
|
629
714
|
* go to the definition of that identifier (where this thing was first defined)
|
|
715
|
+
* @param srcPath The absolute path to the source file on disk
|
|
630
716
|
*/
|
|
631
|
-
getDefinition(
|
|
632
|
-
let file = this.getFile(
|
|
717
|
+
getDefinition(srcPath, position) {
|
|
718
|
+
let file = this.getFile(srcPath);
|
|
633
719
|
if (!file) {
|
|
634
720
|
return [];
|
|
635
721
|
}
|
|
@@ -645,9 +731,12 @@ class Program {
|
|
|
645
731
|
return results;
|
|
646
732
|
}
|
|
647
733
|
}
|
|
648
|
-
|
|
734
|
+
/**
|
|
735
|
+
* @param srcPath The absolute path to the source file on disk
|
|
736
|
+
*/
|
|
737
|
+
getHover(srcPath, position) {
|
|
649
738
|
//find the file
|
|
650
|
-
let file = this.getFile(
|
|
739
|
+
let file = this.getFile(srcPath);
|
|
651
740
|
if (!file) {
|
|
652
741
|
return null;
|
|
653
742
|
}
|
|
@@ -655,10 +744,11 @@ class Program {
|
|
|
655
744
|
}
|
|
656
745
|
/**
|
|
657
746
|
* Compute code actions for the given file and range
|
|
747
|
+
* @param srcPath The absolute path to the source file on disk
|
|
658
748
|
*/
|
|
659
|
-
getCodeActions(
|
|
749
|
+
getCodeActions(srcPath, range) {
|
|
660
750
|
const codeActions = [];
|
|
661
|
-
const file = this.getFile(
|
|
751
|
+
const file = this.getFile(srcPath);
|
|
662
752
|
if (file) {
|
|
663
753
|
const diagnostics = this
|
|
664
754
|
//get all current diagnostics (filtered by diagnostic filters)
|
|
@@ -702,7 +792,7 @@ class Program {
|
|
|
702
792
|
return [];
|
|
703
793
|
}
|
|
704
794
|
const results = new Map();
|
|
705
|
-
let
|
|
795
|
+
let functionExpression = file.getFunctionExpressionAtPosition(position);
|
|
706
796
|
let identifierInfo = this.getPartialStatementInfo(file, position);
|
|
707
797
|
if (identifierInfo.statementType === '') {
|
|
708
798
|
// just general function calls
|
|
@@ -720,10 +810,12 @@ class Program {
|
|
|
720
810
|
else if (identifierInfo.statementType === '.') {
|
|
721
811
|
//if m class reference.. then
|
|
722
812
|
//only get statements from the class I am in..
|
|
723
|
-
if (
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
813
|
+
if (functionExpression) {
|
|
814
|
+
const currentToken = file.parser.getTokenAt(position);
|
|
815
|
+
for (let scope of this.getScopesForFile(file)) {
|
|
816
|
+
scope.linkSymbolTable();
|
|
817
|
+
let myClass = file.getClassFromToken(currentToken, functionExpression, scope);
|
|
818
|
+
if (myClass) {
|
|
727
819
|
let classes = scope.getClassHierarchy(myClass.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
|
|
728
820
|
//and anything from any class in scope to a non m class
|
|
729
821
|
for (let statement of [...classes].filter((i) => (0, reflection_1.isClassMethodStatement)(i.item))) {
|
|
@@ -734,6 +826,7 @@ class Program {
|
|
|
734
826
|
}
|
|
735
827
|
}
|
|
736
828
|
}
|
|
829
|
+
scope.unlinkSymbolTable();
|
|
737
830
|
}
|
|
738
831
|
}
|
|
739
832
|
if (identifierInfo.dotPart) {
|
|
@@ -794,12 +887,12 @@ class Program {
|
|
|
794
887
|
if (!itemCounts.isArgStartFound) {
|
|
795
888
|
//try to get sig help based on the name
|
|
796
889
|
index = position.character;
|
|
797
|
-
let currentToken = file.getTokenAt(position);
|
|
890
|
+
let currentToken = file.parser.getTokenAt(position);
|
|
798
891
|
if (currentToken && currentToken.kind !== lexer_1.TokenKind.Comment) {
|
|
799
892
|
name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
|
|
800
893
|
if (!name) {
|
|
801
894
|
//try the previous token, incase we're on a bracket
|
|
802
|
-
currentToken = file.getPreviousToken(currentToken);
|
|
895
|
+
currentToken = file.parser.getPreviousToken(currentToken);
|
|
803
896
|
name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
|
|
804
897
|
}
|
|
805
898
|
if (name === null || name === void 0 ? void 0 : name.indexOf('.')) {
|
|
@@ -906,9 +999,12 @@ class Program {
|
|
|
906
999
|
}
|
|
907
1000
|
return itemCounts;
|
|
908
1001
|
}
|
|
909
|
-
|
|
1002
|
+
/**
|
|
1003
|
+
* @param srcPath The absolute path to the source file on disk
|
|
1004
|
+
*/
|
|
1005
|
+
getReferences(srcPath, position) {
|
|
910
1006
|
//find the file
|
|
911
|
-
let file = this.getFile(
|
|
1007
|
+
let file = this.getFile(srcPath);
|
|
912
1008
|
if (!file) {
|
|
913
1009
|
return null;
|
|
914
1010
|
}
|
|
@@ -926,8 +1022,8 @@ class Program {
|
|
|
926
1022
|
*/
|
|
927
1023
|
let resultPkgPaths = {};
|
|
928
1024
|
//restrict to only .brs files
|
|
929
|
-
for (
|
|
930
|
-
|
|
1025
|
+
for (const key in this.files) {
|
|
1026
|
+
const file = this.files[key];
|
|
931
1027
|
if (
|
|
932
1028
|
//is a BrightScript or BrighterScript file
|
|
933
1029
|
(file.extension === '.bs' || file.extension === '.brs') &&
|
|
@@ -935,14 +1031,12 @@ class Program {
|
|
|
935
1031
|
lowerSourcePkgPath !== file.pkgPath.toLowerCase()) {
|
|
936
1032
|
//add the relative path
|
|
937
1033
|
let relativePath = util_1.util.getRelativePath(sourcePkgPath, file.pkgPath).replace(/\\/g, '/');
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (!resultPkgPaths[lowerFilePkgPath]) {
|
|
942
|
-
resultPkgPaths[lowerFilePkgPath] = true;
|
|
1034
|
+
const lowerPkgPath = file.pkgPath.toLowerCase();
|
|
1035
|
+
if (!resultPkgPaths[lowerPkgPath]) {
|
|
1036
|
+
resultPkgPaths[lowerPkgPath] = true;
|
|
943
1037
|
result.push({
|
|
944
1038
|
label: relativePath,
|
|
945
|
-
detail: file.
|
|
1039
|
+
detail: file.srcPath,
|
|
946
1040
|
kind: vscode_languageserver_1.CompletionItemKind.File,
|
|
947
1041
|
textEdit: {
|
|
948
1042
|
newText: relativePath,
|
|
@@ -951,11 +1045,11 @@ class Program {
|
|
|
951
1045
|
});
|
|
952
1046
|
//add the absolute path
|
|
953
1047
|
result.push({
|
|
954
|
-
label:
|
|
955
|
-
detail: file.
|
|
1048
|
+
label: file.pkgPath,
|
|
1049
|
+
detail: file.srcPath,
|
|
956
1050
|
kind: vscode_languageserver_1.CompletionItemKind.File,
|
|
957
1051
|
textEdit: {
|
|
958
|
-
newText:
|
|
1052
|
+
newText: file.pkgPath,
|
|
959
1053
|
range: scriptImport.filePathRange
|
|
960
1054
|
}
|
|
961
1055
|
});
|
|
@@ -967,50 +1061,64 @@ class Program {
|
|
|
967
1061
|
/**
|
|
968
1062
|
* Transpile a single file and get the result as a string.
|
|
969
1063
|
* This does not write anything to the file system.
|
|
1064
|
+
* @param srcPath The absolute path to the source file on disk
|
|
970
1065
|
*/
|
|
971
|
-
getTranspiledFileContents(
|
|
972
|
-
let file = this.getFile(
|
|
1066
|
+
getTranspiledFileContents(srcPath) {
|
|
1067
|
+
let file = this.getFile(srcPath);
|
|
973
1068
|
let result = file.transpile();
|
|
974
|
-
return Object.assign(Object.assign({}, result), {
|
|
1069
|
+
return Object.assign(Object.assign({}, result), { srcPath: file.srcPath, pkgPath: file.pkgPath });
|
|
975
1070
|
}
|
|
976
1071
|
async transpile(fileEntries, stagingFolderPath) {
|
|
977
|
-
// map fileEntries using their path as key
|
|
1072
|
+
// map fileEntries using their path as key to avoid excessive "find()" operations
|
|
978
1073
|
const mappedFileEntries = fileEntries.reduce((collection, entry) => {
|
|
979
1074
|
collection[(0, util_1.standardizePath) `${entry.src}`] = entry;
|
|
980
1075
|
return collection;
|
|
981
1076
|
}, {});
|
|
982
|
-
const entries =
|
|
983
|
-
|
|
1077
|
+
const entries = [];
|
|
1078
|
+
for (const key in this.files) {
|
|
1079
|
+
const file = this.files[key];
|
|
1080
|
+
let filePathObj = mappedFileEntries[(0, util_1.standardizePath) `${file.srcPath}`];
|
|
984
1081
|
if (!filePathObj) {
|
|
985
1082
|
//this file has been added in-memory, from a plugin, for example
|
|
986
1083
|
filePathObj = {
|
|
987
1084
|
//add an interpolated src path (since it doesn't actually exist in memory)
|
|
988
|
-
src: `bsc:/${file.pkgPath}`,
|
|
1085
|
+
src: `bsc-in-memory:/${util_1.util.removeProtocol(file.pkgPath)}`,
|
|
989
1086
|
dest: file.pkgPath
|
|
990
1087
|
};
|
|
991
1088
|
}
|
|
992
|
-
//
|
|
993
|
-
let outputPath = filePathObj.dest
|
|
1089
|
+
//prep the output path
|
|
1090
|
+
let outputPath = filePathObj.dest
|
|
1091
|
+
//replace any leading protocol
|
|
1092
|
+
.replace(/^[-a-z_]+:\//, '')
|
|
1093
|
+
//change any .bs file extension to .brs
|
|
1094
|
+
.replace(/\.bs$/gi, '.brs');
|
|
994
1095
|
//prepend the staging folder path
|
|
995
1096
|
outputPath = (0, util_1.standardizePath) `${stagingFolderPath}/${outputPath}`;
|
|
996
|
-
|
|
1097
|
+
entries.push({
|
|
997
1098
|
file: file,
|
|
998
1099
|
outputPath: outputPath
|
|
999
|
-
};
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
this.plugins.emit('beforeProgramTranspile', {
|
|
1103
|
+
program: this,
|
|
1104
|
+
entries: entries
|
|
1000
1105
|
});
|
|
1001
|
-
this.plugins.emit('beforeProgramTranspile', this, entries);
|
|
1002
1106
|
const promises = entries.map(async (entry) => {
|
|
1003
1107
|
//skip transpiling typedef files
|
|
1004
1108
|
if ((0, reflection_1.isBrsFile)(entry.file) && entry.file.isTypedef) {
|
|
1005
1109
|
return;
|
|
1006
1110
|
}
|
|
1007
|
-
this.plugins.emit('beforeFileTranspile',
|
|
1111
|
+
this.plugins.emit('beforeFileTranspile', {
|
|
1112
|
+
program: this,
|
|
1113
|
+
file: entry.file,
|
|
1114
|
+
outputPath: entry.outputPath
|
|
1115
|
+
});
|
|
1008
1116
|
const { file, outputPath } = entry;
|
|
1009
1117
|
const result = file.transpile();
|
|
1010
1118
|
//make sure the full dir path exists
|
|
1011
1119
|
await fsExtra.ensureDir(path.dirname(outputPath));
|
|
1012
1120
|
if (await fsExtra.pathExists(outputPath)) {
|
|
1013
|
-
throw new Error(`Error while transpiling "${file.
|
|
1121
|
+
throw new Error(`Error while transpiling "${file.srcPath}". A file already exists at "${outputPath}" and will not be overwritten.`);
|
|
1014
1122
|
}
|
|
1015
1123
|
const writeMapPromise = result.map ? fsExtra.writeFile(`${outputPath}.map`, result.map.toString()) : null;
|
|
1016
1124
|
await Promise.all([
|
|
@@ -1022,14 +1130,21 @@ class Program {
|
|
|
1022
1130
|
const typedefPath = outputPath.replace(/\.brs$/i, '.d.bs');
|
|
1023
1131
|
await fsExtra.writeFile(typedefPath, typedef);
|
|
1024
1132
|
}
|
|
1025
|
-
this.plugins.emit('afterFileTranspile',
|
|
1133
|
+
this.plugins.emit('afterFileTranspile', {
|
|
1134
|
+
program: this,
|
|
1135
|
+
file: entry.file,
|
|
1136
|
+
outputPath: entry.outputPath
|
|
1137
|
+
});
|
|
1026
1138
|
});
|
|
1027
1139
|
//if there's no bslib file already loaded into the program, copy it to the staging directory
|
|
1028
|
-
if (!this.
|
|
1140
|
+
if (!this.getFile(bslibAliasedRokuModulesPkgPath) && !this.getFile(`pkg:/source/bslib.brs`)) {
|
|
1029
1141
|
promises.push(util_1.util.copyBslibToStaging(stagingFolderPath));
|
|
1030
1142
|
}
|
|
1031
1143
|
await Promise.all(promises);
|
|
1032
|
-
this.plugins.emit('afterProgramTranspile',
|
|
1144
|
+
this.plugins.emit('afterProgramTranspile', {
|
|
1145
|
+
program: this,
|
|
1146
|
+
entries: entries
|
|
1147
|
+
});
|
|
1033
1148
|
}
|
|
1034
1149
|
/**
|
|
1035
1150
|
* Find a list of files in the program that have a function with the given name (case INsensitive)
|
|
@@ -1088,11 +1203,13 @@ class Program {
|
|
|
1088
1203
|
return this._manifest;
|
|
1089
1204
|
}
|
|
1090
1205
|
dispose() {
|
|
1091
|
-
|
|
1092
|
-
|
|
1206
|
+
var _a, _b;
|
|
1207
|
+
for (const key in this.files) {
|
|
1208
|
+
const file = this.files[key];
|
|
1209
|
+
(_a = file.dispose) === null || _a === void 0 ? void 0 : _a.call(file);
|
|
1093
1210
|
}
|
|
1094
1211
|
for (let name in this.scopes) {
|
|
1095
|
-
this.scopes[name].dispose();
|
|
1212
|
+
(_b = this.scopes[name]) === null || _b === void 0 ? void 0 : _b.dispose();
|
|
1096
1213
|
}
|
|
1097
1214
|
this.globalScope.dispose();
|
|
1098
1215
|
this.dependencyGraph.dispose();
|