brighterscript 1.0.0-alpha.5 → 1.0.0-alpha.51

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.
Files changed (652) hide show
  1. package/README.md +79 -138
  2. package/bsconfig.schema.json +196 -5
  3. package/dist/ActionPipeline.d.ts +10 -0
  4. package/dist/ActionPipeline.js +40 -0
  5. package/dist/ActionPipeline.js.map +1 -0
  6. package/dist/AstValidationSegmenter.d.ts +45 -0
  7. package/dist/AstValidationSegmenter.js +322 -0
  8. package/dist/AstValidationSegmenter.js.map +1 -0
  9. package/dist/BsConfig.d.ts +161 -43
  10. package/dist/BusyStatusTracker.d.ts +61 -0
  11. package/dist/BusyStatusTracker.js +148 -0
  12. package/dist/BusyStatusTracker.js.map +1 -0
  13. package/dist/Cache.d.ts +3 -8
  14. package/dist/Cache.js +9 -14
  15. package/dist/Cache.js.map +1 -1
  16. package/dist/CacheVerifier.d.ts +7 -0
  17. package/dist/CacheVerifier.js +20 -0
  18. package/dist/CacheVerifier.js.map +1 -0
  19. package/dist/CodeActionUtil.d.ts +29 -4
  20. package/dist/CodeActionUtil.js +22 -5
  21. package/dist/CodeActionUtil.js.map +1 -1
  22. package/dist/CommentFlagProcessor.d.ts +20 -15
  23. package/dist/CommentFlagProcessor.js +143 -58
  24. package/dist/CommentFlagProcessor.js.map +1 -1
  25. package/dist/CrossScopeValidator.d.ts +68 -0
  26. package/dist/CrossScopeValidator.js +650 -0
  27. package/dist/CrossScopeValidator.js.map +1 -0
  28. package/dist/DependencyGraph.d.ts +8 -3
  29. package/dist/DependencyGraph.js +49 -16
  30. package/dist/DependencyGraph.js.map +1 -1
  31. package/dist/DiagnosticCollection.d.ts +21 -5
  32. package/dist/DiagnosticCollection.js +77 -24
  33. package/dist/DiagnosticCollection.js.map +1 -1
  34. package/dist/DiagnosticFilterer.d.ts +27 -6
  35. package/dist/DiagnosticFilterer.js +273 -60
  36. package/dist/DiagnosticFilterer.js.map +1 -1
  37. package/dist/DiagnosticManager.d.ts +83 -0
  38. package/dist/DiagnosticManager.js +422 -0
  39. package/dist/DiagnosticManager.js.map +1 -0
  40. package/dist/DiagnosticMessages.d.ts +602 -196
  41. package/dist/DiagnosticMessages.js +926 -342
  42. package/dist/DiagnosticMessages.js.map +1 -1
  43. package/dist/DiagnosticSeverityAdjuster.d.ts +7 -0
  44. package/dist/DiagnosticSeverityAdjuster.js +45 -0
  45. package/dist/DiagnosticSeverityAdjuster.js.map +1 -0
  46. package/dist/FunctionScope.d.ts +28 -0
  47. package/dist/FunctionScope.js +52 -0
  48. package/dist/FunctionScope.js.map +1 -0
  49. package/dist/KeyedThrottler.d.ts +3 -3
  50. package/dist/KeyedThrottler.js +3 -3
  51. package/dist/KeyedThrottler.js.map +1 -1
  52. package/dist/LanguageServer.d.ts +136 -104
  53. package/dist/LanguageServer.js +577 -741
  54. package/dist/LanguageServer.js.map +1 -1
  55. package/dist/Logger.d.ts +17 -13
  56. package/dist/Logger.js +64 -34
  57. package/dist/Logger.js.map +1 -1
  58. package/dist/PluginInterface.d.ts +32 -10
  59. package/dist/PluginInterface.js +117 -7
  60. package/dist/PluginInterface.js.map +1 -1
  61. package/dist/Program.d.ts +302 -98
  62. package/dist/Program.js +1613 -726
  63. package/dist/Program.js.map +1 -1
  64. package/dist/ProgramBuilder.d.ts +39 -22
  65. package/dist/ProgramBuilder.js +245 -179
  66. package/dist/ProgramBuilder.js.map +1 -1
  67. package/dist/Scope.d.ts +227 -106
  68. package/dist/Scope.js +609 -557
  69. package/dist/Scope.js.map +1 -1
  70. package/dist/ScopeNamespaceLookup.d.ts +73 -0
  71. package/dist/ScopeNamespaceLookup.js +242 -0
  72. package/dist/ScopeNamespaceLookup.js.map +1 -0
  73. package/dist/SemanticTokenUtils.js +5 -1
  74. package/dist/SemanticTokenUtils.js.map +1 -1
  75. package/dist/Stopwatch.d.ts +4 -0
  76. package/dist/Stopwatch.js +8 -1
  77. package/dist/Stopwatch.js.map +1 -1
  78. package/dist/SymbolTable.d.ts +145 -26
  79. package/dist/SymbolTable.js +575 -64
  80. package/dist/SymbolTable.js.map +1 -1
  81. package/dist/SymbolTypeFlag.d.ts +9 -0
  82. package/dist/SymbolTypeFlag.js +14 -0
  83. package/dist/SymbolTypeFlag.js.map +1 -0
  84. package/dist/Throttler.d.ts +12 -0
  85. package/dist/Throttler.js +39 -0
  86. package/dist/Throttler.js.map +1 -1
  87. package/dist/Watcher.d.ts +0 -3
  88. package/dist/Watcher.js +0 -3
  89. package/dist/Watcher.js.map +1 -1
  90. package/dist/XmlScope.d.ts +5 -15
  91. package/dist/XmlScope.js +34 -90
  92. package/dist/XmlScope.js.map +1 -1
  93. package/dist/astUtils/CachedLookups.d.ts +50 -0
  94. package/dist/astUtils/CachedLookups.js +337 -0
  95. package/dist/astUtils/CachedLookups.js.map +1 -0
  96. package/dist/astUtils/Editor.d.ts +69 -0
  97. package/dist/astUtils/Editor.js +245 -0
  98. package/dist/astUtils/Editor.js.map +1 -0
  99. package/dist/astUtils/creators.d.ts +54 -19
  100. package/dist/astUtils/creators.js +242 -42
  101. package/dist/astUtils/creators.js.map +1 -1
  102. package/dist/astUtils/reflection.d.ts +199 -85
  103. package/dist/astUtils/reflection.js +518 -145
  104. package/dist/astUtils/reflection.js.map +1 -1
  105. package/dist/astUtils/stackedVisitor.js.map +1 -1
  106. package/dist/astUtils/visitors.d.ts +117 -53
  107. package/dist/astUtils/visitors.js +95 -15
  108. package/dist/astUtils/visitors.js.map +1 -1
  109. package/dist/astUtils/xml.d.ts +9 -8
  110. package/dist/astUtils/xml.js +12 -7
  111. package/dist/astUtils/xml.js.map +1 -1
  112. package/dist/bscPlugin/BscPlugin.d.ts +26 -4
  113. package/dist/bscPlugin/BscPlugin.js +96 -4
  114. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  115. package/dist/bscPlugin/CallExpressionInfo.d.ts +36 -0
  116. package/dist/bscPlugin/CallExpressionInfo.js +142 -0
  117. package/dist/bscPlugin/CallExpressionInfo.js.map +1 -0
  118. package/dist/bscPlugin/FileWriter.d.ts +19 -0
  119. package/dist/bscPlugin/FileWriter.js +79 -0
  120. package/dist/bscPlugin/FileWriter.js.map +1 -0
  121. package/dist/bscPlugin/SignatureHelpUtil.d.ts +10 -0
  122. package/dist/bscPlugin/SignatureHelpUtil.js +137 -0
  123. package/dist/bscPlugin/SignatureHelpUtil.js.map +1 -0
  124. package/dist/bscPlugin/codeActions/CodeActionsProcessor.d.ts +109 -7
  125. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +676 -26
  126. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  127. package/dist/bscPlugin/codeActions/FixAllCodeActionsProcessor.d.ts +17 -0
  128. package/dist/bscPlugin/codeActions/FixAllCodeActionsProcessor.js +66 -0
  129. package/dist/bscPlugin/codeActions/FixAllCodeActionsProcessor.js.map +1 -0
  130. package/dist/bscPlugin/codeActions/codeActionHelpers.d.ts +18 -0
  131. package/dist/bscPlugin/codeActions/codeActionHelpers.js +31 -0
  132. package/dist/bscPlugin/codeActions/codeActionHelpers.js.map +1 -0
  133. package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +65 -0
  134. package/dist/bscPlugin/completions/CompletionsProcessor.js +633 -0
  135. package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -0
  136. package/dist/bscPlugin/definition/DefinitionProvider.d.ts +13 -0
  137. package/dist/bscPlugin/definition/DefinitionProvider.js +220 -0
  138. package/dist/bscPlugin/definition/DefinitionProvider.js.map +1 -0
  139. package/dist/bscPlugin/fileProviders/FileProvider.d.ts +9 -0
  140. package/dist/bscPlugin/fileProviders/FileProvider.js +51 -0
  141. package/dist/bscPlugin/fileProviders/FileProvider.js.map +1 -0
  142. package/dist/bscPlugin/hover/HoverProcessor.d.ts +18 -0
  143. package/dist/bscPlugin/hover/HoverProcessor.js +238 -0
  144. package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -0
  145. package/dist/bscPlugin/references/ReferencesProvider.d.ts +12 -0
  146. package/dist/bscPlugin/references/ReferencesProvider.js +57 -0
  147. package/dist/bscPlugin/references/ReferencesProvider.js.map +1 -0
  148. package/dist/bscPlugin/selectionRanges/SelectionRangesProcessor.d.ts +7 -0
  149. package/dist/bscPlugin/selectionRanges/SelectionRangesProcessor.js +77 -0
  150. package/dist/bscPlugin/selectionRanges/SelectionRangesProcessor.js.map +1 -0
  151. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +14 -0
  152. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +164 -0
  153. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -0
  154. package/dist/bscPlugin/serialize/BslibManager.d.ts +12 -0
  155. package/dist/bscPlugin/serialize/BslibManager.js +46 -0
  156. package/dist/bscPlugin/serialize/BslibManager.js.map +1 -0
  157. package/dist/bscPlugin/serialize/FileSerializer.d.ts +9 -0
  158. package/dist/bscPlugin/serialize/FileSerializer.js +80 -0
  159. package/dist/bscPlugin/serialize/FileSerializer.js.map +1 -0
  160. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.d.ts +7 -0
  161. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js +22 -0
  162. package/dist/bscPlugin/symbols/DocumentSymbolProcessor.js.map +1 -0
  163. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.d.ts +7 -0
  164. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js +26 -0
  165. package/dist/bscPlugin/symbols/WorkspaceSymbolProcessor.js.map +1 -0
  166. package/dist/bscPlugin/symbols/symbolUtils.d.ts +5 -0
  167. package/dist/bscPlugin/symbols/symbolUtils.js +141 -0
  168. package/dist/bscPlugin/symbols/symbolUtils.js.map +1 -0
  169. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.d.ts +34 -0
  170. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js +504 -0
  171. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +1 -0
  172. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.d.ts +12 -0
  173. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js +99 -0
  174. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js.map +1 -0
  175. package/dist/bscPlugin/validation/BrsFileAfterValidator.d.ts +7 -0
  176. package/dist/bscPlugin/validation/BrsFileAfterValidator.js +18 -0
  177. package/dist/bscPlugin/validation/BrsFileAfterValidator.js.map +1 -0
  178. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +51 -0
  179. package/dist/bscPlugin/validation/BrsFileValidator.js +714 -0
  180. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -0
  181. package/dist/bscPlugin/validation/ProgramValidator.d.ts +11 -0
  182. package/dist/bscPlugin/validation/ProgramValidator.js +33 -0
  183. package/dist/bscPlugin/validation/ProgramValidator.js.map +1 -0
  184. package/dist/bscPlugin/validation/ScopeValidator.d.ts +158 -0
  185. package/dist/bscPlugin/validation/ScopeValidator.js +1481 -0
  186. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -0
  187. package/dist/bscPlugin/validation/XmlFileValidator.d.ts +8 -0
  188. package/dist/bscPlugin/validation/XmlFileValidator.js +50 -0
  189. package/dist/bscPlugin/validation/XmlFileValidator.js.map +1 -0
  190. package/dist/cli.js +140 -28
  191. package/dist/cli.js.map +1 -1
  192. package/dist/common/Sequencer.d.ts +53 -0
  193. package/dist/common/Sequencer.js +233 -0
  194. package/dist/common/Sequencer.js.map +1 -0
  195. package/dist/deferred.d.ts +5 -3
  196. package/dist/deferred.js +10 -0
  197. package/dist/deferred.js.map +1 -1
  198. package/dist/diagnosticUtils.d.ts +61 -4
  199. package/dist/diagnosticUtils.js +285 -25
  200. package/dist/diagnosticUtils.js.map +1 -1
  201. package/dist/examples/plugins/removePrint.d.ts +2 -2
  202. package/dist/examples/plugins/removePrint.js +8 -12
  203. package/dist/examples/plugins/removePrint.js.map +1 -1
  204. package/dist/files/AssetFile.d.ts +24 -0
  205. package/dist/files/AssetFile.js +25 -0
  206. package/dist/files/AssetFile.js.map +1 -0
  207. package/dist/files/BrsFile.d.ts +161 -87
  208. package/dist/files/BrsFile.js +919 -936
  209. package/dist/files/BrsFile.js.map +1 -1
  210. package/dist/files/BscFile.d.ts +102 -0
  211. package/dist/files/BscFile.js +15 -0
  212. package/dist/files/BscFile.js.map +1 -0
  213. package/dist/files/Factory.d.ts +25 -0
  214. package/dist/files/Factory.js +22 -0
  215. package/dist/files/Factory.js.map +1 -0
  216. package/dist/files/LazyFileData.d.ts +21 -0
  217. package/dist/files/LazyFileData.js +54 -0
  218. package/dist/files/LazyFileData.js.map +1 -0
  219. package/dist/files/XmlFile.d.ts +80 -41
  220. package/dist/files/XmlFile.js +162 -137
  221. package/dist/files/XmlFile.js.map +1 -1
  222. package/dist/globalCallables.d.ts +3 -1
  223. package/dist/globalCallables.js +424 -184
  224. package/dist/globalCallables.js.map +1 -1
  225. package/dist/index.d.ts +32 -4
  226. package/dist/index.js +54 -7
  227. package/dist/index.js.map +1 -1
  228. package/dist/interfaces.d.ts +987 -125
  229. package/dist/interfaces.js +21 -0
  230. package/dist/interfaces.js.map +1 -1
  231. package/dist/lexer/Lexer.d.ts +51 -12
  232. package/dist/lexer/Lexer.js +214 -65
  233. package/dist/lexer/Lexer.js.map +1 -1
  234. package/dist/lexer/Token.d.ts +27 -11
  235. package/dist/lexer/Token.js +10 -2
  236. package/dist/lexer/Token.js.map +1 -1
  237. package/dist/lexer/TokenKind.d.ts +48 -2
  238. package/dist/lexer/TokenKind.js +167 -10
  239. package/dist/lexer/TokenKind.js.map +1 -1
  240. package/dist/logging.d.ts +14 -0
  241. package/dist/logging.js +29 -0
  242. package/dist/logging.js.map +1 -0
  243. package/dist/lsp/ActionQueue.d.ts +35 -0
  244. package/dist/lsp/ActionQueue.js +115 -0
  245. package/dist/lsp/ActionQueue.js.map +1 -0
  246. package/dist/lsp/DocumentManager.d.ts +63 -0
  247. package/dist/lsp/DocumentManager.js +122 -0
  248. package/dist/lsp/DocumentManager.js.map +1 -0
  249. package/dist/lsp/LspProject.d.ts +287 -0
  250. package/dist/lsp/LspProject.js +3 -0
  251. package/dist/lsp/LspProject.js.map +1 -0
  252. package/dist/lsp/PathFilterer.d.ts +75 -0
  253. package/dist/lsp/PathFilterer.js +196 -0
  254. package/dist/lsp/PathFilterer.js.map +1 -0
  255. package/dist/lsp/Project.d.ts +200 -0
  256. package/dist/lsp/Project.js +562 -0
  257. package/dist/lsp/Project.js.map +1 -0
  258. package/dist/lsp/ProjectManager.d.ts +288 -0
  259. package/dist/lsp/ProjectManager.js +967 -0
  260. package/dist/lsp/ProjectManager.js.map +1 -0
  261. package/dist/lsp/ReaderWriterManager.d.ts +21 -0
  262. package/dist/lsp/ReaderWriterManager.js +60 -0
  263. package/dist/lsp/ReaderWriterManager.js.map +1 -0
  264. package/dist/lsp/worker/MessageHandler.d.ts +99 -0
  265. package/dist/lsp/worker/MessageHandler.js +138 -0
  266. package/dist/lsp/worker/MessageHandler.js.map +1 -0
  267. package/dist/lsp/worker/WorkerPool.d.ts +38 -0
  268. package/dist/lsp/worker/WorkerPool.js +78 -0
  269. package/dist/lsp/worker/WorkerPool.js.map +1 -0
  270. package/dist/lsp/worker/WorkerThreadProject.d.ts +168 -0
  271. package/dist/lsp/worker/WorkerThreadProject.js +205 -0
  272. package/dist/lsp/worker/WorkerThreadProject.js.map +1 -0
  273. package/dist/lsp/worker/WorkerThreadProjectRunner.d.ts +15 -0
  274. package/dist/lsp/worker/WorkerThreadProjectRunner.js +58 -0
  275. package/dist/lsp/worker/WorkerThreadProjectRunner.js.map +1 -0
  276. package/dist/lsp/worker/run.js +14 -0
  277. package/dist/lsp/worker/run.js.map +1 -0
  278. package/dist/parser/AstNode.d.ts +205 -0
  279. package/dist/parser/AstNode.js +305 -0
  280. package/dist/parser/AstNode.js.map +1 -0
  281. package/dist/parser/BrightScriptDocParser.d.ts +56 -0
  282. package/dist/parser/BrightScriptDocParser.js +294 -0
  283. package/dist/parser/BrightScriptDocParser.js.map +1 -0
  284. package/dist/parser/BrsTranspileState.d.ts +22 -3
  285. package/dist/parser/BrsTranspileState.js +19 -0
  286. package/dist/parser/BrsTranspileState.js.map +1 -1
  287. package/dist/parser/Expression.d.ts +601 -220
  288. package/dist/parser/Expression.js +1516 -502
  289. package/dist/parser/Expression.js.map +1 -1
  290. package/dist/parser/Parser.d.ts +137 -121
  291. package/dist/parser/Parser.js +1808 -982
  292. package/dist/parser/Parser.js.map +1 -1
  293. package/dist/parser/SGParser.d.ts +30 -13
  294. package/dist/parser/SGParser.js +94 -56
  295. package/dist/parser/SGParser.js.map +1 -1
  296. package/dist/parser/SGTypes.d.ts +134 -46
  297. package/dist/parser/SGTypes.js +206 -115
  298. package/dist/parser/SGTypes.js.map +1 -1
  299. package/dist/parser/Statement.d.ts +854 -267
  300. package/dist/parser/Statement.js +2416 -621
  301. package/dist/parser/Statement.js.map +1 -1
  302. package/dist/parser/TranspileState.d.ts +30 -14
  303. package/dist/parser/TranspileState.js +124 -27
  304. package/dist/parser/TranspileState.js.map +1 -1
  305. package/dist/preprocessor/Manifest.d.ts +6 -6
  306. package/dist/preprocessor/Manifest.js +17 -38
  307. package/dist/preprocessor/Manifest.js.map +1 -1
  308. package/dist/roku-types/data.json +20554 -0
  309. package/dist/roku-types/index.d.ts +5726 -0
  310. package/dist/roku-types/index.js +11 -0
  311. package/dist/roku-types/index.js.map +1 -0
  312. package/dist/types/ArrayType.d.ts +12 -5
  313. package/dist/types/ArrayType.js +95 -25
  314. package/dist/types/ArrayType.js.map +1 -1
  315. package/dist/types/AssociativeArrayType.d.ts +15 -0
  316. package/dist/types/AssociativeArrayType.js +64 -0
  317. package/dist/types/AssociativeArrayType.js.map +1 -0
  318. package/dist/types/BaseFunctionType.d.ts +10 -0
  319. package/dist/types/BaseFunctionType.js +26 -0
  320. package/dist/types/BaseFunctionType.js.map +1 -0
  321. package/dist/types/BooleanType.d.ts +9 -5
  322. package/dist/types/BooleanType.js +19 -8
  323. package/dist/types/BooleanType.js.map +1 -1
  324. package/dist/types/BscType.d.ts +41 -3
  325. package/dist/types/BscType.js +152 -0
  326. package/dist/types/BscType.js.map +1 -1
  327. package/dist/types/BscTypeKind.d.ts +28 -0
  328. package/dist/types/BscTypeKind.js +33 -0
  329. package/dist/types/BscTypeKind.js.map +1 -0
  330. package/dist/types/BuiltInInterfaceAdder.d.ts +28 -0
  331. package/dist/types/BuiltInInterfaceAdder.js +212 -0
  332. package/dist/types/BuiltInInterfaceAdder.js.map +1 -0
  333. package/dist/types/CallFuncableType.d.ts +24 -0
  334. package/dist/types/CallFuncableType.js +91 -0
  335. package/dist/types/CallFuncableType.js.map +1 -0
  336. package/dist/types/ClassType.d.ts +17 -0
  337. package/dist/types/ClassType.js +63 -0
  338. package/dist/types/ClassType.js.map +1 -0
  339. package/dist/types/ComponentType.d.ts +22 -0
  340. package/dist/types/ComponentType.js +110 -0
  341. package/dist/types/ComponentType.js.map +1 -0
  342. package/dist/types/DoubleType.d.ts +10 -5
  343. package/dist/types/DoubleType.js +21 -17
  344. package/dist/types/DoubleType.js.map +1 -1
  345. package/dist/types/DynamicType.d.ts +13 -5
  346. package/dist/types/DynamicType.js +26 -5
  347. package/dist/types/DynamicType.js.map +1 -1
  348. package/dist/types/EnumType.d.ts +42 -0
  349. package/dist/types/EnumType.js +101 -0
  350. package/dist/types/EnumType.js.map +1 -0
  351. package/dist/types/FloatType.d.ts +10 -5
  352. package/dist/types/FloatType.js +21 -17
  353. package/dist/types/FloatType.js.map +1 -1
  354. package/dist/types/FunctionType.d.ts +8 -22
  355. package/dist/types/FunctionType.js +25 -63
  356. package/dist/types/FunctionType.js.map +1 -1
  357. package/dist/types/InheritableType.d.ts +29 -0
  358. package/dist/types/InheritableType.js +173 -0
  359. package/dist/types/InheritableType.js.map +1 -0
  360. package/dist/types/InlineInterfaceType.d.ts +5 -0
  361. package/dist/types/InlineInterfaceType.js +17 -0
  362. package/dist/types/InlineInterfaceType.js.map +1 -0
  363. package/dist/types/IntegerType.d.ts +10 -5
  364. package/dist/types/IntegerType.js +21 -17
  365. package/dist/types/IntegerType.js.map +1 -1
  366. package/dist/types/InterfaceType.d.ts +14 -6
  367. package/dist/types/InterfaceType.js +30 -15
  368. package/dist/types/InterfaceType.js.map +1 -1
  369. package/dist/types/IntersectionType.d.ts +29 -0
  370. package/dist/types/IntersectionType.js +256 -0
  371. package/dist/types/IntersectionType.js.map +1 -0
  372. package/dist/types/InvalidType.d.ts +10 -5
  373. package/dist/types/InvalidType.js +21 -9
  374. package/dist/types/InvalidType.js.map +1 -1
  375. package/dist/types/LongIntegerType.d.ts +10 -5
  376. package/dist/types/LongIntegerType.js +21 -17
  377. package/dist/types/LongIntegerType.js.map +1 -1
  378. package/dist/types/NamespaceType.d.ts +12 -0
  379. package/dist/types/NamespaceType.js +28 -0
  380. package/dist/types/NamespaceType.js.map +1 -0
  381. package/dist/types/ObjectType.d.ts +12 -5
  382. package/dist/types/ObjectType.js +25 -8
  383. package/dist/types/ObjectType.js.map +1 -1
  384. package/dist/types/ReferenceType.d.ts +123 -0
  385. package/dist/types/ReferenceType.js +726 -0
  386. package/dist/types/ReferenceType.js.map +1 -0
  387. package/dist/types/StringType.d.ts +12 -5
  388. package/dist/types/StringType.js +23 -8
  389. package/dist/types/StringType.js.map +1 -1
  390. package/dist/types/TypeStatementType.d.ts +19 -0
  391. package/dist/types/TypeStatementType.js +56 -0
  392. package/dist/types/TypeStatementType.js.map +1 -0
  393. package/dist/types/TypedFunctionType.d.ts +34 -0
  394. package/dist/types/TypedFunctionType.js +157 -0
  395. package/dist/types/TypedFunctionType.js.map +1 -0
  396. package/dist/types/UninitializedType.d.ts +11 -6
  397. package/dist/types/UninitializedType.js +20 -11
  398. package/dist/types/UninitializedType.js.map +1 -1
  399. package/dist/types/UnionType.d.ts +27 -0
  400. package/dist/types/UnionType.js +196 -0
  401. package/dist/types/UnionType.js.map +1 -0
  402. package/dist/types/VoidType.d.ts +11 -5
  403. package/dist/types/VoidType.js +22 -8
  404. package/dist/types/VoidType.js.map +1 -1
  405. package/dist/types/helpers.d.ts +51 -0
  406. package/dist/types/helpers.js +329 -0
  407. package/dist/types/helpers.js.map +1 -0
  408. package/dist/types/index.d.ts +22 -0
  409. package/dist/types/index.js +39 -0
  410. package/dist/types/index.js.map +1 -0
  411. package/dist/types/roFunctionType.d.ts +11 -0
  412. package/dist/types/roFunctionType.js +37 -0
  413. package/dist/types/roFunctionType.js.map +1 -0
  414. package/dist/util.d.ts +325 -185
  415. package/dist/util.js +2135 -568
  416. package/dist/util.js.map +1 -1
  417. package/dist/validators/ClassValidator.d.ts +9 -15
  418. package/dist/validators/ClassValidator.js +93 -138
  419. package/dist/validators/ClassValidator.js.map +1 -1
  420. package/package.json +183 -138
  421. package/CHANGELOG.md +0 -1188
  422. package/dist/astUtils/creators.spec.js +0 -21
  423. package/dist/astUtils/creators.spec.js.map +0 -1
  424. package/dist/astUtils/index.d.ts +0 -7
  425. package/dist/astUtils/index.js +0 -26
  426. package/dist/astUtils/index.js.map +0 -1
  427. package/dist/astUtils/reflection.spec.d.ts +0 -1
  428. package/dist/astUtils/reflection.spec.js +0 -292
  429. package/dist/astUtils/reflection.spec.js.map +0 -1
  430. package/dist/astUtils/stackedVisitor.spec.d.ts +0 -1
  431. package/dist/astUtils/stackedVisitor.spec.js +0 -79
  432. package/dist/astUtils/stackedVisitor.spec.js.map +0 -1
  433. package/dist/astUtils/visitors.spec.d.ts +0 -1
  434. package/dist/astUtils/visitors.spec.js +0 -854
  435. package/dist/astUtils/visitors.spec.js.map +0 -1
  436. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.d.ts +0 -1
  437. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +0 -194
  438. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +0 -1
  439. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.d.ts +0 -7
  440. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js +0 -63
  441. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js.map +0 -1
  442. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.d.ts +0 -1
  443. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js +0 -45
  444. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +0 -1
  445. package/dist/files/BrsFile.Class.spec.d.ts +0 -1
  446. package/dist/files/BrsFile.Class.spec.js +0 -1081
  447. package/dist/files/BrsFile.Class.spec.js.map +0 -1
  448. package/dist/files/BrsFile.spec.d.ts +0 -1
  449. package/dist/files/BrsFile.spec.js +0 -2524
  450. package/dist/files/BrsFile.spec.js.map +0 -1
  451. package/dist/files/XmlFile.spec.d.ts +0 -1
  452. package/dist/files/XmlFile.spec.js +0 -1065
  453. package/dist/files/XmlFile.spec.js.map +0 -1
  454. package/dist/files/tests/imports.spec.d.ts +0 -1
  455. package/dist/files/tests/imports.spec.js +0 -241
  456. package/dist/files/tests/imports.spec.js.map +0 -1
  457. package/dist/lexer/Character.spec.d.ts +0 -1
  458. package/dist/lexer/Character.spec.js +0 -27
  459. package/dist/lexer/Character.spec.js.map +0 -1
  460. package/dist/lexer/Lexer.spec.d.ts +0 -1
  461. package/dist/lexer/Lexer.spec.js +0 -1101
  462. package/dist/lexer/Lexer.spec.js.map +0 -1
  463. package/dist/lexer/index.d.ts +0 -3
  464. package/dist/lexer/index.js +0 -17
  465. package/dist/lexer/index.js.map +0 -1
  466. package/dist/parser/Parser.Class.spec.d.ts +0 -1
  467. package/dist/parser/Parser.Class.spec.js +0 -390
  468. package/dist/parser/Parser.Class.spec.js.map +0 -1
  469. package/dist/parser/Parser.spec.d.ts +0 -4
  470. package/dist/parser/Parser.spec.js +0 -1216
  471. package/dist/parser/Parser.spec.js.map +0 -1
  472. package/dist/parser/SGParser.spec.d.ts +0 -1
  473. package/dist/parser/SGParser.spec.js +0 -145
  474. package/dist/parser/SGParser.spec.js.map +0 -1
  475. package/dist/parser/SGTypes.spec.d.ts +0 -1
  476. package/dist/parser/SGTypes.spec.js +0 -351
  477. package/dist/parser/SGTypes.spec.js.map +0 -1
  478. package/dist/parser/Statement.spec.d.ts +0 -1
  479. package/dist/parser/Statement.spec.js +0 -94
  480. package/dist/parser/Statement.spec.js.map +0 -1
  481. package/dist/parser/index.d.ts +0 -3
  482. package/dist/parser/index.js +0 -16
  483. package/dist/parser/index.js.map +0 -1
  484. package/dist/parser/tests/Parser.spec.d.ts +0 -18
  485. package/dist/parser/tests/Parser.spec.js +0 -35
  486. package/dist/parser/tests/Parser.spec.js.map +0 -1
  487. package/dist/parser/tests/controlFlow/For.spec.d.ts +0 -1
  488. package/dist/parser/tests/controlFlow/For.spec.js +0 -161
  489. package/dist/parser/tests/controlFlow/For.spec.js.map +0 -1
  490. package/dist/parser/tests/controlFlow/ForEach.spec.d.ts +0 -1
  491. package/dist/parser/tests/controlFlow/ForEach.spec.js +0 -106
  492. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +0 -1
  493. package/dist/parser/tests/controlFlow/If.spec.d.ts +0 -1
  494. package/dist/parser/tests/controlFlow/If.spec.js +0 -551
  495. package/dist/parser/tests/controlFlow/If.spec.js.map +0 -1
  496. package/dist/parser/tests/controlFlow/While.spec.d.ts +0 -1
  497. package/dist/parser/tests/controlFlow/While.spec.js +0 -107
  498. package/dist/parser/tests/controlFlow/While.spec.js.map +0 -1
  499. package/dist/parser/tests/expression/Additive.spec.d.ts +0 -1
  500. package/dist/parser/tests/expression/Additive.spec.js +0 -99
  501. package/dist/parser/tests/expression/Additive.spec.js.map +0 -1
  502. package/dist/parser/tests/expression/ArrayLiterals.spec.d.ts +0 -1
  503. package/dist/parser/tests/expression/ArrayLiterals.spec.js +0 -254
  504. package/dist/parser/tests/expression/ArrayLiterals.spec.js.map +0 -1
  505. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.d.ts +0 -1
  506. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +0 -266
  507. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +0 -1
  508. package/dist/parser/tests/expression/Boolean.spec.d.ts +0 -1
  509. package/dist/parser/tests/expression/Boolean.spec.js +0 -83
  510. package/dist/parser/tests/expression/Boolean.spec.js.map +0 -1
  511. package/dist/parser/tests/expression/Call.spec.d.ts +0 -1
  512. package/dist/parser/tests/expression/Call.spec.js +0 -134
  513. package/dist/parser/tests/expression/Call.spec.js.map +0 -1
  514. package/dist/parser/tests/expression/Exponential.spec.d.ts +0 -1
  515. package/dist/parser/tests/expression/Exponential.spec.js +0 -37
  516. package/dist/parser/tests/expression/Exponential.spec.js.map +0 -1
  517. package/dist/parser/tests/expression/Function.spec.d.ts +0 -1
  518. package/dist/parser/tests/expression/Function.spec.js +0 -403
  519. package/dist/parser/tests/expression/Function.spec.js.map +0 -1
  520. package/dist/parser/tests/expression/Indexing.spec.d.ts +0 -1
  521. package/dist/parser/tests/expression/Indexing.spec.js +0 -219
  522. package/dist/parser/tests/expression/Indexing.spec.js.map +0 -1
  523. package/dist/parser/tests/expression/Multiplicative.spec.d.ts +0 -1
  524. package/dist/parser/tests/expression/Multiplicative.spec.js +0 -67
  525. package/dist/parser/tests/expression/Multiplicative.spec.js.map +0 -1
  526. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.d.ts +0 -1
  527. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +0 -201
  528. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +0 -1
  529. package/dist/parser/tests/expression/PrefixUnary.spec.d.ts +0 -1
  530. package/dist/parser/tests/expression/PrefixUnary.spec.js +0 -105
  531. package/dist/parser/tests/expression/PrefixUnary.spec.js.map +0 -1
  532. package/dist/parser/tests/expression/Primary.spec.d.ts +0 -1
  533. package/dist/parser/tests/expression/Primary.spec.js +0 -149
  534. package/dist/parser/tests/expression/Primary.spec.js.map +0 -1
  535. package/dist/parser/tests/expression/Relational.spec.d.ts +0 -1
  536. package/dist/parser/tests/expression/Relational.spec.js +0 -83
  537. package/dist/parser/tests/expression/Relational.spec.js.map +0 -1
  538. package/dist/parser/tests/expression/SourceLiteralExpression.spec.d.ts +0 -1
  539. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js +0 -201
  540. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js.map +0 -1
  541. package/dist/parser/tests/expression/TemplateStringExpression.spec.d.ts +0 -1
  542. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +0 -202
  543. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +0 -1
  544. package/dist/parser/tests/expression/TernaryExpression.spec.d.ts +0 -1
  545. package/dist/parser/tests/expression/TernaryExpression.spec.js +0 -323
  546. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +0 -1
  547. package/dist/parser/tests/statement/AssignmentOperators.spec.d.ts +0 -1
  548. package/dist/parser/tests/statement/AssignmentOperators.spec.js +0 -79
  549. package/dist/parser/tests/statement/AssignmentOperators.spec.js.map +0 -1
  550. package/dist/parser/tests/statement/Declaration.spec.d.ts +0 -1
  551. package/dist/parser/tests/statement/Declaration.spec.js +0 -108
  552. package/dist/parser/tests/statement/Declaration.spec.js.map +0 -1
  553. package/dist/parser/tests/statement/Dim.spec.d.ts +0 -1
  554. package/dist/parser/tests/statement/Dim.spec.js +0 -73
  555. package/dist/parser/tests/statement/Dim.spec.js.map +0 -1
  556. package/dist/parser/tests/statement/Function.spec.d.ts +0 -1
  557. package/dist/parser/tests/statement/Function.spec.js +0 -332
  558. package/dist/parser/tests/statement/Function.spec.js.map +0 -1
  559. package/dist/parser/tests/statement/Goto.spec.d.ts +0 -1
  560. package/dist/parser/tests/statement/Goto.spec.js +0 -50
  561. package/dist/parser/tests/statement/Goto.spec.js.map +0 -1
  562. package/dist/parser/tests/statement/Increment.spec.d.ts +0 -1
  563. package/dist/parser/tests/statement/Increment.spec.js +0 -117
  564. package/dist/parser/tests/statement/Increment.spec.js.map +0 -1
  565. package/dist/parser/tests/statement/LibraryStatement.spec.d.ts +0 -1
  566. package/dist/parser/tests/statement/LibraryStatement.spec.js +0 -74
  567. package/dist/parser/tests/statement/LibraryStatement.spec.js.map +0 -1
  568. package/dist/parser/tests/statement/Misc.spec.d.ts +0 -1
  569. package/dist/parser/tests/statement/Misc.spec.js +0 -333
  570. package/dist/parser/tests/statement/Misc.spec.js.map +0 -1
  571. package/dist/parser/tests/statement/PrintStatement.spec.d.ts +0 -1
  572. package/dist/parser/tests/statement/PrintStatement.spec.js +0 -181
  573. package/dist/parser/tests/statement/PrintStatement.spec.js.map +0 -1
  574. package/dist/parser/tests/statement/ReturnStatement.spec.d.ts +0 -1
  575. package/dist/parser/tests/statement/ReturnStatement.spec.js +0 -94
  576. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +0 -1
  577. package/dist/parser/tests/statement/Set.spec.d.ts +0 -1
  578. package/dist/parser/tests/statement/Set.spec.js +0 -218
  579. package/dist/parser/tests/statement/Set.spec.js.map +0 -1
  580. package/dist/parser/tests/statement/Stop.spec.d.ts +0 -1
  581. package/dist/parser/tests/statement/Stop.spec.js +0 -37
  582. package/dist/parser/tests/statement/Stop.spec.js.map +0 -1
  583. package/dist/parser/tests/statement/Throw.spec.d.ts +0 -1
  584. package/dist/parser/tests/statement/Throw.spec.js +0 -35
  585. package/dist/parser/tests/statement/Throw.spec.js.map +0 -1
  586. package/dist/parser/tests/statement/TryCatch.spec.d.ts +0 -1
  587. package/dist/parser/tests/statement/TryCatch.spec.js +0 -140
  588. package/dist/parser/tests/statement/TryCatch.spec.js.map +0 -1
  589. package/dist/preprocessor/Chunk.d.ts +0 -82
  590. package/dist/preprocessor/Chunk.js +0 -77
  591. package/dist/preprocessor/Chunk.js.map +0 -1
  592. package/dist/preprocessor/Manifest.spec.d.ts +0 -0
  593. package/dist/preprocessor/Manifest.spec.js +0 -105
  594. package/dist/preprocessor/Manifest.spec.js.map +0 -1
  595. package/dist/preprocessor/Preprocessor.d.ts +0 -60
  596. package/dist/preprocessor/Preprocessor.js +0 -156
  597. package/dist/preprocessor/Preprocessor.js.map +0 -1
  598. package/dist/preprocessor/Preprocessor.spec.d.ts +0 -1
  599. package/dist/preprocessor/Preprocessor.spec.js +0 -152
  600. package/dist/preprocessor/Preprocessor.spec.js.map +0 -1
  601. package/dist/preprocessor/PreprocessorParser.d.ts +0 -61
  602. package/dist/preprocessor/PreprocessorParser.js +0 -194
  603. package/dist/preprocessor/PreprocessorParser.js.map +0 -1
  604. package/dist/preprocessor/PreprocessorParser.spec.d.ts +0 -1
  605. package/dist/preprocessor/PreprocessorParser.spec.js +0 -116
  606. package/dist/preprocessor/PreprocessorParser.spec.js.map +0 -1
  607. package/dist/preprocessor/index.d.ts +0 -3
  608. package/dist/preprocessor/index.js +0 -16
  609. package/dist/preprocessor/index.js.map +0 -1
  610. package/dist/types/ArrayType.spec.d.ts +0 -1
  611. package/dist/types/ArrayType.spec.js +0 -30
  612. package/dist/types/ArrayType.spec.js.map +0 -1
  613. package/dist/types/BooleanType.spec.d.ts +0 -1
  614. package/dist/types/BooleanType.spec.js +0 -12
  615. package/dist/types/BooleanType.spec.js.map +0 -1
  616. package/dist/types/CustomType.d.ts +0 -10
  617. package/dist/types/CustomType.js +0 -35
  618. package/dist/types/CustomType.js.map +0 -1
  619. package/dist/types/DoubleType.spec.d.ts +0 -1
  620. package/dist/types/DoubleType.spec.js +0 -12
  621. package/dist/types/DoubleType.spec.js.map +0 -1
  622. package/dist/types/DynamicType.spec.d.ts +0 -1
  623. package/dist/types/DynamicType.spec.js +0 -12
  624. package/dist/types/DynamicType.spec.js.map +0 -1
  625. package/dist/types/FloatType.spec.d.ts +0 -1
  626. package/dist/types/FloatType.spec.js +0 -12
  627. package/dist/types/FloatType.spec.js.map +0 -1
  628. package/dist/types/FunctionType.spec.d.ts +0 -1
  629. package/dist/types/FunctionType.spec.js +0 -29
  630. package/dist/types/FunctionType.spec.js.map +0 -1
  631. package/dist/types/IntegerType.spec.d.ts +0 -1
  632. package/dist/types/IntegerType.spec.js +0 -12
  633. package/dist/types/IntegerType.spec.js.map +0 -1
  634. package/dist/types/InvalidType.spec.d.ts +0 -1
  635. package/dist/types/InvalidType.spec.js +0 -12
  636. package/dist/types/InvalidType.spec.js.map +0 -1
  637. package/dist/types/LazyType.d.ts +0 -15
  638. package/dist/types/LazyType.js +0 -32
  639. package/dist/types/LazyType.js.map +0 -1
  640. package/dist/types/LongIntegerType.spec.d.ts +0 -1
  641. package/dist/types/LongIntegerType.spec.js +0 -12
  642. package/dist/types/LongIntegerType.spec.js.map +0 -1
  643. package/dist/types/ObjectType.spec.d.ts +0 -1
  644. package/dist/types/ObjectType.spec.js +0 -12
  645. package/dist/types/ObjectType.spec.js.map +0 -1
  646. package/dist/types/StringType.spec.d.ts +0 -1
  647. package/dist/types/StringType.spec.js +0 -12
  648. package/dist/types/StringType.spec.js.map +0 -1
  649. package/dist/types/VoidType.spec.d.ts +0 -1
  650. package/dist/types/VoidType.spec.js +0 -12
  651. package/dist/types/VoidType.spec.js.map +0 -1
  652. /package/dist/{astUtils/creators.spec.d.ts → lsp/worker/run.d.ts} +0 -0
