brighterscript 1.0.0-alpha.13 → 1.0.0-alpha.16

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 (229) hide show
  1. package/CHANGELOG.md +87 -2
  2. package/dist/Cache.d.ts +3 -8
  3. package/dist/Cache.js +9 -14
  4. package/dist/Cache.js.map +1 -1
  5. package/dist/DependencyGraph.js +5 -4
  6. package/dist/DependencyGraph.js.map +1 -1
  7. package/dist/DiagnosticMessages.d.ts +21 -1
  8. package/dist/DiagnosticMessages.js +21 -1
  9. package/dist/DiagnosticMessages.js.map +1 -1
  10. package/dist/LanguageServer.d.ts +1 -6
  11. package/dist/LanguageServer.js +0 -9
  12. package/dist/LanguageServer.js.map +1 -1
  13. package/dist/PluginInterface.d.ts +3 -3
  14. package/dist/PluginInterface.js +3 -0
  15. package/dist/PluginInterface.js.map +1 -1
  16. package/dist/Program.d.ts +30 -16
  17. package/dist/Program.js +108 -43
  18. package/dist/Program.js.map +1 -1
  19. package/dist/ProgramBuilder.js +3 -3
  20. package/dist/ProgramBuilder.js.map +1 -1
  21. package/dist/Scope.d.ts +29 -15
  22. package/dist/Scope.js +68 -28
  23. package/dist/Scope.js.map +1 -1
  24. package/dist/SymbolTable.d.ts +1 -1
  25. package/dist/XmlScope.d.ts +3 -3
  26. package/dist/astUtils/AstEditor.d.ts +6 -0
  27. package/dist/astUtils/AstEditor.js +10 -0
  28. package/dist/astUtils/AstEditor.js.map +1 -1
  29. package/dist/astUtils/AstEditor.spec.js +37 -0
  30. package/dist/astUtils/AstEditor.spec.js.map +1 -1
  31. package/dist/astUtils/creators.d.ts +8 -4
  32. package/dist/astUtils/creators.js +87 -6
  33. package/dist/astUtils/creators.js.map +1 -1
  34. package/dist/astUtils/reflection.d.ts +5 -1
  35. package/dist/astUtils/reflection.js +15 -3
  36. package/dist/astUtils/reflection.js.map +1 -1
  37. package/dist/astUtils/reflection.spec.js +11 -10
  38. package/dist/astUtils/reflection.spec.js.map +1 -1
  39. package/dist/astUtils/visitors.d.ts +3 -1
  40. package/dist/astUtils/visitors.js.map +1 -1
  41. package/dist/astUtils/visitors.spec.js +8 -8
  42. package/dist/astUtils/visitors.spec.js.map +1 -1
  43. package/dist/bscPlugin/BscPlugin.d.ts +4 -1
  44. package/dist/bscPlugin/BscPlugin.js +21 -2
  45. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  46. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +3 -3
  47. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  48. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  49. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +9 -0
  50. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +97 -0
  51. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -0
  52. package/dist/bscPlugin/semanticTokens/{SemanticTokensProcessor.spec.d.ts → BrsFileSemanticTokensProcessor.spec.d.ts} +0 -0
  53. package/dist/bscPlugin/semanticTokens/{SemanticTokensProcessor.spec.js → BrsFileSemanticTokensProcessor.spec.js} +30 -2
  54. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -0
  55. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.d.ts +8 -0
  56. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js +36 -0
  57. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js.map +1 -0
  58. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +9 -0
  59. package/dist/bscPlugin/validation/BrsFileValidator.js +66 -0
  60. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -0
  61. package/dist/bscPlugin/validation/ScopeValidator.d.ts +11 -0
  62. package/dist/bscPlugin/validation/ScopeValidator.js +94 -0
  63. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -0
  64. package/dist/diagnosticUtils.js +3 -3
  65. package/dist/diagnosticUtils.js.map +1 -1
  66. package/dist/files/BrsFile.Class.spec.js +382 -232
  67. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  68. package/dist/files/BrsFile.d.ts +26 -12
  69. package/dist/files/BrsFile.js +268 -119
  70. package/dist/files/BrsFile.js.map +1 -1
  71. package/dist/files/BrsFile.spec.js +570 -168
  72. package/dist/files/BrsFile.spec.js.map +1 -1
  73. package/dist/files/XmlFile.d.ts +11 -10
  74. package/dist/files/XmlFile.js +16 -11
  75. package/dist/files/XmlFile.js.map +1 -1
  76. package/dist/files/XmlFile.spec.js +60 -58
  77. package/dist/files/XmlFile.spec.js.map +1 -1
  78. package/dist/files/tests/imports.spec.js +8 -6
  79. package/dist/files/tests/imports.spec.js.map +1 -1
  80. package/dist/index.d.ts +12 -3
  81. package/dist/index.js +21 -4
  82. package/dist/index.js.map +1 -1
  83. package/dist/interfaces.d.ts +63 -35
  84. package/dist/lexer/Lexer.js +1 -2
  85. package/dist/lexer/Lexer.js.map +1 -1
  86. package/dist/lexer/Lexer.spec.js +470 -462
  87. package/dist/lexer/Lexer.spec.js.map +1 -1
  88. package/dist/lexer/TokenKind.d.ts +2 -0
  89. package/dist/lexer/TokenKind.js +5 -0
  90. package/dist/lexer/TokenKind.js.map +1 -1
  91. package/dist/parser/Expression.d.ts +18 -16
  92. package/dist/parser/Expression.js +57 -48
  93. package/dist/parser/Expression.js.map +1 -1
  94. package/dist/parser/Parser.Class.spec.js +33 -32
  95. package/dist/parser/Parser.Class.spec.js.map +1 -1
  96. package/dist/parser/Parser.d.ts +28 -7
  97. package/dist/parser/Parser.js +508 -296
  98. package/dist/parser/Parser.js.map +1 -1
  99. package/dist/parser/Parser.spec.js +157 -35
  100. package/dist/parser/Parser.spec.js.map +1 -1
  101. package/dist/parser/SGTypes.spec.js +9 -9
  102. package/dist/parser/SGTypes.spec.js.map +1 -1
  103. package/dist/parser/Statement.d.ts +80 -20
  104. package/dist/parser/Statement.js +257 -92
  105. package/dist/parser/Statement.js.map +1 -1
  106. package/dist/parser/tests/Parser.spec.d.ts +3 -3
  107. package/dist/parser/tests/Parser.spec.js +4 -4
  108. package/dist/parser/tests/Parser.spec.js.map +1 -1
  109. package/dist/parser/tests/controlFlow/For.spec.js +40 -40
  110. package/dist/parser/tests/controlFlow/For.spec.js.map +1 -1
  111. package/dist/parser/tests/controlFlow/ForEach.spec.js +22 -21
  112. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  113. package/dist/parser/tests/controlFlow/If.spec.js +100 -99
  114. package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
  115. package/dist/parser/tests/controlFlow/While.spec.js +25 -25
  116. package/dist/parser/tests/controlFlow/While.spec.js.map +1 -1
  117. package/dist/parser/tests/expression/Additive.spec.js +21 -21
  118. package/dist/parser/tests/expression/Additive.spec.js.map +1 -1
  119. package/dist/parser/tests/expression/ArrayLiterals.spec.js +91 -91
  120. package/dist/parser/tests/expression/ArrayLiterals.spec.js.map +1 -1
  121. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +102 -102
  122. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
  123. package/dist/parser/tests/expression/Boolean.spec.js +15 -15
  124. package/dist/parser/tests/expression/Boolean.spec.js.map +1 -1
  125. package/dist/parser/tests/expression/Call.spec.js +22 -21
  126. package/dist/parser/tests/expression/Call.spec.js.map +1 -1
  127. package/dist/parser/tests/expression/Exponential.spec.js +11 -11
  128. package/dist/parser/tests/expression/Exponential.spec.js.map +1 -1
  129. package/dist/parser/tests/expression/Function.spec.js +171 -171
  130. package/dist/parser/tests/expression/Function.spec.js.map +1 -1
  131. package/dist/parser/tests/expression/Indexing.spec.js +50 -50
  132. package/dist/parser/tests/expression/Indexing.spec.js.map +1 -1
  133. package/dist/parser/tests/expression/Multiplicative.spec.js +25 -25
  134. package/dist/parser/tests/expression/Multiplicative.spec.js.map +1 -1
  135. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +16 -16
  136. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  137. package/dist/parser/tests/expression/PrefixUnary.spec.js +26 -26
  138. package/dist/parser/tests/expression/PrefixUnary.spec.js.map +1 -1
  139. package/dist/parser/tests/expression/Primary.spec.js +27 -27
  140. package/dist/parser/tests/expression/Primary.spec.js.map +1 -1
  141. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +3 -2
  142. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  143. package/dist/parser/tests/expression/Relational.spec.js +25 -25
  144. package/dist/parser/tests/expression/Relational.spec.js.map +1 -1
  145. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +7 -7
  146. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  147. package/dist/parser/tests/expression/TernaryExpression.spec.js +6 -6
  148. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  149. package/dist/parser/tests/statement/AssignmentOperators.spec.js +15 -15
  150. package/dist/parser/tests/statement/AssignmentOperators.spec.js.map +1 -1
  151. package/dist/parser/tests/statement/Declaration.spec.js +20 -20
  152. package/dist/parser/tests/statement/Declaration.spec.js.map +1 -1
  153. package/dist/parser/tests/statement/Enum.spec.d.ts +1 -0
  154. package/dist/parser/tests/statement/Enum.spec.js +774 -0
  155. package/dist/parser/tests/statement/Enum.spec.js.map +1 -0
  156. package/dist/parser/tests/statement/For.spec.d.ts +1 -0
  157. package/dist/parser/tests/statement/For.spec.js +46 -0
  158. package/dist/parser/tests/statement/For.spec.js.map +1 -0
  159. package/dist/parser/tests/statement/ForEach.spec.d.ts +1 -0
  160. package/dist/parser/tests/statement/ForEach.spec.js +37 -0
  161. package/dist/parser/tests/statement/ForEach.spec.js.map +1 -0
  162. package/dist/parser/tests/statement/Function.spec.js +121 -120
  163. package/dist/parser/tests/statement/Function.spec.js.map +1 -1
  164. package/dist/parser/tests/statement/Goto.spec.js +9 -8
  165. package/dist/parser/tests/statement/Goto.spec.js.map +1 -1
  166. package/dist/parser/tests/statement/Increment.spec.js +22 -22
  167. package/dist/parser/tests/statement/Increment.spec.js.map +1 -1
  168. package/dist/parser/tests/statement/InterfaceStatement.spec.js +12 -0
  169. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  170. package/dist/parser/tests/statement/LibraryStatement.spec.js +7 -7
  171. package/dist/parser/tests/statement/LibraryStatement.spec.js.map +1 -1
  172. package/dist/parser/tests/statement/Misc.spec.js +71 -70
  173. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  174. package/dist/parser/tests/statement/PrintStatement.spec.js +17 -17
  175. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  176. package/dist/parser/tests/statement/ReturnStatement.spec.js +33 -33
  177. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +1 -1
  178. package/dist/parser/tests/statement/Set.spec.js +53 -53
  179. package/dist/parser/tests/statement/Set.spec.js.map +1 -1
  180. package/dist/parser/tests/statement/Stop.spec.js +7 -6
  181. package/dist/parser/tests/statement/Stop.spec.js.map +1 -1
  182. package/dist/preprocessor/Chunk.d.ts +1 -1
  183. package/dist/preprocessor/Preprocessor.d.ts +1 -1
  184. package/dist/preprocessor/Preprocessor.js +7 -7
  185. package/dist/preprocessor/Preprocessor.js.map +1 -1
  186. package/dist/types/ArrayType.d.ts +8 -5
  187. package/dist/types/ArrayType.js +45 -9
  188. package/dist/types/ArrayType.js.map +1 -1
  189. package/dist/types/ArrayType.spec.js +62 -3
  190. package/dist/types/ArrayType.spec.js.map +1 -1
  191. package/dist/types/BscType.d.ts +1 -1
  192. package/dist/types/CustomType.d.ts +1 -1
  193. package/dist/types/CustomType.js +4 -2
  194. package/dist/types/CustomType.js.map +1 -1
  195. package/dist/types/FunctionType.d.ts +7 -6
  196. package/dist/types/FunctionType.js +21 -18
  197. package/dist/types/FunctionType.js.map +1 -1
  198. package/dist/types/FunctionType.spec.js +6 -0
  199. package/dist/types/FunctionType.spec.js.map +1 -1
  200. package/dist/types/LazyType.d.ts +1 -2
  201. package/dist/types/LazyType.js +1 -5
  202. package/dist/types/LazyType.js.map +1 -1
  203. package/dist/types/UniversalFunctionType.d.ts +9 -0
  204. package/dist/types/UniversalFunctionType.js +25 -0
  205. package/dist/types/UniversalFunctionType.js.map +1 -0
  206. package/dist/types/helpers.js +1 -1
  207. package/dist/types/helpers.js.map +1 -1
  208. package/dist/util.d.ts +26 -10
  209. package/dist/util.js +145 -61
  210. package/dist/util.js.map +1 -1
  211. package/dist/validators/ClassValidator.js +17 -24
  212. package/dist/validators/ClassValidator.js.map +1 -1
  213. package/package.json +3 -3
  214. package/dist/astUtils/index.d.ts +0 -7
  215. package/dist/astUtils/index.js +0 -26
  216. package/dist/astUtils/index.js.map +0 -1
  217. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.d.ts +0 -7
  218. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js +0 -63
  219. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js.map +0 -1
  220. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +0 -1
  221. package/dist/lexer/index.d.ts +0 -3
  222. package/dist/lexer/index.js +0 -18
  223. package/dist/lexer/index.js.map +0 -1
  224. package/dist/parser/index.d.ts +0 -3
  225. package/dist/parser/index.js +0 -16
  226. package/dist/parser/index.js.map +0 -1
  227. package/dist/preprocessor/index.d.ts +0 -3
  228. package/dist/preprocessor/index.js +0 -16
  229. package/dist/preprocessor/index.js.map +0 -1
