brighterscript 0.42.0 → 0.45.0

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 (201) hide show
  1. package/CHANGELOG.md +49 -0
  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/DiagnosticMessages.d.ts +21 -1
  6. package/dist/DiagnosticMessages.js +20 -0
  7. package/dist/DiagnosticMessages.js.map +1 -1
  8. package/dist/LanguageServer.d.ts +1 -6
  9. package/dist/LanguageServer.js +3 -12
  10. package/dist/LanguageServer.js.map +1 -1
  11. package/dist/PluginInterface.d.ts +3 -3
  12. package/dist/PluginInterface.js +3 -0
  13. package/dist/PluginInterface.js.map +1 -1
  14. package/dist/Program.d.ts +68 -25
  15. package/dist/Program.js +169 -76
  16. package/dist/Program.js.map +1 -1
  17. package/dist/ProgramBuilder.js +6 -6
  18. package/dist/ProgramBuilder.js.map +1 -1
  19. package/dist/Scope.d.ts +18 -11
  20. package/dist/Scope.js +41 -14
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/XmlScope.d.ts +3 -3
  23. package/dist/astUtils/AstEditor.d.ts +6 -0
  24. package/dist/astUtils/AstEditor.js +10 -0
  25. package/dist/astUtils/AstEditor.js.map +1 -1
  26. package/dist/astUtils/AstEditor.spec.js +37 -0
  27. package/dist/astUtils/AstEditor.spec.js.map +1 -1
  28. package/dist/astUtils/reflection.d.ts +5 -2
  29. package/dist/astUtils/reflection.js +14 -2
  30. package/dist/astUtils/reflection.js.map +1 -1
  31. package/dist/astUtils/reflection.spec.js +6 -6
  32. package/dist/astUtils/reflection.spec.js.map +1 -1
  33. package/dist/astUtils/visitors.d.ts +3 -1
  34. package/dist/astUtils/visitors.js.map +1 -1
  35. package/dist/astUtils/visitors.spec.js +15 -18
  36. package/dist/astUtils/visitors.spec.js.map +1 -1
  37. package/dist/bscPlugin/BscPlugin.d.ts +4 -1
  38. package/dist/bscPlugin/BscPlugin.js +21 -2
  39. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  40. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +3 -3
  41. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  42. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +18 -16
  43. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  44. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +9 -0
  45. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +97 -0
  46. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -0
  47. package/dist/bscPlugin/semanticTokens/{SemanticTokensProcessor.spec.d.ts → BrsFileSemanticTokensProcessor.spec.d.ts} +0 -0
  48. package/dist/bscPlugin/semanticTokens/{SemanticTokensProcessor.spec.js → BrsFileSemanticTokensProcessor.spec.js} +32 -4
  49. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -0
  50. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.d.ts +8 -0
  51. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js +36 -0
  52. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js.map +1 -0
  53. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +9 -0
  54. package/dist/bscPlugin/validation/BrsFileValidator.js +66 -0
  55. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -0
  56. package/dist/bscPlugin/validation/ScopeValidator.d.ts +11 -0
  57. package/dist/bscPlugin/validation/ScopeValidator.js +94 -0
  58. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -0
  59. package/dist/files/BrsFile.Class.spec.js +218 -114
  60. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  61. package/dist/files/BrsFile.d.ts +27 -8
  62. package/dist/files/BrsFile.js +216 -95
  63. package/dist/files/BrsFile.js.map +1 -1
  64. package/dist/files/BrsFile.spec.js +338 -190
  65. package/dist/files/BrsFile.spec.js.map +1 -1
  66. package/dist/files/XmlFile.d.ts +11 -5
  67. package/dist/files/XmlFile.js +15 -9
  68. package/dist/files/XmlFile.js.map +1 -1
  69. package/dist/files/XmlFile.spec.js +118 -114
  70. package/dist/files/XmlFile.spec.js.map +1 -1
  71. package/dist/files/tests/imports.spec.js +29 -27
  72. package/dist/files/tests/imports.spec.js.map +1 -1
  73. package/dist/index.d.ts +12 -3
  74. package/dist/index.js +21 -4
  75. package/dist/index.js.map +1 -1
  76. package/dist/interfaces.d.ts +50 -9
  77. package/dist/lexer/Lexer.js +1 -2
  78. package/dist/lexer/Lexer.js.map +1 -1
  79. package/dist/lexer/Lexer.spec.js +470 -462
  80. package/dist/lexer/Lexer.spec.js.map +1 -1
  81. package/dist/lexer/TokenKind.d.ts +2 -0
  82. package/dist/lexer/TokenKind.js +5 -0
  83. package/dist/lexer/TokenKind.js.map +1 -1
  84. package/dist/parser/Expression.d.ts +1 -1
  85. package/dist/parser/Expression.js +10 -10
  86. package/dist/parser/Expression.js.map +1 -1
  87. package/dist/parser/Parser.Class.spec.js +32 -31
  88. package/dist/parser/Parser.Class.spec.js.map +1 -1
  89. package/dist/parser/Parser.d.ts +23 -2
  90. package/dist/parser/Parser.js +445 -254
  91. package/dist/parser/Parser.js.map +1 -1
  92. package/dist/parser/Parser.spec.js +86 -24
  93. package/dist/parser/Parser.spec.js.map +1 -1
  94. package/dist/parser/SGParser.spec.js +1 -1
  95. package/dist/parser/SGParser.spec.js.map +1 -1
  96. package/dist/parser/Statement.d.ts +54 -2
  97. package/dist/parser/Statement.js +162 -9
  98. package/dist/parser/Statement.js.map +1 -1
  99. package/dist/parser/Statement.spec.js +5 -5
  100. package/dist/parser/Statement.spec.js.map +1 -1
  101. package/dist/parser/tests/Parser.spec.d.ts +3 -3
  102. package/dist/parser/tests/Parser.spec.js +4 -4
  103. package/dist/parser/tests/Parser.spec.js.map +1 -1
  104. package/dist/parser/tests/controlFlow/For.spec.js +40 -40
  105. package/dist/parser/tests/controlFlow/For.spec.js.map +1 -1
  106. package/dist/parser/tests/controlFlow/ForEach.spec.js +22 -21
  107. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  108. package/dist/parser/tests/controlFlow/If.spec.js +100 -99
  109. package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
  110. package/dist/parser/tests/controlFlow/While.spec.js +25 -25
  111. package/dist/parser/tests/controlFlow/While.spec.js.map +1 -1
  112. package/dist/parser/tests/expression/Additive.spec.js +21 -21
  113. package/dist/parser/tests/expression/Additive.spec.js.map +1 -1
  114. package/dist/parser/tests/expression/ArrayLiterals.spec.js +91 -91
  115. package/dist/parser/tests/expression/ArrayLiterals.spec.js.map +1 -1
  116. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +102 -102
  117. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
  118. package/dist/parser/tests/expression/Boolean.spec.js +15 -15
  119. package/dist/parser/tests/expression/Boolean.spec.js.map +1 -1
  120. package/dist/parser/tests/expression/Call.spec.js +22 -21
  121. package/dist/parser/tests/expression/Call.spec.js.map +1 -1
  122. package/dist/parser/tests/expression/Exponential.spec.js +11 -11
  123. package/dist/parser/tests/expression/Exponential.spec.js.map +1 -1
  124. package/dist/parser/tests/expression/Function.spec.js +171 -171
  125. package/dist/parser/tests/expression/Function.spec.js.map +1 -1
  126. package/dist/parser/tests/expression/Indexing.spec.js +50 -50
  127. package/dist/parser/tests/expression/Indexing.spec.js.map +1 -1
  128. package/dist/parser/tests/expression/Multiplicative.spec.js +25 -25
  129. package/dist/parser/tests/expression/Multiplicative.spec.js.map +1 -1
  130. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +17 -17
  131. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  132. package/dist/parser/tests/expression/PrefixUnary.spec.js +26 -26
  133. package/dist/parser/tests/expression/PrefixUnary.spec.js.map +1 -1
  134. package/dist/parser/tests/expression/Primary.spec.js +27 -27
  135. package/dist/parser/tests/expression/Primary.spec.js.map +1 -1
  136. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +4 -3
  137. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  138. package/dist/parser/tests/expression/Relational.spec.js +25 -25
  139. package/dist/parser/tests/expression/Relational.spec.js.map +1 -1
  140. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +8 -8
  141. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  142. package/dist/parser/tests/expression/TernaryExpression.spec.js +7 -7
  143. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  144. package/dist/parser/tests/statement/AssignmentOperators.spec.js +15 -15
  145. package/dist/parser/tests/statement/AssignmentOperators.spec.js.map +1 -1
  146. package/dist/parser/tests/statement/Declaration.spec.js +20 -20
  147. package/dist/parser/tests/statement/Declaration.spec.js.map +1 -1
  148. package/dist/parser/tests/statement/Enum.spec.d.ts +1 -0
  149. package/dist/parser/tests/statement/Enum.spec.js +774 -0
  150. package/dist/parser/tests/statement/Enum.spec.js.map +1 -0
  151. package/dist/parser/tests/statement/Function.spec.js +121 -120
  152. package/dist/parser/tests/statement/Function.spec.js.map +1 -1
  153. package/dist/parser/tests/statement/Goto.spec.js +9 -8
  154. package/dist/parser/tests/statement/Goto.spec.js.map +1 -1
  155. package/dist/parser/tests/statement/Increment.spec.js +22 -22
  156. package/dist/parser/tests/statement/Increment.spec.js.map +1 -1
  157. package/dist/parser/tests/statement/InterfaceStatement.spec.js +12 -0
  158. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  159. package/dist/parser/tests/statement/LibraryStatement.spec.js +7 -7
  160. package/dist/parser/tests/statement/LibraryStatement.spec.js.map +1 -1
  161. package/dist/parser/tests/statement/Misc.spec.js +71 -70
  162. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  163. package/dist/parser/tests/statement/PrintStatement.spec.js +17 -17
  164. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  165. package/dist/parser/tests/statement/ReturnStatement.spec.js +33 -33
  166. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +1 -1
  167. package/dist/parser/tests/statement/Set.spec.js +53 -53
  168. package/dist/parser/tests/statement/Set.spec.js.map +1 -1
  169. package/dist/parser/tests/statement/Stop.spec.js +7 -6
  170. package/dist/parser/tests/statement/Stop.spec.js.map +1 -1
  171. package/dist/preprocessor/Chunk.d.ts +1 -1
  172. package/dist/preprocessor/Preprocessor.d.ts +1 -1
  173. package/dist/preprocessor/Preprocessor.js +7 -7
  174. package/dist/preprocessor/Preprocessor.js.map +1 -1
  175. package/dist/types/FunctionType.d.ts +2 -2
  176. package/dist/types/FunctionType.js +3 -3
  177. package/dist/types/FunctionType.js.map +1 -1
  178. package/dist/types/FunctionType.spec.js +2 -2
  179. package/dist/types/FunctionType.spec.js.map +1 -1
  180. package/dist/util.d.ts +27 -1
  181. package/dist/util.js +96 -29
  182. package/dist/util.js.map +1 -1
  183. package/dist/validators/ClassValidator.js +20 -27
  184. package/dist/validators/ClassValidator.js.map +1 -1
  185. package/package.json +2 -1
  186. package/dist/astUtils/index.d.ts +0 -7
  187. package/dist/astUtils/index.js +0 -26
  188. package/dist/astUtils/index.js.map +0 -1
  189. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.d.ts +0 -7
  190. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js +0 -63
  191. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js.map +0 -1
  192. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +0 -1
  193. package/dist/lexer/index.d.ts +0 -3
  194. package/dist/lexer/index.js +0 -18
  195. package/dist/lexer/index.js.map +0 -1
  196. package/dist/parser/index.d.ts +0 -3
  197. package/dist/parser/index.js +0 -16
  198. package/dist/parser/index.js.map +0 -1
  199. package/dist/preprocessor/index.d.ts +0 -3
  200. package/dist/preprocessor/index.js +0 -16
  201. package/dist/preprocessor/index.js.map +0 -1
