brighterscript 0.41.4 → 0.43.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 (157) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/LanguageServer.js +0 -9
  3. package/dist/LanguageServer.js.map +1 -1
  4. package/dist/Program.d.ts +1 -1
  5. package/dist/Program.js +45 -10
  6. package/dist/Program.js.map +1 -1
  7. package/dist/ProgramBuilder.js +3 -3
  8. package/dist/ProgramBuilder.js.map +1 -1
  9. package/dist/Scope.d.ts +6 -5
  10. package/dist/Scope.js +33 -32
  11. package/dist/Scope.js.map +1 -1
  12. package/dist/astUtils/AstEditor.d.ts +27 -0
  13. package/dist/astUtils/AstEditor.js +97 -0
  14. package/dist/astUtils/AstEditor.js.map +1 -0
  15. package/dist/astUtils/AstEditor.spec.d.ts +1 -0
  16. package/dist/astUtils/AstEditor.spec.js +133 -0
  17. package/dist/astUtils/AstEditor.spec.js.map +1 -0
  18. package/dist/astUtils/reflection.d.ts +2 -1
  19. package/dist/astUtils/reflection.js +6 -2
  20. package/dist/astUtils/reflection.js.map +1 -1
  21. package/dist/astUtils/reflection.spec.js +6 -6
  22. package/dist/astUtils/reflection.spec.js.map +1 -1
  23. package/dist/astUtils/visitors.spec.js +13 -16
  24. package/dist/astUtils/visitors.spec.js.map +1 -1
  25. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +3 -3
  26. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  27. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js +4 -4
  28. package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.js.map +1 -1
  29. package/dist/files/BrsFile.Class.spec.js +182 -78
  30. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  31. package/dist/files/BrsFile.d.ts +11 -5
  32. package/dist/files/BrsFile.js +111 -77
  33. package/dist/files/BrsFile.js.map +1 -1
  34. package/dist/files/BrsFile.spec.js +150 -101
  35. package/dist/files/BrsFile.spec.js.map +1 -1
  36. package/dist/files/XmlFile.d.ts +1 -0
  37. package/dist/files/XmlFile.js +9 -8
  38. package/dist/files/XmlFile.js.map +1 -1
  39. package/dist/files/XmlFile.spec.js +42 -40
  40. package/dist/files/XmlFile.spec.js.map +1 -1
  41. package/dist/files/tests/imports.spec.js +6 -4
  42. package/dist/files/tests/imports.spec.js.map +1 -1
  43. package/dist/globalCallables.d.ts +3 -1
  44. package/dist/globalCallables.js +200 -100
  45. package/dist/globalCallables.js.map +1 -1
  46. package/dist/index.d.ts +12 -3
  47. package/dist/index.js +21 -4
  48. package/dist/index.js.map +1 -1
  49. package/dist/interfaces.d.ts +49 -4
  50. package/dist/lexer/Lexer.js +1 -2
  51. package/dist/lexer/Lexer.js.map +1 -1
  52. package/dist/lexer/Lexer.spec.js +462 -462
  53. package/dist/lexer/Lexer.spec.js.map +1 -1
  54. package/dist/parser/Expression.d.ts +1 -1
  55. package/dist/parser/Expression.js +10 -10
  56. package/dist/parser/Expression.js.map +1 -1
  57. package/dist/parser/Parser.Class.spec.js +32 -31
  58. package/dist/parser/Parser.Class.spec.js.map +1 -1
  59. package/dist/parser/Parser.d.ts +13 -1
  60. package/dist/parser/Parser.js +355 -253
  61. package/dist/parser/Parser.js.map +1 -1
  62. package/dist/parser/Parser.spec.js +86 -24
  63. package/dist/parser/Parser.spec.js.map +1 -1
  64. package/dist/parser/Statement.d.ts +2 -2
  65. package/dist/parser/Statement.js +8 -8
  66. package/dist/parser/Statement.js.map +1 -1
  67. package/dist/parser/Statement.spec.js +4 -4
  68. package/dist/parser/Statement.spec.js.map +1 -1
  69. package/dist/parser/tests/Parser.spec.d.ts +3 -3
  70. package/dist/parser/tests/Parser.spec.js +4 -4
  71. package/dist/parser/tests/Parser.spec.js.map +1 -1
  72. package/dist/parser/tests/controlFlow/For.spec.js +40 -40
  73. package/dist/parser/tests/controlFlow/For.spec.js.map +1 -1
  74. package/dist/parser/tests/controlFlow/ForEach.spec.js +22 -21
  75. package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
  76. package/dist/parser/tests/controlFlow/If.spec.js +100 -99
  77. package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
  78. package/dist/parser/tests/controlFlow/While.spec.js +25 -25
  79. package/dist/parser/tests/controlFlow/While.spec.js.map +1 -1
  80. package/dist/parser/tests/expression/Additive.spec.js +21 -21
  81. package/dist/parser/tests/expression/Additive.spec.js.map +1 -1
  82. package/dist/parser/tests/expression/ArrayLiterals.spec.js +91 -91
  83. package/dist/parser/tests/expression/ArrayLiterals.spec.js.map +1 -1
  84. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +102 -102
  85. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
  86. package/dist/parser/tests/expression/Boolean.spec.js +15 -15
  87. package/dist/parser/tests/expression/Boolean.spec.js.map +1 -1
  88. package/dist/parser/tests/expression/Call.spec.js +22 -21
  89. package/dist/parser/tests/expression/Call.spec.js.map +1 -1
  90. package/dist/parser/tests/expression/Exponential.spec.js +11 -11
  91. package/dist/parser/tests/expression/Exponential.spec.js.map +1 -1
  92. package/dist/parser/tests/expression/Function.spec.js +171 -171
  93. package/dist/parser/tests/expression/Function.spec.js.map +1 -1
  94. package/dist/parser/tests/expression/Indexing.spec.js +50 -50
  95. package/dist/parser/tests/expression/Indexing.spec.js.map +1 -1
  96. package/dist/parser/tests/expression/Multiplicative.spec.js +25 -25
  97. package/dist/parser/tests/expression/Multiplicative.spec.js.map +1 -1
  98. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +30 -18
  99. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  100. package/dist/parser/tests/expression/PrefixUnary.spec.js +26 -26
  101. package/dist/parser/tests/expression/PrefixUnary.spec.js.map +1 -1
  102. package/dist/parser/tests/expression/Primary.spec.js +27 -27
  103. package/dist/parser/tests/expression/Primary.spec.js.map +1 -1
  104. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +3 -2
  105. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  106. package/dist/parser/tests/expression/Relational.spec.js +25 -25
  107. package/dist/parser/tests/expression/Relational.spec.js.map +1 -1
  108. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +7 -7
  109. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  110. package/dist/parser/tests/expression/TernaryExpression.spec.js +6 -6
  111. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  112. package/dist/parser/tests/statement/AssignmentOperators.spec.js +15 -15
  113. package/dist/parser/tests/statement/AssignmentOperators.spec.js.map +1 -1
  114. package/dist/parser/tests/statement/Declaration.spec.js +20 -20
  115. package/dist/parser/tests/statement/Declaration.spec.js.map +1 -1
  116. package/dist/parser/tests/statement/Function.spec.js +121 -120
  117. package/dist/parser/tests/statement/Function.spec.js.map +1 -1
  118. package/dist/parser/tests/statement/Goto.spec.js +9 -8
  119. package/dist/parser/tests/statement/Goto.spec.js.map +1 -1
  120. package/dist/parser/tests/statement/Increment.spec.js +22 -22
  121. package/dist/parser/tests/statement/Increment.spec.js.map +1 -1
  122. package/dist/parser/tests/statement/InterfaceStatement.spec.js +12 -0
  123. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  124. package/dist/parser/tests/statement/LibraryStatement.spec.js +7 -7
  125. package/dist/parser/tests/statement/LibraryStatement.spec.js.map +1 -1
  126. package/dist/parser/tests/statement/Misc.spec.js +71 -70
  127. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  128. package/dist/parser/tests/statement/PrintStatement.spec.js +17 -17
  129. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  130. package/dist/parser/tests/statement/ReturnStatement.spec.js +33 -33
  131. package/dist/parser/tests/statement/ReturnStatement.spec.js.map +1 -1
  132. package/dist/parser/tests/statement/Set.spec.js +53 -53
  133. package/dist/parser/tests/statement/Set.spec.js.map +1 -1
  134. package/dist/parser/tests/statement/Stop.spec.js +7 -6
  135. package/dist/parser/tests/statement/Stop.spec.js.map +1 -1
  136. package/dist/preprocessor/Chunk.d.ts +1 -1
  137. package/dist/preprocessor/Preprocessor.d.ts +1 -1
  138. package/dist/preprocessor/Preprocessor.js +7 -7
  139. package/dist/preprocessor/Preprocessor.js.map +1 -1
  140. package/dist/util.d.ts +5 -1
  141. package/dist/util.js +43 -33
  142. package/dist/util.js.map +1 -1
  143. package/dist/validators/ClassValidator.js +20 -27
  144. package/dist/validators/ClassValidator.js.map +1 -1
  145. package/package.json +2 -1
  146. package/dist/astUtils/index.d.ts +0 -7
  147. package/dist/astUtils/index.js +0 -26
  148. package/dist/astUtils/index.js.map +0 -1
  149. package/dist/lexer/index.d.ts +0 -3
  150. package/dist/lexer/index.js +0 -18
  151. package/dist/lexer/index.js.map +0 -1
  152. package/dist/parser/index.d.ts +0 -3
  153. package/dist/parser/index.js +0 -16
  154. package/dist/parser/index.js.map +0 -1
  155. package/dist/preprocessor/index.d.ts +0 -3
  156. package/dist/preprocessor/index.js +0 -16
  157. 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,7 @@ 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;