@@ -6,8 +6,11 @@ const vscode_languageserver_1 = require("vscode-languageserver");
6
6
  const chalk_1 = require("chalk");
7
7
  const path = require("path");
8
8
  const DiagnosticMessages_1 = require("../DiagnosticMessages");
9
- const lexer_1 = require("../lexer");
10
- const parser_1 = require("../parser");
9
+ const Token_1 = require("../lexer/Token");
10
+ const Lexer_1 = require("../lexer/Lexer");
11
+ const TokenKind_1 = require("../lexer/TokenKind");
12
+ const Parser_1 = require("../parser/Parser");
13
+ const DynamicType_1 = require("../types/DynamicType");
11
14
  const util_1 = require("../util");
12
15
  const BrsTranspileState_1 = require("../parser/BrsTranspileState");
13
16
  const Preprocessor_1 = require("../preprocessor/Preprocessor");
@@ -19,8 +22,6 @@ const CommentFlagProcessor_1 = require("../CommentFlagProcessor");
19
22
  const BscType_1 = require("../types/BscType");
20
23
  const UninitializedType_1 = require("../types/UninitializedType");
21
24
  const InvalidType_1 = require("../types/InvalidType");
22
- const globalCallables_1 = require("../globalCallables");
23
- const DynamicType_1 = require("../types/DynamicType");
24
25
  /**
25
26
  * Holds all details about this file within the scope of the whole program
26
27
  */