@@ -7,8 +7,9 @@ const chalk_1 = require("chalk");
7
7
  const path = require("path");
8
8
  const DiagnosticMessages_1 = require("../DiagnosticMessages");
9
9
  const FunctionScope_1 = require("../FunctionScope");
10
- const lexer_1 = require("../lexer");
11
- const parser_1 = require("../parser");
10
+ const Lexer_1 = require("../lexer/Lexer");
11
+ const TokenKind_1 = require("../lexer/TokenKind");
12
+ const Parser_1 = require("../parser/Parser");
12
13
  const DynamicType_1 = require("../types/DynamicType");
13
14
  const FunctionType_1 = require("../types/FunctionType");
14
15
  const VoidType_1 = require("../types/VoidType");
@@ -36,7 +37,12 @@ class BrsFile {
36
37
  /**
37
38
  * The parseMode used for the parser for this file
38
39
  */
39
- this.parseMode = parser_1.ParseMode.BrightScript;
40
+ this.parseMode = Parser_1.ParseMode.BrightScript;
41
+ /**
42
+ * Indicates whether this file needs to be validated.
43
+ * Files are only ever validated a single time
44
+ */
45
+ this.isValidated = false;
40
46
  this.diagnostics = [];
41
47
  this.commentFlags = [];
42
48
  this.callables = [];
@@ -57,7 +63,7 @@ class BrsFile {
57
63
  //all BrighterScript files need to be transpiled
58
64
  if ((_a = this.extension) === null || _a === void 0 ? void 0 : _a.endsWith('.bs')) {
59
65
  this.needsTranspiled = true;
60
- this.parseMode = parser_1.ParseMode.BrighterScript;
66
+ this.parseMode = Parser_1.ParseMode.BrighterScript;
61
67
  }
62
68
  this.isTypedef = this.extension === '.d.bs';
63
69
  if (!this.isTypedef) {
@@ -145,11 +151,13 @@ class BrsFile {
145
151
  this.diagnostics = [];
146
152
  //if we have a typedef file, skip parsing this file
147
153
  if (this.hasTypedef) {
154
+ //skip validation since the typedef is shadowing this file
155
+ this.isValidated = true;
148
156
  return;
149
157
  }
150
158
  //tokenize the input file
151
159
  let lexer = this.program.logger.time(Logger_1.LogLevel.debug, ['lexer.lex', chalk_1.default.green(this.pathAbsolute)], () => {
152
- return lexer_1.Lexer.scan(fileContents, {
160
+ return Lexer_1.Lexer.scan(fileContents, {
153
161
  includeWhitespace: false
154
162
  });
155
163
  });
@@ -172,32 +180,50 @@ class BrsFile {
172
180
  //if the preprocessor generated tokens, use them.
173
181
  let tokens = preprocessor.processedTokens.length > 0 ? preprocessor.processedTokens : lexer.tokens;
174
182
  this.program.logger.time(Logger_1.LogLevel.debug, ['parser.parse', chalk_1.default.green(this.pathAbsolute)], () => {
175
- this._parser = parser_1.Parser.parse(tokens, {
183
+ this._parser = Parser_1.Parser.parse(tokens, {
176
184
  mode: this.parseMode,
177
185
  logger: this.program.logger
178
186
  });
179
187
  });
180
188
  //absorb all lexing/preprocessing/parsing diagnostics
181
189
  this.diagnostics.push(...lexer.diagnostics, ...preprocessor.diagnostics, ...this._parser.diagnostics);
182
- //notify AST ready
183
- this.program.plugins.emit('afterFileParse', this);
184
190
  //extract all callables from this file
185
191
  this.findCallables();
186
192
  //find all places where a sub/function is being called
187
193
  this.findFunctionCalls();
188
- this.findAndValidateImportAndImportStatements();
194
+ //register all import statements for use in the rest of the program
195
+ this.registerImports();
189
196
  //attach this file to every diagnostic
190
197
  for (let diagnostic of this.diagnostics) {
191
198
  diagnostic.file = this;
192
199
  }
193
200
  }
194
201
  catch (e) {
195
- this._parser = new parser_1.Parser();
202
+ this._parser = new Parser_1.Parser();
196
203
  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)))));
197
204
  }
198
205
  }
199
- findAndValidateImportAndImportStatements() {
200
- var _a;
206
+ registerImports() {
207
+ var _a, _b, _c, _d;
208
+ 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 : []) {
209
+ //register import statements
210
+ if ((0, reflection_1.isImportStatement)(statement) && statement.filePathToken) {
211
+ this.ownScriptImports.push({
212
+ filePathRange: statement.filePathToken.range,
213
+ pkgPath: util_1.util.getPkgPathFromTarget(this.pkgPath, statement.filePath),
214
+ sourceFile: this,
215
+ text: (_d = statement.filePathToken) === null || _d === void 0 ? void 0 : _d.text
216
+ });
217
+ }
218
+ }
219
+ }
220
+ validate() {
221
+ //only validate the file if it was actually parsed (skip files containing typedefs)
222
+ if (!this.hasTypedef) {
223
+ this.validateImportStatements();
224
+ }
225
+ }
226
+ validateImportStatements() {
201
227
  let topOfFileIncludeStatements = [];
202
228
  for (let stmt of this.ast.statements) {
203
229
  //skip comments
@@ -218,15 +244,6 @@ class BrsFile {
218
244
  ...this._parser.references.importStatements
219
245
  ];
220
246
  for (let result of statements) {
221
- //register import statements
222
- if ((0, reflection_1.isImportStatement)(result) && result.filePathToken) {
223
- this.ownScriptImports.push({
224
- filePathRange: result.filePathToken.range,
225
- pkgPath: util_1.util.getPkgPathFromTarget(this.pkgPath, result.filePath),
226
- sourceFile: this,
227
- text: (_a = result.filePathToken) === null || _a === void 0 ? void 0 : _a.text
228
- });
229
- }
230
247
  //if this statement is not one of the top-of-file statements,
231
248
  //then add a diagnostic explaining that it is invalid
232
249
  if (!topOfFileIncludeStatements.includes(result)) {
@@ -284,7 +301,7 @@ class BrsFile {
284
301
  const processor = new CommentFlagProcessor_1.CommentFlagProcessor(this, ['rem', `'`], DiagnosticMessages_1.diagnosticCodes, [DiagnosticMessages_1.DiagnosticCodeMap.unknownDiagnosticCode]);
285
302
  this.commentFlags = [];
286
303
  for (let token of tokens) {
287
- if (token.kind === lexer_1.TokenKind.Comment) {
304
+ if (token.kind === TokenKind_1.TokenKind.Comment) {
288
305
  processor.tryAdd(token.text, token.range);
289
306
  }
290
307
  }
@@ -374,9 +391,9 @@ class BrsFile {
374
391
  }
375
392
  functionType.setName(assignment.name.text);
376
393
  for (let param of assignment.value.parameters) {
377
- let isRequired = !param.defaultValue;
394
+ let isOptional = !!param.defaultValue;
378
395
  //TODO compute optional parameters
379
- functionType.addParameter(param.name.text, param.type, isRequired);
396
+ functionType.addParameter(param.name.text, param.type, isOptional);
380
397
  }
381
398
  return functionType;
382
399
  //literal
@@ -436,8 +453,8 @@ class BrsFile {
436
453
  isRestArgument: false
437
454
  };
438
455
  params.push(callableParam);
439
- let isRequired = !param.defaultValue;
440
- functionType.addParameter(callableParam.name, callableParam.type, isRequired);
456
+ let isOptional = !!param.defaultValue;
457
+ functionType.addParameter(callableParam.name, callableParam.type, isOptional);
441
458
  }
442
459
  this.callables.push({
443
460
  isSub: statement.func.functionType.text.toLowerCase() === 'sub',
@@ -555,6 +572,20 @@ class BrsFile {
555
572
  }
556
573
  }
557
574
  }
575
+ /**
576
+ * Find the NamespaceStatement enclosing the given position
577
+ * @param position
578
+ * @param functionScopes
579
+ */
580
+ getNamespaceStatementForPosition(position) {
581
+ if (position) {
582
+ for (const statement of this.parser.references.namespaceStatements) {
583
+ if (util_1.util.rangeContains(statement.range, position)) {
584
+ return statement;
585
+ }
586
+ }
587
+ }
588
+ }
558
589
  /**
559
590
  * Get completions available at the given cursor. This aggregates all values from this file and the current scope.
560
591
  */
@@ -570,10 +601,10 @@ class BrsFile {
570
601
  //if cursor is within a comment, disable completions
571
602
  let currentToken = this.getTokenAt(position);
572
603
  const tokenKind = currentToken === null || currentToken === void 0 ? void 0 : currentToken.kind;
573
- if (tokenKind === lexer_1.TokenKind.Comment) {
604
+ if (tokenKind === TokenKind_1.TokenKind.Comment) {
574
605
  return [];
575
606
  }
576
- else if (tokenKind === lexer_1.TokenKind.StringLiteral || tokenKind === lexer_1.TokenKind.TemplateStringQuasi) {
607
+ else if (tokenKind === TokenKind_1.TokenKind.StringLiteral || tokenKind === TokenKind_1.TokenKind.TemplateStringQuasi) {
577
608
  const match = /^("?)(pkg|libpkg):/.exec(currentToken.text);
578
609
  if (match) {
579
610
  const [, openingQuote, fileProtocol] = match;
@@ -597,38 +628,44 @@ class BrsFile {
597
628
  return [];
598
629
  }
599
630
  }
600
- let namespaceCompletions = this.getNamespaceCompletions(currentToken, this.parseMode, scope);
631
+ const namespaceCompletions = this.getNamespaceCompletions(currentToken, this.parseMode, scope);
601
632
  if (namespaceCompletions.length > 0) {
602
- return namespaceCompletions;
633
+ return [...namespaceCompletions];
634
+ }
635
+ const enumMemberCompletions = this.getEnumMemberStatementCompletions(currentToken, this.parseMode, scope);
636
+ if (enumMemberCompletions.length > 0) {
637
+ // no other completion is valid in this case
638
+ return enumMemberCompletions;
603
639
  }
604
640
  //determine if cursor is inside a function
605
641
  let functionScope = this.getFunctionScopeAtPosition(position);
606
642
  if (!functionScope) {
607
643
  //we aren't in any function scope, so return the keyword completions and namespaces
608
- if (this.getTokenBefore(currentToken, lexer_1.TokenKind.New)) {
644
+ if (this.getTokenBefore(currentToken, TokenKind_1.TokenKind.New)) {
609
645
  // there's a new keyword, so only class types are viable here
610
646
  return [...this.getGlobalClassStatementCompletions(currentToken, this.parseMode)];
611
647
  }
612
648
  else {
613
- return [...exports.KeywordCompletions, ...this.getGlobalClassStatementCompletions(currentToken, this.parseMode), ...namespaceCompletions];
649
+ return [
650
+ ...exports.KeywordCompletions,
651
+ ...this.getGlobalClassStatementCompletions(currentToken, this.parseMode),
652
+ ...namespaceCompletions,
653
+ ...this.getNonNamespacedEnumStatementCompletions(currentToken, this.parseMode, scope)
654
+ ];
614
655
  }
615
656
  }
616
657
  const classNameCompletions = this.getGlobalClassStatementCompletions(currentToken, this.parseMode);
617
- const newToken = this.getTokenBefore(currentToken, lexer_1.TokenKind.New);
658
+ const newToken = this.getTokenBefore(currentToken, TokenKind_1.TokenKind.New);
618
659
  if (newToken) {
619
- //we are after a new keyword; so we can only be namespaces or classes at this point
660
+ //we are after a new keyword; so we can only be top-level namespaces or classes at this point
620
661
  result.push(...classNameCompletions);
621
662
  result.push(...namespaceCompletions);
622
663
  return result;
623
664
  }
624
- if (this.tokenFollows(currentToken, lexer_1.TokenKind.Goto)) {
665
+ if (this.tokenFollows(currentToken, TokenKind_1.TokenKind.Goto)) {
625
666
  return this.getLabelCompletion(functionScope);
626
667
  }
627
- if (this.isPositionNextToTokenKind(position, lexer_1.TokenKind.Dot)) {
628
- if (namespaceCompletions.length > 0) {
629
- //if we matched a namespace, after a dot, it can't be anything else but something from our namespace completions
630
- return namespaceCompletions;
631
- }
668
+ if (this.isPositionNextToTokenKind(position, TokenKind_1.TokenKind.Dot)) {
632
669
  const selfClassMemberCompletions = this.getClassMemberCompletions(position, currentToken, functionScope, scope);
633
670
  if (selfClassMemberCompletions.size > 0) {
634
671
  return [...selfClassMemberCompletions.values()].filter((i) => i.label !== 'new');
@@ -648,6 +685,8 @@ class BrsFile {
648
685
  result.push(...namespaceCompletions);
649
686
  //include class names
650
687
  result.push(...classNameCompletions);
688
+ //include enums
689
+ result.push(...this.getNonNamespacedEnumStatementCompletions(currentToken, this.parseMode, scope));
651
690
  //include the global callables
652
691
  result.push(...scope.getCallablesAsCompletions(this.parseMode));
653
692
  //add `m` because that's always valid within a function
@@ -670,7 +709,7 @@ class BrsFile {
670
709
  kind: (0, reflection_1.isFunctionType)(variable.type) ? vscode_languageserver_1.CompletionItemKind.Function : vscode_languageserver_1.CompletionItemKind.Variable
671
710
  });
672
711
  }
673
- if (this.parseMode === parser_1.ParseMode.BrighterScript) {
712
+ if (this.parseMode === Parser_1.ParseMode.BrighterScript) {
674
713
  //include the first part of namespaces
675
714
  let namespaces = scope.getAllNamespaceStatements();
676
715
  for (let stmt of namespaces) {
@@ -700,7 +739,7 @@ class BrsFile {
700
739
  let classStatement = this.getClassFromMReference(position, currentToken, functionScope);
701
740
  let results = new Map();
702
741
  if (classStatement) {
703
- let classes = scope.getClassHierarchy(classStatement.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
742
+ let classes = scope.getClassHierarchy(classStatement.item.getName(Parser_1.ParseMode.BrighterScript).toLowerCase());
704
743
  for (let cs of classes) {
705
744
  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 : []]) {
706
745
  if (!results.has(member.name.text.toLowerCase())) {
@@ -716,28 +755,27 @@ class BrsFile {
716
755
  }
717
756
  getClassFromMReference(position, currentToken, functionScope) {
718
757
  let previousToken = this.getPreviousToken(currentToken);
719
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Dot) {
758
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Dot) {
720
759
  previousToken = this.getPreviousToken(previousToken);
721
760
  }
722
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Identifier && (previousToken === null || previousToken === void 0 ? void 0 : previousToken.text.toLowerCase()) === 'm' && (0, reflection_1.isClassMethodStatement)(functionScope.func.functionStatement)) {
761
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Identifier && (previousToken === null || previousToken === void 0 ? void 0 : previousToken.text.toLowerCase()) === 'm' && (0, reflection_1.isClassMethodStatement)(functionScope.func.functionStatement)) {
723
762
  return { item: this.parser.references.classStatements.find((cs) => util_1.util.rangeContains(cs.range, position)), file: this };
724
763
  }
725
764
  return undefined;
726
765
  }
727
766
  getGlobalClassStatementCompletions(currentToken, parseMode) {
728
767
  var _a;
729
- if (parseMode === parser_1.ParseMode.BrightScript) {
768
+ if (parseMode === Parser_1.ParseMode.BrightScript) {
730
769
  return [];
731
770
  }
732
771
  let results = new Map();
733
- let completionName = (_a = this.getPartialVariableName(currentToken, [lexer_1.TokenKind.New])) === null || _a === void 0 ? void 0 : _a.toLowerCase();
772
+ let completionName = (_a = this.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New])) === null || _a === void 0 ? void 0 : _a.toLowerCase();
734
773
  if (completionName === null || completionName === void 0 ? void 0 : completionName.includes('.')) {
735
774
  return [];
736
775
  }
737
776
  let scopes = this.program.getScopesForFile(this);
738
777
  for (let scope of scopes) {
739
778
  let classMap = scope.getClassMap();
740
- // let viableKeys = [...classMap.keys()].filter((k) => k.startsWith(completionName));
741
779
  for (const key of [...classMap.keys()]) {
742
780
  let cs = classMap.get(key).item;
743
781
  if (!results.has(cs.name.text)) {
@@ -750,19 +788,70 @@ class BrsFile {
750
788
  }
751
789
  return [...results.values()];
752
790
  }
791
+ getNonNamespacedEnumStatementCompletions(currentToken, parseMode, scope) {
792
+ var _a, _b;
793
+ if (parseMode !== Parser_1.ParseMode.BrighterScript) {
794
+ return [];
795
+ }
796
+ 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) + '.';
797
+ const results = new Map();
798
+ const enumMap = scope.getEnumMap();
799
+ for (const key of [...enumMap.keys()]) {
800
+ const enumStatement = enumMap.get(key).item;
801
+ const fullName = enumStatement.fullName;
802
+ //if the enum is contained within our own namespace, or if it's a non-namespaced enum
803
+ if (fullName.startsWith(containingNamespaceName) || !fullName.includes('.')) {
804
+ results.set(fullName, {
805
+ label: enumStatement.name,
806
+ kind: vscode_languageserver_1.CompletionItemKind.Enum
807
+ });
808
+ }
809
+ }
810
+ return [...results.values()];
811
+ }
812
+ getEnumMemberStatementCompletions(currentToken, parseMode, scope) {
813
+ var _a, _b, _c, _d, _e;
814
+ if (parseMode === Parser_1.ParseMode.BrightScript || !currentToken) {
815
+ return [];
816
+ }
817
+ const results = new Map();
818
+ const completionName = (_a = this.getPartialVariableName(currentToken)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
819
+ //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
820
+ if (!completionName || !completionName.includes('.')) {
821
+ return [];
822
+ }
823
+ const enumNameLower = (_b = completionName === null || completionName === void 0 ? void 0 : completionName.split(/\.(\w+)?$/)[0]) === null || _b === void 0 ? void 0 : _b.toLowerCase();
824
+ const namespaceNameLower = (_c = this.getNamespaceStatementForPosition(currentToken.range.end)) === null || _c === void 0 ? void 0 : _c.name.toLowerCase();
825
+ const enumMap = scope.getEnumMap();
826
+ //get the enum statement with this name (check without namespace prefix first, then with inferred namespace prefix next)
827
+ const enumStatement = (_e = ((_d = enumMap.get(enumNameLower)) !== null && _d !== void 0 ? _d : enumMap.get(namespaceNameLower + '.' + enumNameLower))) === null || _e === void 0 ? void 0 : _e.item;
828
+ //if we found an enum with this name
829
+ if (enumStatement) {
830
+ for (const member of enumStatement.getMembers()) {
831
+ const name = enumStatement.fullName + '.' + member.name;
832
+ const nameLower = name.toLowerCase();
833
+ results.set(nameLower, {
834
+ label: member.name,
835
+ kind: vscode_languageserver_1.CompletionItemKind.EnumMember
836
+ });
837
+ }
838
+ }
839
+ return [...results.values()];
840
+ }
753
841
  getNamespaceCompletions(currentToken, parseMode, scope) {
754
842
  //BrightScript does not support namespaces, so return an empty list in that case
755
- if (parseMode === parser_1.ParseMode.BrightScript) {
843
+ if (parseMode === Parser_1.ParseMode.BrightScript) {
756
844
  return [];
757
845
  }
758
- let completionName = this.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
759
- if (!completionName) {
846
+ const completionName = this.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New]);
847
+ //if we don't have a completion name, or if there's no period in the name, then this is not a namespaced variable
848
+ if (!completionName || !completionName.includes('.')) {
760
849
  return [];
761
850
  }
762
851
  //remove any trailing identifer and then any trailing dot, to give us the
763
852
  //name of its immediate parent namespace
764
- let closestParentNamespaceName = completionName.replace(/\.([a-z0-9_]*)?$/gi, '');
765
- let newToken = this.getTokenBefore(currentToken, lexer_1.TokenKind.New);
853
+ let closestParentNamespaceName = completionName.replace(/\.([a-z0-9_]*)?$/gi, '').toLowerCase();
854
+ let newToken = this.getTokenBefore(currentToken, TokenKind_1.TokenKind.New);
766
855
  let result = new Map();
767
856
  for (let [, namespace] of scope.namespaceLookup) {
768
857
  //completionName = "NameA."
@@ -770,7 +859,7 @@ class BrsFile {
770
859
  //NameA
771
860
  //NameA.NameB
772
861
  //NameA.NameB.NameC
773
- if (namespace.fullName.toLowerCase() === closestParentNamespaceName.toLowerCase()) {
862
+ if (namespace.fullName.toLowerCase() === closestParentNamespaceName) {
774
863
  //add all of this namespace's immediate child namespaces, bearing in mind if we are after a new keyword
775
864
  for (let [, ns] of namespace.namespaces) {
776
865
  if (!newToken || ns.statements.find((s) => (0, reflection_1.isClassStatement)(s))) {
@@ -785,20 +874,22 @@ class BrsFile {
785
874
  //add function and class statement completions
786
875
  for (let stmt of namespace.statements) {
787
876
  if ((0, reflection_1.isClassStatement)(stmt)) {
788
- if (!result.has(stmt.name.text)) {
789
- result.set(stmt.name.text, {
790
- label: stmt.name.text,
791
- kind: vscode_languageserver_1.CompletionItemKind.Class
792
- });
793
- }
877
+ result.set(stmt.name.text, {
878
+ label: stmt.name.text,
879
+ kind: vscode_languageserver_1.CompletionItemKind.Class
880
+ });
794
881
  }
795
882
  else if ((0, reflection_1.isFunctionStatement)(stmt) && !newToken) {
796
- if (!result.has(stmt.name.text)) {
797
- result.set(stmt.name.text, {
798
- label: stmt.name.text,
799
- kind: vscode_languageserver_1.CompletionItemKind.Function
800
- });
801
- }
883
+ result.set(stmt.name.text, {
884
+ label: stmt.name.text,
885
+ kind: vscode_languageserver_1.CompletionItemKind.Function
886
+ });
887
+ }
888
+ else if ((0, reflection_1.isEnumStatement)(stmt) && !newToken) {
889
+ result.set(stmt.name, {
890
+ label: stmt.name,
891
+ kind: vscode_languageserver_1.CompletionItemKind.Enum
892
+ });
802
893
  }
803
894
  }
804
895
  }
@@ -811,11 +902,11 @@ class BrsFile {
811
902
  return undefined;
812
903
  }
813
904
  let location;
814
- const nameParts = this.getPartialVariableName(token, [lexer_1.TokenKind.New]).split('.');
905
+ const nameParts = this.getPartialVariableName(token, [TokenKind_1.TokenKind.New]).split('.');
815
906
  const endName = nameParts[nameParts.length - 1].toLowerCase();
816
907
  const namespaceName = nameParts.slice(0, -1).join('.').toLowerCase();
817
908
  const statementHandler = (statement) => {
818
- if (!location && statement.getName(parser_1.ParseMode.BrighterScript).toLowerCase() === namespaceName) {
909
+ if (!location && statement.getName(Parser_1.ParseMode.BrighterScript).toLowerCase() === namespaceName) {
819
910
  const namespaceItemStatementHandler = (statement) => {
820
911
  if (!location && statement.name.text.toLowerCase() === endName) {
821
912
  const uri = util_1.util.pathToUri(file.pathAbsolute);
@@ -841,7 +932,7 @@ class BrsFile {
841
932
  * Given a current token, walk
842
933
  */
843
934
  getPartialVariableName(currentToken, excludeTokens = null) {
844
- let identifierAndDotKinds = [lexer_1.TokenKind.Identifier, ...lexer_1.AllowedLocalIdentifiers, lexer_1.TokenKind.Dot];
935
+ let identifierAndDotKinds = [TokenKind_1.TokenKind.Identifier, ...TokenKind_1.AllowedLocalIdentifiers, TokenKind_1.TokenKind.Dot];
845
936
  //consume tokens backwards until we find something other than a dot or an identifier
846
937
  let tokens = [];
847
938
  const parser = this.parser;
@@ -867,17 +958,17 @@ class BrsFile {
867
958
  const previousToken = this.getPreviousToken(closestToken);
868
959
  const previousTokenKind = previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind;
869
960
  //next to matched token
870
- if (!closestToken || closestToken.kind === lexer_1.TokenKind.Eof) {
961
+ if (!closestToken || closestToken.kind === TokenKind_1.TokenKind.Eof) {
871
962
  return false;
872
963
  }
873
964
  else if (closestToken.kind === tokenKind) {
874
965
  return true;
875
966
  }
876
- else if (closestToken.kind === lexer_1.TokenKind.Newline || previousTokenKind === lexer_1.TokenKind.Newline) {
967
+ else if (closestToken.kind === TokenKind_1.TokenKind.Newline || previousTokenKind === TokenKind_1.TokenKind.Newline) {
877
968
  return false;
878
969
  //next to an identifier, which is next to token kind
879
970
  }
880
- else if (closestToken.kind === lexer_1.TokenKind.Identifier && previousTokenKind === tokenKind) {
971
+ else if (closestToken.kind === TokenKind_1.TokenKind.Identifier && previousTokenKind === tokenKind) {
881
972
  return true;
882
973
  }
883
974
  else {
@@ -888,7 +979,7 @@ class BrsFile {
888
979
  const index = this.parser.tokens.indexOf(currentToken);
889
980
  for (let i = index - 1; i >= 0; i--) {
890
981
  currentToken = this.parser.tokens[i];
891
- if (currentToken.kind === lexer_1.TokenKind.Newline) {
982
+ if (currentToken.kind === TokenKind_1.TokenKind.Newline) {
892
983
  break;
893
984
  }
894
985
  else if (currentToken.kind === tokenKind) {
@@ -908,7 +999,7 @@ class BrsFile {
908
999
  let tokens = [];
909
1000
  for (let i = this.parser.tokens.indexOf(currentToken); direction === -1 ? i >= 0 : i === this.parser.tokens.length; i += direction) {
910
1001
  currentToken = this.parser.tokens[i];
911
- if (currentToken.kind === lexer_1.TokenKind.Newline || currentToken.kind === tokenKind) {
1002
+ if (currentToken.kind === TokenKind_1.TokenKind.Newline || currentToken.kind === tokenKind) {
912
1003
  break;
913
1004
  }
914
1005
  tokens.push(currentToken);
@@ -1052,7 +1143,7 @@ class BrsFile {
1052
1143
  else {
1053
1144
  return;
1054
1145
  }
1055
- const name = (0, reflection_1.isClassFieldStatement)(statement) ? statement.name.text : statement.getName(parser_1.ParseMode.BrighterScript);
1146
+ const name = (0, reflection_1.isClassFieldStatement)(statement) ? statement.name.text : statement.getName(Parser_1.ParseMode.BrighterScript);
1056
1147
  return vscode_languageserver_1.DocumentSymbol.create(name, '', symbolKind, statement.range, statement.range, children);
1057
1148
  }
1058
1149
  /**
@@ -1086,9 +1177,9 @@ class BrsFile {
1086
1177
  else {
1087
1178
  return symbols;
1088
1179
  }
1089
- const name = statement.getName(parser_1.ParseMode.BrighterScript);
1180
+ const name = statement.getName(Parser_1.ParseMode.BrighterScript);
1090
1181
  const uri = util_1.util.pathToUri(this.pathAbsolute);
1091
- 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));
1182
+ 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));
1092
1183
  symbols.push(symbol);
1093
1184
  return symbols;
1094
1185
  }
@@ -1102,8 +1193,8 @@ class BrsFile {
1102
1193
  const token = this.getTokenAt(position);
1103
1194
  // 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
1104
1195
  let definitionTokenTypes = [
1105
- lexer_1.TokenKind.Identifier,
1106
- lexer_1.TokenKind.StringLiteral
1196
+ TokenKind_1.TokenKind.Identifier,
1197
+ TokenKind_1.TokenKind.StringLiteral
1107
1198
  ];
1108
1199
  //throw out invalid tokens and the wrong kind of tokens
1109
1200
  if (!token || !definitionTokenTypes.includes(token.kind)) {
@@ -1111,7 +1202,7 @@ class BrsFile {
1111
1202
  }
1112
1203
  let textToSearchFor = token.text.toLowerCase();
1113
1204
  const previousToken = this.getTokenAt({ line: token.range.start.line, character: token.range.start.character });
1114
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Callfunc) {
1205
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Callfunc) {
1115
1206
  for (const scope of this.program.getScopes()) {
1116
1207
  //to only get functions defined in interface methods
1117
1208
  const callable = scope.getAllCallables().find((c) => c.callable.name.toLowerCase() === textToSearchFor); // eslint-disable-line @typescript-eslint/no-loop-func
@@ -1121,7 +1212,7 @@ class BrsFile {
1121
1212
  }
1122
1213
  return results;
1123
1214
  }
1124
- let classToken = this.getTokenBefore(token, lexer_1.TokenKind.Class);
1215
+ let classToken = this.getTokenBefore(token, TokenKind_1.TokenKind.Class);
1125
1216
  if (classToken) {
1126
1217
  let cs = this.parser.references.classStatements.find((cs) => cs.classKeyword.range === classToken.range);
1127
1218
  if (cs === null || cs === void 0 ? void 0 : cs.parentClassName) {
@@ -1133,7 +1224,7 @@ class BrsFile {
1133
1224
  }
1134
1225
  return results;
1135
1226
  }
1136
- if (token.kind === lexer_1.TokenKind.StringLiteral) {
1227
+ if (token.kind === TokenKind_1.TokenKind.StringLiteral) {
1137
1228
  // We need to strip off the quotes but only if present
1138
1229
  const startIndex = textToSearchFor.startsWith('"') ? 1 : 0;
1139
1230
  let endIndex = textToSearchFor.length;
@@ -1153,7 +1244,7 @@ class BrsFile {
1153
1244
  results.push(vscode_languageserver_1.Location.create(uri, varDeclaration.nameRange));
1154
1245
  }
1155
1246
  }
1156
- if (this.tokenFollows(token, lexer_1.TokenKind.Goto)) {
1247
+ if (this.tokenFollows(token, TokenKind_1.TokenKind.Goto)) {
1157
1248
  for (const label of functionScope.labelStatements) {
1158
1249
  if (label.name.toLocaleLowerCase() === textToSearchFor) {
1159
1250
  const uri = util_1.util.pathToUri(this.pathAbsolute);
@@ -1170,7 +1261,7 @@ class BrsFile {
1170
1261
  continue;
1171
1262
  }
1172
1263
  filesSearched.add(file);
1173
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Dot && file.parseMode === parser_1.ParseMode.BrighterScript) {
1264
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Dot && file.parseMode === Parser_1.ParseMode.BrighterScript) {
1174
1265
  results.push(...this.getClassMemberDefinitions(textToSearchFor, file));
1175
1266
  const namespaceDefinition = this.getNamespaceDefinitions(token, file);
1176
1267
  if (namespaceDefinition) {
@@ -1214,14 +1305,15 @@ class BrsFile {
1214
1305
  return results;
1215
1306
  }
1216
1307
  getHover(position) {
1308
+ const fence = (code) => util_1.util.mdFence(code, 'brightscript');
1217
1309
  //get the token at the position
1218
1310
  let token = this.getTokenAt(position);
1219
1311
  let hoverTokenTypes = [
1220
- lexer_1.TokenKind.Identifier,
1221
- lexer_1.TokenKind.Function,
1222
- lexer_1.TokenKind.EndFunction,
1223
- lexer_1.TokenKind.Sub,
1224
- lexer_1.TokenKind.EndSub
1312
+ TokenKind_1.TokenKind.Identifier,
1313
+ TokenKind_1.TokenKind.Function,
1314
+ TokenKind_1.TokenKind.EndFunction,
1315
+ TokenKind_1.TokenKind.Sub,
1316
+ TokenKind_1.TokenKind.EndSub
1225
1317
  ];
1226
1318
  //throw out invalid tokens and the wrong kind of tokens
1227
1319
  if (!token || !hoverTokenTypes.includes(token.kind)) {
@@ -1247,7 +1339,7 @@ class BrsFile {
1247
1339
  return {
1248
1340
  range: token.range,
1249
1341
  //append the variable name to the front for scope
1250
- contents: typeText
1342
+ contents: fence(typeText)
1251
1343
  };
1252
1344
  }
1253
1345
  }
@@ -1255,7 +1347,7 @@ class BrsFile {
1255
1347
  if (labelStatement.name.toLocaleLowerCase() === lowerTokenText) {
1256
1348
  return {
1257
1349
  range: token.range,
1258
- contents: `${labelStatement.name}: label`
1350
+ contents: fence(`${labelStatement.name}: label`)
1259
1351
  };
1260
1352
  }
1261
1353
  }
@@ -1269,12 +1361,41 @@ class BrsFile {
1269
1361
  if (callable) {
1270
1362
  return {
1271
1363
  range: token.range,
1272
- contents: callable.type.toString()
1364
+ contents: this.getCallableDocumentation(callable)
1273
1365
  };
1274
1366
  }
1275
1367
  }
1276
1368
  }
1277
1369
  }
1370
+ /**
1371
+ * Build a hover documentation for a callable.
1372
+ */
1373
+ getCallableDocumentation(callable) {
1374
+ var _a;
1375
+ const comments = [];
1376
+ const tokens = callable.file.parser.tokens;
1377
+ const idx = tokens.indexOf((_a = callable.functionStatement) === null || _a === void 0 ? void 0 : _a.func.functionType);
1378
+ for (let i = idx - 1; i >= 0; i--) {
1379
+ const token = tokens[i];
1380
+ //skip whitespace and newline chars
1381
+ if (token.kind === TokenKind_1.TokenKind.Comment) {
1382
+ comments.push(token);
1383
+ }
1384
+ else if (token.kind === TokenKind_1.TokenKind.Newline || token.kind === TokenKind_1.TokenKind.Whitespace) {
1385
+ //skip these tokens
1386
+ continue;
1387
+ //any other token means there are no more comments
1388
+ }
1389
+ else {
1390
+ break;
1391
+ }
1392
+ }
1393
+ let result = util_1.util.mdFence(callable.type.toString(), 'brightscript');
1394
+ if (comments.length > 0) {
1395
+ result += '\n***\n' + comments.reverse().map(x => x.text.replace(/^('|rem)/i, '')).join('\n');
1396
+ }
1397
+ return result;
1398
+ }
1278
1399
  getSignatureHelpForNamespaceMethods(callableName, dottedGetText, scope) {
1279
1400
  var _a;
1280
1401
  if (!dottedGetText) {
@@ -1321,12 +1442,12 @@ class BrsFile {
1321
1442
  }
1322
1443
  }
1323
1444
  const kind = currentToken.kind;
1324
- if (kind === lexer_1.TokenKind.Comment) {
1445
+ if (kind === TokenKind_1.TokenKind.Comment) {
1325
1446
  // Strip off common leading characters to make it easier to read
1326
1447
  const commentText = currentToken.text.replace(/^[' *\/]+/, '');
1327
1448
  functionComments.unshift(commentText);
1328
1449
  }
1329
- else if (kind === lexer_1.TokenKind.Newline) {
1450
+ else if (kind === TokenKind_1.TokenKind.Newline) {
1330
1451
  if (functionComments.length === 0) {
1331
1452
  continue;
1332
1453
  }
@@ -1385,7 +1506,7 @@ class BrsFile {
1385
1506
  const classConstructor = this.getClassMethod(classStatement, 'new');
1386
1507
  let sigHelp = classConstructor ? this.getSignatureHelpForStatement(classConstructor) : undefined;
1387
1508
  if (sigHelp) {
1388
- sigHelp.key = classStatement.getName(parser_1.ParseMode.BrighterScript);
1509
+ sigHelp.key = classStatement.getName(Parser_1.ParseMode.BrighterScript);
1389
1510
  sigHelp.signature.label = sigHelp.signature.label.replace(/(function|sub) new/, sigHelp.key);
1390
1511
  }
1391
1512
  return sigHelp;
@@ -1462,7 +1583,7 @@ exports.BrsFile = BrsFile;
1462
1583
  * List of completions for all valid keywords/reserved words.
1463
1584
  * Build this list once because it won't change for the lifetime of this process
1464
1585
  */
1465
- exports.KeywordCompletions = Object.keys(lexer_1.Keywords)
1586
+ exports.KeywordCompletions = Object.keys(TokenKind_1.Keywords)
1466
1587
  //remove any keywords with whitespace
1467
1588
  .filter(x => !x.includes(' '))
1468
1589
  //create completions