package/dist/Program.js CHANGED
@@ -6,33 +6,63 @@ const fsExtra = require("fs-extra");
6
6
  const path = require("path");
7
7
  const vscode_languageserver_1 = require("vscode-languageserver");
8
8
  const Scope_1 = require("./Scope");
9
+ const SymbolTable_1 = require("./SymbolTable");
9
10
  const DiagnosticMessages_1 = require("./DiagnosticMessages");
10
- const BrsFile_1 = require("./files/BrsFile");
11
- const XmlFile_1 = require("./files/XmlFile");
11
+ const CodeActionUtil_1 = require("./CodeActionUtil");
12
12
  const util_1 = require("./util");
13
13
  const XmlScope_1 = require("./XmlScope");
14
- const DiagnosticFilterer_1 = require("./DiagnosticFilterer");
15
14
  const DependencyGraph_1 = require("./DependencyGraph");
16
- const Logger_1 = require("./Logger");
15
+ const logging_1 = require("./logging");
17
16
  const chalk_1 = require("chalk");
18
17
  const globalCallables_1 = require("./globalCallables");
19
18
  const Manifest_1 = require("./preprocessor/Manifest");
20
19
  const vscode_uri_1 = require("vscode-uri");
21
20
  const PluginInterface_1 = require("./PluginInterface");