@@ -41,9 +42,10 @@ class BrsFile {
41
42
  /**
42
43
  * The parseMode used for the parser for this file
43
44
  */
44
- this.parseMode = parser_1.ParseMode.BrightScript;
45
+ this.parseMode = Parser_1.ParseMode.BrightScript;
45
46
  /**
46
47
  * Indicates whether this file needs to be validated.
48
+ * Files are only ever validated a single time
47
49
  */
48
50
  this.isValidated = false;
49
51
  this.diagnostics = [];
@@ -64,7 +66,7 @@ class BrsFile {
64
66
  //all BrighterScript files need to be transpiled
65
67
  if ((_a = this.extension) === null || _a === void 0 ? void 0 : _a.endsWith('.bs')) {
66
68
  this.needsTranspiled = true;
67
- this.parseMode = parser_1.ParseMode.BrighterScript;
69
+ this.parseMode = Parser_1.ParseMode.BrighterScript;
68
70
  }
69
71
  this.isTypedef = this.extension === '.d.bs';
70
72
  if (!this.isTypedef) {
@@ -111,9 +113,8 @@ class BrsFile {
111
113
  * Also notify the dependency graph of our current dependencies so other dependents can be notified.
112
114
  */
113
115
  attachDependencyGraph(dependencyGraph) {
114
- if (this.unsubscribeFromDependencyGraph) {
115
- this.unsubscribeFromDependencyGraph();
116
- }
116
+ var _a;
117
+ (_a = this.unsubscribeFromDependencyGraph) === null || _a === void 0 ? void 0 : _a.call(this);
117
118
  //event that fires anytime a dependency changes
118
119
  this.unsubscribeFromDependencyGraph = dependencyGraph.onchange(this.dependencyGraphKey, () => {
119
120
  this.resolveTypedef();
@@ -135,11 +136,13 @@ class BrsFile {
135
136
  this.diagnostics = [];
136
137
  //if we have a typedef file, skip parsing this file
137
138
  if (this.hasTypedef) {
139
+ //skip validation since the typedef is shadowing this file
140
+ this.isValidated = true;
138
141
  return;
139
142
  }
140
143
  //tokenize the input file
141
144
  let lexer = this.program.logger.time(Logger_1.LogLevel.debug, ['lexer.lex', chalk_1.default.green(this.srcPath)], () => {
142
- return lexer_1.Lexer.scan(fileContents, {
145
+ return Lexer_1.Lexer.scan(fileContents, {
143
146
  includeWhitespace: false
144
147
  });
145
148
  });
@@ -162,7 +165,7 @@ class BrsFile {
162
165
  //if the preprocessor generated tokens, use them.
163
166
  let tokens = preprocessor.processedTokens.length > 0 ? preprocessor.processedTokens : lexer.tokens;
164
167
  this.program.logger.time(Logger_1.LogLevel.debug, ['parser.parse', chalk_1.default.green(this.srcPath)], () => {
165
- this._parser = parser_1.Parser.parse(tokens, {
168
+ this._parser = Parser_1.Parser.parse(tokens, {
166
169
  mode: this.parseMode,
167
170
  logger: this.program.logger
168
171
  });
@@ -173,20 +176,39 @@ class BrsFile {
173
176
  this.findCallables();
174
177
  //find all places where a sub/function is being called
175
178
  this.findFunctionCalls();
176
- this.findAndValidateImportAndImportStatements();
179
+ //register all import statements for use in the rest of the program
180
+ this.registerImports();
177
181
  //attach this file to every diagnostic
178
182
  for (let diagnostic of this.diagnostics) {
179
183
  diagnostic.file = this;
180
184
  }
181
185
  }
182
186
  catch (e) {
183
- this._parser = new parser_1.Parser();
187
+ this._parser = new Parser_1.Parser();
184
188
  this.diagnostics.push(Object.assign({ file: this, range: util_1.util.createRange(0, 0, 0, Number.MAX_VALUE) }, DiagnosticMessages_1.DiagnosticMessages.genericParserMessage('Critical error parsing file: ' + JSON.stringify((0, serialize_error_1.serializeError)(e)))));
185
189
  }
186
190
  }
187
- validate() { }
188
- findAndValidateImportAndImportStatements() {
189
- var _a;
191
+ registerImports() {
192
+ var _a, _b, _c, _d;
193
+ for (const statement of (_c = (_b = (_a = this.parser) === null || _a === void 0 ? void 0 : _a.references) === null || _b === void 0 ? void 0 : _b.importStatements) !== null && _c !== void 0 ? _c : []) {
194
+ //register import statements
195
+ if ((0, reflection_1.isImportStatement)(statement) && statement.filePathToken) {
196
+ this.ownScriptImports.push({
197
+ filePathRange: statement.filePathToken.range,
198
+ pkgPath: util_1.util.getPkgPathFromTarget(this.pkgPath, statement.filePath),
199
+ sourceFile: this,
200
+ text: (_d = statement.filePathToken) === null || _d === void 0 ? void 0 : _d.text
201
+ });
202
+ }
203
+ }
204
+ }
205
+ validate() {
206
+ //only validate the file if it was actually parsed (skip files containing typedefs)
207
+ if (!this.hasTypedef) {
208
+ this.validateImportStatements();
209
+ }
210
+ }
211
+ validateImportStatements() {
190
212
  let topOfFileIncludeStatements = [];
191
213
  for (let stmt of this.ast.statements) {
192
214
  //skip comments
@@ -207,15 +229,6 @@ class BrsFile {
207
229
  ...this._parser.references.importStatements
208
230
  ];
209
231
  for (let result of statements) {
210
- //register import statements
211
- if ((0, reflection_1.isImportStatement)(result) && result.filePathToken) {
212
- this.ownScriptImports.push({
213
- filePathRange: result.filePathToken.range,
214
- pkgPath: util_1.util.getPkgPathFromTarget(this.pkgPath, result.filePath),
215
- sourceFile: this,
216
- text: (_a = result.filePathToken) === null || _a === void 0 ? void 0 : _a.text
217
- });
218
- }
219
232
  //if this statement is not one of the top-of-file statements,
220
233
  //then add a diagnostic explaining that it is invalid
221
234
  if (!topOfFileIncludeStatements.includes(result)) {
@@ -273,7 +286,7 @@ class BrsFile {
273
286
  const processor = new CommentFlagProcessor_1.CommentFlagProcessor(this, ['rem', `'`], DiagnosticMessages_1.diagnosticCodes, [DiagnosticMessages_1.DiagnosticCodeMap.unknownDiagnosticCode]);
274
287
  this.commentFlags = [];
275
288
  for (let token of tokens) {
276
- if (token.kind === lexer_1.TokenKind.Comment) {
289
+ if (token.kind === TokenKind_1.TokenKind.Comment) {
277
290
  processor.tryAdd(token.text, token.range);
278
291
  }
279
292
  }
@@ -325,7 +338,7 @@ class BrsFile {
325
338
  let args = [];
326
339
  //TODO convert if stmts to use instanceof instead
327
340
  for (let arg of expression.args) {
328
- let inferredType = (0, parser_1.getBscTypeFromExpression)(arg, func);
341
+ let inferredType = (0, Parser_1.getBscTypeFromExpression)(arg, func);
329
342
  let argText = '';
330
343
  // Get the text to display for the arg
331
344
  if (arg.token) {
@@ -333,7 +346,7 @@ class BrsFile {
333
346
  //is a function call being passed into argument
334
347
  }
335
348
  else if (arg.name) {
336
- if ((0, lexer_1.isToken)(arg.name)) {
349
+ if ((0, Token_1.isToken)(arg.name)) {
337
350
  argText = arg.name.text;
338
351
  }
339
352
  }
@@ -388,6 +401,20 @@ class BrsFile {
388
401
  }
389
402
  }
390
403
  }
404
+ /**
405
+ * Find the NamespaceStatement enclosing the given position
406
+ * @param position
407
+ * @param functionScopes
408
+ */
409
+ getNamespaceStatementForPosition(position) {
410
+ if (position) {
411
+ for (const statement of this.parser.references.namespaceStatements) {
412
+ if (util_1.util.rangeContains(statement.range, position)) {
413
+ return statement;
414
+ }
415
+ }
416
+ }
417
+ }
391
418
  /**
392
419
  * Get completions available at the given cursor. This aggregates all values from this file and the current scope.
393
420
  */
@@ -404,10 +431,10 @@ class BrsFile {
404
431
  //if cursor is within a comment, disable completions
405
432
  let currentToken = this.parser.getTokenAt(position);
406
433
  const tokenKind = currentToken === null || currentToken === void 0 ? void 0 : currentToken.kind;
407
- if (tokenKind === lexer_1.TokenKind.Comment) {
434
+ if (tokenKind === TokenKind_1.TokenKind.Comment) {
408
435
  return [];
409
436
  }
410
- else if (tokenKind === lexer_1.TokenKind.StringLiteral || tokenKind === lexer_1.TokenKind.TemplateStringQuasi) {
437
+ else if (tokenKind === TokenKind_1.TokenKind.StringLiteral || tokenKind === TokenKind_1.TokenKind.TemplateStringQuasi) {
411
438
  const match = /^("?)(pkg|libpkg):/.exec(currentToken.text);
412
439
  if (match) {
413
440
  const [, openingQuote, fileProtocol] = match;
@@ -431,38 +458,44 @@ class BrsFile {
431
458
  return [];
432
459
  }
433
460
  }
434
- let namespaceCompletions = this.getNamespaceCompletions(currentToken, this.parseMode, scope);
461
+ const namespaceCompletions = this.getNamespaceCompletions(currentToken, this.parseMode, scope);
435
462
  if (namespaceCompletions.length > 0) {
436
- return namespaceCompletions;
463
+ return [...namespaceCompletions];
464
+ }
465
+ const enumMemberCompletions = this.getEnumMemberStatementCompletions(currentToken, this.parseMode, scope);
466
+ if (enumMemberCompletions.length > 0) {
467
+ // no other completion is valid in this case
468
+ return enumMemberCompletions;
437
469
  }
438
470
  //determine if cursor is inside a function
439
471
  let functionExpression = this.getFunctionExpressionAtPosition(position);
440
472
  if (!functionExpression) {
441
473
  //we aren't in any function scope, so return the keyword completions and namespaces
442
- if (this.parser.getTokenBefore(currentToken, lexer_1.TokenKind.New)) {
474
+ if (this.parser.getTokenBefore(currentToken, TokenKind_1.TokenKind.New)) {
443
475
  // there's a new keyword, so only class types are viable here
444
476
  return [...this.getGlobalClassStatementCompletions(currentToken, this.parseMode)];
445
477
  }
446
478
  else {
447
- return [...exports.KeywordCompletions, ...this.getGlobalClassStatementCompletions(currentToken, this.parseMode), ...namespaceCompletions];
479
+ return [
480
+ ...exports.KeywordCompletions,
481
+ ...this.getGlobalClassStatementCompletions(currentToken, this.parseMode),
482
+ ...namespaceCompletions,
483
+ ...this.getNonNamespacedEnumStatementCompletions(currentToken, this.parseMode, scope)
484
+ ];
448
485
  }
449
486
  }
450
487
  const classNameCompletions = this.getGlobalClassStatementCompletions(currentToken, this.parseMode);
451
- const newToken = this.parser.getTokenBefore(currentToken, lexer_1.TokenKind.New);
488
+ const newToken = this.parser.getTokenBefore(currentToken, TokenKind_1.TokenKind.New);
452
489
  if (newToken) {
453
- //we are after a new keyword; so we can only be namespaces or classes at this point
490
+ //we are after a new keyword; so we can only be top-level namespaces or classes at this point
454
491
  result.push(...classNameCompletions);
455
492
  result.push(...namespaceCompletions);
456
493
  return result;
457
494
  }
458
- if (this.parser.tokenFollows(currentToken, lexer_1.TokenKind.Goto)) {
495
+ if (this.parser.tokenFollows(currentToken, TokenKind_1.TokenKind.Goto)) {
459
496
  return this.getLabelCompletion(functionExpression);
460
497
  }
461
- if (this.parser.isPositionNextToTokenKind(position, lexer_1.TokenKind.Dot)) {
462
- if (namespaceCompletions.length > 0) {
463
- //if we matched a namespace, after a dot, it can't be anything else but something from our namespace completions
464
- return namespaceCompletions;
465
- }
498
+ if (this.parser.isPositionNextToTokenKind(position, TokenKind_1.TokenKind.Dot)) {
466
499
  const selfClassMemberCompletions = this.getClassMemberCompletions(position, currentToken, functionExpression, scope);
467
500
  if (selfClassMemberCompletions.size > 0) {
468
501
  return [...selfClassMemberCompletions.values()].filter((i) => i.label !== 'new');
@@ -487,6 +520,8 @@ class BrsFile {
487
520
  result.push(...namespaceCompletions);
488
521
  //include class names
489
522
  result.push(...classNameCompletions);
523
+ //include enums
524
+ result.push(...this.getNonNamespacedEnumStatementCompletions(currentToken, this.parseMode, scope));
490
525
  //include the global callables
491
526
  result.push(...scope.getCallablesAsCompletions(this.parseMode));
492
527
  //add `m` because that's always valid within a function
@@ -514,7 +549,7 @@ class BrsFile {
514
549
  // kind: isFunctionType(foundType) ? CompletionItemKind.Function : CompletionItemKind.Variable
515
550
  });
516
551
  }
517
- if (this.parseMode === parser_1.ParseMode.BrighterScript) {
552
+ if (this.parseMode === Parser_1.ParseMode.BrighterScript) {
518
553
  //include the first part of namespaces
519
554
  let namespaces = scope.getAllNamespaceStatements();
520
555
  for (let stmt of namespaces) {
@@ -552,7 +587,7 @@ class BrsFile {
552
587
  let classStatement = this.getClassFromToken(currentToken, functionExpression, scope);
553
588
  let results = new Map();
554
589
  if (classStatement) {
555
- let classes = scope.getClassHierarchy(classStatement.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
590
+ let classes = scope.getClassHierarchy(classStatement.item.getName(Parser_1.ParseMode.BrighterScript).toLowerCase());
556
591
  for (let cs of classes) {
557
592
  for (let member of [...(_b = (_a = cs === null || cs === void 0 ? void 0 : cs.item) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : [], ...(_d = (_c = cs === null || cs === void 0 ? void 0 : cs.item) === null || _c === void 0 ? void 0 : _c.methods) !== null && _d !== void 0 ? _d : []]) {
558
593
  if (!results.has(member.name.text.toLowerCase())) {
@@ -602,7 +637,7 @@ class BrsFile {
602
637
  let startsWithNamespace = '';
603
638
  let namespaceContainer;
604
639
  let tokenChain = [...originalTokenChain];
605
- while (tokenChain[0] && tokenChain[0].usage === parser_1.TokenUsage.Direct) {
640
+ while (tokenChain[0] && tokenChain[0].usage === Parser_1.TokenUsage.Direct) {
606
641
  const namespaceNameToCheck = `${startsWithNamespace}${startsWithNamespace.length > 0 ? '.' : ''}${tokenChain[0].token.text}`.toLowerCase();
607
642
  const foundNamespace = scope.namespaceLookup.get(namespaceNameToCheck);
608
643
  if (foundNamespace) {
@@ -633,7 +668,7 @@ class BrsFile {
633
668
  let useExpandedTextOnly = false;
634
669
  if (containingClass.name === currentToken) {
635
670
  symbolType = containingClass.getCustomType();
636
- expandedText = `class ${containingClass.getName(parser_1.ParseMode.BrighterScript)}`;
671
+ expandedText = `class ${containingClass.getName(Parser_1.ParseMode.BrighterScript)}`;
637
672
  useExpandedTextOnly = true;
638
673
  currentClassRef = containingClass;
639
674
  }
@@ -652,13 +687,13 @@ class BrsFile {
652
687
  // check if this is a method declaration
653
688
  currentClassRef = containingClass;
654
689
  symbolType = containingClass === null || containingClass === void 0 ? void 0 : containingClass.memberTable.getSymbolType(currentTokenLower, true, { file: this, scope: scope });
655
- expandedText = [containingClass.getName(parser_1.ParseMode.BrighterScript), currentToken.text].join('.');
690
+ expandedText = [containingClass.getName(Parser_1.ParseMode.BrighterScript), currentToken.text].join('.');
656
691
  }
657
692
  else if (!func) {
658
693
  // check if this is a field declaration
659
694
  currentClassRef = containingClass;
660
695
  symbolType = containingClass === null || containingClass === void 0 ? void 0 : containingClass.memberTable.getSymbolType(currentTokenLower, true, { file: this, scope: scope });
661
- expandedText = [containingClass.getName(parser_1.ParseMode.BrighterScript), currentToken.text].join('.');
696
+ expandedText = [containingClass.getName(Parser_1.ParseMode.BrighterScript), currentToken.text].join('.');
662
697
  }
663
698
  if (symbolType) {
664
699
  return { type: symbolType, expandedTokenText: expandedText, symbolContainer: currentClassRef, useExpandedTextOnly: useExpandedTextOnly };
@@ -690,7 +725,7 @@ class BrsFile {
690
725
  */
691
726
  getSymbolTypeFromToken(currentToken, functionExpression, scope) {
692
727
  var _a, _b, _c, _d, _e;
693
- if (!scope) {
728
+ if (!scope || !currentToken) {
694
729
  return undefined;
695
730
  }
696
731
  const cachedSymbolData = scope.symbolCache.get(currentToken);
@@ -720,13 +755,14 @@ class BrsFile {
720
755
  const typeContext = { file: this, scope: scope, position: (_c = tokenChain[0]) === null || _c === void 0 ? void 0 : _c.token.range.start };
721
756
  for (const tokenChainMember of tokenChain) {
722
757
  const token = tokenChainMember === null || tokenChainMember === void 0 ? void 0 : tokenChainMember.token;
758
+ const tokenUsage = tokenChainMember === null || tokenChainMember === void 0 ? void 0 : tokenChainMember.usage;
723
759
  const tokenLowerText = token.text.toLowerCase();
724
760
  if (tokenLowerText === 'super' && (0, reflection_1.isClassStatement)(symbolContainer) && tokenFoundCount === 0) {
725
761
  /// Special cases for first item in chain inside a class
726
762
  symbolContainer = scope === null || scope === void 0 ? void 0 : scope.getParentClass(symbolContainer);
727
763
  currentSymbolTable = (_d = symbolContainer) === null || _d === void 0 ? void 0 : _d.memberTable;
728
764
  if (symbolContainer && currentSymbolTable) {
729
- tokenText.push(symbolContainer.getName(parser_1.ParseMode.BrighterScript));
765
+ tokenText.push(symbolContainer.getName(Parser_1.ParseMode.BrighterScript));
730
766
  tokenFoundCount++;
731
767
  continue;
732
768
  }
@@ -738,7 +774,7 @@ class BrsFile {
738
774
  symbolType = currentSymbolTable.getSymbolType(tokenLowerText, true, typeContext);
739
775
  if (tokenFoundCount === 0 && !symbolType) {
740
776
  //check for global callable
741
- symbolType = (_e = globalCallables_1.globalCallableMap.get(tokenLowerText)) === null || _e === void 0 ? void 0 : _e.type;
777
+ symbolType = (_e = scope.getGlobalCallableByName(tokenLowerText)) === null || _e === void 0 ? void 0 : _e.type;
742
778
  }
743
779
  if (symbolType) {
744
780
  // found this symbol, and it's valid. increase found counter
@@ -750,10 +786,13 @@ class BrsFile {
750
786
  // the next symbol to check will be the return value of this function
751
787
  symbolType = (0, BscType_1.getTypeFromContext)(symbolType.returnType, typeContext);
752
788
  if (tokenFoundCount < tokenChain.length) {
753
- // We're still
789
+ // We still have more tokens, but remember the last known reference
754
790
  symbolTypeBeforeReference = symbolType;
755
791
  }
756
792
  }
793
+ if ((0, reflection_1.isArrayType)(symbolType) && tokenUsage === Parser_1.TokenUsage.ArrayReference) {
794
+ symbolType = (0, BscType_1.getTypeFromContext)(symbolType.getDefaultType(typeContext), typeContext);
795
+ }
757
796
  if (symbolType === null || symbolType === void 0 ? void 0 : symbolType.memberTable) {
758
797
  if ((0, reflection_1.isCustomType)(symbolType)) {
759
798
  // we're currently looking at a customType, that has it's own symbol table
@@ -783,9 +822,12 @@ class BrsFile {
783
822
  tokenText.push(token.text);
784
823
  break;
785
824
  }
786
- if (tokenText.length > 2) {
787
- tokenText.shift(); // only care about last two symbols
788
- }
825
+ }
826
+ if (tokenText.length > 2) {
827
+ // TokenText is used for hovers. We only need the last two tokens for a hover
828
+ // So in a long chain (e.g. klass.getData()[0].anotherKlass.property), the hover
829
+ // for the last token should just be "AnotherKlass.property", not the whole chain
830
+ tokenText = tokenText.slice(-2);
789
831
  }
790
832
  let expandedTokenText = tokenText.join('.');
791
833
  let backUpReturnType;
@@ -827,18 +869,17 @@ class BrsFile {
827
869
  }
828
870
  getGlobalClassStatementCompletions(currentToken, parseMode) {
829
871
  var _a;
830
- if (parseMode === parser_1.ParseMode.BrightScript) {
872
+ if (parseMode === Parser_1.ParseMode.BrightScript) {
831
873
  return [];
832
874
  }
833
875
  let results = new Map();
834
- let completionName = (_a = this.getPartialVariableName(currentToken, [lexer_1.TokenKind.New])) === null || _a === void 0 ? void 0 : _a.toLowerCase();
876
+ let completionName = (_a = this.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New])) === null || _a === void 0 ? void 0 : _a.toLowerCase();
835
877
  if (completionName === null || completionName === void 0 ? void 0 : completionName.includes('.')) {
836
878
  return [];
837
879
  }
838
880
  let scopes = this.program.getScopesForFile(this);
839
881
  for (let scope of scopes) {
840
882
  let classMap = scope.getClassMap();
841
- // let viableKeys = [...classMap.keys()].filter((k) => k.startsWith(completionName));
842
883
  for (const key of [...classMap.keys()]) {
843
884
  let cs = classMap.get(key).item;
844
885
  if (!results.has(cs.name.text)) {
@@ -851,19 +892,70 @@ class BrsFile {
851
892
  }
852
893
  return [...results.values()];
853
894
  }
895
+ getNonNamespacedEnumStatementCompletions(currentToken, parseMode, scope) {
896
+ var _a, _b;
897
+ if (parseMode !== Parser_1.ParseMode.BrighterScript) {
898
+ return [];
899
+ }
900
+ const containingNamespaceName = ((_b = this.getNamespaceStatementForPosition((_a = currentToken === null || currentToken === void 0 ? void 0 : currentToken.range) === null || _a === void 0 ? void 0 : _a.start)) === null || _b === void 0 ? void 0 : _b.name) + '.';
901
+ const results = new Map();
902
+ const enumMap = scope.getEnumMap();
903
+ for (const key of [...enumMap.keys()]) {
904
+ const enumStatement = enumMap.get(key).item;
905
+ const fullName = enumStatement.fullName;
906
+ //if the enum is contained within our own namespace, or if it's a non-namespaced enum
907
+ if (fullName.startsWith(containingNamespaceName) || !fullName.includes('.')) {
908
+ results.set(fullName, {
909
+ label: enumStatement.name,
910
+ kind: vscode_languageserver_1.CompletionItemKind.Enum
911
+ });
912
+ }
913
+ }
914
+ return [...results.values()];
915
+ }
916
+ getEnumMemberStatementCompletions(currentToken, parseMode, scope) {
917
+ var _a, _b, _c, _d, _e;
918
+ if (parseMode === Parser_1.ParseMode.BrightScript || !currentToken) {
919
+ return [];
920
+ }
921
+ const results = new Map();
922
+ const completionName = (_a = this.getPartialVariableName(currentToken)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
923
+ //if we don't have a completion name, or if there's no period in the name, then this is not to the right of an enum name
924
+ if (!completionName || !completionName.includes('.')) {
925
+ return [];
926
+ }
927
+ const enumNameLower = (_b = completionName === null || completionName === void 0 ? void 0 : completionName.split(/\.(\w+)?$/)[0]) === null || _b === void 0 ? void 0 : _b.toLowerCase();
928
+ const namespaceNameLower = (_c = this.getNamespaceStatementForPosition(currentToken.range.end)) === null || _c === void 0 ? void 0 : _c.name.toLowerCase();
929
+ const enumMap = scope.getEnumMap();
930
+ //get the enum statement with this name (check without namespace prefix first, then with inferred namespace prefix next)
931
+ const enumStatement = (_e = ((_d = enumMap.get(enumNameLower)) !== null && _d !== void 0 ? _d : enumMap.get(namespaceNameLower + '.' + enumNameLower))) === null || _e === void 0 ? void 0 : _e.item;
932
+ //if we found an enum with this name
933
+ if (enumStatement) {
934
+ for (const member of enumStatement.getMembers()) {
935
+ const name = enumStatement.fullName + '.' + member.name;
936
+ const nameLower = name.toLowerCase();
937
+ results.set(nameLower, {
938
+ label: member.name,
939
+ kind: vscode_languageserver_1.CompletionItemKind.EnumMember
940
+ });
941
+ }
942
+ }
943
+ return [...results.values()];
944
+ }
854
945
  getNamespaceCompletions(currentToken, parseMode, scope) {
855
946
  //BrightScript does not support namespaces, so return an empty list in that case
856
- if (parseMode === parser_1.ParseMode.BrightScript) {
947
+ if (parseMode === Parser_1.ParseMode.BrightScript) {
857
948
  return [];
858
949
  }
859
- let completionName = this.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
860
- if (!completionName) {
950
+ const completionName = this.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New]);
951
+ //if we don't have a completion name, or if there's no period in the name, then this is not a namespaced variable
952
+ if (!completionName || !completionName.includes('.')) {
861
953
  return [];
862
954
  }
863
955
  //remove any trailing identifer and then any trailing dot, to give us the
864
956
  //name of its immediate parent namespace
865
- let closestParentNamespaceName = completionName.replace(/\.([a-z0-9_]*)?$/gi, '');
866
- let newToken = this.parser.getTokenBefore(currentToken, lexer_1.TokenKind.New);
957
+ let closestParentNamespaceName = completionName.replace(/\.([a-z0-9_]*)?$/gi, '').toLowerCase();
958
+ let newToken = this.parser.getTokenBefore(currentToken, TokenKind_1.TokenKind.New);
867
959
  let result = new Map();
868
960
  for (let [, namespace] of scope.namespaceLookup) {
869
961
  //completionName = "NameA."
@@ -871,7 +963,7 @@ class BrsFile {
871
963
  //NameA
872
964
  //NameA.NameB
873
965
  //NameA.NameB.NameC
874
- if (namespace.fullName.toLowerCase() === closestParentNamespaceName.toLowerCase()) {
966
+ if (namespace.fullName.toLowerCase() === closestParentNamespaceName) {
875
967
  //add all of this namespace's immediate child namespaces, bearing in mind if we are after a new keyword
876
968
  for (let [, ns] of namespace.namespaces) {
877
969
  if (!newToken || ns.statements.find((s) => (0, reflection_1.isClassStatement)(s))) {
@@ -886,20 +978,22 @@ class BrsFile {
886
978
  //add function and class statement completions
887
979
  for (let stmt of namespace.statements) {
888
980
  if ((0, reflection_1.isClassStatement)(stmt)) {
889
- if (!result.has(stmt.name.text)) {
890
- result.set(stmt.name.text, {
891
- label: stmt.name.text,
892
- kind: vscode_languageserver_1.CompletionItemKind.Class
893
- });
894
- }
981
+ result.set(stmt.name.text, {
982
+ label: stmt.name.text,
983
+ kind: vscode_languageserver_1.CompletionItemKind.Class
984
+ });
895
985
  }
896
986
  else if ((0, reflection_1.isFunctionStatement)(stmt) && !newToken) {
897
- if (!result.has(stmt.name.text)) {
898
- result.set(stmt.name.text, {
899
- label: stmt.name.text,
900
- kind: vscode_languageserver_1.CompletionItemKind.Function
901
- });
902
- }
987
+ result.set(stmt.name.text, {
988
+ label: stmt.name.text,
989
+ kind: vscode_languageserver_1.CompletionItemKind.Function
990
+ });
991
+ }
992
+ else if ((0, reflection_1.isEnumStatement)(stmt) && !newToken) {
993
+ result.set(stmt.name, {
994
+ label: stmt.name,
995
+ kind: vscode_languageserver_1.CompletionItemKind.Enum
996
+ });
903
997
  }
904
998
  }
905
999
  }
@@ -912,11 +1006,11 @@ class BrsFile {
912
1006
  return undefined;
913
1007
  }
914
1008
  let location;
915
- const nameParts = this.getPartialVariableName(token, [lexer_1.TokenKind.New]).split('.');
1009
+ const nameParts = this.getPartialVariableName(token, [TokenKind_1.TokenKind.New]).split('.');
916
1010
  const endName = nameParts[nameParts.length - 1].toLowerCase();
917
1011
  const namespaceName = nameParts.slice(0, -1).join('.').toLowerCase();
918
1012
  const statementHandler = (statement) => {
919
- if (!location && statement.getName(parser_1.ParseMode.BrighterScript).toLowerCase() === namespaceName) {
1013
+ if (!location && statement.getName(Parser_1.ParseMode.BrighterScript).toLowerCase() === namespaceName) {
920
1014
  const namespaceItemStatementHandler = (statement) => {
921
1015
  if (!location && statement.name.text.toLowerCase() === endName) {
922
1016
  const uri = util_1.util.pathToUri(file.srcPath);
@@ -942,7 +1036,7 @@ class BrsFile {
942
1036
  * Given a current token, walk
943
1037
  */
944
1038
  getPartialVariableName(currentToken, excludeTokens = null) {
945
- let identifierAndDotKinds = [lexer_1.TokenKind.Identifier, ...lexer_1.AllowedLocalIdentifiers, lexer_1.TokenKind.Dot];
1039
+ let identifierAndDotKinds = [TokenKind_1.TokenKind.Identifier, ...TokenKind_1.AllowedLocalIdentifiers, TokenKind_1.TokenKind.Dot];
946
1040
  //consume tokens backwards until we find something other than a dot or an identifier
947
1041
  let tokens = [];
948
1042
  const parser = this.parser;
@@ -1073,7 +1167,7 @@ class BrsFile {
1073
1167
  else {
1074
1168
  return;
1075
1169
  }
1076
- const name = (0, reflection_1.isClassFieldStatement)(statement) ? statement.name.text : statement.getName(parser_1.ParseMode.BrighterScript);
1170
+ const name = (0, reflection_1.isClassFieldStatement)(statement) ? statement.name.text : statement.getName(Parser_1.ParseMode.BrighterScript);
1077
1171
  return vscode_languageserver_1.DocumentSymbol.create(name, '', symbolKind, statement.range, statement.range, children);
1078
1172
  }
1079
1173
  /**
@@ -1107,9 +1201,9 @@ class BrsFile {
1107
1201
  else {
1108
1202
  return symbols;
1109
1203
  }
1110
- const name = statement.getName(parser_1.ParseMode.BrighterScript);
1204
+ const name = statement.getName(Parser_1.ParseMode.BrighterScript);
1111
1205
  const uri = util_1.util.pathToUri(this.srcPath);
1112
- const symbol = vscode_languageserver_1.SymbolInformation.create(name, symbolKind, statement.range, uri, containerStatement === null || containerStatement === void 0 ? void 0 : containerStatement.getName(parser_1.ParseMode.BrighterScript));
1206
+ const symbol = vscode_languageserver_1.SymbolInformation.create(name, symbolKind, statement.range, uri, containerStatement === null || containerStatement === void 0 ? void 0 : containerStatement.getName(Parser_1.ParseMode.BrighterScript));
1113
1207
  symbols.push(symbol);
1114
1208
  return symbols;
1115
1209
  }
@@ -1123,8 +1217,8 @@ class BrsFile {
1123
1217
  const token = this.parser.getTokenAt(position);
1124
1218
  // While certain other tokens are allowed as local variables (AllowedLocalIdentifiers: https://github.com/rokucommunity/brighterscript/blob/master/src/lexer/TokenKind.ts#L418), these are converted by the parser to TokenKind.Identifier by the time we retrieve the token using getTokenAt
1125
1219
  let definitionTokenTypes = [
1126
- lexer_1.TokenKind.Identifier,
1127
- lexer_1.TokenKind.StringLiteral
1220
+ TokenKind_1.TokenKind.Identifier,
1221
+ TokenKind_1.TokenKind.StringLiteral
1128
1222
  ];
1129
1223
  //throw out invalid tokens and the wrong kind of tokens
1130
1224
  if (!token || !definitionTokenTypes.includes(token.kind)) {
@@ -1132,7 +1226,7 @@ class BrsFile {
1132
1226
  }
1133
1227
  let textToSearchFor = token.text.toLowerCase();
1134
1228
  const previousToken = this.parser.getTokenAt({ line: token.range.start.line, character: token.range.start.character });
1135
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Callfunc) {
1229
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Callfunc) {
1136
1230
  for (const scope of this.program.getScopes()) {
1137
1231
  //to only get functions defined in interface methods
1138
1232
  const callable = scope.getAllCallables().find((c) => c.callable.name.toLowerCase() === textToSearchFor); // eslint-disable-line @typescript-eslint/no-loop-func
@@ -1142,7 +1236,7 @@ class BrsFile {
1142
1236
  }
1143
1237
  return results;
1144
1238
  }
1145
- let classToken = this.parser.getTokenBefore(token, lexer_1.TokenKind.Class);
1239
+ let classToken = this.parser.getTokenBefore(token, TokenKind_1.TokenKind.Class);
1146
1240
  if (classToken) {
1147
1241
  let cs = this.parser.references.classStatements.find((cs) => cs.classKeyword.range === classToken.range);
1148
1242
  if (cs === null || cs === void 0 ? void 0 : cs.parentClassName) {
@@ -1154,7 +1248,7 @@ class BrsFile {
1154
1248
  }
1155
1249
  return results;
1156
1250
  }
1157
- if (token.kind === lexer_1.TokenKind.StringLiteral) {
1251
+ if (token.kind === TokenKind_1.TokenKind.StringLiteral) {
1158
1252
  // We need to strip off the quotes but only if present
1159
1253
  const startIndex = textToSearchFor.startsWith('"') ? 1 : 0;
1160
1254
  let endIndex = textToSearchFor.length;
@@ -1173,7 +1267,7 @@ class BrsFile {
1173
1267
  results.push(vscode_languageserver_1.Location.create(uri, symbol.range));
1174
1268
  }
1175
1269
  }
1176
- if (this.parser.tokenFollows(token, lexer_1.TokenKind.Goto)) {
1270
+ if (this.parser.tokenFollows(token, TokenKind_1.TokenKind.Goto)) {
1177
1271
  for (const label of func.labelStatements) {
1178
1272
  if (label.tokens.identifier.text.toLocaleLowerCase() === textToSearchFor) {
1179
1273
  const uri = util_1.util.pathToUri(this.srcPath);
@@ -1189,7 +1283,7 @@ class BrsFile {
1189
1283
  continue;
1190
1284
  }
1191
1285
  filesSearched.add(file);
1192
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Dot && file.parseMode === parser_1.ParseMode.BrighterScript) {
1286
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Dot && file.parseMode === Parser_1.ParseMode.BrighterScript) {
1193
1287
  results.push(...this.getClassMemberDefinitions(textToSearchFor, file));
1194
1288
  const namespaceDefinition = this.getNamespaceDefinitions(token, file);
1195
1289
  if (namespaceDefinition) {
@@ -1234,14 +1328,15 @@ class BrsFile {
1234
1328
  }
1235
1329
  getHover(position) {
1236
1330
  var _a, _b, _c;
1331
+ const fence = (code) => util_1.util.mdFence(code, 'brightscript');
1237
1332
  //get the token at the position
1238
1333
  let token = this.parser.getTokenAt(position);
1239
1334
  let hoverTokenTypes = [
1240
- lexer_1.TokenKind.Identifier,
1241
- lexer_1.TokenKind.Function,
1242
- lexer_1.TokenKind.EndFunction,
1243
- lexer_1.TokenKind.Sub,
1244
- lexer_1.TokenKind.EndSub
1335
+ TokenKind_1.TokenKind.Identifier,
1336
+ TokenKind_1.TokenKind.Function,
1337
+ TokenKind_1.TokenKind.EndFunction,
1338
+ TokenKind_1.TokenKind.Sub,
1339
+ TokenKind_1.TokenKind.EndSub
1245
1340
  ];
1246
1341
  //throw out invalid tokens and the wrong kind of tokens
1247
1342
  if (!token || !hoverTokenTypes.includes(token.kind)) {
@@ -1262,48 +1357,95 @@ class BrsFile {
1262
1357
  }
1263
1358
  }
1264
1359
  }
1265
- const typeTexts = [];
1266
- for (const scope of this.program.getScopesForFile(this)) {
1360
+ const typeTexts = new Set();
1361
+ const fileScopes = this.program.getScopesForFile(this).sort((a, b) => { var _a; return (_a = a.dependencyGraphKey) === null || _a === void 0 ? void 0 : _a.localeCompare(b.dependencyGraphKey); });
1362
+ const callables = [];
1363
+ for (const scope of fileScopes) {
1267
1364
  scope.linkSymbolTable();
1365
+ const typeContext = { file: this, scope: scope, position: position };
1268
1366
  const typeTextPair = this.getSymbolTypeFromToken(token, func, scope);
1269
1367
  if (typeTextPair) {
1270
1368
  let scopeTypeText = '';
1271
1369
  if ((0, reflection_1.isFunctionType)(typeTextPair.type)) {
1272
- scopeTypeText = (_b = typeTextPair.type) === null || _b === void 0 ? void 0 : _b.toString();
1370
+ scopeTypeText = (_b = typeTextPair.type) === null || _b === void 0 ? void 0 : _b.toString(typeContext);
1371
+ //keep unique references to the callables for this function
1372
+ if (!typeTexts.has(scopeTypeText)) {
1373
+ callables.push(scope.getCallableByName(lowerTokenText));
1374
+ }
1273
1375
  }
1274
1376
  else if (typeTextPair.useExpandedTextOnly) {
1275
1377
  scopeTypeText = typeTextPair.expandedTokenText;
1276
1378
  }
1277
1379
  else {
1278
- scopeTypeText = `${typeTextPair.expandedTokenText} as ${(_c = typeTextPair.type) === null || _c === void 0 ? void 0 : _c.toString()}`;
1380
+ scopeTypeText = `${typeTextPair.expandedTokenText} as ${(_c = typeTextPair.type) === null || _c === void 0 ? void 0 : _c.toString(typeContext)}`;
1279
1381
  }
1280
- if (scopeTypeText && !typeTexts.includes(scopeTypeText)) {
1281
- typeTexts.push(scopeTypeText);
1382
+ if (scopeTypeText) {
1383
+ typeTexts.add(scopeTypeText);
1282
1384
  }
1283
1385
  }
1284
1386
  scope.unlinkSymbolTable();
1285
1387
  }
1286
- const typeText = typeTexts.join(' | ');
1287
- if (typeText) {
1388
+ if (callables.length === typeTexts.size) {
1389
+ //this is a function in all scopes, so build the function hover
1288
1390
  return {
1289
1391
  range: token.range,
1290
- contents: typeText
1392
+ contents: this.getCallableDocumentation([...typeTexts], callables)
1393
+ };
1394
+ }
1395
+ else if ((typeTexts === null || typeTexts === void 0 ? void 0 : typeTexts.size) > 0) {
1396
+ const typeText = [...typeTexts].join(' | ');
1397
+ return {
1398
+ range: token.range,
1399
+ contents: fence(typeText)
1291
1400
  };
1292
1401
  }
1293
1402
  }
1294
- //look through all callables in relevant scopes
1295
- {
1296
- let scopes = this.program.getScopesForFile(this);
1297
- for (let scope of scopes) {
1298
- let callable = scope.getCallableByName(lowerTokenText);
1299
- if (callable) {
1300
- return {
1301
- range: token.range,
1302
- contents: callable.type.toString()
1303
- };
1304
- }
1403
+ // //look through all callables in relevant scopes
1404
+ // {
1405
+ // let scopes = this.program.getScopesForFile(this);
1406
+ // for (let scope of scopes) {
1407
+ // let callable = scope.getCallableByName(lowerTokenText);
1408
+ // if (callable) {
1409
+ // return {
1410
+ // range: token.range,
1411
+ // contents: this.getCallableDocumentation(callables)
1412
+ // };
1413
+ // }
1414
+ // }
1415
+ // }
1416
+ }
1417
+ /**
1418
+ * Build a hover documentation for a callable.
1419
+ */
1420
+ getCallableDocumentation(typeTexts, callables) {
1421
+ var _a;
1422
+ const callable = callables[0];
1423
+ const typeText = typeTexts[0];
1424
+ const comments = [];
1425
+ const tokens = callable === null || callable === void 0 ? void 0 : callable.file.parser.tokens;
1426
+ const idx = tokens === null || tokens === void 0 ? void 0 : tokens.indexOf((_a = callable.functionStatement) === null || _a === void 0 ? void 0 : _a.func.functionType);
1427
+ for (let i = idx - 1; i >= 0; i--) {
1428
+ const token = tokens[i];
1429
+ //skip whitespace and newline chars
1430
+ if (token.kind === TokenKind_1.TokenKind.Comment) {
1431
+ comments.push(token);
1432
+ }
1433
+ else if (token.kind === TokenKind_1.TokenKind.Newline || token.kind === TokenKind_1.TokenKind.Whitespace) {
1434
+ //skip these tokens
1435
+ continue;
1436
+ //any other token means there are no more comments
1437
+ }
1438
+ else {
1439
+ break;
1305
1440
  }
1306
1441
  }
1442
+ //message indicating if there are variations. example: (+3 variations) if there are 4 unique function signatures
1443
+ const multiText = callables.length > 1 ? ` (+${callables.length - 1} variations)` : '';
1444
+ let result = util_1.util.mdFence(typeText + multiText, 'brightscript');
1445
+ if (comments.length > 0) {
1446
+ result += '\n***\n' + comments.reverse().map(x => x.text.replace(/^('|rem)/i, '')).join('\n');
1447
+ }
1448
+ return result;
1307
1449
  }
1308
1450
  getSignatureHelpForNamespaceMethods(callableName, dottedGetText, scope) {
1309
1451
  var _a;
@@ -1351,12 +1493,12 @@ class BrsFile {
1351
1493
  }
1352
1494
  }
1353
1495
  const kind = currentToken.kind;
1354
- if (kind === lexer_1.TokenKind.Comment) {
1496
+ if (kind === TokenKind_1.TokenKind.Comment) {
1355
1497
  // Strip off common leading characters to make it easier to read
1356
1498
  const commentText = currentToken.text.replace(/^[' *\/]+/, '');
1357
1499
  functionComments.unshift(commentText);
1358
1500
  }
1359
- else if (kind === lexer_1.TokenKind.Newline) {
1501
+ else if (kind === TokenKind_1.TokenKind.Newline) {
1360
1502
  if (functionComments.length === 0) {
1361
1503
  continue;
1362
1504
  }
@@ -1416,7 +1558,7 @@ class BrsFile {
1416
1558
  const classConstructor = this.getClassMethod(classStatement, 'new');
1417
1559
  let sigHelp = classConstructor ? this.getSignatureHelpForStatement(classConstructor) : undefined;
1418
1560
  if (sigHelp) {
1419
- sigHelp.key = classStatement.getName(parser_1.ParseMode.BrighterScript);
1561
+ sigHelp.key = classStatement.getName(Parser_1.ParseMode.BrighterScript);
1420
1562
  sigHelp.signature.label = sigHelp.signature.label.replace(/(function|sub) new/, sigHelp.key);
1421
1563
  }
1422
1564
  return sigHelp;
@@ -1484,8 +1626,15 @@ class BrsFile {
1484
1626
  return programNode.toString();
1485
1627
  }
1486
1628
  dispose() {
1487
- var _a;
1629
+ var _a, _b;
1488
1630
  (_a = this._parser) === null || _a === void 0 ? void 0 : _a.dispose();
1631
+ //unsubscribe from any DependencyGraph subscriptions
1632
+ (_b = this.unsubscribeFromDependencyGraph) === null || _b === void 0 ? void 0 : _b.call(this);
1633
+ //deleting these properties result in lower memory usage (garbage collection is magic!)
1634
+ delete this.fileContents;
1635
+ delete this._parser;
1636
+ delete this.callables;
1637
+ delete this.functionCalls;
1489
1638
  }
1490
1639
  }
1491
1640
  exports.BrsFile = BrsFile;
@@ -1493,7 +1642,7 @@ exports.BrsFile = BrsFile;
1493
1642
  * List of completions for all valid keywords/reserved words.
1494
1643
  * Build this list once because it won't change for the lifetime of this process
1495
1644
  */
1496
- exports.KeywordCompletions = Object.keys(lexer_1.Keywords)
1645
+ exports.KeywordCompletions = Object.keys(TokenKind_1.Keywords)
1497
1646
  //remove any keywords with whitespace
1498
1647
  .filter(x => !x.includes(' '))
1499
1648
  //create completions