40
41
  this.diagnostics = [];
41
42
  this.commentFlags = [];
42
43
  this.callables = [];
@@ -57,7 +58,7 @@ class BrsFile {
57
58
  //all BrighterScript files need to be transpiled
58
59
  if ((_a = this.extension) === null || _a === void 0 ? void 0 : _a.endsWith('.bs')) {
59
60
  this.needsTranspiled = true;
60
- this.parseMode = parser_1.ParseMode.BrighterScript;
61
+ this.parseMode = Parser_1.ParseMode.BrighterScript;
61
62
  }
62
63
  this.isTypedef = this.extension === '.d.bs';
63
64
  if (!this.isTypedef) {
@@ -149,7 +150,7 @@ class BrsFile {
149
150
  }
150
151
  //tokenize the input file
151
152
  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, {
153
+ return Lexer_1.Lexer.scan(fileContents, {
153
154
  includeWhitespace: false
154
155
  });
155
156
  });
@@ -172,32 +173,50 @@ class BrsFile {
172
173
  //if the preprocessor generated tokens, use them.
173
174
  let tokens = preprocessor.processedTokens.length > 0 ? preprocessor.processedTokens : lexer.tokens;
174
175
  this.program.logger.time(Logger_1.LogLevel.debug, ['parser.parse', chalk_1.default.green(this.pathAbsolute)], () => {
175
- this._parser = parser_1.Parser.parse(tokens, {
176
+ this._parser = Parser_1.Parser.parse(tokens, {
176
177
  mode: this.parseMode,
177
178
  logger: this.program.logger
178
179
  });
179
180
  });
180
181
  //absorb all lexing/preprocessing/parsing diagnostics
181
182
  this.diagnostics.push(...lexer.diagnostics, ...preprocessor.diagnostics, ...this._parser.diagnostics);
182
- //notify AST ready
183
- this.program.plugins.emit('afterFileParse', this);
184
183
  //extract all callables from this file
185
184
  this.findCallables();
186
185
  //find all places where a sub/function is being called
187
186
  this.findFunctionCalls();
188
- this.findAndValidateImportAndImportStatements();
187
+ //register all import statements for use in the rest of the program
188
+ this.registerImports();
189
189
  //attach this file to every diagnostic
190
190
  for (let diagnostic of this.diagnostics) {
191
191
  diagnostic.file = this;
192
192
  }
193
193
  }
194
194
  catch (e) {
195
- this._parser = new parser_1.Parser();
195
+ this._parser = new Parser_1.Parser();
196
196
  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
197
  }
198
198
  }
199
- findAndValidateImportAndImportStatements() {
200
- var _a;
199
+ registerImports() {
200
+ var _a, _b, _c, _d;
201
+ 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 : []) {
202
+ //register import statements
203
+ if ((0, reflection_1.isImportStatement)(statement) && statement.filePathToken) {
204
+ this.ownScriptImports.push({
205
+ filePathRange: statement.filePathToken.range,
206
+ pkgPath: util_1.util.getPkgPathFromTarget(this.pkgPath, statement.filePath),
207
+ sourceFile: this,
208
+ text: (_d = statement.filePathToken) === null || _d === void 0 ? void 0 : _d.text
209
+ });
210
+ }
211
+ }
212
+ }
213
+ validate() {
214
+ //only validate the file if it was actually parsed (skip files containing typedefs)
215
+ if (!this.hasTypedef) {
216
+ this.validateImportStatements();
217
+ }
218
+ }
219
+ validateImportStatements() {
201
220
  let topOfFileIncludeStatements = [];
202
221
  for (let stmt of this.ast.statements) {
203
222
  //skip comments
@@ -218,15 +237,6 @@ class BrsFile {
218
237
  ...this._parser.references.importStatements
219
238
  ];
220
239
  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
240
  //if this statement is not one of the top-of-file statements,
231
241
  //then add a diagnostic explaining that it is invalid
232
242
  if (!topOfFileIncludeStatements.includes(result)) {
@@ -284,7 +294,7 @@ class BrsFile {
284
294
  const processor = new CommentFlagProcessor_1.CommentFlagProcessor(this, ['rem', `'`], DiagnosticMessages_1.diagnosticCodes, [DiagnosticMessages_1.DiagnosticCodeMap.unknownDiagnosticCode]);
285
295
  this.commentFlags = [];
286
296
  for (let token of tokens) {
287
- if (token.kind === lexer_1.TokenKind.Comment) {
297
+ if (token.kind === TokenKind_1.TokenKind.Comment) {
288
298
  processor.tryAdd(token.text, token.range);
289
299
  }
290
300
  }
@@ -570,10 +580,10 @@ class BrsFile {
570
580
  //if cursor is within a comment, disable completions
571
581
  let currentToken = this.getTokenAt(position);
572
582
  const tokenKind = currentToken === null || currentToken === void 0 ? void 0 : currentToken.kind;
573
- if (tokenKind === lexer_1.TokenKind.Comment) {
583
+ if (tokenKind === TokenKind_1.TokenKind.Comment) {
574
584
  return [];
575
585
  }
576
- else if (tokenKind === lexer_1.TokenKind.StringLiteral || tokenKind === lexer_1.TokenKind.TemplateStringQuasi) {
586
+ else if (tokenKind === TokenKind_1.TokenKind.StringLiteral || tokenKind === TokenKind_1.TokenKind.TemplateStringQuasi) {
577
587
  const match = /^("?)(pkg|libpkg):/.exec(currentToken.text);
578
588
  if (match) {
579
589
  const [, openingQuote, fileProtocol] = match;
@@ -605,7 +615,7 @@ class BrsFile {
605
615
  let functionScope = this.getFunctionScopeAtPosition(position);
606
616
  if (!functionScope) {
607
617
  //we aren't in any function scope, so return the keyword completions and namespaces
608
- if (this.getTokenBefore(currentToken, lexer_1.TokenKind.New)) {
618
+ if (this.getTokenBefore(currentToken, TokenKind_1.TokenKind.New)) {
609
619
  // there's a new keyword, so only class types are viable here
610
620
  return [...this.getGlobalClassStatementCompletions(currentToken, this.parseMode)];
611
621
  }
@@ -614,17 +624,17 @@ class BrsFile {
614
624
  }
615
625
  }
616
626
  const classNameCompletions = this.getGlobalClassStatementCompletions(currentToken, this.parseMode);
617
- const newToken = this.getTokenBefore(currentToken, lexer_1.TokenKind.New);
627
+ const newToken = this.getTokenBefore(currentToken, TokenKind_1.TokenKind.New);
618
628
  if (newToken) {
619
629
  //we are after a new keyword; so we can only be namespaces or classes at this point
620
630
  result.push(...classNameCompletions);
621
631
  result.push(...namespaceCompletions);
622
632
  return result;
623
633
  }
624
- if (this.tokenFollows(currentToken, lexer_1.TokenKind.Goto)) {
634
+ if (this.tokenFollows(currentToken, TokenKind_1.TokenKind.Goto)) {
625
635
  return this.getLabelCompletion(functionScope);
626
636
  }
627
- if (this.isPositionNextToTokenKind(position, lexer_1.TokenKind.Dot)) {
637
+ if (this.isPositionNextToTokenKind(position, TokenKind_1.TokenKind.Dot)) {
628
638
  if (namespaceCompletions.length > 0) {
629
639
  //if we matched a namespace, after a dot, it can't be anything else but something from our namespace completions
630
640
  return namespaceCompletions;
@@ -670,7 +680,7 @@ class BrsFile {
670
680
  kind: (0, reflection_1.isFunctionType)(variable.type) ? vscode_languageserver_1.CompletionItemKind.Function : vscode_languageserver_1.CompletionItemKind.Variable
671
681
  });
672
682
  }
673
- if (this.parseMode === parser_1.ParseMode.BrighterScript) {
683
+ if (this.parseMode === Parser_1.ParseMode.BrighterScript) {
674
684
  //include the first part of namespaces
675
685
  let namespaces = scope.getAllNamespaceStatements();
676
686
  for (let stmt of namespaces) {
@@ -700,7 +710,7 @@ class BrsFile {
700
710
  let classStatement = this.getClassFromMReference(position, currentToken, functionScope);
701
711
  let results = new Map();
702
712
  if (classStatement) {
703
- let classes = scope.getClassHierarchy(classStatement.item.getName(parser_1.ParseMode.BrighterScript).toLowerCase());
713
+ let classes = scope.getClassHierarchy(classStatement.item.getName(Parser_1.ParseMode.BrighterScript).toLowerCase());
704
714
  for (let cs of classes) {
705
715
  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
716
  if (!results.has(member.name.text.toLowerCase())) {
@@ -716,21 +726,21 @@ class BrsFile {
716
726
  }
717
727
  getClassFromMReference(position, currentToken, functionScope) {
718
728
  let previousToken = this.getPreviousToken(currentToken);
719
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Dot) {
729
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Dot) {
720
730
  previousToken = this.getPreviousToken(previousToken);
721
731
  }
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)) {
732
+ 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
733
  return { item: this.parser.references.classStatements.find((cs) => util_1.util.rangeContains(cs.range, position)), file: this };
724
734
  }
725
735
  return undefined;
726
736
  }
727
737
  getGlobalClassStatementCompletions(currentToken, parseMode) {
728
738
  var _a;
729
- if (parseMode === parser_1.ParseMode.BrightScript) {
739
+ if (parseMode === Parser_1.ParseMode.BrightScript) {
730
740
  return [];
731
741
  }
732
742
  let results = new Map();
733
- let completionName = (_a = this.getPartialVariableName(currentToken, [lexer_1.TokenKind.New])) === null || _a === void 0 ? void 0 : _a.toLowerCase();
743
+ let completionName = (_a = this.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New])) === null || _a === void 0 ? void 0 : _a.toLowerCase();
734
744
  if (completionName === null || completionName === void 0 ? void 0 : completionName.includes('.')) {
735
745
  return [];
736
746
  }
@@ -752,21 +762,19 @@ class BrsFile {
752
762
  }
753
763
  getNamespaceCompletions(currentToken, parseMode, scope) {
754
764
  //BrightScript does not support namespaces, so return an empty list in that case
755
- if (parseMode === parser_1.ParseMode.BrightScript) {
765
+ if (parseMode === Parser_1.ParseMode.BrightScript) {
756
766
  return [];
757
767
  }
758
- let completionName = this.getPartialVariableName(currentToken, [lexer_1.TokenKind.New]);
768
+ let completionName = this.getPartialVariableName(currentToken, [TokenKind_1.TokenKind.New]);
759
769
  if (!completionName) {
760
770
  return [];
761
771
  }
762
772
  //remove any trailing identifer and then any trailing dot, to give us the
763
773
  //name of its immediate parent namespace
764
774
  let closestParentNamespaceName = completionName.replace(/\.([a-z0-9_]*)?$/gi, '');
765
- let newToken = this.getTokenBefore(currentToken, lexer_1.TokenKind.New);
766
- let namespaceLookup = scope.namespaceLookup;
775
+ let newToken = this.getTokenBefore(currentToken, TokenKind_1.TokenKind.New);
767
776
  let result = new Map();
768
- for (let key in namespaceLookup) {
769
- let namespace = namespaceLookup[key.toLowerCase()];
777
+ for (let [, namespace] of scope.namespaceLookup) {
770
778
  //completionName = "NameA."
771
779
  //completionName = "NameA.Na
772
780
  //NameA
@@ -774,8 +782,7 @@ class BrsFile {
774
782
  //NameA.NameB.NameC
775
783
  if (namespace.fullName.toLowerCase() === closestParentNamespaceName.toLowerCase()) {
776
784
  //add all of this namespace's immediate child namespaces, bearing in mind if we are after a new keyword
777
- for (let childKey in namespace.namespaces) {
778
- const ns = namespace.namespaces[childKey];
785
+ for (let [, ns] of namespace.namespaces) {
779
786
  if (!newToken || ns.statements.find((s) => (0, reflection_1.isClassStatement)(s))) {
780
787
  if (!result.has(ns.lastPartName)) {
781
788
  result.set(ns.lastPartName, {
@@ -814,11 +821,11 @@ class BrsFile {
814
821
  return undefined;
815
822
  }
816
823
  let location;
817
- const nameParts = this.getPartialVariableName(token, [lexer_1.TokenKind.New]).split('.');
824
+ const nameParts = this.getPartialVariableName(token, [TokenKind_1.TokenKind.New]).split('.');
818
825
  const endName = nameParts[nameParts.length - 1].toLowerCase();
819
826
  const namespaceName = nameParts.slice(0, -1).join('.').toLowerCase();
820
827
  const statementHandler = (statement) => {
821
- if (!location && statement.getName(parser_1.ParseMode.BrighterScript).toLowerCase() === namespaceName) {
828
+ if (!location && statement.getName(Parser_1.ParseMode.BrighterScript).toLowerCase() === namespaceName) {
822
829
  const namespaceItemStatementHandler = (statement) => {
823
830
  if (!location && statement.name.text.toLowerCase() === endName) {
824
831
  const uri = util_1.util.pathToUri(file.pathAbsolute);
@@ -844,7 +851,7 @@ class BrsFile {
844
851
  * Given a current token, walk
845
852
  */
846
853
  getPartialVariableName(currentToken, excludeTokens = null) {
847
- let identifierAndDotKinds = [lexer_1.TokenKind.Identifier, ...lexer_1.AllowedLocalIdentifiers, lexer_1.TokenKind.Dot];
854
+ let identifierAndDotKinds = [TokenKind_1.TokenKind.Identifier, ...TokenKind_1.AllowedLocalIdentifiers, TokenKind_1.TokenKind.Dot];
848
855
  //consume tokens backwards until we find something other than a dot or an identifier
849
856
  let tokens = [];
850
857
  const parser = this.parser;
@@ -870,17 +877,17 @@ class BrsFile {
870
877
  const previousToken = this.getPreviousToken(closestToken);
871
878
  const previousTokenKind = previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind;
872
879
  //next to matched token
873
- if (!closestToken || closestToken.kind === lexer_1.TokenKind.Eof) {
880
+ if (!closestToken || closestToken.kind === TokenKind_1.TokenKind.Eof) {
874
881
  return false;
875
882
  }
876
883
  else if (closestToken.kind === tokenKind) {
877
884
  return true;
878
885
  }
879
- else if (closestToken.kind === lexer_1.TokenKind.Newline || previousTokenKind === lexer_1.TokenKind.Newline) {
886
+ else if (closestToken.kind === TokenKind_1.TokenKind.Newline || previousTokenKind === TokenKind_1.TokenKind.Newline) {
880
887
  return false;
881
888
  //next to an identifier, which is next to token kind
882
889
  }
883
- else if (closestToken.kind === lexer_1.TokenKind.Identifier && previousTokenKind === tokenKind) {
890
+ else if (closestToken.kind === TokenKind_1.TokenKind.Identifier && previousTokenKind === tokenKind) {
884
891
  return true;
885
892
  }
886
893
  else {
@@ -891,7 +898,7 @@ class BrsFile {
891
898
  const index = this.parser.tokens.indexOf(currentToken);
892
899
  for (let i = index - 1; i >= 0; i--) {
893
900
  currentToken = this.parser.tokens[i];
894
- if (currentToken.kind === lexer_1.TokenKind.Newline) {
901
+ if (currentToken.kind === TokenKind_1.TokenKind.Newline) {
895
902
  break;
896
903
  }
897
904
  else if (currentToken.kind === tokenKind) {
@@ -911,7 +918,7 @@ class BrsFile {
911
918
  let tokens = [];
912
919
  for (let i = this.parser.tokens.indexOf(currentToken); direction === -1 ? i >= 0 : i === this.parser.tokens.length; i += direction) {
913
920
  currentToken = this.parser.tokens[i];
914
- if (currentToken.kind === lexer_1.TokenKind.Newline || currentToken.kind === tokenKind) {
921
+ if (currentToken.kind === TokenKind_1.TokenKind.Newline || currentToken.kind === tokenKind) {
915
922
  break;
916
923
  }
917
924
  tokens.push(currentToken);
@@ -937,7 +944,7 @@ class BrsFile {
937
944
  //find the first scope that contains this namespace
938
945
  let scopes = this.program.getScopesForFile(this);
939
946
  for (let scope of scopes) {
940
- if (scope.namespaceLookup[lowerName]) {
947
+ if (scope.namespaceLookup.has(lowerName)) {
941
948
  return true;
942
949
  }
943
950
  }
@@ -955,7 +962,7 @@ class BrsFile {
955
962
  if (lowerCalleeName) {
956
963
  let scopes = this.program.getScopesForFile(this);
957
964
  for (let scope of scopes) {
958
- let namespace = scope.namespaceLookup[namespaceName.toLowerCase()];
965
+ let namespace = scope.namespaceLookup.get(namespaceName.toLowerCase());
959
966
  if (namespace.functionStatements[lowerCalleeName]) {
960
967
  return true;
961
968
  }
@@ -1055,7 +1062,7 @@ class BrsFile {
1055
1062
  else {
1056
1063
  return;
1057
1064
  }
1058
- const name = (0, reflection_1.isClassFieldStatement)(statement) ? statement.name.text : statement.getName(parser_1.ParseMode.BrighterScript);
1065
+ const name = (0, reflection_1.isClassFieldStatement)(statement) ? statement.name.text : statement.getName(Parser_1.ParseMode.BrighterScript);
1059
1066
  return vscode_languageserver_1.DocumentSymbol.create(name, '', symbolKind, statement.range, statement.range, children);
1060
1067
  }
1061
1068
  /**
@@ -1089,9 +1096,9 @@ class BrsFile {
1089
1096
  else {
1090
1097
  return symbols;
1091
1098
  }
1092
- const name = statement.getName(parser_1.ParseMode.BrighterScript);
1099
+ const name = statement.getName(Parser_1.ParseMode.BrighterScript);
1093
1100
  const uri = util_1.util.pathToUri(this.pathAbsolute);
1094
- 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));
1101
+ 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));
1095
1102
  symbols.push(symbol);
1096
1103
  return symbols;
1097
1104
  }
@@ -1105,8 +1112,8 @@ class BrsFile {
1105
1112
  const token = this.getTokenAt(position);
1106
1113
  // 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
1107
1114
  let definitionTokenTypes = [
1108
- lexer_1.TokenKind.Identifier,
1109
- lexer_1.TokenKind.StringLiteral
1115
+ TokenKind_1.TokenKind.Identifier,
1116
+ TokenKind_1.TokenKind.StringLiteral
1110
1117
  ];
1111
1118
  //throw out invalid tokens and the wrong kind of tokens
1112
1119
  if (!token || !definitionTokenTypes.includes(token.kind)) {
@@ -1114,7 +1121,7 @@ class BrsFile {
1114
1121
  }
1115
1122
  let textToSearchFor = token.text.toLowerCase();
1116
1123
  const previousToken = this.getTokenAt({ line: token.range.start.line, character: token.range.start.character });
1117
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Callfunc) {
1124
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Callfunc) {
1118
1125
  for (const scope of this.program.getScopes()) {
1119
1126
  //to only get functions defined in interface methods
1120
1127
  const callable = scope.getAllCallables().find((c) => c.callable.name.toLowerCase() === textToSearchFor); // eslint-disable-line @typescript-eslint/no-loop-func
@@ -1124,7 +1131,7 @@ class BrsFile {
1124
1131
  }
1125
1132
  return results;
1126
1133
  }
1127
- let classToken = this.getTokenBefore(token, lexer_1.TokenKind.Class);
1134
+ let classToken = this.getTokenBefore(token, TokenKind_1.TokenKind.Class);
1128
1135
  if (classToken) {
1129
1136
  let cs = this.parser.references.classStatements.find((cs) => cs.classKeyword.range === classToken.range);
1130
1137
  if (cs === null || cs === void 0 ? void 0 : cs.parentClassName) {
@@ -1136,7 +1143,7 @@ class BrsFile {
1136
1143
  }
1137
1144
  return results;
1138
1145
  }
1139
- if (token.kind === lexer_1.TokenKind.StringLiteral) {
1146
+ if (token.kind === TokenKind_1.TokenKind.StringLiteral) {
1140
1147
  // We need to strip off the quotes but only if present
1141
1148
  const startIndex = textToSearchFor.startsWith('"') ? 1 : 0;
1142
1149
  let endIndex = textToSearchFor.length;
@@ -1156,7 +1163,7 @@ class BrsFile {
1156
1163
  results.push(vscode_languageserver_1.Location.create(uri, varDeclaration.nameRange));
1157
1164
  }
1158
1165
  }
1159
- if (this.tokenFollows(token, lexer_1.TokenKind.Goto)) {
1166
+ if (this.tokenFollows(token, TokenKind_1.TokenKind.Goto)) {
1160
1167
  for (const label of functionScope.labelStatements) {
1161
1168
  if (label.name.toLocaleLowerCase() === textToSearchFor) {
1162
1169
  const uri = util_1.util.pathToUri(this.pathAbsolute);
@@ -1173,7 +1180,7 @@ class BrsFile {
1173
1180
  continue;
1174
1181
  }
1175
1182
  filesSearched.add(file);
1176
- if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === lexer_1.TokenKind.Dot && file.parseMode === parser_1.ParseMode.BrighterScript) {
1183
+ if ((previousToken === null || previousToken === void 0 ? void 0 : previousToken.kind) === TokenKind_1.TokenKind.Dot && file.parseMode === Parser_1.ParseMode.BrighterScript) {
1177
1184
  results.push(...this.getClassMemberDefinitions(textToSearchFor, file));
1178
1185
  const namespaceDefinition = this.getNamespaceDefinitions(token, file);
1179
1186
  if (namespaceDefinition) {
@@ -1217,14 +1224,15 @@ class BrsFile {
1217
1224
  return results;
1218
1225
  }
1219
1226
  getHover(position) {
1227
+ const fence = (code) => util_1.util.mdFence(code, 'brightscript');
1220
1228
  //get the token at the position
1221
1229
  let token = this.getTokenAt(position);
1222
1230
  let hoverTokenTypes = [
1223
- lexer_1.TokenKind.Identifier,
1224
- lexer_1.TokenKind.Function,
1225
- lexer_1.TokenKind.EndFunction,
1226
- lexer_1.TokenKind.Sub,
1227
- lexer_1.TokenKind.EndSub
1231
+ TokenKind_1.TokenKind.Identifier,
1232
+ TokenKind_1.TokenKind.Function,
1233
+ TokenKind_1.TokenKind.EndFunction,
1234
+ TokenKind_1.TokenKind.Sub,
1235
+ TokenKind_1.TokenKind.EndSub
1228
1236
  ];
1229
1237
  //throw out invalid tokens and the wrong kind of tokens
1230
1238
  if (!token || !hoverTokenTypes.includes(token.kind)) {
@@ -1250,7 +1258,7 @@ class BrsFile {
1250
1258
  return {
1251
1259
  range: token.range,
1252
1260
  //append the variable name to the front for scope
1253
- contents: typeText
1261
+ contents: fence(typeText)
1254
1262
  };
1255
1263
  }
1256
1264
  }
@@ -1258,7 +1266,7 @@ class BrsFile {
1258
1266
  if (labelStatement.name.toLocaleLowerCase() === lowerTokenText) {
1259
1267
  return {
1260
1268
  range: token.range,
1261
- contents: `${labelStatement.name}: label`
1269
+ contents: fence(`${labelStatement.name}: label`)
1262
1270
  };
1263
1271
  }
1264
1272
  }
@@ -1272,21 +1280,47 @@ class BrsFile {
1272
1280
  if (callable) {
1273
1281
  return {
1274
1282
  range: token.range,
1275
- contents: callable.type.toString()
1283
+ contents: this.getCallableDocumentation(callable)
1276
1284
  };
1277
1285
  }
1278
1286
  }
1279
1287
  }
1280
1288
  }
1289
+ /**
1290
+ * Build a hover documentation for a callable.
1291
+ */
1292
+ getCallableDocumentation(callable) {
1293
+ const comments = [];
1294
+ const tokens = callable.file.parser.tokens;
1295
+ const idx = tokens.indexOf(callable.functionStatement.func.functionType);
1296
+ for (let i = idx - 1; i >= 0; i--) {
1297
+ const token = tokens[i];
1298
+ //skip whitespace and newline chars
1299
+ if (token.kind === TokenKind_1.TokenKind.Comment) {
1300
+ comments.push(token);
1301
+ }
1302
+ else if (token.kind === TokenKind_1.TokenKind.Newline || token.kind === TokenKind_1.TokenKind.Whitespace) {
1303
+ //skip these tokens
1304
+ continue;
1305
+ //any other token means there are no more comments
1306
+ }
1307
+ else {
1308
+ break;
1309
+ }
1310
+ }
1311
+ let result = util_1.util.mdFence(callable.type.toString(), 'brightscript');
1312
+ if (comments.length > 0) {
1313
+ result += '\n***\n' + comments.reverse().map(x => x.text.replace(/^('|rem)/i, '')).join('\n');
1314
+ }
1315
+ return result;
1316
+ }
1281
1317
  getSignatureHelpForNamespaceMethods(callableName, dottedGetText, scope) {
1282
1318
  var _a;
1283
1319
  if (!dottedGetText) {
1284
1320
  return [];
1285
1321
  }
1286
- let namespaceLookup = scope.namespaceLookup;
1287
1322
  let resultsMap = new Map();
1288
- for (let key in namespaceLookup) {
1289
- let namespace = namespaceLookup[key.toLowerCase()];
1323
+ for (let [, namespace] of scope.namespaceLookup) {
1290
1324
  //completionName = "NameA."
1291
1325
  //completionName = "NameA.Na
1292
1326
  //NameA
@@ -1326,12 +1360,12 @@ class BrsFile {
1326
1360
  }
1327
1361
  }
1328
1362
  const kind = currentToken.kind;
1329
- if (kind === lexer_1.TokenKind.Comment) {
1363
+ if (kind === TokenKind_1.TokenKind.Comment) {
1330
1364
  // Strip off common leading characters to make it easier to read
1331
1365
  const commentText = currentToken.text.replace(/^[' *\/]+/, '');
1332
1366
  functionComments.unshift(commentText);
1333
1367
  }
1334
- else if (kind === lexer_1.TokenKind.Newline) {
1368
+ else if (kind === TokenKind_1.TokenKind.Newline) {
1335
1369
  if (functionComments.length === 0) {
1336
1370
  continue;
1337
1371
  }
@@ -1390,7 +1424,7 @@ class BrsFile {
1390
1424
  const classConstructor = this.getClassMethod(classStatement, 'new');
1391
1425
  let sigHelp = classConstructor ? this.getSignatureHelpForStatement(classConstructor) : undefined;
1392
1426
  if (sigHelp) {
1393
- sigHelp.key = classStatement.getName(parser_1.ParseMode.BrighterScript);
1427
+ sigHelp.key = classStatement.getName(Parser_1.ParseMode.BrighterScript);
1394
1428
  sigHelp.signature.label = sigHelp.signature.label.replace(/(function|sub) new/, sigHelp.key);
1395
1429
  }
1396
1430
  return sigHelp;
@@ -1467,7 +1501,7 @@ exports.BrsFile = BrsFile;
1467
1501
  * List of completions for all valid keywords/reserved words.
1468
1502
  * Build this list once because it won't change for the lifetime of this process
1469
1503
  */
1470
- exports.KeywordCompletions = Object.keys(lexer_1.Keywords)
1504
+ exports.KeywordCompletions = Object.keys(TokenKind_1.Keywords)
1471
1505
  //remove any keywords with whitespace
1472
1506
  .filter(x => !x.includes(' '))
1473
1507
  //create completions