22
21
  const reflection_1 = require("./astUtils/reflection");
23
- const parser_1 = require("./parser");
24
- const lexer_1 = require("./lexer");
25
22
  const BscPlugin_1 = require("./bscPlugin/BscPlugin");
23
+ const Editor_1 = require("./astUtils/Editor");
24
+ const IntegerType_1 = require("./types/IntegerType");
25
+ const StringType_1 = require("./types/StringType");
26
+ const BooleanType_1 = require("./types/BooleanType");
27
+ const DoubleType_1 = require("./types/DoubleType");
28
+ const DynamicType_1 = require("./types/DynamicType");
29
+ const FloatType_1 = require("./types/FloatType");
30
+ const LongIntegerType_1 = require("./types/LongIntegerType");
31
+ const ObjectType_1 = require("./types/ObjectType");
32
+ const VoidType_1 = require("./types/VoidType");
33
+ const FunctionType_1 = require("./types/FunctionType");
34
+ const Factory_1 = require("./files/Factory");
35
+ const ActionPipeline_1 = require("./ActionPipeline");
36
+ const LazyFileData_1 = require("./files/LazyFileData");
26
37
  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`;
38
+ const roku_types_1 = require("./roku-types");
39
+ const ComponentType_1 = require("./types/ComponentType");
40
+ const InterfaceType_1 = require("./types/InterfaceType");
41
+ const BuiltInInterfaceAdder_1 = require("./types/BuiltInInterfaceAdder");
42
+ const visitors_1 = require("./astUtils/visitors");
43
+ const thenby_1 = require("thenby");
44
+ const CrossScopeValidator_1 = require("./CrossScopeValidator");
45
+ const DiagnosticManager_1 = require("./DiagnosticManager");
46
+ const ProgramValidator_1 = require("./bscPlugin/validation/ProgramValidator");
47
+ const ReferenceType_1 = require("./types/ReferenceType");
48
+ const helpers_1 = require("./types/helpers");
49
+ const CallExpressionInfo_1 = require("./bscPlugin/CallExpressionInfo");
50
+ const SignatureHelpUtil_1 = require("./bscPlugin/SignatureHelpUtil");
51
+ const Sequencer_1 = require("./common/Sequencer");
52
+ const deferred_1 = require("./deferred");
53
+ const roFunctionType_1 = require("./types/roFunctionType");
54
+ const bslibNonAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/rokucommunity_bslib/bslib.brs`;
55
+ const bslibAliasedRokuModulesPkgPath = (0, util_1.standardizePath) `source/roku_modules/bslib/bslib.brs`;
29
56
  class Program {
30
57
  constructor(
31
58
  /**
32
59
  * The root directory for this program
33
60
  */
34
- options, logger, plugins) {
35
- this.options = options;
61
+ options, logger, plugins, diagnosticsManager) {
62
+ /**
63
+ * An editor that plugins can use to modify program-level things during the build flow. Don't use this to edit files (they have their own `.editor`)
64
+ */
65
+ this.editor = new Editor_1.Editor();
36
66
  /**
37
67
  * A graph of all files and their dependencies.
38
68
  * For example:
@@ -40,20 +70,37 @@ class Program {
40
70
  * lib2.brs -> [lib3.brs] //via an import statement
41
71
  */
42
72
  this.dependencyGraph = new DependencyGraph_1.DependencyGraph();
43
- this.diagnosticFilterer = new DiagnosticFilterer_1.DiagnosticFilterer();
44
73
  /**
45
- * A set of diagnostics. This does not include any of the scope diagnostics.
46
- * Should only be set from `this.validate()`
74
+ * A scope that contains all built-in global functions.
75
+ * All scopes should directly or indirectly inherit from this scope
76
+ */
77
+ this.globalScope = undefined;
78
+ this.fileSymbolInformation = new Map();
79
+ /**
80
+ * Map of typetime symbols which depend upon the key symbol
47
81
  */
48
- this.diagnostics = [];
82
+ this.symbolDependencies = new Map();
49
83
  /**
50
- * A map of every file loaded ino this program, indexed by its lower-case pkgPath
84
+ * Symbol Table for storing custom component types
85
+ * This is a sibling to the global table (as Components can be used/referenced anywhere)
86
+ * Keeping custom components out of the global table and in a specific symbol table
87
+ * compartmentalizes their use
51
88
  */
52
- this.pkgMap = {};
89
+ this.componentsTable = new SymbolTable_1.SymbolTable('Custom Components');
53
90
  /**
54
- * A map of every file loaded into this program, indexed by its lower-case srcPath
91
+ * A map of every file loaded into this program, indexed by its original file location
55
92
  */
56
93
  this.files = {};
94
+ /**
95
+ * A map of every file loaded into this program, indexed by its destPath
96
+ */
97
+ this.destMap = new Map();
98
+ /**
99
+ * Plugins can contribute multiple virtual files for a single physical file.
100
+ * This collection links the virtual files back to the physical file that produced them.
101
+ * The key is the standardized and lower-cased srcPath
102
+ */
103
+ this.fileClusters = new Map();
57
104
  this.scopes = {};
58
105
  /**
59
106
  * A map of every component currently loaded into the program, indexed by the component name.
@@ -62,28 +109,184 @@ class Program {
62
109
  * but if you do, only ever use the component at index 0.
63
110
  */
64
111
  this.components = {};
112
+ /**
113
+ * Keeps a set of all the components that need to have their types updated during the current validation cycle
114
+ * Map <componentKey, componentName>
115
+ */
116
+ this.componentSymbolsToUpdate = new Map();
117
+ this.crossScopeValidation = new CrossScopeValidator_1.CrossScopeValidator(this);
118
+ this.isFirstValidation = true;
119
+ this.validationDetails = {
120
+ brsFilesValidated: [],
121
+ xmlFilesValidated: [],
122
+ changedSymbols: new Map(),
123
+ changedComponentTypes: [],
124
+ scopesToValidate: [],
125
+ filesToBeValidatedInScopeContext: new Set()
126
+ };
127
+ this.lastValidationInfo = {
128
+ brsFilesSrcPath: new Set(),
129
+ xmlFilesSrcPath: new Set(),
130
+ scopeNames: new Set(),
131
+ componentsRebuilt: new Set()
132
+ };
133
+ /**
134
+ * Counter used to track which validation run is being logged
135
+ */
136
+ this.validationRunSequence = 1;
137
+ /**
138
+ * How many milliseconds can pass while doing synchronous operations in validate before we register a short timeout (i.e. yield to the event loop)
139
+ */
140
+ this.validationMinSyncDuration = 75;
141
+ this.getFilePathCache = new Map();
142
+ this.sortedScopeNames = undefined;
143
+ this.getTranspiledFileContentsPipeline = new ActionPipeline_1.ActionPipeline();
144
+ this.buildPipeline = new ActionPipeline_1.ActionPipeline();
65
145
  this.options = util_1.util.normalizeConfig(options);
66
- this.logger = logger || new Logger_1.Logger(options.logLevel);
67
- this.plugins = plugins || new PluginInterface_1.default([], this.logger);
146
+ this.logger = logger !== null && logger !== void 0 ? logger : (0, logging_1.createLogger)(options);
147
+ this.plugins = plugins || new PluginInterface_1.default([], { logger: this.logger });
148
+ this.diagnostics = diagnosticsManager || new DiagnosticManager_1.DiagnosticManager();
149
+ //try to find a location for the diagnostic if it doesn't have one
150
+ this.diagnostics.locationResolver = (args) => {
151
+ //find the first xml scope for this diagnostic
152
+ for (let context of args.contexts) {
153
+ if ((0, reflection_1.isXmlScope)(context.scope) && (0, reflection_1.isXmlFile)(context.scope.xmlFile)) {
154
+ return util_1.util.createLocation(0, 0, 0, 100, context.scope.xmlFile.srcPath);
155
+ }
156
+ }
157
+ //we couldn't find an xml scope for this, so try to find the manifest file instead
158
+ const manifest = this.getFile('manifest', false);
159
+ if (manifest) {
160
+ return util_1.util.createLocation(0, 0, 0, 100, manifest.srcPath);
161
+ }
162
+ //if we still don't have a manifest, try to find the first file in the program
163
+ for (const key in this.files) {
164
+ if ((0, reflection_1.isBrsFile)(this.files[key]) || (0, reflection_1.isXmlFile)(this.files[key])) {
165
+ return util_1.util.createLocation(0, 0, 0, 100, this.files[key].srcPath);
166
+ }
167
+ }
168
+ this.logger.warn(`Unable to find a location for the diagnostic.`, args);
169
+ //we couldn't find any locations for the file, so just return undefined
170
+ return undefined;
171
+ };
172
+ // initialize the diagnostics Manager
173
+ this.diagnostics.logger = this.logger;
174
+ this.diagnostics.options = this.options;
175
+ this.diagnostics.program = this;
68
176
  //inject the bsc plugin as the first plugin in the stack.
69
177
  this.plugins.addFirst(new BscPlugin_1.BscPlugin());
70
178
  //normalize the root dir path
71
179
  this.options.rootDir = util_1.util.getRootDir(this.options);
72
180
  this.createGlobalScope();
181
+ this.fileFactory = new Factory_1.FileFactory(this);
73
182
  }
74
183
  createGlobalScope() {
75
184
  //create the 'global' scope
76
185
  this.globalScope = new Scope_1.Scope('global', this, 'scope:global');
77
186
  this.globalScope.attachDependencyGraph(this.dependencyGraph);
78
187
  this.scopes.global = this.globalScope;
188
+ this.populateGlobalSymbolTable();
189
+ this.globalScope.symbolTable.addSibling(this.componentsTable);
79
190
  //hardcode the files list for global scope to only contain the global file
80
191
  this.globalScope.getAllFiles = () => [globalCallables_1.globalFile];
192
+ globalCallables_1.globalFile.isValidated = true;
81
193
  this.globalScope.validate();
82
- //for now, disable validation of global scope because the global files have some duplicate method declarations
83
- this.globalScope.getDiagnostics = () => [];
84
194
  //TODO we might need to fix this because the isValidated clears stuff now
85
195
  this.globalScope.isValidated = true;
86
196
  }
197
+ recursivelyAddNodeToSymbolTable(nodeData) {
198
+ if (!nodeData) {
199
+ return;
200
+ }
201
+ let nodeType;
202
+ const nodeName = util_1.util.getSgNodeTypeName(nodeData.name);
203
+ if (!this.globalScope.symbolTable.hasSymbol(nodeName, 2 /* SymbolTypeFlag.typetime */)) {
204
+ let parentNode;
205
+ if (nodeData.extends) {
206
+ const parentNodeData = roku_types_1.nodes[nodeData.extends.name.toLowerCase()];
207
+ try {
208
+ parentNode = this.recursivelyAddNodeToSymbolTable(parentNodeData);
209
+ }
210
+ catch (error) {
211
+ this.logger.error(error, nodeData);
212
+ }
213
+ }
214
+ nodeType = new ComponentType_1.ComponentType(nodeData.name, parentNode);
215
+ nodeType.addBuiltInInterfaces();
216
+ nodeType.isBuiltIn = true;
217
+ if (nodeData.name === 'Node') {
218
+ // Add `roSGNode` as shorthand for `roSGNodeNode`
219
+ this.globalScope.symbolTable.addSymbol('roSGNode', { description: nodeData.description, isBuiltIn: true }, nodeType, 2 /* SymbolTypeFlag.typetime */);
220
+ }
221
+ this.globalScope.symbolTable.addSymbol(nodeName, { description: nodeData.description, isBuiltIn: true }, nodeType, 2 /* SymbolTypeFlag.typetime */);
222
+ }
223
+ else {
224
+ nodeType = this.globalScope.symbolTable.getSymbolType(nodeName, { flags: 2 /* SymbolTypeFlag.typetime */ });
225
+ }
226
+ return nodeType;
227
+ }
228
+ /**
229
+ * Do all setup required for the global symbol table.
230
+ */
231
+ populateGlobalSymbolTable() {
232
+ //Setup primitive types in global symbolTable
233
+ const builtInSymbolData = { isBuiltIn: true };
234
+ this.globalScope.symbolTable.addSymbol('boolean', builtInSymbolData, BooleanType_1.BooleanType.instance, 2 /* SymbolTypeFlag.typetime */);
235
+ this.globalScope.symbolTable.addSymbol('double', builtInSymbolData, DoubleType_1.DoubleType.instance, 2 /* SymbolTypeFlag.typetime */);
236
+ this.globalScope.symbolTable.addSymbol('dynamic', builtInSymbolData, DynamicType_1.DynamicType.instance, 2 /* SymbolTypeFlag.typetime */);
237
+ this.globalScope.symbolTable.addSymbol('float', builtInSymbolData, FloatType_1.FloatType.instance, 2 /* SymbolTypeFlag.typetime */);
238
+ this.globalScope.symbolTable.addSymbol('function', builtInSymbolData, FunctionType_1.FunctionType.instance, 2 /* SymbolTypeFlag.typetime */);
239
+ this.globalScope.symbolTable.addSymbol('integer', builtInSymbolData, IntegerType_1.IntegerType.instance, 2 /* SymbolTypeFlag.typetime */);
240
+ this.globalScope.symbolTable.addSymbol('longinteger', builtInSymbolData, LongIntegerType_1.LongIntegerType.instance, 2 /* SymbolTypeFlag.typetime */);
241
+ this.globalScope.symbolTable.addSymbol('object', builtInSymbolData, ObjectType_1.ObjectType.instance, 2 /* SymbolTypeFlag.typetime */);
242
+ this.globalScope.symbolTable.addSymbol('string', builtInSymbolData, StringType_1.StringType.instance, 2 /* SymbolTypeFlag.typetime */);
243
+ this.globalScope.symbolTable.addSymbol('void', builtInSymbolData, VoidType_1.VoidType.instance, 2 /* SymbolTypeFlag.typetime */);
244
+ BuiltInInterfaceAdder_1.BuiltInInterfaceAdder.getLookupTable = () => this.globalScope.symbolTable;
245
+ for (const callable of globalCallables_1.globalCallables) {
246
+ this.globalScope.symbolTable.addSymbol(callable.name, Object.assign(Object.assign({}, builtInSymbolData), { description: callable.shortDescription }), callable.type, 1 /* SymbolTypeFlag.runtime */);
247
+ }
248
+ for (const ifaceData of Object.values(roku_types_1.interfaces)) {
249
+ const ifaceType = new InterfaceType_1.InterfaceType(ifaceData.name);
250
+ ifaceType.addBuiltInInterfaces();
251
+ ifaceType.isBuiltIn = true;
252
+ this.globalScope.symbolTable.addSymbol(ifaceData.name, Object.assign(Object.assign({}, builtInSymbolData), { description: ifaceData.description }), ifaceType, 2 /* SymbolTypeFlag.typetime */);
253
+ }
254
+ for (const componentData of Object.values(roku_types_1.components)) {
255
+ let roComponentType;
256
+ const lowerComponentName = componentData.name.toLowerCase();
257
+ if (lowerComponentName === 'rosgnode') {
258
+ // we will add `roSGNode` as shorthand for `roSGNodeNode`, since all roSgNode components are SceneGraph nodes
259
+ continue;
260
+ }
261
+ if (lowerComponentName === 'rofunction') {
262
+ roComponentType = new roFunctionType_1.roFunctionType();
263
+ }
264
+ else {
265
+ roComponentType = new InterfaceType_1.InterfaceType(componentData.name);
266
+ }
267
+ roComponentType.addBuiltInInterfaces();
268
+ roComponentType.isBuiltIn = true;
269
+ this.globalScope.symbolTable.addSymbol(componentData.name, Object.assign(Object.assign({}, builtInSymbolData), { description: componentData.description }), roComponentType, 2 /* SymbolTypeFlag.typetime */);
270
+ }
271
+ for (const nodeData of Object.values(roku_types_1.nodes)) {
272
+ this.recursivelyAddNodeToSymbolTable(nodeData);
273
+ }
274
+ for (const eventData of Object.values(roku_types_1.events)) {
275
+ const eventType = new InterfaceType_1.InterfaceType(eventData.name);
276
+ eventType.addBuiltInInterfaces();
277
+ eventType.isBuiltIn = true;
278
+ this.globalScope.symbolTable.addSymbol(eventData.name, Object.assign(Object.assign({}, builtInSymbolData), { description: eventData.description }), eventType, 2 /* SymbolTypeFlag.typetime */);
279
+ }
280
+ }
281
+ addFileSymbolInfo(file) {
282
+ this.fileSymbolInformation.set(file.pkgPath, {
283
+ provides: file.providedSymbols,
284
+ requires: file.requiredSymbols
285
+ });
286
+ }
287
+ getFileSymbolInfo(file) {
288
+ return this.fileSymbolInformation.get(file.pkgPath);
289
+ }
87
290
  /**
88
291
  * The path to bslib.brs (the BrightScript runtime for certain BrighterScript features)
89
292
  */
@@ -98,7 +301,7 @@ class Program {
98
301
  //default to the embedded version
99
302
  }
100
303
  else {
101
- return `pkg:/source/bslib.brs`;
304
+ return `${this.options.bslibDestinationDir}${path.sep}bslib.brs`;
102
305
  }
103
306
  }
104
307
  get bslibPrefix() {
@@ -110,17 +313,126 @@ class Program {
110
313
  }
111
314
  }
112
315
  /**
113
- * Get a copy of the list of files currently loaded in the program
316
+ * Look up the set of `BrsFile`s that declare any part of the given namespace name
317
+ * (lowercased). Returns `undefined` when no file contributes.
318
+ * @internal
319
+ */
320
+ getNamespaceContributors(namespaceNameLower) {
321
+ if (!this.namespaceContributors) {
322
+ this.namespaceContributors = this.buildNamespaceContributors();
323
+ }
324
+ return this.namespaceContributors.get(namespaceNameLower);
325
+ }
326
+ buildNamespaceContributors() {
327
+ const contributors = new Map();
328
+ for (const file of Object.values(this.files)) {
329
+ if ((0, reflection_1.isBrsFile)(file)) {
330
+ // eslint-disable-next-line @typescript-eslint/dot-notation
331
+ for (const nameLower of file['getNamespaceContributions']().keys()) {
332
+ let set = contributors.get(nameLower);
333
+ if (!set) {
334
+ set = new Set();
335
+ contributors.set(nameLower, set);
336
+ }
337
+ set.add(file);
338
+ }
339
+ }
340
+ }
341
+ return contributors;
342
+ }
343
+ /**
344
+ * Get or build the shared aggregate for a namespace whose in-scope contributors
345
+ * include more than one file. The aggregate's heavy fields are computed once per
346
+ * unique `(nameLower, contributing-files-set)` and reused across every scope that
347
+ * sees the same set.
348
+ * @internal
349
+ */
350
+ getAggregateNamespaceContainer(nameLower, contributions) {
351
+ if (!this.aggregateNamespaceContainerCache) {
352
+ this.aggregateNamespaceContainerCache = new Map();
353
+ }
354
+ //sorted pkgPaths ensure two scopes with the same contributor set hit the same key
355
+ const key = nameLower + '|' + contributions
356
+ .map(c => c.file.pkgPath.toLowerCase())
357
+ .sort()
358
+ .join('|');
359
+ let aggregate = this.aggregateNamespaceContainerCache.get(key);
360
+ if (!aggregate) {
361
+ aggregate = this.buildAggregateNamespaceContainer(contributions);
362
+ this.aggregateNamespaceContainerCache.set(key, aggregate);
363
+ }
364
+ return aggregate;
365
+ }
366
+ buildAggregateNamespaceContainer(contributions) {
367
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
368
+ const first = contributions[0];
369
+ //field order matches the NamespaceContainer interface declaration so aggregates
370
+ //share a single V8 hidden class with the per-scope wrapper containers
371
+ const aggregate = {
372
+ file: first.file,
373
+ fullName: first.fullName,
374
+ nameRange: first.nameRange,
375
+ lastPartName: first.lastPartName,
376
+ namespaces: new Map(),
377
+ namespaceStatements: undefined,
378
+ statements: undefined,
379
+ classStatements: undefined,
380
+ functionStatements: undefined,
381
+ enumStatements: undefined,
382
+ constStatements: undefined,
383
+ symbolTable: undefined
384
+ };
385
+ for (const contribution of contributions) {
386
+ if ((_a = contribution.namespaceStatements) === null || _a === void 0 ? void 0 : _a.length) {
387
+ ((_b = aggregate.namespaceStatements) !== null && _b !== void 0 ? _b : (aggregate.namespaceStatements = [])).push(...contribution.namespaceStatements);
388
+ }
389
+ if ((_c = contribution.statements) === null || _c === void 0 ? void 0 : _c.length) {
390
+ ((_d = aggregate.statements) !== null && _d !== void 0 ? _d : (aggregate.statements = [])).push(...contribution.statements);
391
+ }
392
+ if (contribution.classStatements) {
393
+ aggregate.classStatements = Object.assign(Object.assign({}, ((_e = aggregate.classStatements) !== null && _e !== void 0 ? _e : {})), contribution.classStatements);
394
+ }
395
+ if (contribution.functionStatements) {
396
+ aggregate.functionStatements = Object.assign(Object.assign({}, ((_f = aggregate.functionStatements) !== null && _f !== void 0 ? _f : {})), contribution.functionStatements);
397
+ }
398
+ if (contribution.enumStatements) {
399
+ (_g = aggregate.enumStatements) !== null && _g !== void 0 ? _g : (aggregate.enumStatements = new Map());
400
+ for (const [key, value] of contribution.enumStatements) {
401
+ aggregate.enumStatements.set(key, value);
402
+ }
403
+ }
404
+ if (contribution.constStatements) {
405
+ (_h = aggregate.constStatements) !== null && _h !== void 0 ? _h : (aggregate.constStatements = new Map());
406
+ for (const [key, value] of contribution.constStatements) {
407
+ aggregate.constStatements.set(key, value);
408
+ }
409
+ }
410
+ if (contribution.symbolTable) {
411
+ (_j = aggregate.symbolTable) !== null && _j !== void 0 ? _j : (aggregate.symbolTable = new SymbolTable_1.SymbolTable(`Namespace Multi-File Aggregate: '${first.fullName}'`));
412
+ aggregate.symbolTable.mergeSymbolTable(contribution.symbolTable);
413
+ }
414
+ }
415
+ return aggregate;
416
+ }
417
+ /**
418
+ * Invalidate the program-level namespace contributors map and the slow-path aggregate
419
+ * cache. Called by `setFile` and `removeFile`; downstream scope namespace lookups
420
+ * already rebuild via the dependency-graph invalidation chain, so this only needs
421
+ * to drop the cached maps.
114
422
  */
