brighterscript 1.0.0-alpha.11 → 1.0.0-alpha.15

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