brighterscript 1.0.0-alpha.52 → 1.0.0-alpha.53
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/README.md +5 -3
- package/bsconfig.schema.json +151 -0
- package/dist/BsConfig.d.ts +158 -44
- package/dist/BsConfig.js +24 -0
- package/dist/BsConfig.js.map +1 -1
- package/dist/BusyStatusTracker.js +3 -0
- package/dist/BusyStatusTracker.js.map +1 -1
- package/dist/CrossScopeValidator.js +5 -0
- package/dist/CrossScopeValidator.js.map +1 -1
- package/dist/DiagnosticFilterer.d.ts +17 -1
- package/dist/DiagnosticFilterer.js +69 -6
- package/dist/DiagnosticFilterer.js.map +1 -1
- package/dist/DiagnosticManager.d.ts +11 -1
- package/dist/DiagnosticManager.js +22 -3
- package/dist/DiagnosticManager.js.map +1 -1
- package/dist/DiagnosticMessages.d.ts +93 -4
- package/dist/DiagnosticMessages.js +91 -5
- package/dist/DiagnosticMessages.js.map +1 -1
- package/dist/LanguageServer.d.ts +15 -3
- package/dist/LanguageServer.js +53 -6
- package/dist/LanguageServer.js.map +1 -1
- package/dist/Logger.d.ts +7 -7
- package/dist/Logger.js +2 -2
- package/dist/Logger.js.map +1 -1
- package/dist/PluginInterface.js +2 -1
- package/dist/PluginInterface.js.map +1 -1
- package/dist/Program.d.ts +106 -9
- package/dist/Program.js +273 -23
- package/dist/Program.js.map +1 -1
- package/dist/ProgramBuilder.js +6 -0
- package/dist/ProgramBuilder.js.map +1 -1
- package/dist/RokuConstants.d.ts +123 -0
- package/dist/RokuConstants.js +87 -0
- package/dist/RokuConstants.js.map +1 -0
- package/dist/ScopeNamespaceLookup.js.map +1 -1
- package/dist/Stopwatch.js +2 -2
- package/dist/Stopwatch.js.map +1 -1
- package/dist/SymbolTable.d.ts +1 -0
- package/dist/SymbolTable.js +20 -12
- package/dist/SymbolTable.js.map +1 -1
- package/dist/Throttler.d.ts +1 -1
- package/dist/Throttler.js.map +1 -1
- package/dist/astUtils/reflection.d.ts +6 -1
- package/dist/astUtils/reflection.js +15 -4
- package/dist/astUtils/reflection.js.map +1 -1
- package/dist/astUtils/visitors.d.ts +1 -1
- package/dist/astUtils/visitors.js +2 -0
- package/dist/astUtils/visitors.js.map +1 -1
- package/dist/bscPlugin/BscPlugin.d.ts +2 -2
- package/dist/bscPlugin/BscPlugin.js +1 -1
- package/dist/bscPlugin/BscPlugin.js.map +1 -1
- package/dist/bscPlugin/CallExpressionInfo.js.map +1 -1
- package/dist/bscPlugin/SignatureHelpUtil.js.map +1 -1
- package/dist/bscPlugin/codeActions/FixAllCodeActionsProcessor.d.ts +3 -3
- package/dist/bscPlugin/codeActions/FixAllCodeActionsProcessor.js.map +1 -1
- package/dist/bscPlugin/completions/CompletionsProcessor.js +2 -3
- package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -1
- package/dist/bscPlugin/definition/DefinitionProvider.d.ts +67 -2
- package/dist/bscPlugin/definition/DefinitionProvider.js +244 -5
- package/dist/bscPlugin/definition/DefinitionProvider.js.map +1 -1
- package/dist/bscPlugin/references/ReferencesProvider.js +7 -1
- package/dist/bscPlugin/references/ReferencesProvider.js.map +1 -1
- package/dist/bscPlugin/validation/BrsFileValidator.d.ts +40 -6
- package/dist/bscPlugin/validation/BrsFileValidator.js +109 -18
- package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -1
- package/dist/bscPlugin/validation/ProgramValidator.d.ts +10 -0
- package/dist/bscPlugin/validation/ProgramValidator.js +54 -0
- package/dist/bscPlugin/validation/ProgramValidator.js.map +1 -1
- package/dist/bscPlugin/validation/ScopeValidator.js +55 -30
- package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -1
- package/dist/bscPlugin/validation/XmlFileValidator.d.ts +18 -0
- package/dist/bscPlugin/validation/XmlFileValidator.js +51 -0
- package/dist/bscPlugin/validation/XmlFileValidator.js.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/diagnosticUtils.d.ts +1 -1
- package/dist/diagnosticUtils.js.map +1 -1
- package/dist/files/BrsFile.d.ts +2 -2
- package/dist/files/BrsFile.js +2 -2
- package/dist/files/BrsFile.js.map +1 -1
- package/dist/files/XmlFile.d.ts +51 -1
- package/dist/files/XmlFile.js +264 -0
- package/dist/files/XmlFile.js.map +1 -1
- package/dist/formatUtils.d.ts +20 -0
- package/dist/formatUtils.js +38 -0
- package/dist/formatUtils.js.map +1 -0
- package/dist/globalCallables.d.ts +1 -1
- package/dist/globalCallables.js +4 -1
- package/dist/globalCallables.js.map +1 -1
- package/dist/interfaces.d.ts +60 -5
- package/dist/interfaces.js.map +1 -1
- package/dist/lexer/Lexer.d.ts +26 -0
- package/dist/lexer/Lexer.js +140 -15
- package/dist/lexer/Lexer.js.map +1 -1
- package/dist/lexer/TokenKind.d.ts +27 -0
- package/dist/lexer/TokenKind.js +77 -3
- package/dist/lexer/TokenKind.js.map +1 -1
- package/dist/lsp/ActionQueue.js +1 -2
- package/dist/lsp/ActionQueue.js.map +1 -1
- package/dist/lsp/DocumentManager.js +4 -0
- package/dist/lsp/DocumentManager.js.map +1 -1
- package/dist/lsp/LspProject.d.ts +2 -2
- package/dist/lsp/Project.d.ts +2 -2
- package/dist/lsp/Project.js +2 -0
- package/dist/lsp/Project.js.map +1 -1
- package/dist/lsp/ProjectManager.d.ts +7 -2
- package/dist/lsp/ProjectManager.js +3 -1
- package/dist/lsp/ProjectManager.js.map +1 -1
- package/dist/lsp/worker/MessageHandler.d.ts +1 -0
- package/dist/lsp/worker/MessageHandler.js +9 -0
- package/dist/lsp/worker/MessageHandler.js.map +1 -1
- package/dist/lsp/worker/WorkerPool.d.ts +51 -17
- package/dist/lsp/worker/WorkerPool.js +118 -40
- package/dist/lsp/worker/WorkerPool.js.map +1 -1
- package/dist/lsp/worker/WorkerThreadProject.d.ts +16 -3
- package/dist/lsp/worker/WorkerThreadProject.js +50 -10
- package/dist/lsp/worker/WorkerThreadProject.js.map +1 -1
- package/dist/lsp/worker/WorkerThreadProjectRunner.js +6 -0
- package/dist/lsp/worker/WorkerThreadProjectRunner.js.map +1 -1
- package/dist/lsp/worker/run.js +20 -3
- package/dist/lsp/worker/run.js.map +1 -1
- package/dist/parser/AstNode.d.ts +59 -2
- package/dist/parser/AstNode.js +63 -5
- package/dist/parser/AstNode.js.map +1 -1
- package/dist/parser/BrsTranspileState.d.ts +44 -0
- package/dist/parser/BrsTranspileState.js +49 -0
- package/dist/parser/BrsTranspileState.js.map +1 -1
- package/dist/parser/Expression.d.ts +56 -8
- package/dist/parser/Expression.js +91 -8
- package/dist/parser/Expression.js.map +1 -1
- package/dist/parser/Parser.d.ts +0 -5
- package/dist/parser/Parser.js +5 -9
- package/dist/parser/Parser.js.map +1 -1
- package/dist/parser/SGParser.d.ts +7 -1
- package/dist/parser/SGParser.js +20 -10
- package/dist/parser/SGParser.js.map +1 -1
- package/dist/parser/SGTypes.js +8 -2
- package/dist/parser/SGTypes.js.map +1 -1
- package/dist/parser/Statement.d.ts +21 -10
- package/dist/parser/Statement.js +61 -1
- package/dist/parser/Statement.js.map +1 -1
- package/dist/preprocessor/Manifest.d.ts +16 -0
- package/dist/preprocessor/Manifest.js +21 -5
- package/dist/preprocessor/Manifest.js.map +1 -1
- package/dist/roku-types/index.d.ts +3 -0
- package/dist/types/BuiltInInterfaceAdder.js +1 -1
- package/dist/types/BuiltInInterfaceAdder.js.map +1 -1
- package/dist/types/InheritableType.d.ts +0 -1
- package/dist/types/InheritableType.js +12 -11
- package/dist/types/InheritableType.js.map +1 -1
- package/dist/types/InterfaceType.js +6 -0
- package/dist/types/InterfaceType.js.map +1 -1
- package/dist/types/IntersectionType.d.ts +1 -1
- package/dist/types/IntersectionType.js +7 -7
- package/dist/types/IntersectionType.js.map +1 -1
- package/dist/types/ReferenceType.js +1 -1
- package/dist/types/ReferenceType.js.map +1 -1
- package/dist/types/helpers.d.ts +2 -2
- package/dist/types/helpers.js +8 -5
- package/dist/types/helpers.js.map +1 -1
- package/dist/util.d.ts +38 -4
- package/dist/util.js +296 -29
- package/dist/util.js.map +1 -1
- package/package.json +12 -18
package/dist/Program.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.Program = void 0;
|
|
|
4
4
|
const assert = require("assert");
|
|
5
5
|
const fsExtra = require("fs-extra");
|
|
6
6
|
const path = require("path");
|
|
7
|
+
const semver = require("semver");
|
|
7
8
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
8
9
|
const Scope_1 = require("./Scope");
|
|
9
10
|
const SymbolTable_1 = require("./SymbolTable");
|
|
@@ -16,6 +17,7 @@ const logging_1 = require("./logging");
|
|
|
16
17
|
const chalk_1 = require("chalk");
|
|
17
18
|
const globalCallables_1 = require("./globalCallables");
|
|
18
19
|
const Manifest_1 = require("./preprocessor/Manifest");
|
|
20
|
+
const RokuConstants_1 = require("./RokuConstants");
|
|
19
21
|
const vscode_uri_1 = require("vscode-uri");
|
|
20
22
|
const PluginInterface_1 = require("./PluginInterface");
|
|
21
23
|
const reflection_1 = require("./astUtils/reflection");
|
|
@@ -53,6 +55,10 @@ const deferred_1 = require("./deferred");
|
|
|
53
55
|
const roFunctionType_1 = require("./types/roFunctionType");
|
|
54
56
|
const bslibNonAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/rokucommunity_bslib/bslib.brs`;
|
|
55
57
|
const bslibAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/bslib/bslib.brs`;
|
|
58
|
+
/**
|
|
59
|
+
* The built-in Roku SceneGraph nodes, keyed by their lower-case name
|
|
60
|
+
*/
|
|
61
|
+
const builtInSceneGraphNodes = roku_types_1.nodes;
|
|
56
62
|
class Program {
|
|
57
63
|
constructor(
|
|
58
64
|
/**
|
|
@@ -121,6 +127,7 @@ class Program {
|
|
|
121
127
|
xmlFilesValidated: [],
|
|
122
128
|
changedSymbols: new Map(),
|
|
123
129
|
changedComponentTypes: [],
|
|
130
|
+
scopesToInvalidate: [],
|
|
124
131
|
scopesToValidate: [],
|
|
125
132
|
filesToBeValidatedInScopeContext: new Set()
|
|
126
133
|
};
|
|
@@ -140,12 +147,19 @@ class Program {
|
|
|
140
147
|
this.validationMinSyncDuration = 75;
|
|
141
148
|
this.getFilePathCache = new Map();
|
|
142
149
|
this.sortedScopeNames = undefined;
|
|
150
|
+
this.scopesPerFile = new Map();
|
|
143
151
|
this.getTranspiledFileContentsPipeline = new ActionPipeline_1.ActionPipeline();
|
|
144
152
|
this.buildPipeline = new ActionPipeline_1.ActionPipeline();
|
|
145
153
|
this.options = util_1.util.normalizeConfig(options);
|
|
146
154
|
this.logger = logger !== null && logger !== void 0 ? logger : (0, logging_1.createLogger)(options);
|
|
147
155
|
this.plugins = plugins || new PluginInterface_1.default([], { logger: this.logger });
|
|
148
156
|
this.diagnostics = diagnosticsManager || new DiagnosticManager_1.DiagnosticManager();
|
|
157
|
+
//surface warnings for any deprecated bsconfig options that were used to build `this.options`
|
|
158
|
+
const deprecationDiagnostics = this.options._deprecationDiagnostics;
|
|
159
|
+
if ((deprecationDiagnostics === null || deprecationDiagnostics === void 0 ? void 0 : deprecationDiagnostics.length) > 0) {
|
|
160
|
+
this.diagnostics.register(deprecationDiagnostics);
|
|
161
|
+
}
|
|
162
|
+
delete this.options._deprecationDiagnostics;
|
|
149
163
|
//try to find a location for the diagnostic if it doesn't have one
|
|
150
164
|
this.diagnostics.locationResolver = (args) => {
|
|
151
165
|
//find the first xml scope for this diagnostic
|
|
@@ -657,6 +671,112 @@ class Program {
|
|
|
657
671
|
var _a;
|
|
658
672
|
return (_a = this.getComponent(componentName)) === null || _a === void 0 ? void 0 : _a.scope;
|
|
659
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Get the names of all known SceneGraph nodes: the built-in Roku nodes plus every component
|
|
676
|
+
* defined in this program. Names keep their original casing and are deduplicated by lower-case name.
|
|
677
|
+
*/
|
|
678
|
+
getSceneGraphNodeNames() {
|
|
679
|
+
var _a, _b;
|
|
680
|
+
const namesByLowerName = new Map();
|
|
681
|
+
for (const node of Object.values(builtInSceneGraphNodes)) {
|
|
682
|
+
namesByLowerName.set(node.name.toLowerCase(), node.name);
|
|
683
|
+
}
|
|
684
|
+
for (const componentName in this.components) {
|
|
685
|
+
const displayName = (_b = (_a = this.components[componentName][0]) === null || _a === void 0 ? void 0 : _a.file.componentName) === null || _b === void 0 ? void 0 : _b.text;
|
|
686
|
+
if (displayName) {
|
|
687
|
+
namesByLowerName.set(displayName.toLowerCase(), displayName);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return [...namesByLowerName.values()];
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Determine whether a SceneGraph node with the given name exists, either as a built-in Roku node
|
|
694
|
+
* or as a component defined in this program.
|
|
695
|
+
*/
|
|
696
|
+
hasSceneGraphNode(nodeName) {
|
|
697
|
+
if (!nodeName) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
return !!builtInSceneGraphNodes[nodeName.toLowerCase()] || !!this.getComponent(nodeName);
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Get the built-in Roku node data and/or the project component file backing a SceneGraph node name.
|
|
704
|
+
* Returns `undefined` when no node or component matches.
|
|
705
|
+
*/
|
|
706
|
+
getSceneGraphNode(nodeName) {
|
|
707
|
+
var _a;
|
|
708
|
+
if (!nodeName) {
|
|
709
|
+
return undefined;
|
|
710
|
+
}
|
|
711
|
+
const builtInNode = builtInSceneGraphNodes[nodeName.toLowerCase()];
|
|
712
|
+
const componentFile = (_a = this.getComponent(nodeName)) === null || _a === void 0 ? void 0 : _a.file;
|
|
713
|
+
if (!builtInNode && !componentFile) {
|
|
714
|
+
return undefined;
|
|
715
|
+
}
|
|
716
|
+
return { builtInNode: builtInNode, componentFile: componentFile };
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Get every field available on a SceneGraph node, walking the full `extends` chain across both
|
|
720
|
+
* built-in Roku nodes and project components. Fields declared closer to the node take precedence
|
|
721
|
+
* over inherited fields with the same name.
|
|
722
|
+
*/
|
|
723
|
+
getSceneGraphNodeFields(nodeName) {
|
|
724
|
+
const fieldsByLowerName = new Map();
|
|
725
|
+
const visitedNodeNames = new Set();
|
|
726
|
+
const addField = (field) => {
|
|
727
|
+
if (!field.name) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const lowerName = field.name.toLowerCase();
|
|
731
|
+
//the first definition we encounter is the closest one, so it wins
|
|
732
|
+
if (!fieldsByLowerName.has(lowerName)) {
|
|
733
|
+
fieldsByLowerName.set(lowerName, field);
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
const walk = (currentNodeName, origin) => {
|
|
737
|
+
var _a, _b, _c, _d, _e, _f;
|
|
738
|
+
const lowerName = currentNodeName === null || currentNodeName === void 0 ? void 0 : currentNodeName.toLowerCase();
|
|
739
|
+
if (!lowerName || visitedNodeNames.has(lowerName)) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
visitedNodeNames.add(lowerName);
|
|
743
|
+
//prefer a project component (a project component can shadow a built-in of the same name)
|
|
744
|
+
const component = this.getComponent(currentNodeName);
|
|
745
|
+
if (component) {
|
|
746
|
+
for (const field of (_c = (_b = (_a = component.file.ast.componentElement) === null || _a === void 0 ? void 0 : _a.interfaceElement) === null || _b === void 0 ? void 0 : _b.fields) !== null && _c !== void 0 ? _c : []) {
|
|
747
|
+
addField({ name: field.id, type: field.type, default: field.value, origin: origin });
|
|
748
|
+
}
|
|
749
|
+
const parentName = (_d = component.file.ast.componentElement) === null || _d === void 0 ? void 0 : _d.extends;
|
|
750
|
+
if (parentName) {
|
|
751
|
+
walk(parentName, 'inherited');
|
|
752
|
+
}
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const builtInNode = builtInSceneGraphNodes[lowerName];
|
|
756
|
+
if (builtInNode) {
|
|
757
|
+
for (const field of (_e = builtInNode.fields) !== null && _e !== void 0 ? _e : []) {
|
|
758
|
+
addField({
|
|
759
|
+
name: field.name,
|
|
760
|
+
type: field.type,
|
|
761
|
+
default: field.default,
|
|
762
|
+
description: field.description,
|
|
763
|
+
accessPermission: field.accessPermission,
|
|
764
|
+
origin: origin
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
if ((_f = builtInNode.extends) === null || _f === void 0 ? void 0 : _f.name) {
|
|
768
|
+
walk(builtInNode.extends.name, 'inherited');
|
|
769
|
+
}
|
|
770
|
+
else if (lowerName !== 'node') {
|
|
771
|
+
//some scraped node entries are missing `extends` data, but every SceneGraph node
|
|
772
|
+
//ultimately descends from Node, so fall back to Node to keep its universal fields
|
|
773
|
+
walk('Node', 'inherited');
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
walk(nodeName, 'own');
|
|
778
|
+
return [...fieldsByLowerName.values()];
|
|
779
|
+
}
|
|
660
780
|
/**
|
|
661
781
|
* Update internal maps with this file reference
|
|
662
782
|
*/
|
|
@@ -665,9 +785,11 @@ class Program {
|
|
|
665
785
|
file: file,
|
|
666
786
|
program: this
|
|
667
787
|
};
|
|
788
|
+
this.scopesPerFile.clear();
|
|
668
789
|
this.plugins.emit('beforeAddFile', fileAddEvent);
|
|
669
790
|
this.files[file.srcPath.toLowerCase()] = file;
|
|
670
791
|
this.destMap.set(file.destPath.toLowerCase(), file);
|
|
792
|
+
this.plugins.emit('addFile', fileAddEvent);
|
|
671
793
|
this.plugins.emit('afterAddFile', fileAddEvent);
|
|
672
794
|
return file;
|
|
673
795
|
}
|
|
@@ -677,6 +799,7 @@ class Program {
|
|
|
677
799
|
unassignFile(file) {
|
|
678
800
|
delete this.files[file.srcPath.toLowerCase()];
|
|
679
801
|
this.destMap.delete(file.destPath.toLowerCase());
|
|
802
|
+
this.scopesPerFile.clear();
|
|
680
803
|
return file;
|
|
681
804
|
}
|
|
682
805
|
setFile(fileParam, fileData) {
|
|
@@ -785,6 +908,7 @@ class Program {
|
|
|
785
908
|
destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
|
|
786
909
|
}
|
|
787
910
|
else {
|
|
911
|
+
//`fileParam` here is `FileObj | { srcPath?: string; pkgPath?: string }`; duck-type across both shapes
|
|
788
912
|
let param = fileParam;
|
|
789
913
|
if (param.src) {
|
|
790
914
|
srcPath = (0, util_1.standardizePath) `${param.src}`;
|
|
@@ -884,6 +1008,7 @@ class Program {
|
|
|
884
1008
|
this.diagnostics.clearForFile(file.srcPath);
|
|
885
1009
|
const event = { file: file, program: this };
|
|
886
1010
|
this.plugins.emit('beforeRemoveFile', event);
|
|
1011
|
+
this.plugins.emit('removeFile', event);
|
|
887
1012
|
//if there is a scope named the same as this file's path, remove it (i.e. xml scopes)
|
|
888
1013
|
let scope = this.scopes[file.destPath];
|
|
889
1014
|
if (scope) {
|
|
@@ -950,11 +1075,12 @@ class Program {
|
|
|
950
1075
|
const xmlFilesValidated = this.validationDetails.xmlFilesValidated;
|
|
951
1076
|
const changedSymbols = this.validationDetails.changedSymbols;
|
|
952
1077
|
const changedComponentTypes = this.validationDetails.changedComponentTypes;
|
|
1078
|
+
const scopesToInvalidate = this.validationDetails.scopesToInvalidate;
|
|
953
1079
|
const scopesToValidate = this.validationDetails.scopesToValidate;
|
|
954
1080
|
const filesToBeValidatedInScopeContext = this.validationDetails.filesToBeValidatedInScopeContext;
|
|
955
1081
|
//validate every file
|
|
956
1082
|
let logValidateEnd = (status) => { };
|
|
957
|
-
//will be populated later on during the
|
|
1083
|
+
//will be populated later on during the corresponding sequencer event
|
|
958
1084
|
let filesToProcess;
|
|
959
1085
|
const sequencer = new Sequencer_1.Sequencer({
|
|
960
1086
|
name: 'program.validate',
|
|
@@ -1028,7 +1154,7 @@ class Program {
|
|
|
1028
1154
|
if ((0, reflection_1.isXmlFile)(file)) {
|
|
1029
1155
|
this.addDeferredComponentTypeSymbolCreation(file);
|
|
1030
1156
|
}
|
|
1031
|
-
else if ((0, reflection_1.isBrsFile)(file)) {
|
|
1157
|
+
else if ((0, reflection_1.isBrsFile)(file) && !this.isFirstValidation) {
|
|
1032
1158
|
const fileHasChanges = file.providedSymbols.changes.get(1 /* SymbolTypeFlag.runtime */).size > 0 || file.providedSymbols.changes.get(2 /* SymbolTypeFlag.typetime */).size > 0;
|
|
1033
1159
|
if (fileHasChanges) {
|
|
1034
1160
|
for (const scope of this.getScopesForFile(file)) {
|
|
@@ -1136,9 +1262,11 @@ class Program {
|
|
|
1136
1262
|
this.logger.time(logging_1.LogLevel.info, ['addDiagnosticsForScopes'], () => {
|
|
1137
1263
|
this.crossScopeValidation.addDiagnosticsForScopes(scopesToCheck);
|
|
1138
1264
|
});
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1265
|
+
if (!this.isFirstValidation) {
|
|
1266
|
+
const filesToRevalidate = this.crossScopeValidation.getFilesRequiringChangedSymbol(scopesToCheck, changedSymbols);
|
|
1267
|
+
for (const file of filesToRevalidate) {
|
|
1268
|
+
filesToBeValidatedInScopeContext.add(file);
|
|
1269
|
+
}
|
|
1142
1270
|
}
|
|
1143
1271
|
this.currentScopeValidationOptions = {
|
|
1144
1272
|
filesToBeValidatedInScopeContext: filesToBeValidatedInScopeContext,
|
|
@@ -1149,18 +1277,29 @@ class Program {
|
|
|
1149
1277
|
//can reset changedComponent types
|
|
1150
1278
|
this.validationDetails.changedComponentTypes = [];
|
|
1151
1279
|
})
|
|
1152
|
-
.forEach('
|
|
1280
|
+
.forEach('invalidating file segments', () => filesToBeValidatedInScopeContext, (file) => {
|
|
1153
1281
|
if ((0, reflection_1.isBrsFile)(file)) {
|
|
1154
1282
|
file.validationSegmenter.unValidateAllSegments();
|
|
1155
|
-
|
|
1283
|
+
if (!this.isFirstValidation) {
|
|
1284
|
+
scopesToInvalidate.push(...this.getScopesForFile(file));
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
})
|
|
1288
|
+
.once('invalidate affected scopes', () => {
|
|
1289
|
+
if (this.isFirstValidation) {
|
|
1290
|
+
for (const scope of this.getAllUserScopes()) {
|
|
1291
|
+
scope.invalidate();
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
else {
|
|
1295
|
+
for (const scope of scopesToInvalidate) {
|
|
1156
1296
|
scope.invalidate();
|
|
1157
1297
|
}
|
|
1158
1298
|
}
|
|
1159
1299
|
})
|
|
1160
1300
|
.once('checking scopes to validate', () => {
|
|
1161
1301
|
//sort the scope names so we get consistent results
|
|
1162
|
-
for (const
|
|
1163
|
-
let scope = this.scopes[scopeName];
|
|
1302
|
+
for (const scope of this.getAllUserScopes()) {
|
|
1164
1303
|
if (scope.shouldValidate(this.currentScopeValidationOptions)) {
|
|
1165
1304
|
scopesToValidate.push(scope);
|
|
1166
1305
|
}
|
|
@@ -1184,17 +1323,21 @@ class Program {
|
|
|
1184
1323
|
})
|
|
1185
1324
|
.once('detect duplicate component names', () => {
|
|
1186
1325
|
this.detectDuplicateComponentNames();
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
this.
|
|
1190
|
-
this.validationDetails.scopesToValidate = [];
|
|
1191
|
-
this.validationDetails.filesToBeValidatedInScopeContext = new Set();
|
|
1326
|
+
})
|
|
1327
|
+
.once('detect diagnostic filter issues', () => {
|
|
1328
|
+
this.diagnostics.detectPathLikeDiagnosticFilterCodes(this.options, { tags: [ProgramValidator_1.ProgramValidatorDiagnosticsTag] });
|
|
1192
1329
|
})
|
|
1193
1330
|
.onCancel(() => {
|
|
1194
1331
|
logValidateEnd('cancelled');
|
|
1195
1332
|
})
|
|
1196
1333
|
.onSuccess(() => {
|
|
1197
1334
|
logValidateEnd();
|
|
1335
|
+
this.isFirstValidation = false;
|
|
1336
|
+
// can reset other validation details
|
|
1337
|
+
this.validationDetails.changedSymbols = new Map();
|
|
1338
|
+
this.validationDetails.scopesToInvalidate = [];
|
|
1339
|
+
this.validationDetails.scopesToValidate = [];
|
|
1340
|
+
this.validationDetails.filesToBeValidatedInScopeContext = new Set();
|
|
1198
1341
|
})
|
|
1199
1342
|
.onComplete(() => {
|
|
1200
1343
|
var _a, _b;
|
|
@@ -1228,8 +1371,7 @@ class Program {
|
|
|
1228
1371
|
}
|
|
1229
1372
|
getScopesForCrossScopeValidation(someComponentTypeChanged, didProvidedSymbolChange) {
|
|
1230
1373
|
const scopesForCrossScopeValidation = [];
|
|
1231
|
-
for (let
|
|
1232
|
-
let scope = this.scopes[scopeName];
|
|
1374
|
+
for (let scope of this.getAllUserScopes()) {
|
|
1233
1375
|
if (this.globalScope === scope) {
|
|
1234
1376
|
continue;
|
|
1235
1377
|
}
|
|
@@ -1358,12 +1500,19 @@ class Program {
|
|
|
1358
1500
|
}
|
|
1359
1501
|
return this.sortedScopeNames;
|
|
1360
1502
|
}
|
|
1503
|
+
getAllUserScopes() {
|
|
1504
|
+
return Object.values(this.scopes).filter(s => s.name !== 'global');
|
|
1505
|
+
}
|
|
1361
1506
|
/**
|
|
1362
1507
|
* Get a list of all scopes the file is loaded into
|
|
1363
1508
|
* @param file the file
|
|
1364
1509
|
*/
|
|
1365
1510
|
getScopesForFile(file) {
|
|
1366
1511
|
const resolvedFile = typeof file === 'string' ? this.getFile(file) : file;
|
|
1512
|
+
const cachedResult = this.scopesPerFile.get(resolvedFile);
|
|
1513
|
+
if (cachedResult) {
|
|
1514
|
+
return cachedResult;
|
|
1515
|
+
}
|
|
1367
1516
|
let result = [];
|
|
1368
1517
|
if (resolvedFile) {
|
|
1369
1518
|
const scopeKeys = this.getSortedScopeNames();
|
|
@@ -1374,6 +1523,7 @@ class Program {
|
|
|
1374
1523
|
}
|
|
1375
1524
|
}
|
|
1376
1525
|
}
|
|
1526
|
+
this.scopesPerFile.set(resolvedFile, result);
|
|
1377
1527
|
return result;
|
|
1378
1528
|
}
|
|
1379
1529
|
/**
|
|
@@ -1515,9 +1665,6 @@ class Program {
|
|
|
1515
1665
|
this.plugins.emit('afterProvideDefinition', event);
|
|
1516
1666
|
return event.definitions;
|
|
1517
1667
|
}
|
|
1518
|
-
/**
|
|
1519
|
-
* Get hover information for a file and position
|
|
1520
|
-
*/
|
|
1521
1668
|
getHover(srcPath, position) {
|
|
1522
1669
|
let file = this.getFile(srcPath);
|
|
1523
1670
|
let result;
|
|
@@ -1643,8 +1790,9 @@ class Program {
|
|
|
1643
1790
|
}
|
|
1644
1791
|
/**
|
|
1645
1792
|
* Compute "source fix all" code actions for the given file.
|
|
1646
|
-
* Fires the `
|
|
1647
|
-
*
|
|
1793
|
+
* Fires the `provideSourceFixAllCodeActions` plugin event (along with its before/after variants)
|
|
1794
|
+
* with all diagnostics for the file (no range filter), then converts each contributed
|
|
1795
|
+
* SourceFixAllCodeAction into an LSP CodeAction.
|
|
1648
1796
|
*/
|
|
1649
1797
|
getSourceFixAllCodeActions(srcPath) {
|
|
1650
1798
|
const actions = [];
|
|
@@ -1655,13 +1803,16 @@ class Program {
|
|
|
1655
1803
|
.getDiagnostics()
|
|
1656
1804
|
.filter(x => { var _a; return ((_a = x.location) === null || _a === void 0 ? void 0 : _a.uri) === fileUri; });
|
|
1657
1805
|
const scopes = this.getScopesForFile(file);
|
|
1658
|
-
|
|
1806
|
+
const event = {
|
|
1659
1807
|
program: this,
|
|
1660
1808
|
file: file,
|
|
1661
1809
|
diagnostics: diagnostics,
|
|
1662
1810
|
scopes: scopes,
|
|
1663
1811
|
actions: actions
|
|
1664
|
-
}
|
|
1812
|
+
};
|
|
1813
|
+
this.plugins.emit('beforeProvideSourceFixAllCodeActions', event);
|
|
1814
|
+
this.plugins.emit('provideSourceFixAllCodeActions', event);
|
|
1815
|
+
this.plugins.emit('afterProvideSourceFixAllCodeActions', event);
|
|
1665
1816
|
}
|
|
1666
1817
|
return actions.map(action => {
|
|
1667
1818
|
var _a;
|
|
@@ -1899,6 +2050,7 @@ class Program {
|
|
|
1899
2050
|
files: files,
|
|
1900
2051
|
outDir: outDir
|
|
1901
2052
|
});
|
|
2053
|
+
await this.plugins.emitAsync('writeProgram', programEvent);
|
|
1902
2054
|
//empty the out directory
|
|
1903
2055
|
await fsExtra.emptyDir(outDir);
|
|
1904
2056
|
const serializedFiles = [...files]
|
|
@@ -1931,6 +2083,7 @@ class Program {
|
|
|
1931
2083
|
editor: this.editor,
|
|
1932
2084
|
files: (_a = options === null || options === void 0 ? void 0 : options.files) !== null && _a !== void 0 ? _a : Object.values(this.files)
|
|
1933
2085
|
});
|
|
2086
|
+
await this.plugins.emitAsync('buildProgram', event);
|
|
1934
2087
|
//prepare the program (and files) for building
|
|
1935
2088
|
event.files = await this.prepare(event.files);
|
|
1936
2089
|
//stage the entire program
|
|
@@ -2073,9 +2226,13 @@ class Program {
|
|
|
2073
2226
|
const parsedManifest = (0, Manifest_1.parseManifest)(contents);
|
|
2074
2227
|
this.buildBsConstsIntoParsedManifest(parsedManifest);
|
|
2075
2228
|
this._manifest = parsedManifest;
|
|
2229
|
+
this._manifestEntries = (0, Manifest_1.parseManifestEntries)(contents);
|
|
2230
|
+
this._manifestPath = manifestPath;
|
|
2076
2231
|
}
|
|
2077
2232
|
catch (e) {
|
|
2078
2233
|
this._manifest = new Map();
|
|
2234
|
+
this._manifestEntries = [];
|
|
2235
|
+
this._manifestPath = undefined;
|
|
2079
2236
|
}
|
|
2080
2237
|
}
|
|
2081
2238
|
/**
|
|
@@ -2087,6 +2244,99 @@ class Program {
|
|
|
2087
2244
|
}
|
|
2088
2245
|
return this._manifest;
|
|
2089
2246
|
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Get the manifest as a list of `{ key, value, range }` entries with line/column ranges
|
|
2249
|
+
* suitable for attaching diagnostics to specific manifest lines.
|
|
2250
|
+
*
|
|
2251
|
+
* NOTE: protected for now. The shape of this data is likely to evolve as we build out
|
|
2252
|
+
* more manifest-aware features (validation rules, autocomplete, etc.). External plugins
|
|
2253
|
+
* shouldn't depend on this until we commit to a stable API.
|
|
2254
|
+
*/
|
|
2255
|
+
getManifestEntries() {
|
|
2256
|
+
if (!this._manifestEntries) {
|
|
2257
|
+
this.loadManifest();
|
|
2258
|
+
}
|
|
2259
|
+
return this._manifestEntries;
|
|
2260
|
+
}
|
|
2261
|
+
/**
|
|
2262
|
+
* Returns the absolute path of the loaded manifest file, or undefined if no manifest was found.
|
|
2263
|
+
*
|
|
2264
|
+
* NOTE: protected for now. Once brighterscript treats the manifest as a proper file
|
|
2265
|
+
* (with editor / BscFile integration) callers should consume that instead of poking at
|
|
2266
|
+
* the Program. External plugins shouldn't depend on this in the meantime.
|
|
2267
|
+
*/
|
|
2268
|
+
getManifestPath() {
|
|
2269
|
+
if (!this._manifest) {
|
|
2270
|
+
this.loadManifest();
|
|
2271
|
+
}
|
|
2272
|
+
return this._manifestPath;
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* The minimum Roku firmware version brighterscript should assume the user is targeting.
|
|
2276
|
+
* If `options.minFirmwareVersion` is set AND parseable as semver, the canonical coerced
|
|
2277
|
+
* form ("15.0" → "15.0.0") wins; otherwise (unset or unparseable) falls back to
|
|
2278
|
+
* {@link DEFAULT_MIN_FIRMWARE_VERSION}. The return is always a valid semver string so
|
|
2279
|
+
* downstream callers can pass it directly to `semver.gte`/`semver.lt` without re-coercing.
|
|
2280
|
+
* Cached after first call.
|
|
2281
|
+
*/
|
|
2282
|
+
getMinFirmwareVersion() {
|
|
2283
|
+
if (this._minFirmwareVersion === undefined) {
|
|
2284
|
+
const userValue = this.options.minFirmwareVersion;
|
|
2285
|
+
const coerced = userValue ? semver.coerce(userValue) : undefined;
|
|
2286
|
+
this._minFirmwareVersion = coerced ? coerced.version : RokuConstants_1.DEFAULT_MIN_FIRMWARE_VERSION;
|
|
2287
|
+
}
|
|
2288
|
+
return this._minFirmwareVersion;
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* What the project's target firmware natively understands, derived from
|
|
2292
|
+
* {@link getMinFirmwareVersion}. These are facts about the device, not decisions about what
|
|
2293
|
+
* to do — a caller finding a missing capability decides whether to transpile around it (as
|
|
2294
|
+
* `continue` does) or report a diagnostic (as optional chaining does).
|
|
2295
|
+
* Cached after first call.
|
|
2296
|
+
*/
|
|
2297
|
+
get firmwareCapabilities() {
|
|
2298
|
+
if (this._firmwareCapabilities === undefined) {
|
|
2299
|
+
this._firmwareCapabilities = (0, RokuConstants_1.getFirmwareCapabilities)(this.getMinFirmwareVersion());
|
|
2300
|
+
}
|
|
2301
|
+
return this._firmwareCapabilities;
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Returns the effective `rsg_version` for this program in canonical semver form (e.g. "1.2"
|
|
2305
|
+
* → "1.2.0"). If the manifest declares a value explicitly and it's parseable, the canonical
|
|
2306
|
+
* form wins; otherwise (manifest silent OR value is malformed) we fall back to the highest
|
|
2307
|
+
* known rsg_version whose `becameDefaultAt` is `<=` the effective minimum firmware version,
|
|
2308
|
+
* driven by {@link RSG_VERSIONS}. Cached after first call.
|
|
2309
|
+
*
|
|
2310
|
+
* Manifest validation (format errors, etc.) happens in `ProgramValidator` against the raw
|
|
2311
|
+
* entry — that path doesn't go through this method.
|
|
2312
|
+
*/
|
|
2313
|
+
getRsgVersion() {
|
|
2314
|
+
if (this._rsgVersion !== undefined) {
|
|
2315
|
+
return this._rsgVersion;
|
|
2316
|
+
}
|
|
2317
|
+
const explicit = this.getManifest().get('rsg_version');
|
|
2318
|
+
const explicitCoerced = explicit !== undefined ? semver.coerce(explicit.trim()) : undefined;
|
|
2319
|
+
if (explicitCoerced) {
|
|
2320
|
+
this._rsgVersion = explicitCoerced.version;
|
|
2321
|
+
return this._rsgVersion;
|
|
2322
|
+
}
|
|
2323
|
+
//walk known rsg_versions in descending order (newest first) and return the first whose
|
|
2324
|
+
//becameDefaultAt <= effective firmware. As long as some entry has becameDefaultAt: '0.0.0'
|
|
2325
|
+
//(currently `1.1`), this loop always finds a match.
|
|
2326
|
+
const minFirmwareVersion = this.getMinFirmwareVersion();
|
|
2327
|
+
const candidates = Object.entries(RokuConstants_1.RSG_VERSIONS)
|
|
2328
|
+
.filter(([, info]) => info.becameDefaultAt !== undefined)
|
|
2329
|
+
.sort(([a], [b]) => semver.rcompare(semver.coerce(a), semver.coerce(b)));
|
|
2330
|
+
for (const [version, info] of candidates) {
|
|
2331
|
+
if (semver.gte(minFirmwareVersion, info.becameDefaultAt)) {
|
|
2332
|
+
this._rsgVersion = semver.coerce(version).version;
|
|
2333
|
+
return this._rsgVersion;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
//unreachable as long as RSG_VERSIONS contains an entry with becameDefaultAt: '0.0.0'
|
|
2337
|
+
this._rsgVersion = RokuConstants_1.DEFAULT_MIN_FIRMWARE_VERSION;
|
|
2338
|
+
return this._rsgVersion;
|
|
2339
|
+
}
|
|
2090
2340
|
dispose() {
|
|
2091
2341
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2092
2342
|
this.plugins.emit('beforeRemoveProgram', { program: this });
|