115
- getFiles() {
116
- return Object.values(this.files);
423
+ invalidateNamespaceContributorCache() {
424
+ this.namespaceContributors = undefined;
425
+ this.aggregateNamespaceContainerCache = undefined;
117
426
  }
118
427
  addScope(scope) {
119
428
  this.scopes[scope.name] = scope;
120
- this.plugins.emit('afterScopeCreate', {
121
- program: this,
122
- scope: scope
123
- });
429
+ delete this.sortedScopeNames;
430
+ }
431
+ removeScope(scope) {
432
+ if (this.scopes[scope.name]) {
433
+ delete this.scopes[scope.name];
434
+ delete this.sortedScopeNames;
435
+ }
124
436
  }
125
437
  /**
126
438
  * Get the component with the specified name
@@ -129,19 +441,34 @@ class Program {
129
441
  var _a;
130
442
  if (componentName) {
131
443
  //return the first compoment in the list with this name
132
- //(components are ordered in this list by pkgPath to ensure consistency)
444
+ //(components are ordered in this list by destPath to ensure consistency)
133
445
  return (_a = this.components[componentName.toLowerCase()]) === null || _a === void 0 ? void 0 : _a[0];
134
446
  }
135
447
  else {
136
448
  return undefined;
137
449
  }
138
450
  }
451
+ /**
452
+ * Get the sorted names of custom components
453
+ */
454
+ getSortedComponentNames() {
455
+ const componentNames = Object.keys(this.components);
456
+ componentNames.sort((a, b) => {
457
+ if (a < b) {
458
+ return -1;
459
+ }
460
+ else if (b < a) {
461
+ return 1;
462
+ }
463
+ return 0;
464
+ });
465
+ return componentNames;
466
+ }
139
467
  /**
140
468
  * Register (or replace) the reference to a component in the component map
141
469
  */
142
470
  registerComponent(xmlFile, scope) {
143
- var _a, _b;
144
- const key = ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
471
+ const key = this.getComponentKey(xmlFile);
145
472
  if (!this.components[key]) {
146
473
  this.components[key] = [];
147
474
  }
@@ -149,15 +476,25 @@ class Program {
149
476
  file: xmlFile,
150
477
  scope: scope
151
478
  });
152
- this.components[key].sort((x, y) => x.file.pkgPath.toLowerCase().localeCompare(y.file.pkgPath.toLowerCase()));
479
+ this.components[key].sort((a, b) => {
480
+ const pathA = a.file.destPath.toLowerCase();
481
+ const pathB = b.file.destPath.toLowerCase();
482
+ if (pathA < pathB) {
483
+ return -1;
484
+ }
485
+ else if (pathA > pathB) {
486
+ return 1;
487
+ }
488
+ return 0;
489
+ });
153
490
  this.syncComponentDependencyGraph(this.components[key]);
491
+ this.addDeferredComponentTypeSymbolCreation(xmlFile);
154
492
  }
155
493
  /**
156
494
  * Remove the specified component from the components map
157
495
  */
158
496
  unregisterComponent(xmlFile) {
159
- var _a, _b;
160
- const key = ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
497
+ const key = this.getComponentKey(xmlFile);
161
498
  const arr = this.components[key] || [];
162
499
  for (let i = 0; i < arr.length; i++) {
163
500
  if (arr[i].file === xmlFile) {
@@ -166,6 +503,84 @@ class Program {
166
503
  }
167
504
  }
168
505
  this.syncComponentDependencyGraph(arr);
506
+ this.addDeferredComponentTypeSymbolCreation(xmlFile);
507
+ }
508
+ /**
509
+ * Adds a component described in an XML to the set of components that needs to be updated this validation cycle.
510
+ * @param xmlFile XML file with <component> tag
511
+ */
512
+ addDeferredComponentTypeSymbolCreation(xmlFile) {
513
+ var _a;
514
+ const componentKey = this.getComponentKey(xmlFile);
515
+ const componentName = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text;
516
+ if (this.componentSymbolsToUpdate.has(componentKey)) {
517
+ return;
518
+ }
519
+ this.componentSymbolsToUpdate.set(componentKey, componentName);
520
+ }
521
+ getComponentKey(xmlFile) {
522
+ var _a, _b;
523
+ return ((_b = (_a = xmlFile.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : xmlFile.pkgPath).toLowerCase();
524
+ }
525
+ /**
526
+ * Resolves symbol table with the first component in this.components to have the same name as the component in the file
527
+ * @param componentKey key getting a component from `this.components`
528
+ * @param componentName the unprefixed name of the component that will be added (e.g. 'MyLabel' NOT 'roSgNodeMyLabel')
529
+ */
530
+ updateComponentSymbolInGlobalScope(componentKey, componentName) {
531
+ const symbolName = componentName ? util_1.util.getSgNodeTypeName(componentName) : undefined;
532
+ if (!symbolName) {
533
+ return;
534
+ }
535
+ const components = this.components[componentKey] || [];
536
+ const previousComponentType = this.componentsTable.getSymbolType(symbolName, { flags: 2 /* SymbolTypeFlag.typetime */ });
537
+ // Remove any existing symbols that match
538
+ this.componentsTable.removeSymbol(symbolName);
539
+ if (components.length > 0) {
540
+ // There is a component that can be added - use it.
541
+ const componentScope = components[0].scope;
542
+ this.componentsTable.removeSymbol(symbolName);
543
+ componentScope.linkSymbolTable();
544
+ const componentType = componentScope.getComponentType();
545
+ if (componentType) {
546
+ this.componentsTable.addSymbol(symbolName, {}, componentType, 2 /* SymbolTypeFlag.typetime */);
547
+ }
548
+ const typeData = {};
549
+ const isSameAsPrevious = previousComponentType && componentType.isEqual(previousComponentType, typeData);
550
+ const isComponentTypeDifferent = !previousComponentType || (0, reflection_1.isReferenceType)(previousComponentType) || !isSameAsPrevious;
551
+ componentScope.unlinkSymbolTable();
552
+ return isComponentTypeDifferent;
553
+ }
554
+ // There was a previous component type, but no new one, so it's different
555
+ return !!previousComponentType;
556
+ }
557
+ /**
558
+ * Adds a reference type to the global symbol table with the first component in this.components to have the same name as the component in the file
559
+ * This is so on a first validation, these types can be resolved in teh future (eg. when the actual component is created)
560
+ * If we don't add reference types at this top level, they will be created at the file level, and will never get resolved
561
+ * @param componentKey key getting a component from `this.components`
562
+ * @param componentName the unprefixed name of the component that will be added (e.g. 'MyLabel' NOT 'roSgNodeMyLabel')
563
+ */
564
+ addComponentReferenceType(componentKey, componentName) {
565
+ const symbolName = componentName ? util_1.util.getSgNodeTypeName(componentName) : undefined;
566
+ if (!symbolName) {
567
+ return;
568
+ }
569
+ const components = this.components[componentKey] || [];
570
+ if (components.length > 0) {
571
+ // There is a component that can be added,
572
+ if (!this.componentsTable.hasSymbol(symbolName, 2 /* SymbolTypeFlag.typetime */)) {
573
+ // it doesn't already exist in the table
574
+ const componentRefType = new ReferenceType_1.ReferenceType(symbolName, symbolName, 2 /* SymbolTypeFlag.typetime */, () => this.componentsTable);
575
+ if (componentRefType) {
576
+ this.componentsTable.addSymbol(symbolName, {}, componentRefType, 2 /* SymbolTypeFlag.typetime */);
577
+ }
578
+ }
579
+ }
580
+ else {
581
+ // there is no component. remove from table
582
+ this.componentsTable.removeSymbol(symbolName);
583
+ }
169
584
  }
170
585
  /**
171
586
  * re-attach the dependency graph with a new key for any component who changed
@@ -179,6 +594,7 @@ class Program {
179
594
  //attach (or re-attach) the dependencyGraph for every component whose position changed
180
595
  if (file.dependencyGraphIndex !== i) {
181
596
  file.dependencyGraphIndex = i;
597
+ this.dependencyGraph.addOrReplace(file.dependencyGraphKey, file.dependencies);
182
598
  file.attachDependencyGraph(this.dependencyGraph);
183
599
  scope.attachDependencyGraph(this.dependencyGraph);
184
600
  }
@@ -190,9 +606,10 @@ class Program {
190
606
  */
191
607
  getUnreferencedFiles() {
192
608
  let result = [];
193
- for (let key in this.files) {
194
- const file = this.files[key];
195
- if (!this.fileIsIncludedInAnyScope(file)) {
609
+ for (let filePath in this.files) {
610
+ let file = this.files[filePath];
611
+ //is this file part of a scope
612
+ if (!this.getFirstScopeForFile(file)) {
196
613
  //no scopes reference this file. add it to the list
197
614
  result.push(file);
198
615
  }
@@ -200,37 +617,14 @@ class Program {
200
617
  return result;
201
618
  }
202
619
  /**
203
- * Get the list of errors for the entire program. It's calculated on the fly
204
- * by walking through every file, so call this sparingly.
620
+ * Get the list of errors for the entire program.
205
621
  */
206
622
  getDiagnostics() {
207
- return this.logger.time(Logger_1.LogLevel.info, ['Program.getDiagnostics()'], () => {
208
- let diagnostics = [...this.diagnostics];
209
- //get the diagnostics from all scopes
210
- for (let scopeName in this.scopes) {
211
- let scope = this.scopes[scopeName];
212
- diagnostics.push(...scope.getDiagnostics());
213
- }
214
- //get the diagnostics from all unreferenced files
215
- let unreferencedFiles = this.getUnreferencedFiles();
216
- for (let file of unreferencedFiles) {
217
- diagnostics.push(...file.getDiagnostics());
218
- }
219
- const filteredDiagnostics = this.logger.time(Logger_1.LogLevel.debug, ['filter diagnostics'], () => {
220
- //filter out diagnostics based on our diagnostic filters
221
- let finalDiagnostics = this.diagnosticFilterer.filter(Object.assign(Object.assign({}, this.options), { rootDir: this.options.rootDir }), diagnostics);
222
- return finalDiagnostics;
223
- });
224
- this.logger.info(`diagnostic counts: total=${chalk_1.default.yellow(diagnostics.length.toString())}, after filter=${chalk_1.default.yellow(filteredDiagnostics.length.toString())}`);
225
- return filteredDiagnostics;
226
- });
227
- }
228
- addDiagnostics(diagnostics) {
229
- this.diagnostics.push(...diagnostics);
623
+ return this.diagnostics.getDiagnostics();
230
624
  }
231
625
  /**
232
626
  * Determine if the specified file is loaded in this program right now.
233
- * @param filePath
627
+ * @param filePath the absolute or relative path to the file
234
628
  * @param normalizePath should the provided path be normalized before use
235
629
  */
236
630
  hasFile(filePath, normalizePath = true) {
@@ -238,12 +632,15 @@ class Program {
238
632
  }
239
633
  /**
240
634
  * roku filesystem is case INsensitive, so find the scope by key case insensitive
241
- * @param scopeName
635
+ * @param scopeName xml scope names are their `destPath`. Source scope is stored with the key `"source"`
242
636
  */
243
637
  getScopeByName(scopeName) {
244
638
  if (!scopeName) {
245
639
  return undefined;
246
640
  }
641
+ //most scopes are xml file pkg paths. however, the ones that are not are single names like "global" and "scope",
642
+ //so it's safe to run the standardizePkgPath method
643
+ scopeName = (0, util_1.standardizePath) `${scopeName}`;
247
644
  let key = Object.keys(this.scopes).find(x => x.toLowerCase() === scopeName.toLowerCase());
248
645
  return this.scopes[key];
249
646
  }
@@ -264,113 +661,181 @@ class Program {
264
661
  * Update internal maps with this file reference
265
662
  */
266
663
  assignFile(file) {
664
+ const fileAddEvent = {
665
+ file: file,
666
+ program: this
667
+ };
668
+ this.plugins.emit('beforeAddFile', fileAddEvent);
267
669
  this.files[file.srcPath.toLowerCase()] = file;
268
- this.pkgMap[file.pkgPath.toLowerCase()] = file;
670
+ this.destMap.set(file.destPath.toLowerCase(), file);
671
+ this.plugins.emit('afterAddFile', fileAddEvent);
672
+ return file;
269
673
  }
270
674
  /**
271
675
  * Remove this file from internal maps
272
676
  */
273
677
  unassignFile(file) {
274
678
  delete this.files[file.srcPath.toLowerCase()];
275
- delete this.pkgMap[file.pkgPath.toLowerCase()];
679
+ this.destMap.delete(file.destPath.toLowerCase());
680
+ return file;
276
681
  }
277
- setFile(fileParam, fileContents) {
278
- assert.ok(fileParam, 'fileParam is required');
279
- let srcPath;
280
- let pkgPath;
281
- if (typeof fileParam === 'string') {
282
- //is a pkg path
283
- if (fileParam.startsWith('pkg:/')) {
284
- //srcPath is the pkgPath relative to the rootDir
285
- srcPath = 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 = util_1.standardizePath `${this.options.rootDir}/${fileParam}`;
297
- pkgPath = util_1.util.sanitizePkgPath(fileParam);
298
- }
299
- //is a FileObj
300
- }
301
- else {
302
- srcPath = util_1.standardizePath `${fileParam.src}`;
303
- pkgPath = util_1.util.sanitizePkgPath(fileParam.dest);
304
- }
305
- const lowerPkgPath = pkgPath.toLowerCase();
306
- return this.logger.time(Logger_1.LogLevel.debug, ['Program.addOrReplaceFile()', chalk_1.default.green(srcPath)], () => {
307
- assert.ok(srcPath, 'srcPath is required');
308
- assert.ok(pkgPath, 'pkgPath is required');
682
+ setFile(fileParam, fileData) {
683
+ //normalize the file paths
684
+ const { srcPath, destPath } = this.getPaths(fileParam, this.options.rootDir);
685
+ //namespace contributions for the new/replaced file may differ; force the
686
+ //program-level contributors map to rebuild on next query
687
+ this.invalidateNamespaceContributorCache();
688
+ let file = this.logger.time(logging_1.LogLevel.debug, ['Program.setFile()', chalk_1.default.green(srcPath)], () => {
689
+ var _a, _b, _c;
309
690
  //if the file is already loaded, remove it
310
691
  if (this.hasFile(srcPath)) {
311
- this.removeFile(srcPath);
692
+ this.removeFile(srcPath, true, true);
312
693
  }
313
- let fileExtension = path.extname(srcPath).toLowerCase();
314
- let file;
315
- const beforeFileParseEvent = {
316
- program: this,
317
- srcPath: srcPath,
318
- source: fileContents
319
- };
320
- if (fileExtension === '.brs' || fileExtension === '.bs') {
321
- let brsFile = new BrsFile_1.BrsFile(srcPath, pkgPath, this);
322
- //add file to the `source` dependency list
323
- if (brsFile.pkgPath.startsWith('pkg:/source/')) {
694
+ const data = new LazyFileData_1.LazyFileData(fileData);
695
+ const event = new ProvideFileEventInternal(this, srcPath, destPath, data, this.fileFactory);
696
+ this.plugins.emit('beforeProvideFile', event);
697
+ this.plugins.emit('provideFile', event);
698
+ this.plugins.emit('afterProvideFile', event);
699
+ //if no files were provided, create a AssetFile to represent it.
700
+ if (event.files.length === 0) {
701
+ event.files.push(this.fileFactory.AssetFile({
702
+ srcPath: event.srcPath,
703
+ destPath: event.destPath,
704
+ pkgPath: event.destPath,
705
+ data: data
706
+ }));
707
+ }
708
+ //find the file instance for the srcPath that triggered this action.
709
+ const primaryFile = event.files.find(x => x.srcPath === srcPath);
710
+ if (!primaryFile) {
711
+ throw new Error(`No file provided for srcPath '${srcPath}'. Instead, received ${JSON.stringify(event.files.map(x => ({
712
+ type: x.type,
713
+ srcPath: x.srcPath,
714
+ destPath: x.destPath
715
+ })))}`);
716
+ }
717
+ //link the virtual files to the primary file
718
+ this.fileClusters.set((_a = primaryFile.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase(), event.files);
719
+ for (const file of event.files) {
720
+ file.srcPath = (0, util_1.standardizePath)(file.srcPath);
721
+ if (file.destPath) {
722
+ file.destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.destPath, this.options.rootDir, '')}`;
723
+ }
724
+ if (file.pkgPath) {
725
+ file.pkgPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(file.pkgPath, this.options.rootDir, '')}`;
726
+ }
727
+ else {
728
+ file.pkgPath = file.destPath;
729
+ }
730
+ file.excludeFromOutput = file.excludeFromOutput === true;
731
+ //set the dependencyGraph key for every file to its destPath
732
+ file.dependencyGraphKey = file.destPath.toLowerCase();
733
+ this.assignFile(file);
734
+ //register a callback anytime this file's dependencies change
735
+ if (typeof file.onDependenciesChanged === 'function') {
736
+ (_b = file.disposables) !== null && _b !== void 0 ? _b : (file.disposables = []);
737
+ file.disposables.push(this.dependencyGraph.onchange(file.dependencyGraphKey, file.onDependenciesChanged.bind(file)));
738
+ }
739
+ //register this file (and its dependencies) with the dependency graph
740
+ this.dependencyGraph.addOrReplace(file.dependencyGraphKey, (_c = file.dependencies) !== null && _c !== void 0 ? _c : []);
741
+ //if this is a `source` file, add it to the source scope's dependency list
742
+ if (this.isSourceBrsFile(file)) {
324
743
  this.createSourceScope();
325
- this.dependencyGraph.addDependency('scope:source', brsFile.dependencyGraphKey);
744
+ this.dependencyGraph.addDependency('scope:source', file.dependencyGraphKey);
745
+ }
746
+ //if this is an xml file in the components folder, register it as a component
747
+ if (this.isComponentsXmlFile(file)) {
748
+ this.plugins.emit('beforeProvideScope', {
749
+ program: this,
750
+ scope: undefined
751
+ });
752
+ //create a new scope for this xml file
753
+ let scope = new XmlScope_1.XmlScope(file, this);
754
+ this.addScope(scope);
755
+ //register this componet now that we have parsed it and know its component name
756
+ this.registerComponent(file, scope);
757
+ this.plugins.emit('provideScope', {
758
+ program: this,
759
+ scope: scope
760
+ });
761
+ //notify plugins that the scope is created and the component is registered
762
+ this.plugins.emit('afterProvideScope', {
763
+ program: this,
764
+ scope: scope
765
+ });
326
766
  }
327
- //add the file to the program
328
- this.assignFile(brsFile);
329
- this.plugins.emit('beforeFileParse', beforeFileParseEvent);
330
- this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
331
- brsFile.parse(beforeFileParseEvent.source);
332
- });
333
- file = brsFile;
334
- brsFile.attachDependencyGraph(this.dependencyGraph);
335
- this.plugins.emit('afterFileParse', {
336
- program: this,
337
- file: brsFile
338
- });
339
- }
340
- else if (
341
- //is xml file
342
- fileExtension === '.xml' &&
343
- //resides in the components folder (Roku will only parse xml files in the components folder)
344
- lowerPkgPath.startsWith('pkg:/components/')) {
345
- let xmlFile = new XmlFile_1.XmlFile(srcPath, pkgPath, this);
346
- this.assignFile(xmlFile);
347
- //add the file to the program
348
- this.plugins.emit('beforeFileParse', beforeFileParseEvent);
349
- this.logger.time(Logger_1.LogLevel.debug, ['parse', chalk_1.default.green(srcPath)], () => {
350
- xmlFile.parse(beforeFileParseEvent.source);
351
- });
352
- file = xmlFile;
353
- //create a new scope for this xml file
354
- let scope = new XmlScope_1.XmlScope(xmlFile, this);
355
- this.addScope(scope);
356
- //register this compoent now that we have parsed it and know its component name
357
- this.registerComponent(xmlFile, scope);
358
- this.plugins.emit('afterFileParse', {
359
- program: this,
360
- file: xmlFile
361
- });
362
767
  }
363
- else {
364
- //TODO do we actually need to implement this? Figure out how to handle img paths
365
- // let genericFile = this.files[srcPath] = <any>{
366
- // srcPath: srcPath,
367
- // pkgPath: pkgPath,
368
- // wasProcessed: true
369
- // } as File;
370
- // file = <any>genericFile;
371
- }
372
- return file;
768
+ return primaryFile;
373
769
  });
770
+ return file;
771
+ }
772
+ /**
773
+ * Given a srcPath, a destPath, or both, resolve whichever is missing, relative to rootDir.
774
+ * @param fileParam an object representing file paths
775
+ * @param rootDir must be a pre-normalized path
776
+ */
777
+ getPaths(fileParam, rootDir) {
778
+ let srcPath;
779
+ let destPath;
780
+ assert.ok(fileParam, 'fileParam is required');
781
+ //lift the path vars from the incoming param
782
+ if (typeof fileParam === 'string') {
783
+ fileParam = this.removePkgPrefix(fileParam);
784
+ srcPath = (0, util_1.standardizePath) `${path.resolve(rootDir, fileParam)}`;
785
+ destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
786
+ }
787
+ else {
788
+ let param = fileParam;
789
+ if (param.src) {
790
+ srcPath = (0, util_1.standardizePath) `${param.src}`;
791
+ }
792
+ if (param.srcPath) {
793
+ srcPath = (0, util_1.standardizePath) `${param.srcPath}`;
794
+ }
795
+ if (param.dest) {
796
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.dest)}`;
797
+ }
798
+ if (param.pkgPath) {
799
+ destPath = (0, util_1.standardizePath) `${this.removePkgPrefix(param.pkgPath)}`;
800
+ }
801
+ }
802
+ //if there's no srcPath, use the destPath to build an absolute srcPath
803
+ if (!srcPath) {
804
+ srcPath = (0, util_1.standardizePath) `${rootDir}/${destPath}`;
805
+ }
806
+ //coerce srcPath to an absolute path
807
+ if (!path.isAbsolute(srcPath)) {
808
+ srcPath = util_1.util.standardizePath(srcPath);
809
+ }
810
+ //if destPath isn't set, compute it from the other paths
811
+ if (!destPath) {
812
+ destPath = (0, util_1.standardizePath) `${util_1.util.replaceCaseInsensitive(srcPath, rootDir, '')}`;
813
+ }
814
+ assert.ok(srcPath, 'fileEntry.src is required');
815
+ assert.ok(destPath, 'fileEntry.dest is required');
816
+ return {
817
+ srcPath: srcPath,
818
+ //remove leading slash
819
+ destPath: destPath.replace(/^[\/\\]+/, '')
820
+ };
821
+ }
822
+ /**
823
+ * Remove any leading `pkg:/` found in the path
824
+ */
825
+ removePkgPrefix(path) {
826
+ return path.replace(/^pkg:\//i, '');
827
+ }
828
+ /**
829
+ * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
830
+ */
831
+ isSourceBrsFile(file) {
832
+ return !!/^(pkg:\/)?source[\/\\]/.exec(file.destPath);
833
+ }
834
+ /**
835
+ * Is this file a .brs file found somewhere within the `pkg:/source/` folder?
836
+ */
837
+ isComponentsXmlFile(file) {
838
+ return (0, reflection_1.isXmlFile)(file) && !!/^(pkg:\/)?components[\/\\]/.exec(file.destPath);
374
839
  }
375
840
  /**
376
841
  * Ensure source scope is created.
@@ -381,238 +846,580 @@ class Program {
381
846
  const sourceScope = new Scope_1.Scope('source', this, 'scope:source');
382
847
  sourceScope.attachDependencyGraph(this.dependencyGraph);
383
848
  this.addScope(sourceScope);
849
+ this.plugins.emit('afterProvideScope', {
850
+ program: this,
851
+ scope: sourceScope
852
+ });
384
853
  }
385
854
  }
386
855
  /**
387
856
  * Remove a set of files from the program
388
- * @param srcPaths
857
+ * @param srcPaths can be an array of srcPath or destPath strings
858
+ * @param normalizePath should this function repair and standardize the filePaths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
389
859
  */
390
- removeFiles(srcPaths) {
860
+ removeFiles(srcPaths, normalizePath = true) {
391
861
  for (let srcPath of srcPaths) {
392
- this.removeFile(srcPath);
862
+ this.removeFile(srcPath, normalizePath);
393
863
  }
394
864
  }
395
865
  /**
396
866
  * Remove a file from the program
397
- * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
867
+ * @param filePath can be a srcPath, a destPath, or a destPath with leading `pkg:/`
398
868
  * @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
-
400
869
  */
401
- removeFile(filePath, normalizePath = true) {
870
+ removeFile(filePath, normalizePath = true, keepSymbolInformation = false) {
871
+ var _a, _b, _c, _d;
402
872
  this.logger.debug('Program.removeFile()', filePath);
403
- let file = this.getFile(filePath, normalizePath);
404
- if (file) {
405
- this.plugins.emit('beforeFileDispose', {
406
- program: this,
407
- file: file
408
- });
873
+ const paths = this.getPaths(filePath, this.options.rootDir);
874
+ //namespace contributions may have included this file; force the program-level
875
+ //contributors map to rebuild on next query
876
+ this.invalidateNamespaceContributorCache();
877
+ //there can be one or more File entries for a single srcPath, so get all of them and remove them all
878
+ const files = (_b = this.fileClusters.get((_a = paths.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase())) !== null && _b !== void 0 ? _b : [this.getFile(filePath, normalizePath)];
879
+ for (const file of files) {
880
+ //if a file has already been removed, nothing more needs to be done here
881
+ if (!file || !this.hasFile(file.srcPath)) {
882
+ continue;
883
+ }
884
+ this.diagnostics.clearForFile(file.srcPath);
885
+ const event = { file: file, program: this };
886
+ this.plugins.emit('beforeRemoveFile', event);
409
887
  //if there is a scope named the same as this file's path, remove it (i.e. xml scopes)
410
- let scope = this.scopes[file.pkgPath];
888
+ let scope = this.scopes[file.destPath];
411
889
  if (scope) {
412
- this.plugins.emit('beforeScopeDispose', {
890
+ this.logger.debug('Removing associated scope', scope.name);
891
+ const scopeRemoveEvent = {
413
892
  program: this,
414
893
  scope: scope
415
- });
894
+ };
895
+ this.plugins.emit('beforeRemoveScope', scopeRemoveEvent);
896
+ this.plugins.emit('removeScope', scopeRemoveEvent);
416
897
  scope.dispose();
417
898
  //notify dependencies of this scope that it has been removed
418
899
  this.dependencyGraph.remove(scope.dependencyGraphKey);
419
- delete this.scopes[file.pkgPath];
420
- this.plugins.emit('afterScopeDispose', {
421
- program: this,
422
- scope: scope
423
- });
900
+ this.removeScope(this.scopes[file.destPath]);
901
+ this.plugins.emit('afterRemoveScope', scopeRemoveEvent);
424
902
  }
425
903
  //remove the file from the program
426
904
  this.unassignFile(file);
427
905
  this.dependencyGraph.remove(file.dependencyGraphKey);
428
906
  //if this is a pkg:/source file, notify the `source` scope that it has changed
429
- if (file.pkgPath.startsWith('pkg:/source/')) {
907
+ if (this.isSourceBrsFile(file)) {
430
908
  this.dependencyGraph.removeDependency('scope:source', file.dependencyGraphKey);
431
909
  }
910
+ if ((0, reflection_1.isBrsFile)(file)) {
911
+ this.logger.debug('Removing file symbol info', file.srcPath);
912
+ if (!keepSymbolInformation) {
913
+ this.fileSymbolInformation.delete(file.pkgPath);
914
+ }
915
+ this.crossScopeValidation.clearResolutionsForFile(file);
916
+ }
917
+ this.diagnostics.clearForFile(file.srcPath);
432
918
  //if this is a component, remove it from our components map
433
- if (reflection_1.isXmlFile(file)) {
919
+ if ((0, reflection_1.isXmlFile)(file)) {
920
+ this.logger.debug('Unregistering component', file.srcPath);
434
921
  this.unregisterComponent(file);
435
922
  }
436
- this.plugins.emit('afterFileDispose', {
437
- program: this,
438
- file: file
439
- });
923
+ this.logger.debug('Disposing file', file.srcPath);
924
+ //dispose any disposable things on the file
925
+ for (const disposable of (_c = file === null || file === void 0 ? void 0 : file.disposables) !== null && _c !== void 0 ? _c : []) {
926
+ disposable();
927
+ }
928
+ //dispose file
929
+ (_d = file === null || file === void 0 ? void 0 : file.dispose) === null || _d === void 0 ? void 0 : _d.call(file);
930
+ this.plugins.emit('afterRemoveFile', event);
440
931
  }
441
932
  }
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);
933
+ validate(options) {
934
+ var _a;
935
+ const validationRunId = this.validationRunSequence++;
936
+ let previousValidationPromise = this.validatePromise;
937
+ const deferred = new deferred_1.Deferred();
938
+ if (options === null || options === void 0 ? void 0 : options.async) {
939
+ //we're async, so create a new promise chain to resolve after this validation is done
940
+ this.validatePromise = Promise.resolve(previousValidationPromise).then(() => {
941
+ return deferred.promise;
942
+ });
943
+ //we are not async but there's a pending promise, then we cannot run this validation
450
944
  }
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
- }
945
+ else if (previousValidationPromise !== undefined) {
946
+ throw new Error('Cannot run synchronous validation while an async validation is in progress');
459
947
  }
460
- }
461
- /**
462
- * Traverse the entire project, and validate all scopes
463
- * @param force - if true, then all scopes are force to validate, even if they aren't marked as dirty
464
- */
465
- validate() {
466
- this.logger.time(Logger_1.LogLevel.log, ['Validating project'], () => {
467
- var _a;
468
- this.diagnostics = [];
469
- this.plugins.emit('beforeProgramValidate', {
948
+ let beforeValidateProgramWasEmitted = false;
949
+ const brsFilesValidated = this.validationDetails.brsFilesValidated;
950
+ const xmlFilesValidated = this.validationDetails.xmlFilesValidated;
951
+ const changedSymbols = this.validationDetails.changedSymbols;
952
+ const changedComponentTypes = this.validationDetails.changedComponentTypes;
953
+ const scopesToValidate = this.validationDetails.scopesToValidate;
954
+ const filesToBeValidatedInScopeContext = this.validationDetails.filesToBeValidatedInScopeContext;
955
+ //validate every file
956
+ let logValidateEnd = (status) => { };
957
+ //will be populated later on during the correspnding sequencer event
958
+ let filesToProcess;
959
+ const sequencer = new Sequencer_1.Sequencer({
960
+ name: 'program.validate',
961
+ cancellationToken: (_a = options === null || options === void 0 ? void 0 : options.cancellationToken) !== null && _a !== void 0 ? _a : new vscode_languageserver_1.CancellationTokenSource().token,
962
+ minSyncDuration: this.validationMinSyncDuration
963
+ });
964
+ //this sequencer allows us to run in both sync and async mode, depending on whether options.async is enabled.
965
+ //We use this to prevent starving the CPU during long validate cycles when running in a language server context
966
+ sequencer
967
+ .once('wait for previous run', () => {
968
+ //if running in async mode, return the previous validation promise to ensure we're only running one at a time
969
+ if (options === null || options === void 0 ? void 0 : options.async) {
970
+ return previousValidationPromise;
971
+ }
972
+ })
973
+ .once('before and on programValidate', () => {
974
+ logValidateEnd = this.logger.timeStart(logging_1.LogLevel.log, `Validating project${this.logger.logLevel > logging_1.LogLevel.log ? ` (run ${validationRunId})` : ''}`);
975
+ this.diagnostics.clearForTag(ProgramValidator_1.ProgramValidatorDiagnosticsTag);
976
+ this.plugins.emit('beforeValidateProgram', {
470
977
  program: this
471
978
  });
472
- //validate every file
473
- for (const file of Object.values(this.files)) {
474
- //find any files NOT loaded into a scope
475
- if (!this.fileIsIncludedInAnyScope(file)) {
476
- this.logger.debug('Program.validate(): fileNotReferenced by any scope', () => chalk_1.default.green(file === null || file === void 0 ? void 0 : file.pkgPath));
477
- //the file is not loaded in any scope
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) }));
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
- });
979
+ beforeValidateProgramWasEmitted = true;
980
+ this.plugins.emit('validateProgram', {
981
+ program: this
982
+ });
983
+ })
984
+ .once('get files to be validated', () => {
985
+ filesToProcess = Object.values(this.files).sort((0, thenby_1.firstBy)(x => x.srcPath)).filter(x => !x.isValidated);
986
+ for (const file of filesToProcess) {
987
+ filesToBeValidatedInScopeContext.add(file);
988
+ }
989
+ })
990
+ .once('add component reference types', () => {
991
+ // Create reference component types for any component that changes
992
+ for (let [componentKey, componentName] of this.componentSymbolsToUpdate.entries()) {
993
+ this.addComponentReferenceType(componentKey, componentName);
994
+ }
995
+ })
996
+ //run before/validate/after as a single per-file action so the Sequencer can't cancel
997
+ //between them. Splitting into three forEach steps would let cancellation land after
998
+ //`validateFile` (where BrsFileValidator pushes per-node symbols via addSymbol) but
999
+ //before `afterValidateFile` (where processSymbolInformation registers validation
1000
+ //segments) — leaving the file in a half-processed state. The next pass would either
1001
+ //re-run BrsFileValidator (pushing duplicate symbols → CrossScopeValidator name-
1002
+ //collision) or skip the file in scope validation (no segments to walk → no
1003
+ //diagnostics emitted). Atomic-per-file means either the file is fully processed or
1004
+ //it's untouched. Plugins that need an all-files-done signal should use
1005
+ //`afterValidateProgram` instead of `afterValidateFile`.
1006
+ .forEach('validateFile', () => filesToProcess, (file) => {
1007
+ this.plugins.emit('beforeValidateFile', {
1008
+ program: this,
1009
+ file: file
1010
+ });
1011
+ this.plugins.emit('validateFile', {
1012
+ program: this,
1013
+ file: file
1014
+ });
1015
+ this.plugins.emit('afterValidateFile', {
1016
+ program: this,
1017
+ file: file
1018
+ });
1019
+ file.isValidated = true;
1020
+ if ((0, reflection_1.isBrsFile)(file)) {
1021
+ brsFilesValidated.push(file);
1022
+ }
1023
+ else if ((0, reflection_1.isXmlFile)(file)) {
1024
+ xmlFilesValidated.push(file);
1025
+ }
1026
+ })
1027
+ .forEach('do deferred component creation', () => [...brsFilesValidated, ...xmlFilesValidated], (file) => {
1028
+ if ((0, reflection_1.isXmlFile)(file)) {
1029
+ this.addDeferredComponentTypeSymbolCreation(file);
1030
+ }
1031
+ else if ((0, reflection_1.isBrsFile)(file)) {
1032
+ const fileHasChanges = file.providedSymbols.changes.get(1 /* SymbolTypeFlag.runtime */).size > 0 || file.providedSymbols.changes.get(2 /* SymbolTypeFlag.typetime */).size > 0;
1033
+ if (fileHasChanges) {
1034
+ for (const scope of this.getScopesForFile(file)) {
1035
+ if ((0, reflection_1.isXmlScope)(scope) && this.doesXmlFileRequireProvidedSymbols(scope.xmlFile, file.providedSymbols.changes)) {
1036
+ this.addDeferredComponentTypeSymbolCreation(scope.xmlFile);
1037
+ }
1038
+ }
493
1039
  }
494
1040
  }
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
- });
1041
+ })
1042
+ .once('build component types for any component that changes', () => {
1043
+ this.logger.time(logging_1.LogLevel.info, ['Build component types'], () => {
1044
+ this.logger.debug(`Component Symbols to update:`, [...this.componentSymbolsToUpdate.entries()].sort());
1045
+ this.lastValidationInfo.componentsRebuilt = new Set();
1046
+ for (let [componentKey, componentName] of this.componentSymbolsToUpdate.entries()) {
1047
+ this.lastValidationInfo.componentsRebuilt.add(componentName === null || componentName === void 0 ? void 0 : componentName.toLowerCase());
1048
+ if (this.updateComponentSymbolInGlobalScope(componentKey, componentName)) {
1049
+ changedComponentTypes.push(util_1.util.getSgNodeTypeName(componentName).toLowerCase());
509
1050
  }
510
1051
  }
1052
+ this.componentSymbolsToUpdate.clear();
511
1053
  });
512
- this.detectDuplicateComponentNames();
513
- this.plugins.emit('afterProgramValidate', {
514
- program: this
515
- });
516
- });
517
- }
518
- /**
519
- * Flag all duplicate component names
520
- */
521
- detectDuplicateComponentNames() {
522
- var _a;
523
- const componentsByName = new Map();
524
- for (const key in this.files) {
525
- const file = this.files[key];
526
- //if this is an XmlFile, and it has a valid `componentName` property
527
- if (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]);
1054
+ })
1055
+ .once('track and update type-time and runtime symbol dependencies and changes', () => {
1056
+ var _a, _b, _c, _d;
1057
+ const changedSymbolsMapArr = (_a = [...brsFilesValidated, ...xmlFilesValidated]) === null || _a === void 0 ? void 0 : _a.map(f => {
1058
+ if ((0, reflection_1.isBrsFile)(f)) {
1059
+ return f.providedSymbols.changes;
1060
+ }
1061
+ return null;
1062
+ }).filter(x => x);
1063
+ // update the map of typetime dependencies
1064
+ for (const file of brsFilesValidated) {
1065
+ for (const [symbolName, provided] of file.providedSymbols.symbolMap.get(2 /* SymbolTypeFlag.typetime */).entries()) {
1066
+ // clear existing dependencies
1067
+ for (const values of this.symbolDependencies.values()) {
1068
+ values.delete(symbolName);
532
1069
  }
533
- else {
534
- componentsByName.get(componentNameLower).push(file);
1070
+ // map types to the set of types that depend upon them
1071
+ for (const dependentSymbol of (_c = (_b = provided.requiredSymbolNames) === null || _b === void 0 ? void 0 : _b.values()) !== null && _c !== void 0 ? _c : []) {
1072
+ const dependentSymbolLower = dependentSymbol.toLowerCase();
1073
+ if (!this.symbolDependencies.has(dependentSymbolLower)) {
1074
+ this.symbolDependencies.set(dependentSymbolLower, new Set());
1075
+ }
1076
+ const symbolsDependentUpon = this.symbolDependencies.get(dependentSymbolLower);
1077
+ symbolsDependentUpon.add(symbolName);
535
1078
  }
536
1079
  }
537
1080
  }
538
- }
539
- for (const xmlFiles of componentsByName.values()) {
540
- //add diagnostics for every duplicate component with this name
541
- if (xmlFiles.length > 1) {
542
- for (let xmlFile of xmlFiles) {
543
- const { componentName } = xmlFile;
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 => {
545
- return {
546
- location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(xmlFile.srcPath).toString(), x.componentName.range),
547
- message: 'Also defined here'
548
- };
549
- }) }));
1081
+ for (const flag of [1 /* SymbolTypeFlag.runtime */, 2 /* SymbolTypeFlag.typetime */]) {
1082
+ const changedSymbolsSetArr = changedSymbolsMapArr.map(symMap => symMap.get(flag));
1083
+ const changedSymbolSet = new Set();
1084
+ for (const changeSet of changedSymbolsSetArr) {
1085
+ for (const change of changeSet) {
1086
+ changedSymbolSet.add(change);
1087
+ }
550
1088
  }
551
- }
1089
+ if (!changedSymbols.has(flag)) {
1090
+ changedSymbols.set(flag, changedSymbolSet);
1091
+ }
1092
+ else {
1093
+ changedSymbols.set(flag, new Set([...changedSymbols.get(flag), ...changedSymbolSet]));
1094
+ }
1095
+ }
1096
+ // update changed symbol set with any changed component
1097
+ for (const changedComponentType of changedComponentTypes) {
1098
+ changedSymbols.get(2 /* SymbolTypeFlag.typetime */).add(changedComponentType);
1099
+ }
1100
+ // Add any additional types that depend on a changed type
1101
+ // as each iteration of the loop might add new types, need to keep checking until nothing new is added
1102
+ const dependentTypesChanged = new Set();
1103
+ let foundDependentTypes = false;
1104
+ const changedTypeSymbols = changedSymbols.get(2 /* SymbolTypeFlag.typetime */);
1105
+ do {
1106
+ foundDependentTypes = false;
1107
+ const allChangedTypesSofar = [...Array.from(changedTypeSymbols), ...Array.from(dependentTypesChanged)];
1108
+ for (const changedSymbol of allChangedTypesSofar) {
1109
+ const symbolsDependentUponChangedSymbol = (_d = this.symbolDependencies.get(changedSymbol)) !== null && _d !== void 0 ? _d : [];
1110
+ for (const symbolName of symbolsDependentUponChangedSymbol) {
1111
+ if (!changedTypeSymbols.has(symbolName) && !dependentTypesChanged.has(symbolName)) {
1112
+ foundDependentTypes = true;
1113
+ dependentTypesChanged.add(symbolName);
1114
+ }
1115
+ }
1116
+ }
1117
+ } while (foundDependentTypes);
1118
+ changedSymbols.set(2 /* SymbolTypeFlag.typetime */, new Set([...changedSymbols.get(2 /* SymbolTypeFlag.typetime */), ...changedTypeSymbols, ...dependentTypesChanged]));
1119
+ this.lastValidationInfo.brsFilesSrcPath = new Set(this.validationDetails.brsFilesValidated.map(f => { var _a, _b; return (_b = (_a = f.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; }));
1120
+ this.lastValidationInfo.xmlFilesSrcPath = new Set(this.validationDetails.xmlFilesValidated.map(f => { var _a, _b; return (_b = (_a = f.srcPath) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; }));
1121
+ // can reset filesValidatedList, because they are no longer needed
1122
+ this.validationDetails.brsFilesValidated = [];
1123
+ this.validationDetails.xmlFilesValidated = [];
1124
+ })
1125
+ .once('tracks changed symbols and prepares files and scopes for validation', () => {
1126
+ if (this.options.logLevel === logging_1.LogLevel.debug) {
1127
+ const changedRuntime = Array.from(changedSymbols.get(1 /* SymbolTypeFlag.runtime */)).sort();
1128
+ this.logger.debug('Changed Symbols (runTime):', changedRuntime.join(', '));
1129
+ const changedTypetime = Array.from(changedSymbols.get(2 /* SymbolTypeFlag.typetime */)).sort();
1130
+ this.logger.debug('Changed Symbols (typeTime):', changedTypetime.join(', '));
1131
+ }
1132
+ const didComponentChange = changedComponentTypes.length > 0;
1133
+ const didProvidedSymbolChange = changedSymbols.get(1 /* SymbolTypeFlag.runtime */).size > 0 || changedSymbols.get(2 /* SymbolTypeFlag.typetime */).size > 0;
1134
+ const scopesToCheck = this.getScopesForCrossScopeValidation(didComponentChange, didProvidedSymbolChange);
1135
+ this.crossScopeValidation.buildComponentsMap();
1136
+ this.logger.time(logging_1.LogLevel.info, ['addDiagnosticsForScopes'], () => {
1137
+ this.crossScopeValidation.addDiagnosticsForScopes(scopesToCheck);
1138
+ });
1139
+ const filesToRevalidate = this.crossScopeValidation.getFilesRequiringChangedSymbol(scopesToCheck, changedSymbols);
1140
+ for (const file of filesToRevalidate) {
1141
+ filesToBeValidatedInScopeContext.add(file);
1142
+ }
1143
+ this.currentScopeValidationOptions = {
1144
+ filesToBeValidatedInScopeContext: filesToBeValidatedInScopeContext,
1145
+ changedSymbols: changedSymbols,
1146
+ changedFiles: Array.from(filesToBeValidatedInScopeContext),
1147
+ initialValidation: this.isFirstValidation
1148
+ };
1149
+ //can reset changedComponent types
1150
+ this.validationDetails.changedComponentTypes = [];
1151
+ })
1152
+ .forEach('invalidate affected scopes', () => filesToBeValidatedInScopeContext, (file) => {
1153
+ if ((0, reflection_1.isBrsFile)(file)) {
1154
+ file.validationSegmenter.unValidateAllSegments();
1155
+ for (const scope of this.getScopesForFile(file)) {
1156
+ scope.invalidate();
1157
+ }
1158
+ }
1159
+ })
1160
+ .once('checking scopes to validate', () => {
1161
+ //sort the scope names so we get consistent results
1162
+ for (const scopeName of this.getSortedScopeNames()) {
1163
+ let scope = this.scopes[scopeName];
1164
+ if (scope.shouldValidate(this.currentScopeValidationOptions)) {
1165
+ scopesToValidate.push(scope);
1166
+ }
1167
+ }
1168
+ this.lastValidationInfo.scopeNames = new Set(scopesToValidate.map(s => { var _a, _b; return (_b = (_a = s.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; }));
1169
+ })
1170
+ .forEach('beforeScopeValidate', () => scopesToValidate, (scope) => {
1171
+ this.plugins.emit('beforeValidateScope', {
1172
+ program: this,
1173
+ scope: scope
1174
+ });
1175
+ })
1176
+ .forEach('validate scope', () => scopesToValidate, (scope) => {
1177
+ scope.validate(this.currentScopeValidationOptions);
1178
+ })
1179
+ .forEach('afterValidateScope', () => scopesToValidate, (scope) => {
1180
+ this.plugins.emit('afterValidateScope', {
1181
+ program: this,
1182
+ scope: scope
1183
+ });
1184
+ })
1185
+ .once('detect duplicate component names', () => {
1186
+ this.detectDuplicateComponentNames();
1187
+ this.isFirstValidation = false;
1188
+ // can reset other validation details
1189
+ this.validationDetails.changedSymbols = new Map();
1190
+ this.validationDetails.scopesToValidate = [];
1191
+ this.validationDetails.filesToBeValidatedInScopeContext = new Set();
1192
+ })
1193
+ .onCancel(() => {
1194
+ logValidateEnd('cancelled');
1195
+ })
1196
+ .onSuccess(() => {
1197
+ logValidateEnd();
1198
+ })
1199
+ .onComplete(() => {
1200
+ var _a, _b;
1201
+ //if we emitted the beforeValidateProgram hook, emit the afterValidateProgram hook as well
1202
+ if (beforeValidateProgramWasEmitted) {
1203
+ const wasCancelled = (_b = (_a = options === null || options === void 0 ? void 0 : options.cancellationToken) === null || _a === void 0 ? void 0 : _a.isCancellationRequested) !== null && _b !== void 0 ? _b : false;
1204
+ this.plugins.emit('afterValidateProgram', {
1205
+ program: this,
1206
+ wasCancelled: wasCancelled
1207
+ });
1208
+ }
1209
+ //log all the sequencer timing metrics if `info` logging is enabled
1210
+ this.logger.info(sequencer.formatMetrics({
1211
+ header: 'Program.validate metrics:',
1212
+ //only include loop iterations if `debug` logging is enabled
1213
+ includeLoopIterations: this.logger.isLogLevelEnabled(logging_1.LogLevel.debug)
1214
+ }));
1215
+ //regardless of the success of the validation, mark this run as complete
1216
+ deferred.resolve();
1217
+ //clear the validatePromise which means we're no longer running a validation
1218
+ this.validatePromise = undefined;
1219
+ });
1220
+ //run the sequencer in async mode if enabled
1221
+ if (options === null || options === void 0 ? void 0 : options.async) {
1222
+ return sequencer.run();
1223
+ //run the sequencer in sync mode
1224
+ }
1225
+ else {
1226
+ return sequencer.runSync();
552
1227
  }
553
1228
  }
554
- /**
555
- * Determine at least one scope has the file
556
- */
557
- fileIsIncludedInAnyScope(file) {
558
- for (let scope of Object.values(this.scopes)) {
559
- if (scope.hasFile(file)) {
1229
+ getScopesForCrossScopeValidation(someComponentTypeChanged, didProvidedSymbolChange) {
1230
+ const scopesForCrossScopeValidation = [];
1231
+ for (let scopeName of this.getSortedScopeNames()) {
1232
+ let scope = this.scopes[scopeName];
1233
+ if (this.globalScope === scope) {
1234
+ continue;
1235
+ }
1236
+ if (someComponentTypeChanged) {
1237
+ scopesForCrossScopeValidation.push(scope);
1238
+ }
1239
+ if (didProvidedSymbolChange && !scope.isValidated) {
1240
+ scopesForCrossScopeValidation.push(scope);
1241
+ }
1242
+ }
1243
+ return scopesForCrossScopeValidation;
1244
+ }
1245
+ doesXmlFileRequireProvidedSymbols(file, providedSymbolsByFlag) {
1246
+ for (const required of file.requiredSymbols) {
1247
+ const symbolNameLower = required.name.toLowerCase();
1248
+ const requiredSymbolIsProvided = providedSymbolsByFlag.get(required.flags).has(symbolNameLower);
1249
+ if (requiredSymbolIsProvided) {
560
1250
  return true;
561
1251
  }
562
1252
  }
563
1253
  return false;
564
1254
  }
1255
+ /**
1256
+ * Flag all duplicate component names
1257
+ */
1258
+ detectDuplicateComponentNames() {
1259
+ const componentsByName = Object.keys(this.files).reduce((map, filePath) => {
1260
+ var _a;
1261
+ const file = this.files[filePath];
1262
+ //if this is an XmlFile, and it has a valid `componentName` property
1263
+ if ((0, reflection_1.isXmlFile)(file) && ((_a = file.componentName) === null || _a === void 0 ? void 0 : _a.text)) {
1264
+ let lowerName = file.componentName.text.toLowerCase();
1265
+ if (!map[lowerName]) {
1266
+ map[lowerName] = [];
1267
+ }
1268
+ map[lowerName].push(file);
1269
+ }
1270
+ return map;
1271
+ }, {});
1272
+ for (let name in componentsByName) {
1273
+ const xmlFiles = componentsByName[name];
1274
+ //add diagnostics for every duplicate component with this name
1275
+ if (xmlFiles.length > 1) {
1276
+ for (let xmlFile of xmlFiles) {
1277
+ const { componentName } = xmlFile;
1278
+ this.diagnostics.register(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.duplicateComponentName(componentName.text)), { location: xmlFile.componentName.location, relatedInformation: xmlFiles.filter(x => x !== xmlFile).map(x => {
1279
+ return {
1280
+ location: x.componentName.location,
1281
+ message: 'Also defined here'
1282
+ };
1283
+ }) }), { tags: [ProgramValidator_1.ProgramValidatorDiagnosticsTag] });
1284
+ }
1285
+ }
1286
+ }
1287
+ }
1288
+ /**
1289
+ * Get the files for a list of filePaths
1290
+ * @param filePaths can be an array of srcPath or a destPath strings
1291
+ * @param normalizePath should this function repair and standardize the paths? Passing false should have a performance boost if you can guarantee your paths are already sanitized
1292
+ */
1293
+ getFiles(filePaths, normalizePath = true) {
1294
+ return filePaths
1295
+ .map(filePath => this.getFile(filePath, normalizePath))
1296
+ .filter(file => file !== undefined);
1297
+ }
565
1298
  /**
566
1299
  * Get the file at the given path
567
- * @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
1300
+ * @param filePath can be a srcPath or a destPath
568
1301
  * @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
569
1302
  */
570
1303
  getFile(filePath, normalizePath = true) {
1304
+ if (this.getFilePathCache.has(filePath)) {
1305
+ const cachedFilePath = this.getFilePathCache.get(filePath);
1306
+ if (cachedFilePath.isDestMap) {
1307
+ return this.destMap.get(cachedFilePath.path);
1308
+ }
1309
+ return this.files[cachedFilePath.path];
1310
+ }
571
1311
  if (typeof filePath !== 'string') {
572
1312
  return undefined;
1313
+ //is the path absolute (or the `virtual:` prefix)
1314
+ }
1315
+ else if (/^(?:(?:virtual:[\/\\])|(?:\w:)|(?:[\/\\]))/gmi.exec(filePath)) {
1316
+ const standardizedPath = (normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase();
1317
+ this.getFilePathCache.set(filePath, { path: standardizedPath });
1318
+ return this.files[standardizedPath];
573
1319
  }
574
- else if (path.isAbsolute(filePath)) {
575
- return this.files[(normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase()];
1320
+ else if (util_1.util.isUriLike(filePath)) {
1321
+ const path = vscode_uri_1.URI.parse(filePath).fsPath;
1322
+ const standardizedPath = (normalizePath ? util_1.util.standardizePath(path) : path).toLowerCase();
1323
+ this.getFilePathCache.set(filePath, { path: standardizedPath });
1324
+ return this.files[standardizedPath];
576
1325
  }
577
1326
  else {
578
- return this.pkgMap[(normalizePath ? util_1.util.sanitizePkgPath(filePath) : filePath).toLowerCase()];
1327
+ const standardizedPath = (normalizePath ? util_1.util.standardizePath(filePath) : filePath).toLowerCase();
1328
+ this.getFilePathCache.set(filePath, { path: standardizedPath, isDestMap: true });
1329
+ return this.destMap.get(standardizedPath);
579
1330
  }
580
1331
  }
1332
+ /**
1333
+ * Gets a sorted list of all scopeNames, always beginning with "global", "source", then any others in alphabetical order
1334
+ */
1335
+ getSortedScopeNames() {
1336
+ if (!this.sortedScopeNames) {
1337
+ this.sortedScopeNames = Object.keys(this.scopes).sort((a, b) => {
1338
+ if (a === 'global') {
1339
+ return -1;
1340
+ }
1341
+ else if (b === 'global') {
1342
+ return 1;
1343
+ }
1344
+ if (a === 'source') {
1345
+ return -1;
1346
+ }
1347
+ else if (b === 'source') {
1348
+ return 1;
1349
+ }
1350
+ if (a < b) {
1351
+ return -1;
1352
+ }
1353
+ else if (b < a) {
1354
+ return 1;
1355
+ }
1356
+ return 0;
1357
+ });
1358
+ }
1359
+ return this.sortedScopeNames;
1360
+ }
581
1361
  /**
582
1362
  * Get a list of all scopes the file is loaded into
583
- * @param file
1363
+ * @param file the file
584
1364
  */
585
1365
  getScopesForFile(file) {
1366
+ const resolvedFile = typeof file === 'string' ? this.getFile(file) : file;
586
1367
  let result = [];
587
- for (let key in this.scopes) {
1368
+ if (resolvedFile) {
1369
+ const scopeKeys = this.getSortedScopeNames();
1370
+ for (let key of scopeKeys) {
1371
+ let scope = this.scopes[key];
1372
+ if (scope.hasFile(resolvedFile)) {
1373
+ result.push(scope);
1374
+ }
1375
+ }
1376
+ }
1377
+ return result;
1378
+ }
1379
+ /**
1380
+ * Get the first found scope for a file.
1381
+ */
1382
+ getFirstScopeForFile(file) {
1383
+ const scopeKeys = this.getSortedScopeNames();
1384
+ for (let key of scopeKeys) {
588
1385
  let scope = this.scopes[key];
589
1386
  if (scope.hasFile(file)) {
590
- result.push(scope);
1387
+ return scope;
591
1388
  }
592
1389
  }
593
- return result;
594
1390
  }
595
1391
  getStatementsByName(name, originFile, namespaceName) {
596
- var _a, _b;
597
1392
  let results = new Map();
598
1393
  const filesSearched = new Set();
599
1394
  let lowerNamespaceName = namespaceName === null || namespaceName === void 0 ? void 0 : namespaceName.toLowerCase();
600
1395
  let lowerName = name === null || name === void 0 ? void 0 : name.toLowerCase();
1396
+ function addToResults(statement, file) {
1397
+ var _a, _b;
1398
+ let parentNamespaceName = (_b = (_a = statement.findAncestor(reflection_1.isNamespaceStatement)) === null || _a === void 0 ? void 0 : _a.getName(originFile.parseMode)) === null || _b === void 0 ? void 0 : _b.toLowerCase();
1399
+ if (statement.tokens.name.text.toLowerCase() === lowerName && (!lowerNamespaceName || parentNamespaceName === lowerNamespaceName)) {
1400
+ if (!results.has(statement)) {
1401
+ results.set(statement, { item: statement, file: file });
1402
+ }
1403
+ }
1404
+ }
601
1405
  //look through all files in scope for matches
602
1406
  for (const scope of this.getScopesForFile(originFile)) {
603
1407
  for (const file of scope.getAllFiles()) {
604
- if (reflection_1.isXmlFile(file) || filesSearched.has(file)) {
1408
+ //skip non-brs files, or files we've already processed
1409
+ if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
605
1410
  continue;
606
1411
  }
607
1412
  filesSearched.add(file);
608
- for (const statement of [...file.parser.references.functionStatements, ...file.parser.references.classStatements.flatMap((cs) => cs.methods)]) {
609
- let parentNamespaceName = (_b = (_a = statement.namespaceName) === null || _a === void 0 ? void 0 : _a.getName(originFile.parseMode)) === null || _b === void 0 ? void 0 : _b.toLowerCase();
610
- if (statement.name.text.toLowerCase() === lowerName && (!parentNamespaceName || parentNamespaceName === lowerNamespaceName)) {
611
- if (!results.has(statement)) {
612
- results.set(statement, { item: statement, file: file });
613
- }
1413
+ file.ast.walk((0, visitors_1.createVisitor)({
1414
+ FunctionStatement: (statement) => {
1415
+ addToResults(statement, file);
1416
+ },
1417
+ MethodStatement: (statement) => {
1418
+ addToResults(statement, file);
614
1419
  }
615
- }
1420
+ }), {
1421
+ walkMode: visitors_1.WalkMode.visitStatements
1422
+ });
616
1423
  }
617
1424
  }
618
1425
  return [...results.values()];
@@ -624,136 +1431,186 @@ class Program {
624
1431
  //get all function names for the xml file and parents
625
1432
  let funcNames = new Set();
626
1433
  let currentScope = scope;
627
- while (reflection_1.isXmlScope(currentScope)) {
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 (reflection_1.isSGInterfaceFunction(member)) {
630
- const name = member.name;
631
- if (!filterName || name === filterName) {
632
- funcNames.add(name);
633
- }
1434
+ while ((0, reflection_1.isXmlScope)(currentScope)) {
1435
+ for (let name of (_b = (_a = currentScope.xmlFile.ast.componentElement.interfaceElement) === null || _a === void 0 ? void 0 : _a.functions.map((f) => f.name)) !== null && _b !== void 0 ? _b : []) {
1436
+ if (!filterName || name === filterName) {
1437
+ funcNames.add(name);
634
1438
  }
635
1439
  }
636
1440
  currentScope = currentScope.getParentScope();
637
1441
  }
638
1442
  //look through all files in scope for matches
639
1443
  for (const file of scope.getOwnFiles()) {
640
- if (reflection_1.isXmlFile(file) || filesSearched.has(file)) {
1444
+ //skip non-brs files, or files we've already processed
1445
+ if (!(0, reflection_1.isBrsFile)(file) || filesSearched.has(file)) {
641
1446
  continue;
642
1447
  }
643
1448
  filesSearched.add(file);
644
- for (const statement of file.parser.references.functionStatements) {
645
- if (funcNames.has(statement.name.text)) {
646
- if (!results.has(statement)) {
647
- results.set(statement, { item: statement, file: file });
1449
+ file.ast.walk((0, visitors_1.createVisitor)({
1450
+ FunctionStatement: (statement) => {
1451
+ if (funcNames.has(statement.tokens.name.text)) {
1452
+ if (!results.has(statement)) {
1453
+ results.set(statement, { item: statement, file: file });
1454
+ }
648
1455
  }
649
1456
  }
650
- }
1457
+ }), {
1458
+ walkMode: visitors_1.WalkMode.visitStatements
1459
+ });
651
1460
  }
652
1461
  return [...results.values()];
653
1462
  }
654
1463
  /**
655
1464
  * Find all available completion items at the given position
656
- * @param srcPath The absolute path to the source file on disk
657
- * @param lineIndex
658
- * @param columnIndex
1465
+ * @param filePath can be a srcPath or a destPath
1466
+ * @param position the position (line & column) where completions should be found
659
1467
  */
660
- getCompletions(srcPath, position) {
661
- let file = this.getFile(srcPath);
1468
+ getCompletions(filePath, position) {
1469
+ let file = this.getFile(filePath);
662
1470
  if (!file) {
663
1471
  return [];
664
1472
  }
665
- let result = [];
666
- if (reflection_1.isBrsFile(file) && file.parser.isPositionNextToTokenKind(position, lexer_1.TokenKind.Callfunc)) {
667
- // is next to a @. callfunc invocation - must be an interface method
668
- for (const scope of this.getScopes().filter((s) => reflection_1.isXmlScope(s))) {
669
- let fileLinks = this.getStatementsForXmlFile(scope);
670
- for (let fileLink of fileLinks) {
671
- result.push(scope.createCompletionFromFunctionStatement(fileLink.item));
672
- }
673
- }
674
- //no other result is possible in this case
675
- return result;
676
- }
677
- //find the scopes for this file
678
- let scopes = this.getScopesForFile(file);
679
- //if there are no scopes, include the global scope so we at least get the built-in functions
680
- scopes = scopes.length > 0 ? scopes : [this.globalScope];
681
- //get the completions from all scopes for this file
682
- let allCompletions = util_1.util.flatMap(scopes.map(ctx => file.getCompletions(position, ctx)), c => c);
683
- //only keep completions common to every scope for this file
684
- let keyCounts = {};
685
- for (let completion of allCompletions) {
686
- let key = `${completion.label}-${completion.kind}`;
687
- keyCounts[key] = keyCounts[key] ? keyCounts[key] + 1 : 1;
688
- if (keyCounts[key] === scopes.length) {
689
- result.push(completion);
690
- }
691
- }
692
- return result;
1473
+ const event = {
1474
+ program: this,
1475
+ file: file,
1476
+ scopes: this.getScopesForFile(file),
1477
+ position: position,
1478
+ completions: []
1479
+ };
1480
+ this.plugins.emit('beforeProvideCompletions', event);
1481
+ this.plugins.emit('provideCompletions', event);
1482
+ this.plugins.emit('afterProvideCompletions', event);
1483
+ return event.completions;
693
1484
  }
694
1485
  /**
695
1486
  * Goes through each file and builds a list of workspace symbols for the program. Used by LanguageServer's onWorkspaceSymbol functionality
696
1487
  */
697
1488
  getWorkspaceSymbols() {
698
- const result = [];
699
- for (const key in this.files) {
700
- const file = this.files[key];
701
- if (reflection_1.isBrsFile(file)) {
702
- result.push(...file.getWorkspaceSymbols());
703
- }
704
- }
705
- return result;
1489
+ const event = {
1490
+ program: this,
1491
+ workspaceSymbols: []
1492
+ };
1493
+ this.plugins.emit('beforeProvideWorkspaceSymbols', event);
1494
+ this.plugins.emit('provideWorkspaceSymbols', event);
1495
+ this.plugins.emit('afterProvideWorkspaceSymbols', event);
1496
+ return event.workspaceSymbols;
706
1497
  }
707
1498
  /**
708
1499
  * Given a position in a file, if the position is sitting on some type of identifier,
709
1500
  * go to the definition of that identifier (where this thing was first defined)
710
- * @param srcPath The absolute path to the source file on disk
711
1501
  */
712
1502
  getDefinition(srcPath, position) {
713
1503
  let file = this.getFile(srcPath);
714
1504
  if (!file) {
715
1505
  return [];
716
1506
  }
717
- if (reflection_1.isBrsFile(file)) {
718
- return file.getDefinition(position);
1507
+ const event = {
1508
+ program: this,
1509
+ file: file,
1510
+ position: position,
1511
+ definitions: []
1512
+ };
1513
+ this.plugins.emit('beforeProvideDefinition', event);
1514
+ this.plugins.emit('provideDefinition', event);
1515
+ this.plugins.emit('afterProvideDefinition', event);
1516
+ return event.definitions;
1517
+ }
1518
+ /**
1519
+ * Get hover information for a file and position
1520
+ */
1521
+ getHover(srcPath, position) {
1522
+ let file = this.getFile(srcPath);
1523
+ let result;
1524
+ if (file) {
1525
+ const event = {
1526
+ program: this,
1527
+ file: file,
1528
+ position: position,
1529
+ scopes: this.getScopesForFile(file),
1530
+ hovers: []
1531
+ };
1532
+ this.plugins.emit('beforeProvideHover', event);
1533
+ this.plugins.emit('provideHover', event);
1534
+ this.plugins.emit('afterProvideHover', event);
1535
+ result = event.hovers;
1536
+ }
1537
+ return result !== null && result !== void 0 ? result : [];
1538
+ }
1539
+ /**
1540
+ * Get full list of document symbols for a file
1541
+ * @param srcPath path to the file
1542
+ */
1543
+ getDocumentSymbols(srcPath) {
1544
+ let file = this.getFile(srcPath);
1545
+ if (file) {
1546
+ const event = {
1547
+ program: this,
1548
+ file: file,
1549
+ documentSymbols: []
1550
+ };
1551
+ this.plugins.emit('beforeProvideDocumentSymbols', event);
1552
+ this.plugins.emit('provideDocumentSymbols', event);
1553
+ this.plugins.emit('afterProvideDocumentSymbols', event);
1554
+ return event.documentSymbols;
719
1555
  }
720
1556
  else {
721
- let results = [];
722
- const scopes = this.getScopesForFile(file);
723
- for (const scope of scopes) {
724
- results = results.concat(...scope.getDefinition(file, position));
725
- }
726
- return results;
1557
+ return undefined;
727
1558
  }
728
1559
  }
729
1560
  /**
730
- * @param srcPath The absolute path to the source file on disk
1561
+ * Get the selection ranges for the given positions in a file. Used for expand/shrink selection.
1562
+ * @param srcPath path to the file
1563
+ * @param positions the positions to get selection ranges for
731
1564
  */
732
- getHover(srcPath, position) {
733
- //find the file
734
- let file = this.getFile(srcPath);
735
- if (!file) {
736
- return null;
1565
+ getSelectionRanges(srcPath, positions) {
1566
+ const file = this.getFile(srcPath);
1567
+ if (file) {
1568
+ const event = {
1569
+ program: this,
1570
+ file: file,
1571
+ positions: positions,
1572
+ selectionRanges: []
1573
+ };
1574
+ this.plugins.emit('beforeProvideSelectionRanges', event);
1575
+ this.plugins.emit('provideSelectionRanges', event);
1576
+ this.plugins.emit('afterProvideSelectionRanges', event);
1577
+ return event.selectionRanges;
737
1578
  }
738
- return Promise.resolve(file.getHover(position));
1579
+ return [];
739
1580
  }
740
1581
  /**
741
1582
  * Compute code actions for the given file and range
742
- * @param srcPath The absolute path to the source file on disk
743
1583
  */
744
1584
  getCodeActions(srcPath, range) {
745
1585
  const codeActions = [];
746
1586
  const file = this.getFile(srcPath);
747
1587
  if (file) {
1588
+ const fileUri = util_1.util.pathToUri(file === null || file === void 0 ? void 0 : file.srcPath);
748
1589
  const diagnostics = this
749
1590
  //get all current diagnostics (filtered by diagnostic filters)
750
1591
  .getDiagnostics()
751
1592
  //only keep diagnostics related to this file
752
- .filter(x => x.file === file)
1593
+ .filter(x => { var _a; return ((_a = x.location) === null || _a === void 0 ? void 0 : _a.uri) === fileUri; })
753
1594
  //only keep diagnostics that touch this range
754
- .filter(x => util_1.util.rangesIntersect(x.range, range));
1595
+ .filter(x => util_1.util.rangesIntersectOrTouch(x.location.range, range));
755
1596
  const scopes = this.getScopesForFile(file);
756
- this.plugins.emit('onGetCodeActions', {
1597
+ this.plugins.emit('beforeProvideCodeActions', {
1598
+ program: this,
1599
+ file: file,
1600
+ range: range,
1601
+ diagnostics: diagnostics,
1602
+ scopes: scopes,
1603
+ codeActions: codeActions
1604
+ });
1605
+ this.plugins.emit('provideCodeActions', {
1606
+ program: this,
1607
+ file: file,
1608
+ range: range,
1609
+ diagnostics: diagnostics,
1610
+ scopes: scopes,
1611
+ codeActions: codeActions
1612
+ });
1613
+ this.plugins.emit('afterProvideCodeActions', {
757
1614
  program: this,
758
1615
  file: file,
759
1616
  range: range,
@@ -764,14 +1621,53 @@ class Program {
764
1621
  }
765
1622
  return codeActions;
766
1623
  }
1624
+ /**
1625
+ * Compute "source fix all" code actions for the given file.
1626
+ * Fires the `onGetSourceFixAllCodeActions` plugin event with all diagnostics for the file (no range filter),
1627
+ * then converts each contributed SourceFixAllCodeAction into an LSP CodeAction.
1628
+ */
1629
+ getSourceFixAllCodeActions(srcPath) {
1630
+ const actions = [];
1631
+ const file = this.getFile(srcPath);
1632
+ if (file) {
1633
+ const fileUri = util_1.util.pathToUri(file.srcPath);
1634
+ const diagnostics = this
1635
+ .getDiagnostics()
1636
+ .filter(x => { var _a; return ((_a = x.location) === null || _a === void 0 ? void 0 : _a.uri) === fileUri; });
1637
+ const scopes = this.getScopesForFile(file);
1638
+ this.plugins.emit('onGetSourceFixAllCodeActions', {
1639
+ program: this,
1640
+ file: file,
1641
+ diagnostics: diagnostics,
1642
+ scopes: scopes,
1643
+ actions: actions
1644
+ });
1645
+ }
1646
+ return actions.map(action => {
1647
+ var _a;
1648
+ return CodeActionUtil_1.codeActionUtil.createCodeAction(Object.assign(Object.assign({}, action), { kind: (_a = action.kind) !== null && _a !== void 0 ? _a : 'source.fixAll.brighterscript' }));
1649
+ });
1650
+ }
767
1651
  /**
768
1652
  * Get semantic tokens for the specified file
769
1653
  */
770
1654
  getSemanticTokens(srcPath) {
771
1655
  const file = this.getFile(srcPath);
772
1656
  if (file) {
1657
+ this.plugins.emit('beforeProvideSemanticTokens', {
1658
+ program: this,
1659
+ file: file,
1660
+ scopes: this.getScopesForFile(file),
1661
+ semanticTokens: undefined
1662
+ });
773
1663
  const result = [];
774
- this.plugins.emit('onGetSemanticTokens', {
1664
+ this.plugins.emit('provideSemanticTokens', {
1665
+ program: this,
1666
+ file: file,
1667
+ scopes: this.getScopesForFile(file),
1668
+ semanticTokens: result
1669
+ });
1670
+ this.plugins.emit('afterProvideSemanticTokens', {
775
1671
  program: this,
776
1672
  file: file,
777
1673
  scopes: this.getScopesForFile(file),
@@ -780,363 +1676,262 @@ class Program {
780
1676
  return result;
781
1677
  }
782
1678
  }
783
- getSignatureHelp(filepath, position) {
784
- var _a;
785
- let file = this.getFile(filepath);
786
- if (!file || !reflection_1.isBrsFile(file)) {
1679
+ getSignatureHelp(filePath, position) {
1680
+ let file = this.getFile(filePath);
1681
+ if (!file || !(0, reflection_1.isBrsFile)(file)) {
787
1682
  return [];
788
1683
  }
789
- const results = new Map();
790
- let functionExpression = file.getFunctionExpressionAtPosition(position);
791
- let identifierInfo = this.getPartialStatementInfo(file, position);
792
- if (identifierInfo.statementType === '') {
793
- // just general function calls
794
- let statements = file.program.getStatementsByName(identifierInfo.name, file);
795
- for (let statement of statements) {
796
- //TODO better handling of collisions - if it's a namespace, then don't show any other overrides
797
- //if we're on m - then limit scope to the current class, if present
798
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
799
- if (sigHelp && !results.has[sigHelp.key]) {
800
- sigHelp.index = identifierInfo.commaCount;
801
- results.set(sigHelp.key, sigHelp);
802
- }
803
- }
804
- }
805
- else if (identifierInfo.statementType === '.') {
806
- //if m class reference.. then
807
- //only get statements from the class I am in..
808
- if (functionExpression) {
809
- let myClass = file.getClassFromMReference(position, file.parser.getTokenAt(position), functionExpression);
810
- if (myClass) {
811
- for (let scope of this.getScopesForFile(myClass.file)) {
812
- let classes = scope.getClassHierarchy(myClass.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
813
- //and anything from any class in scope to a non m class
814
- for (let statement of [...classes].filter((i) => reflection_1.isClassMethodStatement(i.item))) {
815
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
816
- if (sigHelp && !results.has[sigHelp.key]) {
817
- results.set(sigHelp.key, sigHelp);
818
- return;
819
- }
820
- }
821
- }
822
- }
823
- }
824
- if (identifierInfo.dotPart) {
825
- //potential namespaces
826
- let statements = file.program.getStatementsByName(identifierInfo.name, file, identifierInfo.dotPart);
827
- if (statements.length === 0) {
828
- //was not a namespaced function, it could be any method on any class now
829
- statements = file.program.getStatementsByName(identifierInfo.name, file);
830
- }
831
- for (let statement of statements) {
832
- //TODO better handling of collisions - if it's a namespace, then don't show any other overrides
833
- //if we're on m - then limit scope to the current class, if present
834
- let sigHelp = statement.file.getSignatureHelpForStatement(statement.item);
835
- if (sigHelp && !results.has[sigHelp.key]) {
836
- sigHelp.index = identifierInfo.commaCount;
837
- results.set(sigHelp.key, sigHelp);
838
- }
839
- }
840
- }
841
- }
842
- else if (identifierInfo.statementType === '@.') {
843
- for (const scope of this.getScopes().filter((s) => reflection_1.isXmlScope(s))) {
844
- let fileLinks = this.getStatementsForXmlFile(scope, identifierInfo.name);
845
- for (let fileLink of fileLinks) {
846
- let sigHelp = fileLink.file.getSignatureHelpForStatement(fileLink.item);
847
- if (sigHelp && !results.has[sigHelp.key]) {
848
- sigHelp.index = identifierInfo.commaCount;
849
- results.set(sigHelp.key, sigHelp);
850
- }
851
- }
852
- }
853
- }
854
- else if (identifierInfo.statementType === 'new') {
855
- let classItem = file.getClassFileLink(identifierInfo.dotPart ? `${identifierInfo.dotPart}.${identifierInfo.name}` : identifierInfo.name);
856
- let sigHelp = (_a = classItem === null || classItem === void 0 ? void 0 : classItem.file) === null || _a === void 0 ? void 0 : _a.getClassSignatureHelp(classItem === null || classItem === void 0 ? void 0 : classItem.item);
857
- if (sigHelp && !results.has(sigHelp.key)) {
858
- sigHelp.index = identifierInfo.commaCount;
859
- results.set(sigHelp.key, sigHelp);
860
- }
861
- }
862
- return [...results.values()];
1684
+ let callExpressionInfo = new CallExpressionInfo_1.CallExpressionInfo(file, position);
1685
+ let signatureHelpUtil = new SignatureHelpUtil_1.SignatureHelpUtil();
1686
+ return signatureHelpUtil.getSignatureHelpItems(callExpressionInfo);
863
1687
  }
864
- getPartialStatementInfo(file, position) {
865
- let lines = util_1.util.splitIntoLines(file.fileContents);
866
- let line = lines[position.line];
867
- let index = position.character;
868
- let itemCounts = this.getPartialItemCounts(line, index);
869
- if (!itemCounts.isArgStartFound && line.charAt(index) === ')') {
870
- //try previous char, in case we were on a close bracket..
871
- index--;
872
- itemCounts = this.getPartialItemCounts(line, index);
873
- }
874
- let argStartIndex = itemCounts.argStartIndex;
875
- index = itemCounts.argStartIndex - 1;
876
- let statementType = '';
877
- let name;
878
- let dotPart;
879
- if (!itemCounts.isArgStartFound) {
880
- //try to get sig help based on the name
881
- index = position.character;
882
- let currentToken = file.parser.getTokenAt(position);
883
- if (currentToken && currentToken.kind !== lexer_1.TokenKind.Comment) {
884
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
885
- if (!name) {
886
- //try the previous token, incase we're on a bracket
887
- currentToken = file.parser.getPreviousToken(currentToken);
888
- name = file.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
889
- }
890
- if (name === null || name === void 0 ? void 0 : name.indexOf('.')) {
891
- let parts = name.split('.');
892
- name = parts[parts.length - 1];
893
- }
894
- index = currentToken.range.start.character;
895
- argStartIndex = index;
896
- }
897
- else {
898
- // invalid location
899
- index = 0;
900
- itemCounts.comma = 0;
901
- }
902
- }
903
- //this loop is quirky. walk to -1 (which will result in the last char being '' thus satisfying the situation where there is no leading whitespace).
904
- while (index >= -1) {
905
- if (!(/[a-z0-9_\.\@]/i).test(line.charAt(index))) {
906
- if (!name) {
907
- name = line.substring(index + 1, argStartIndex);
908
- }
909
- else {
910
- dotPart = line.substring(index + 1, argStartIndex);
911
- if (dotPart.endsWith('.')) {
912
- dotPart = dotPart.substr(0, dotPart.length - 1);
1688
+ getReferences(srcPath, position) {
1689
+ //find the file
1690
+ let file = this.getFile(srcPath);
1691
+ const event = {
1692
+ program: this,
1693
+ file: file,
1694
+ position: position,
1695
+ references: []
1696
+ };
1697
+ this.plugins.emit('beforeProvideReferences', event);
1698
+ this.plugins.emit('provideReferences', event);
1699
+ this.plugins.emit('afterProvideReferences', event);
1700
+ return event.references;
1701
+ }
1702
+ /**
1703
+ * Transpile a single file and get the result as a string.
1704
+ * This does not write anything to the file system.
1705
+ *
1706
+ * This should only be called by `LanguageServer`.
1707
+ * Internal usage should call `_getTranspiledFileContents` instead.
1708
+ * @param filePath can be a srcPath or a destPath
1709
+ */
1710
+ async getTranspiledFileContents(filePath) {
1711
+ const file = this.getFile(filePath);
1712
+ return this.getTranspiledFileContentsPipeline.run(async () => {
1713
+ const result = {
1714
+ destPath: file.destPath,
1715
+ pkgPath: file.pkgPath,
1716
+ srcPath: file.srcPath
1717
+ };
1718
+ const expectedPkgPath = file.pkgPath.toLowerCase();
1719
+ const expectedMapPath = `${expectedPkgPath}.map`;
1720
+ const expectedTypedefPkgPath = expectedPkgPath.replace(/\.brs$/i, '.d.bs');
1721
+ //add a temporary plugin to tap into the file writing process
1722
+ const plugin = this.plugins.addFirst({
1723
+ name: 'getTranspiledFileContents',
1724
+ beforeWriteFile: (event) => {
1725
+ const pkgPath = event.file.pkgPath.toLowerCase();
1726
+ switch (pkgPath) {
1727
+ //this is the actual transpiled file
1728
+ case expectedPkgPath:
1729
+ result.code = event.file.data.toString();
1730
+ break;
1731
+ //this is the sourcemap
1732
+ case expectedMapPath:
1733
+ result.map = event.file.data.toString();
1734
+ break;
1735
+ //this is the typedef
1736
+ case expectedTypedefPkgPath:
1737
+ result.typedef = event.file.data.toString();
1738
+ break;
1739
+ default:
1740
+ //no idea what this file is. just ignore it
913
1741
  }
1742
+ //mark every file as processed so it they don't get written to the output directory
1743
+ event.processedFiles.add(event.file);
914
1744
  }
915
- break;
916
- }
917
- if (line.substr(index - 2, 2) === '@.') {
918
- statementType = '@.';
919
- name = name || line.substring(index, argStartIndex);
920
- break;
1745
+ });
1746
+ try {
1747
+ //now that the plugin has been registered, run the build with just this file
1748
+ await this.build({
1749
+ files: [file]
1750
+ });
921
1751
  }
922
- else if (line.charAt(index - 1) === '.' && statementType === '') {
923
- statementType = '.';
924
- name = name || line.substring(index, argStartIndex);
925
- argStartIndex = index;
1752
+ finally {
1753
+ this.plugins.remove(plugin);
926
1754
  }
927
- index--;
928
- }
929
- if (line.substring(0, index).trim().endsWith('new')) {
930
- statementType = 'new';
1755
+ return result;
1756
+ });
1757
+ }
1758
+ /**
1759
+ * Get the absolute output path for a file
1760
+ */
1761
+ getOutputPath(file, outDir = this.getOutDir()) {
1762
+ return (0, util_1.standardizePath) `${outDir}/${file.pkgPath}`;
1763
+ }
1764
+ getOutDir(outDir) {
1765
+ var _a, _b;
1766
+ let result = (_a = outDir !== null && outDir !== void 0 ? outDir : this.options.outDir) !== null && _a !== void 0 ? _a : this.options.outDir;
1767
+ if (!result) {
1768
+ result = roku_deploy_1.rokuDeploy.getOptions(this.options).outDir;
931
1769
  }
932
- return {
933
- commaCount: itemCounts.comma,
934
- statementType: statementType,
935
- name: name,
936
- dotPart: dotPart
937
- };
1770
+ result = (0, util_1.standardizePath) `${path.resolve((_b = this.options.cwd) !== null && _b !== void 0 ? _b : process.cwd(), result !== null && result !== void 0 ? result : '/')}`;
1771
+ return result;
938
1772
  }
939
- getPartialItemCounts(line, index) {
940
- let isArgStartFound = false;
941
- let itemCounts = {
942
- normal: 0,
943
- square: 0,
944
- curly: 0,
945
- comma: 0,
946
- endIndex: 0,
947
- argStartIndex: index,
948
- isArgStartFound: false
1773
+ /**
1774
+ * Prepare the program for building
1775
+ * @param files the list of files that should be prepared
1776
+ */
1777
+ async prepare(files) {
1778
+ const programEvent = {
1779
+ program: this,
1780
+ editor: this.editor,
1781
+ files: files
949
1782
  };
950
- while (index >= 0) {
951
- const currentChar = line.charAt(index);
952
- if (currentChar === '\'') { //found comment, invalid index
953
- itemCounts.isArgStartFound = false;
954
- break;
1783
+ //assign an editor to every file
1784
+ for (const file of programEvent.files) {
1785
+ //if the file doesn't have an editor yet, assign one now
1786
+ if (!file.editor) {
1787
+ file.editor = new Editor_1.Editor();
955
1788
  }
956
- if (isArgStartFound) {
957
- if (currentChar !== ' ') {
958
- break;
959
- }
1789
+ }
1790
+ //sort the entries to make transpiling more deterministic
1791
+ programEvent.files.sort((a, b) => {
1792
+ if (a.pkgPath < b.pkgPath) {
1793
+ return -1;
1794
+ }
1795
+ else if (a.pkgPath > b.pkgPath) {
1796
+ return 1;
960
1797
  }
961
1798
  else {
962
- if (currentChar === ')') {
963
- itemCounts.normal++;
964
- }
965
- if (currentChar === ']') {
966
- itemCounts.square++;
967
- }
968
- if (currentChar === '}') {
969
- itemCounts.curly++;
970
- }
971
- if (currentChar === ',' && itemCounts.normal <= 0 && itemCounts.curly <= 0 && itemCounts.square <= 0) {
972
- itemCounts.comma++;
973
- }
974
- if (currentChar === '(') {
975
- if (itemCounts.normal === 0) {
976
- itemCounts.isArgStartFound = true;
977
- itemCounts.argStartIndex = index;
978
- }
979
- else {
980
- itemCounts.normal--;
981
- }
982
- }
983
- if (currentChar === '[') {
984
- itemCounts.square--;
985
- }
986
- if (currentChar === '{') {
987
- itemCounts.curly--;
988
- }
1799
+ return 1;
1800
+ }
1801
+ });
1802
+ await this.plugins.emitAsync('beforePrepareProgram', programEvent);
1803
+ await this.plugins.emitAsync('prepareProgram', programEvent);
1804
+ const outDir = this.getOutDir();
1805
+ const entries = [];
1806
+ for (const file of files) {
1807
+ const scope = this.getFirstScopeForFile(file);
1808
+ //link the symbol table for all the files in this scope
1809
+ scope === null || scope === void 0 ? void 0 : scope.linkSymbolTable();
1810
+ //if the file doesn't have an editor yet, assign one now
1811
+ if (!file.editor) {
1812
+ file.editor = new Editor_1.Editor();
989
1813
  }
990
- index--;
1814
+ const event = {
1815
+ program: this,
1816
+ file: file,
1817
+ editor: file.editor,
1818
+ scope: scope,
1819
+ outputPath: this.getOutputPath(file, outDir)
1820
+ };
1821
+ await this.plugins.emitAsync('beforePrepareFile', event);
1822
+ await this.plugins.emitAsync('prepareFile', event);
1823
+ await this.plugins.emitAsync('afterPrepareFile', event);
1824
+ //TODO remove this in v1
1825
+ entries.push(event);
1826
+ //unlink the symbolTable so the next loop iteration can link theirs
1827
+ scope === null || scope === void 0 ? void 0 : scope.unlinkSymbolTable();
991
1828
  }
992
- return itemCounts;
1829
+ await this.plugins.emitAsync('afterPrepareProgram', programEvent);
1830
+ return files;
993
1831
  }
994
1832
  /**
995
- * @param srcPath The absolute path to the source file on disk
1833
+ * Generate the contents of every file
996
1834
  */
997
- getReferences(srcPath, position) {
998
- //find the file
999
- let file = this.getFile(srcPath);
1000
- if (!file) {
1001
- return null;
1835
+ async serialize(files) {
1836
+ const allFiles = new Map();
1837
+ //exclude prunable files if that option is enabled
1838
+ if (this.options.pruneEmptyCodeFiles === true) {
1839
+ files = files.filter(x => x.canBePruned !== true);
1002
1840
  }
1003
- return file.getReferences(position);
1004
- }
1005
- /**
1006
- * Get a list of all script imports, relative to the specified pkgPath
1007
- * @param sourcePkgPath - the pkgPath of the source that wants to resolve script imports.
1008
- */
1009
- getScriptImportCompletions(sourcePkgPath, scriptImport) {
1010
- let lowerSourcePkgPath = sourcePkgPath.toLowerCase();
1011
- let result = [];
1012
- /**
1013
- * hashtable to prevent duplicate results
1014
- */
1015
- let resultPkgPaths = {};
1016
- //restrict to only .brs files
1017
- for (const key in this.files) {
1018
- const file = this.files[key];
1019
- if (
1020
- //is a BrightScript or BrighterScript file
1021
- (file.extension === '.bs' || file.extension === '.brs') &&
1022
- //this file is not the current file
1023
- lowerSourcePkgPath !== file.pkgPath.toLowerCase()) {
1024
- //add the relative path
1025
- let relativePath = util_1.util.getRelativePath(sourcePkgPath, file.pkgPath).replace(/\\/g, '/');
1026
- const lowerPkgPath = file.pkgPath.toLowerCase();
1027
- if (!resultPkgPaths[lowerPkgPath]) {
1028
- resultPkgPaths[lowerPkgPath] = true;
1029
- result.push({
1030
- label: relativePath,
1031
- detail: file.srcPath,
1032
- kind: vscode_languageserver_1.CompletionItemKind.File,
1033
- textEdit: {
1034
- newText: relativePath,
1035
- range: scriptImport.filePathRange
1036
- }
1037
- });
1038
- //add the absolute path
1039
- result.push({
1040
- label: file.pkgPath,
1041
- detail: file.srcPath,
1042
- kind: vscode_languageserver_1.CompletionItemKind.File,
1043
- textEdit: {
1044
- newText: file.pkgPath,
1045
- range: scriptImport.filePathRange
1046
- }
1047
- });
1048
- }
1841
+ const serializeProgramEvent = await this.plugins.emitAsync('beforeSerializeProgram', {
1842
+ program: this,
1843
+ files: files,
1844
+ result: allFiles
1845
+ });
1846
+ await this.plugins.emitAsync('serializeProgram', serializeProgramEvent);
1847
+ // serialize each file
1848
+ for (const file of files) {
1849
+ let scope = this.getFirstScopeForFile(file);
1850
+ //if the file doesn't have a scope, create a temporary scope for the file so it can depend on scope-level items
1851
+ if (!scope) {
1852
+ scope = new Scope_1.Scope(`temporary-for-${file.pkgPath}`, this);
1853
+ scope.getAllFiles = () => [file];
1854
+ scope.getOwnFiles = scope.getAllFiles;
1049
1855
  }
1856
+ //link the symbol table for all the files in this scope
1857
+ scope === null || scope === void 0 ? void 0 : scope.linkSymbolTable();
1858
+ const event = {
1859
+ program: this,
1860
+ file: file,
1861
+ scope: scope,
1862
+ result: allFiles
1863
+ };
1864
+ await this.plugins.emitAsync('beforeSerializeFile', event);
1865
+ await this.plugins.emitAsync('serializeFile', event);
1866
+ await this.plugins.emitAsync('afterSerializeFile', event);
1867
+ //unlink the symbolTable so the next loop iteration can link theirs
1868
+ scope === null || scope === void 0 ? void 0 : scope.unlinkSymbolTable();
1050
1869
  }
1051
- return result;
1870
+ this.plugins.emit('afterSerializeProgram', serializeProgramEvent);
1871
+ return allFiles;
1052
1872
  }
1053
1873
  /**
1054
- * Transpile a single file and get the result as a string.
1055
- * This does not write anything to the file system.
1056
- * @param srcPath The absolute path to the source file on disk
1874
+ * Write the entire project to disk
1057
1875
  */
1058
- getTranspiledFileContents(srcPath) {
1059
- let file = this.getFile(srcPath);
1060
- let result = file.transpile();
1061
- return Object.assign(Object.assign({}, result), { srcPath: file.srcPath, pkgPath: file.pkgPath });
1062
- }
1063
- async transpile(fileEntries, stagingFolderPath) {
1064
- // map fileEntries using their path as key to avoid excessive "find()" operations
1065
- const mappedFileEntries = fileEntries.reduce((collection, entry) => {
1066
- collection[util_1.standardizePath `${entry.src}`] = entry;
1067
- return collection;
1068
- }, {});
1069
- const entries = [];
1070
- for (const key in this.files) {
1071
- const file = this.files[key];
1072
- let filePathObj = mappedFileEntries[util_1.standardizePath `${file.srcPath}`];
1073
- if (!filePathObj) {
1074
- //this file has been added in-memory, from a plugin, for example
1075
- filePathObj = {
1076
- //add an interpolated src path (since it doesn't actually exist in memory)
1077
- src: `bsc-in-memory:/${util_1.util.removeProtocol(file.pkgPath)}`,
1078
- dest: file.pkgPath
1079
- };
1080
- }
1081
- //prep the output path
1082
- let outputPath = filePathObj.dest
1083
- //replace any leading protocol
1084
- .replace(/^[-a-z_]+:\//, '')
1085
- //change any .bs file extension to .brs
1086
- .replace(/\.bs$/gi, '.brs');
1087
- //prepend the staging folder path
1088
- outputPath = util_1.standardizePath `${stagingFolderPath}/${outputPath}`;
1089
- entries.push({
1090
- file: file,
1091
- outputPath: outputPath
1092
- });
1093
- }
1094
- this.plugins.emit('beforeProgramTranspile', {
1876
+ async write(outDir, files) {
1877
+ const programEvent = await this.plugins.emitAsync('beforeWriteProgram', {
1095
1878
  program: this,
1096
- entries: entries
1879
+ files: files,
1880
+ outDir: outDir
1097
1881
  });
1098
- const promises = entries.map(async (entry) => {
1099
- //skip transpiling typedef files
1100
- if (reflection_1.isBrsFile(entry.file) && entry.file.isTypedef) {
1101
- return;
1102
- }
1103
- this.plugins.emit('beforeFileTranspile', {
1882
+ //empty the out directory
1883
+ await fsExtra.emptyDir(outDir);
1884
+ const serializedFiles = [...files]
1885
+ .map(([, serializedFiles]) => serializedFiles)
1886
+ .flat();
1887
+ //write all the files to disk (asynchronously)
1888
+ await Promise.all(serializedFiles.map(async (file) => {
1889
+ const event = await this.plugins.emitAsync('beforeWriteFile', {
1104
1890
  program: this,
1105
- file: entry.file,
1106
- outputPath: entry.outputPath
1891
+ file: file,
1892
+ outputPath: this.getOutputPath(file, outDir),
1893
+ processedFiles: new Set()
1107
1894
  });
1108
- const { file, outputPath } = entry;
1109
- const result = file.transpile();
1110
- //make sure the full dir path exists
1111
- await fsExtra.ensureDir(path.dirname(outputPath));
1112
- if (await fsExtra.pathExists(outputPath)) {
1113
- throw new Error(`Error while transpiling "${file.srcPath}". A file already exists at "${outputPath}" and will not be overwritten.`);
1114
- }
1115
- const writeMapPromise = result.map ? fsExtra.writeFile(`${outputPath}.map`, result.map.toString()) : null;
1116
- await Promise.all([
1117
- fsExtra.writeFile(outputPath, result.code),
1118
- writeMapPromise
1119
- ]);
1120
- if (reflection_1.isBrsFile(file) && this.options.emitDefinitions) {
1121
- const typedef = file.getTypedef();
1122
- const typedefPath = outputPath.replace(/\.brs$/i, '.d.bs');
1123
- await fsExtra.writeFile(typedefPath, typedef);
1124
- }
1125
- this.plugins.emit('afterFileTranspile', {
1895
+ await this.plugins.emitAsync('writeFile', event);
1896
+ await this.plugins.emitAsync('afterWriteFile', event);
1897
+ }));
1898
+ await this.plugins.emitAsync('afterWriteProgram', programEvent);
1899
+ }
1900
+ /**
1901
+ * Build the project. This transpiles/transforms/copies all files and moves them to the staging directory
1902
+ * @param options the list of options used to build the program
1903
+ */
1904
+ async build(options) {
1905
+ //run a single build at a time
1906
+ await this.buildPipeline.run(async () => {
1907
+ var _a;
1908
+ const outDir = this.getOutDir(options === null || options === void 0 ? void 0 : options.outDir);
1909
+ const event = await this.plugins.emitAsync('beforeBuildProgram', {
1126
1910
  program: this,
1127
- file: entry.file,
1128
- outputPath: entry.outputPath
1911
+ editor: this.editor,
1912
+ files: (_a = options === null || options === void 0 ? void 0 : options.files) !== null && _a !== void 0 ? _a : Object.values(this.files)
1129
1913
  });
1914
+ //prepare the program (and files) for building
1915
+ event.files = await this.prepare(event.files);
1916
+ //stage the entire program
1917
+ const serializedFilesByFile = await this.serialize(event.files);
1918
+ await this.write(outDir, serializedFilesByFile);
1919
+ await this.plugins.emitAsync('afterBuildProgram', event);
1920
+ //undo all edits for the program
1921
+ this.editor.undoAll();
1922
+ //undo all edits for each file
1923
+ for (const file of event.files) {
1924
+ file.editor.undoAll();
1925
+ }
1130
1926
  });
1131
- //if there's no bslib file already loaded into the program, copy it to the staging directory
1132
- if (!this.getFile(bslibAliasedRokuModulesPkgPath) && !this.getFile(`pkg:/source/bslib.brs`)) {
1133
- promises.push(util_1.util.copyBslibToStaging(stagingFolderPath));
1927
+ this.logger.debug('Types Created', helpers_1.TypesCreated);
1928
+ let totalTypesCreated = 0;
1929
+ for (const key in helpers_1.TypesCreated) {
1930
+ if (helpers_1.TypesCreated.hasOwnProperty(key)) {
1931
+ totalTypesCreated += helpers_1.TypesCreated[key];
1932
+ }
1134
1933
  }
1135
- await Promise.all(promises);
1136
- this.plugins.emit('afterProgramTranspile', {
1137
- program: this,
1138
- entries: entries
1139
- });
1934
+ this.logger.info('Total Types Created', totalTypesCreated);
1140
1935
  }
1141
1936
  /**
1142
1937
  * Find a list of files in the program that have a function with the given name (case INsensitive)
@@ -1146,10 +1941,11 @@ class Program {
1146
1941
  const lowerFunctionName = functionName.toLowerCase();
1147
1942
  //find every file with this function defined
1148
1943
  for (const file of Object.values(this.files)) {
1149
- if (reflection_1.isBrsFile(file)) {
1944
+ if ((0, reflection_1.isBrsFile)(file)) {
1150
1945
  //TODO handle namespace-relative function calls
1151
1946
  //if the file has a function with this name
1152
- if (file.parser.references.functionStatementLookup.get(lowerFunctionName) !== undefined) {
1947
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1948
+ if (file['_cachedLookups'].functionStatementMap.get(lowerFunctionName)) {
1153
1949
  files.push(file);
1154
1950
  }
1155
1951
  }
@@ -1157,55 +1953,146 @@ class Program {
1157
1953
  return files;
1158
1954
  }
1159
1955
  /**
1160
- * Find a list of files in the program that have a function with the given name (case INsensitive)
1956
+ * Find a list of files in the program that have a class with the given name (case INsensitive)
1161
1957
  */
1162
1958
  findFilesForClass(className) {
1163
1959
  const files = [];
1164
1960
  const lowerClassName = className.toLowerCase();
1165
1961
  //find every file with this class defined
1166
1962
  for (const file of Object.values(this.files)) {
1167
- if (reflection_1.isBrsFile(file)) {
1963
+ if ((0, reflection_1.isBrsFile)(file)) {
1168
1964
  //TODO handle namespace-relative classes
1169
1965
  //if the file has a function with this name
1170
- if (file.parser.references.classStatementLookup.get(lowerClassName) !== undefined) {
1966
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1967
+ if (file['_cachedLookups'].classStatementMap.get(lowerClassName) !== undefined) {
1968
+ files.push(file);
1969
+ }
1970
+ }
1971
+ }
1972
+ return files;
1973
+ }
1974
+ findFilesForNamespace(name) {
1975
+ const files = [];
1976
+ const lowerName = name.toLowerCase();
1977
+ //find every file with this class defined
1978
+ for (const file of Object.values(this.files)) {
1979
+ if ((0, reflection_1.isBrsFile)(file)) {
1980
+ // eslint-disable-next-line @typescript-eslint/dot-notation
1981
+ if (file['_cachedLookups'].namespaceStatements.find((x) => {
1982
+ const namespaceName = x.name.toLowerCase();
1983
+ return (
1984
+ //the namespace name matches exactly
1985
+ namespaceName === lowerName ||
1986
+ //the full namespace starts with the name (honoring the part boundary)
1987
+ namespaceName.startsWith(lowerName + '.'));
1988
+ })) {
1989
+ files.push(file);
1990
+ }
1991
+ }
1992
+ }
1993
+ return files;
1994
+ }
1995
+ findFilesForEnum(name) {
1996
+ const files = [];
1997
+ const lowerName = name.toLowerCase();
1998
+ //find every file with this enum defined
1999
+ for (const file of Object.values(this.files)) {
2000
+ if ((0, reflection_1.isBrsFile)(file)) {
2001
+ // eslint-disable-next-line @typescript-eslint/dot-notation
2002
+ if (file['_cachedLookups'].enumStatementMap.get(lowerName)) {
1171
2003
  files.push(file);
1172
2004
  }
1173
2005
  }
1174
2006
  }
1175
2007
  return files;
1176
2008
  }
2009
+ /**
2010
+ * Modify a parsed manifest map by reading `bs_const` and injecting values from `options.manifest.bs_const`
2011
+ * @param parsedManifest The manifest map to read from and modify
2012
+ */
2013
+ buildBsConstsIntoParsedManifest(parsedManifest) {
2014
+ var _a, _b;
2015
+ // Lift the bs_consts defined in the manifest
2016
+ let bsConsts = (0, Manifest_1.getBsConst)(parsedManifest, false);
2017
+ // Override or delete any bs_consts defined in the bs config
2018
+ for (const key in (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.manifest) === null || _b === void 0 ? void 0 : _b.bs_const) {
2019
+ const value = this.options.manifest.bs_const[key];
2020
+ if (value === null) {
2021
+ bsConsts.delete(key);
2022
+ }
2023
+ else {
2024
+ bsConsts.set(key, value);
2025
+ }
2026
+ }
2027
+ // convert the new list of bs consts back into a string for the rest of the down stream systems to use
2028
+ let constString = '';
2029
+ for (const [key, value] of bsConsts) {
2030
+ constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
2031
+ }
2032
+ // Set the updated bs_const value
2033
+ parsedManifest.set('bs_const', constString);
2034
+ }
2035
+ /**
2036
+ * Try to find and load the manifest into memory
2037
+ * @param manifestFileObj A pointer to a potential manifest file object found during loading
2038
+ * @param replaceIfAlreadyLoaded should we overwrite the internal `_manifest` if it already exists
2039
+ */
2040
+ loadManifest(manifestFileObj, replaceIfAlreadyLoaded = true) {
2041
+ //if we already have a manifest instance, and should not replace...then don't replace
2042
+ if (!replaceIfAlreadyLoaded && this._manifest) {
2043
+ return;
2044
+ }
2045
+ let manifestPath = manifestFileObj
2046
+ ? manifestFileObj.src
2047
+ : path.join(this.options.rootDir, 'manifest');
2048
+ //store the resolved manifest path so it can be used externally for change detection
2049
+ this.manifestPath = util_1.util.standardizePath(manifestPath);
2050
+ try {
2051
+ // we only load this manifest once, so do it sync to improve speed downstream
2052
+ const contents = fsExtra.readFileSync(manifestPath, 'utf-8');
2053
+ const parsedManifest = (0, Manifest_1.parseManifest)(contents);
2054
+ this.buildBsConstsIntoParsedManifest(parsedManifest);
2055
+ this._manifest = parsedManifest;
2056
+ }
2057
+ catch (e) {
2058
+ this._manifest = new Map();
2059
+ }
2060
+ }
1177
2061
  /**
1178
2062
  * Get a map of the manifest information
1179
2063
  */
1180
2064
  getManifest() {
1181
2065
  if (!this._manifest) {
1182
- //load the manifest file.
1183
- //TODO update this to get the manifest from the files array or require it in the options...we shouldn't assume the location of the manifest
1184
- let manifestPath = path.join(this.options.rootDir, 'manifest');
1185
- let contents;
1186
- try {
1187
- //we only load this manifest once, so do it sync to improve speed downstream
1188
- contents = fsExtra.readFileSync(manifestPath, 'utf-8');
1189
- this._manifest = Manifest_1.parseManifest(contents);
1190
- }
1191
- catch (err) {
1192
- this._manifest = new Map();
1193
- }
2066
+ this.loadManifest();
1194
2067
  }
1195
2068
  return this._manifest;
1196
2069
  }
1197
2070
  dispose() {
1198
- var _a, _b;
1199
- for (const key in this.files) {
1200
- const file = this.files[key];
1201
- (_a = file.dispose) === null || _a === void 0 ? void 0 : _a.call(file);
2071
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2072
+ this.plugins.emit('beforeRemoveProgram', { program: this });
2073
+ for (let filePath in this.files) {
2074
+ (_b = (_a = this.files[filePath]) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
1202
2075
  }
1203
2076
  for (let name in this.scopes) {
1204
- (_b = this.scopes[name]) === null || _b === void 0 ? void 0 : _b.dispose();
2077
+ (_d = (_c = this.scopes[name]) === null || _c === void 0 ? void 0 : _c.dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
1205
2078
  }
1206
- this.globalScope.dispose();
1207
- this.dependencyGraph.dispose();
2079
+ (_f = (_e = this.globalScope) === null || _e === void 0 ? void 0 : _e.dispose) === null || _f === void 0 ? void 0 : _f.call(_e);
2080
+ (_h = (_g = this.dependencyGraph) === null || _g === void 0 ? void 0 : _g.dispose) === null || _h === void 0 ? void 0 : _h.call(_g);
2081
+ this.plugins.emit('removeProgram', { program: this });
2082
+ this.plugins.emit('afterRemoveProgram', { program: this });
1208
2083
  }
1209
2084
  }
1210
2085
  exports.Program = Program;
2086
+ class ProvideFileEventInternal {
2087
+ constructor(program, srcPath, destPath, data, fileFactory) {
2088
+ var _a;
2089
+ this.program = program;
2090
+ this.srcPath = srcPath;
2091
+ this.destPath = destPath;
2092
+ this.data = data;
2093
+ this.fileFactory = fileFactory;
2094
+ this.files = [];
2095
+ this.srcExtension = (_a = path.extname(srcPath)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
2096
+ }
2097
+ }
1211
2098
  //# sourceMappingURL=Program.js.map